[prev in list] [next in list] [prev in thread] [next in thread] 

List:       freedesktop-xdg
Subject:    Re: Proposal for better handling of mimetype icon themeing
From:       Alexander Larsson <alexl () redhat ! com>
Date:       2006-02-07 14:08:32
Message-ID: 1139321312.4927.2.camel () greebo
[Download RAW message or body]

On Mon, 2006-02-06 at 15:42 -0500, Rodney Dawes wrote:
> On Mon, 2006-02-06 at 15:02 +0100, Alexander Larsson wrote:
> > We're in violent agreement then. Lets start by getting the actual spec
> > additions in, and then we can start using it in steps. 
> 
> Yes we are. Indeed. Getting all this in and working, whether it be quick
> and bloodletting, or in little steps, would be great. :)

I got the list-lookup function into the icon theme spec:
http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html

I've attached an updated patch for the specification and
update-mime-database. I don't have write permissions to this module, and
I'd like someone to review the code changes.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Alexander Larsson                                            Red Hat, Inc 
                   alexl@redhat.com    alla@lysator.liu.se 
He's a leather-clad arachnophobic vagrant in drag. She's a disco-crazy 
cat-loving wrestler trying to make a difference in a man's world. They fight 
crime! 

["generic-icons.patch" (generic-icons.patch)]

Index: shared-mime-info-spec.xml
===================================================================
RCS file: /cvs/mime/shared-mime-info/shared-mime-info-spec.xml,v
retrieving revision 1.56
diff -u -p -r1.56 shared-mime-info-spec.xml
--- shared-mime-info-spec.xml	1 Dec 2005 18:53:26 -0000	1.56
+++ shared-mime-info-spec.xml	7 Feb 2006 13:59:23 -0000
@@ -1,8 +1,8 @@
 <?xml version="1.0" standalone="no"?>
 <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
 "/usr/share/sgml/docbook/dtd/xml/4.1.2/docbookx.dtd" [
-  <!ENTITY updated "1 December 2005">
-  <!ENTITY version "0.15">
+  <!ENTITY updated "7 February 2006">
+  <!ENTITY version "0.16">
 ]>
 <article id="index">
 
@@ -171,6 +171,9 @@ The files created by <command>update-mim
 <filename>&lt;MIME&gt;/aliases</filename> (contains a mapping from aliases to MIME \
types)  </para></listitem>
 				<listitem><para>
+<filename>&lt;MIME&gt;/generic_icons</filename> (contains a mapping from MIME types \
to generic icon names used for the type) +				</para></listitem>
+				<listitem><para>
 <filename>&lt;MIME&gt;/XMLnamespaces</filename> (contains a mapping from XML
 (namespaceURI, localName) pairs to MIME types)
 				</para></listitem>
@@ -180,7 +183,7 @@ type, giving details about the type)
 				</para></listitem>
 				<listitem><para>
 <filename>&lt;MIME&gt;/mime.cache</filename> (contains the same information as the \
                <filename>globs</filename>,
-<filename>magic</filename>, <filename>subclasses</filename>, \
<filename>aliases</filename> and  +<filename>magic</filename>, \
<filename>subclasses</filename>, <filename>aliases</filename>, \
<filename>generic_icons</filename> and   <filename>XMLnamespaces</filename> files, in \
a binary, mmappable format)  </para></listitem>
 			</itemizedlist>
@@ -313,6 +316,14 @@ of the document element.
 If <userinput>localName</userinput> is present but empty then the document element \
may have  any name, but the namespace must still match.
 				</para></listitem>
+				<listitem><para>
+<userinput>generic-icon</userinput> contains any number of \
<userinput>icon</userinput> elements +which have <userinput>name</userinput> \
attributes. Icon names for mimetypes are typically generated +directly from the mime \
type name by replacing "/" in the mimetype (so application/x-bzip would be \
+application-x-bzip). However, in many cases you don't want to have a specific icon \
for each mimetype, +since that would mean a lot of icons. If you specify a list of \
generic icons these will be used in +order for the icon if the exact mimetype icon \
doesn't exist. +				</para></listitem>
 			</itemizedlist>
 Applications may also define their own elements, provided they are namespaced to \
