[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><MIME>/aliases</filename> (contains a mapping from aliases to MIME \
types) </para></listitem>
<listitem><para>
+<filename><MIME>/generic_icons</filename> (contains a mapping from MIME types \
to generic icon names used for the type) + </para></listitem>
+ <listitem><para>
<filename><MIME>/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><MIME>/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