prevent collisions.  Unknown elements are copied directly to the output XML files \
like <userinput>comment</userinput> @@ -522,13 +533,13 @@ the namespaceURI.
 		<para>
 The <filename>mime.cache</filename> files contain the same information as the 
 <filename>globs</filename>, <filename>magic</filename>, \
                <filename>subclasses</filename>, 
-<filename>aliases</filename> and <filename>XMLnamespaces</filename> files, in a \
                binary, 
-mmappable format:
+<filename>aliases</filename>, <filename>generic_icons</filename> and \
<filename>XMLnamespaces</filename> +files, in a binary, mmappable format:
 </para>
 <programlisting>
 Header:
 2			CARD16		MAJOR_VERSION	1	
-2			CARD16		MINOR_VERSION	0	
+2			CARD16		MINOR_VERSION	1	
 4			CARD32		ALIAS_LIST_OFFSET
 4			CARD32		PARENT_LIST_OFFSET
 4			CARD32		LITERAL_LIST_OFFSET
@@ -536,6 +547,8 @@ Header:
 4			CARD32		GLOB_LIST_OFFSET
 4			CARD32		MAGIC_LIST_OFFSET
 4			CARD32		NAMESPACE_LIST_OFFSET
+# New Since MINOR_VERSION 1:
+4			CARD32		GENERIC_ICONS_LIST_OFFSET
 
 AliasList:
 4			CARD32		N_ALIASES
@@ -612,6 +625,19 @@ NamespaceEntry:
 4			CARD32		NAMESPACE_URI_OFFSET
 4			CARD32		LOCAL_NAME_OFFSET
 4			CARD32		MIME_TYPE_OFFSET
+
+GenericIconList: (Only exists if MINOR_VERSION >= 1)
+4			CARD32		N_ENTRIES 
+8*N_ENTRIES		GenericIconEntry
+
+GenericIconEntry:
+4			CARD32		MIME_TYPE_OFFSET
+4			CARD32		ICONS_OFFSET
+
+Icons:
+4			CARD32		N_ICONS
+4*N_ICONS		CARD32		ICON_NAME_OFFSET
+
 </programlisting>
 <para>
 Lists in the file are sorted, to enable binary searching. The list of 
Index: update-mime-database.c
===================================================================
RCS file: /cvs/mime/shared-mime-info/update-mime-database.c,v
retrieving revision 1.39
diff -u -p -r1.39 update-mime-database.c
--- update-mime-database.c	19 Dec 2005 16:37:47 -0000	1.39
+++ update-mime-database.c	7 Feb 2006 13:59:23 -0000
@@ -100,6 +100,10 @@ static GHashTable *subclass_hash = NULL;
 /* Maps aliases to Types */
 static GHashTable *alias_hash = NULL;
 
+/* Maps mimetypes to generic icons list */
+static GHashTable *icons_hash = NULL;
+
+
 /* Static prototypes */
 static Magic *magic_new(xmlNode *node, Type *type, GError **error);
 
@@ -261,6 +265,52 @@ static void add_namespace(Type *type, co
 			type);
 }
 
+/* Turn the list of child nodes of 'parent' into a list of Matches */
+static GList *build_generic_icons(xmlNode *parent, GError **error)
+{
+	xmlNode *node;
+	GList *out = NULL;
+
+	g_return_val_if_fail(error != NULL, NULL);
+
+	for (node = parent->xmlChildrenNode; node; node = node->next)
+	{
+		char *name;
+
+		if (node->type != XML_ELEMENT_NODE)
+			continue;
+
+		if (node->ns == NULL || xmlStrcmp(node->ns->href, FREE_NS) != 0)
+		{
+			g_set_error(error, MIME_ERROR, 0,
+				_("Element found with non-freedesktop.org "
+				  "namespace"));
+			break;
+		}
+
+		if (strcmp((char *)node->name, "icon") != 0)
+		{
+			g_set_error(error, MIME_ERROR, 0,
+				_("Expected <icon> element, but found "
+				  "<%s> instead"), node->name);
+			break;
+		}
+
+		name = my_xmlGetNsProp(node, "name", NULL);
+		if (name == NULL || !*name)
+	        {
+			g_set_error(error, MIME_ERROR, 0, "Missing 'name' attribute in <icon> element");
+			break;
+		}
+
+		out = g_list_append(out, g_strdup (name));
+		xmlFree(name);
+	}
+
+	return out;
+}
+
+
 /* 'field' was found in the definition of 'type' and has the freedesktop.org
  * namespace. If it's a known field, process it and return TRUE, else
  * return FALSE to add it to the output XML document.
@@ -306,6 +356,21 @@ static gboolean process_freedesktop_node
 		else
 			g_return_val_if_fail(magic == NULL, FALSE);
 	}
+	else if (strcmp((char *)field->name, "generic-icons") == 0)
+	{
+		GList *list;
+
+		list = build_generic_icons (field, error);
+
+		if (list) {
+			char *typename;
+
+			typename = g_strdup_printf("%s/%s", 
+						   type->media,
+						   type->subtype);
+			g_hash_table_insert(icons_hash, typename, list);
+		}
+	}
 	else if (strcmp((char *)field->name, "comment") == 0 ||
 		 strcmp((char *)field->name, "acronym") == 0 ||
 		 strcmp((char *)field->name, "expanded-acronym") == 0)
@@ -1455,6 +1520,57 @@ static void write_aliases(FILE *stream)
 	g_ptr_array_free(lines, TRUE);
 }
 
+/* Extract one entry from icons_hash and put it in the GPtrArray so
+ * we can sort it.
+ */
+static void add_icon(gpointer key, gpointer value, gpointer data)
+{
+	GString *s;
+	GPtrArray *lines = (GPtrArray *) data;
+	const gchar *type = (gchar *) key;
+	GList *icons = (GList *) value;
+	GList *l;
+
+	s = g_string_new (type);
+	g_string_append (s, ":");
+
+	for (l = icons; l != NULL; l = l->next)
+	  {
+	    g_string_append (s, l->data);
+	    if (l->next != NULL)
+	      g_string_append (s, ",");
+	  }
+	g_string_append (s, "\n");
+	
+	g_ptr_array_add(lines, g_string_free (s, FALSE));
+}
+
+
+/* Write all the collected generic icons */
+static void write_generic_icons(FILE *stream)
+{
+	GPtrArray *lines;
+	int i;
+	
+	lines = g_ptr_array_new();
+
+	g_hash_table_foreach(icons_hash, add_icon, lines);
+
+	g_ptr_array_sort(lines, strcmp2);
+
+	for (i = 0; i < lines->len; i++)
+	{
+		char *line = (char *) lines->pdata[i];
+
+		fwrite(line, 1, strlen(line), stream);
+
+		g_free(line);
+	}
+
+	g_ptr_array_free(lines, TRUE);
+}
+
+
 
 /* Issue a warning if 'path' won't be found by applications */
 static void check_in_path_xdg_data(const char *mime_path)
@@ -1577,7 +1693,7 @@ write_card32 (FILE *cache, guint32 n)
 }
 
 #define MAJOR_VERSION 1
-#define MINOR_VERSION 0
+#define MINOR_VERSION 1
 
 static gboolean
 write_header (FILE *cache,   
@@ -1588,9 +1704,10 @@ write_header (FILE *cache,   
 	      gint  glob_offset,
 	      gint  magic_offset,
 	      gint  namespace_offset,
+	      gint  icons_offset,
 	      guint *offset)
 {
-  *offset = 32;
+  *offset = 36;
 
   return (write_card16 (cache, MAJOR_VERSION) &&
 	  write_card16 (cache, MINOR_VERSION) &&
@@ -1600,10 +1717,10 @@ write_header (FILE *cache,   
 	  write_card32 (cache, suffix_offset) &&
 	  write_card32 (cache, glob_offset) &&
 	  write_card32 (cache, magic_offset) &&
-	  write_card32 (cache, namespace_offset));
+	  write_card32 (cache, namespace_offset) &&
+	  write_card32 (cache, icons_offset));
 }
 
-
 typedef gboolean (FilterFunc) (gpointer key);
 typedef gchar ** (GetValueFunc) (gpointer data, gchar *key);
 
@@ -1746,17 +1863,27 @@ write_alias_cache (FILE       *cache, 
 {
   return write_map (cache, strings, alias_hash, NULL, get_type_value, offset);
 }
-		   
+
+typedef struct
+{
+  FILE         *cache;
+  GHashTable   *pool;
+  guint         offset;
+  GHashTable   *hash_table;
+  gpointer      data;
+  gboolean      error;
+} ListMapData;
+
 static void
-write_parent_entry (gpointer key,
-		    gpointer data)
+write_list_map_entry (gpointer key,
+		      gpointer data)
 {
   gchar *mimetype = (gchar *)key;
-  MapData *map_data = (MapData *)data;
-  guint parents_offset, offset;
-  GList *parents;
+  ListMapData *map_data = (ListMapData *)data;
+  guint list_offset, offset;
+  GList *list;
 
-  parents = (GList *)g_hash_table_lookup (subclass_hash, mimetype);
+  list = (GList *)g_hash_table_lookup (map_data->hash_table, mimetype);
   offset = GPOINTER_TO_UINT (g_hash_table_lookup (map_data->pool, mimetype));
   if (offset == 0)
     {
@@ -1764,36 +1891,36 @@ write_parent_entry (gpointer key,
       map_data->error = TRUE;  
     }
 
-  parents_offset = map_data->offset;
-  map_data->offset += 4 + 4 * g_list_length (parents);
+  list_offset = map_data->offset;
+  map_data->offset += 4 + 4 * g_list_length (list);
 
   if (!write_card32 (map_data->cache, offset) ||
-      !write_card32 (map_data->cache, parents_offset))
+      !write_card32 (map_data->cache, list_offset))
     map_data->error = TRUE;
 }
 
 static void
-write_parent_list (gpointer key,
-		   gpointer data)
+write_list_map_list (gpointer key,
+		     gpointer data)
 {
   gchar *mimetype = (gchar *)key;
-  MapData *map_data = (MapData *)data;
+  ListMapData *map_data = (ListMapData *)data;
   guint offset;
-  GList *parents, *p;
+  GList *items, *p;
 
-  parents = (GList *)g_hash_table_lookup (subclass_hash, mimetype);
+  items = (GList *)g_hash_table_lookup (map_data->hash_table, mimetype);
 
-  if (!write_card32 (map_data->cache, g_list_length (parents)))
+  if (!write_card32 (map_data->cache, g_list_length (items)))
     map_data->error = TRUE;
 
-  for (p = parents; p; p = p->next)
+  for (p = items; p; p = p->next)
     {
-      gchar *parent = (gchar *)p->data;
+      gchar *item = (gchar *)p->data;
       
-      offset = GPOINTER_TO_UINT (g_hash_table_lookup (map_data->pool, parent));
+      offset = GPOINTER_TO_UINT (g_hash_table_lookup (map_data->pool, item));
       if (offset == 0)
 	{
-	  g_printerr ("Missing string: '%s'\n", parent);
+	  g_printerr ("Missing string: '%s'\n", item);
 	  map_data->error = TRUE;  
 	}
       
@@ -1801,23 +1928,24 @@ write_parent_list (gpointer key,
 	map_data->error = TRUE;
     }
 
-  map_data->offset += 4 + 4 * g_list_length (parents);
+  map_data->offset += 4 + 4 * g_list_length (items);
 }
 
 static gboolean
-write_parent_cache (FILE       *cache,
-		    GHashTable *strings,
-		    guint      *offset)
+write_list_map (FILE       *cache,
+		GHashTable *strings,
+		guint      *offset,
+		GHashTable   *map)
 {
   GPtrArray *keys;
-  MapData map_data;
+  ListMapData map_data;
   FilterData filter_data;
 
   keys = g_ptr_array_new ();
 
   filter_data.keys = keys;
   filter_data.filter = NULL;
-  g_hash_table_foreach (subclass_hash, add_key, &filter_data);
+  g_hash_table_foreach (map, add_key, &filter_data);
 
   g_ptr_array_sort (keys, strcmp2);
 
@@ -1828,17 +1956,27 @@ write_parent_cache (FILE       *cache,
   map_data.pool = strings;
   map_data.offset = *offset + 4 + keys->len * 8;
   map_data.error = FALSE;
+  map_data.hash_table = map;
 
-  g_ptr_array_foreach (keys, write_parent_entry, &map_data);
+  g_ptr_array_foreach (keys, write_list_map_entry, &map_data);
 
   map_data.offset = *offset + 4 + keys->len * 8;
-  g_ptr_array_foreach (keys, write_parent_list, &map_data);
+  g_ptr_array_foreach (keys, write_list_map_list, &map_data);
 
   *offset = map_data.offset;
 
   return !map_data.error;
 }
 
+static gboolean
+write_parent_cache (FILE       *cache,
+		    GHashTable *strings,
+		    guint      *offset)
+{
+  return write_list_map (cache, strings, offset, subclass_hash);
+}
+
+
 typedef enum 
 {
   GLOB_LITERAL,
@@ -2387,6 +2525,17 @@ write_namespace_cache (FILE       *cache
 		    get_namespace_value, offset); 
 }
 
+
+
+static gboolean
+write_icons_cache (FILE       *cache,
+		   GHashTable *strings,
+		   guint      *offset)
+{
+  return write_list_map (cache, strings, offset, icons_hash);
+}
+
+
 static void
 collect_alias (gpointer key,
 	       gpointer value,
@@ -2485,6 +2634,21 @@ collect_namespace (gpointer key,
     g_hash_table_insert (strings, ns, NULL);
 }
 
+static void
+collect_icons (gpointer key,
+	       gpointer value,
+	       gpointer data)
+{
+  gchar *type = (gchar *) key;
+  GList *icons = (GList *) value;
+  GHashTable *strings = (GHashTable *)data;
+  GList *l;
+
+  g_hash_table_insert (strings, type, NULL);
+  for (l = icons; l != NULL; l = l->next)
+    g_hash_table_insert (strings, l->data, NULL);
+}
+
 
 static void
 collect_strings (GHashTable *strings)
@@ -2494,6 +2658,7 @@ collect_strings (GHashTable *strings)
   g_hash_table_foreach (globs_hash, collect_glob, strings); 
   g_ptr_array_foreach (magic_array, collect_magic, strings); 
   g_hash_table_foreach (namespace_hash, collect_namespace, strings); 
+  g_hash_table_foreach (icons_hash, collect_icons, strings); 
 }
 
 typedef struct 
@@ -2550,11 +2715,12 @@ write_cache (FILE *cache)
   guint glob_offset;
   guint magic_offset;
   guint namespace_offset;
+  guint icons_offset;
   guint offset;
   GHashTable *strings;
 
   offset = 0;
-  if (!write_header (cache, 0, 0, 0, 0, 0, 0, 0, &offset))
+  if (!write_header (cache, 0, 0, 0, 0, 0, 0, 0, 0, &offset))
     {
       g_printerr ("Failed to write header\n");
       return FALSE;
@@ -2628,13 +2794,22 @@ write_cache (FILE *cache)
     }
   g_print ("Wrote namespace list at %x - %x\n", namespace_offset, offset);
 
+  icons_offset = offset;
+  if (!write_icons_cache (cache, strings, &offset))
+    {
+      g_printerr ("Failed to write icons list\n");
+      return FALSE;
+    }
+  g_print ("Wrote icons list at %x - %x\n", icons_offset, offset);
+
+
   rewind (cache);
   offset = 0; 
 
   if (!write_header (cache, 
 		     alias_offset, parent_offset, literal_offset,
 		     suffix_offset, glob_offset, magic_offset, 
-		     namespace_offset, &offset))
+		     namespace_offset, icons_offset, &offset))
     {
       g_printerr ("Failed to rewrite header\n");
       return FALSE;
@@ -2721,6 +2896,8 @@ int main(int argc, char **argv)
 					      g_free, free_string_list);
 	alias_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
 					   g_free, NULL);
+	icons_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+					   g_free, free_string_list);
 	
 	scan_source_dir(package_dir);
 	g_free(package_dir);
@@ -2824,6 +3001,22 @@ int main(int argc, char **argv)
 		FILE *stream;
 		char *path;
 		
+		path = g_strconcat(mime_dir, "/generic_icons.new", NULL);
+		stream = fopen(path, "wb");
+		if (!stream)
+			g_error("Failed to open '%s' for writing\n",
+				path);
+		
+		write_generic_icons(stream);
+
+		atomic_update(path);
+		g_free(path);
+	}
+
+	{
+		FILE *stream;
+		char *path;
+		
 		path = g_strconcat(mime_dir, "/mime.cache.new", NULL);
 		stream = fopen(path, "wb");
 		if (!stream)



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic