Index: tests/test_gicon.py
===================================================================
--- tests/test_gicon.py	(revision 0)
+++ tests/test_gicon.py	(revision 0)
@@ -0,0 +1,94 @@
+# -*- Mode: Python -*-
+
+import os
+import unittest
+
+from common import gio, glib
+
+
+class TestIcon(unittest.TestCase):
+    def test_eq(self):
+        self.assertEquals(gio.File('foo.png').icon_new(),
+                          gio.File('foo.png').icon_new())
+        self.assertEquals(gio.ThemedIcon('foo'),
+                          gio.ThemedIcon('foo'))
+
+        self.assertNotEqual(gio.File('foo.png').icon_new(),
+                            gio.File('bar.png').icon_new())
+        self.assertNotEquals(gio.ThemedIcon('foo'),
+                             gio.ThemedIcon('bar'))
+        self.assertNotEquals(gio.File('foo.png').icon_new(),
+                             gio.ThemedIcon('foo'))
+
+    def test_hash(self):
+        self.assertEquals(hash(gio.File('foo.png').icon_new()),
+                          hash(gio.File('foo.png').icon_new()))
+        self.assertEquals(hash(gio.ThemedIcon('foo')),
+                          hash(gio.ThemedIcon('foo')))
+
+class TestLoadableIcon(unittest.TestCase):
+    def setUp(self):
+        self.file = open('temp.svg', 'w')
+        self.svg = ('<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
+                    '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" '
+                    '"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">'
+                    '<svg width="32" height="32"/>')
+        self.file.write(self.svg)
+        self.file.close()
+        self.icon = gio.File('temp.svg').icon_new()
+
+    def tearDown(self):
+        if os.path.exists('temp.svg'):
+            os.unlink('temp.svg')
+
+    def test_load(self):
+        stream, type = self.icon.load()
+        try:
+            self.assert_(isinstance(stream, gio.InputStream))
+            self.assertEquals(self.svg, stream.read())
+        finally:
+            stream.close()
+
+    def test_load_async(self):
+        def callback(icon, result):
+            try:
+                stream, type = icon.load_finish(result)
+                self.assert_(isinstance(stream, gio.InputStream))
+                self.assertEquals(self.svg, stream.read())
+            finally:
+                loop.quit()
+                stream.close()
+
+        self.icon.load_async(callback)
+
+        loop = glib.MainLoop()
+        loop.run()
+
+class TestThemedIcon(unittest.TestCase):
+    def setUp(self):
+        self.icon = gio.ThemedIcon("open")
+
+    def test_constructor(self):
+        icon = gio.ThemedIcon('foo')
+        self.assertEquals(['foo'], icon.props.names)
+        self.assert_(not icon.props.use_default_fallbacks)
+
+        icon = gio.ThemedIcon(['foo', 'bar', 'baz'])
+        self.assertEquals(['foo', 'bar', 'baz'], icon.props.names)
+        self.assert_(not icon.props.use_default_fallbacks)
+
+        icon = gio.ThemedIcon('xxx-yyy-zzz', True)
+        self.assertEquals(['xxx-yyy-zzz', 'xxx-yyy', 'xxx'], icon.props.names)
+        self.assert_(icon.props.use_default_fallbacks)
+
+    def test_constructor_illegals(self):
+        self.assertRaises(TypeError, lambda: gio.ThemedIcon(42))
+        self.assertRaises(TypeError, lambda: gio.ThemedIcon(['foo', 42, 'bar']))
+
+    def test_get_names(self):
+        self.assertEquals(self.icon.get_names(), ['open'])
+
+    def test_append_name(self):
+        self.assertEquals(self.icon.get_names(), ['open'])
+        self.icon.append_name('close')
+        self.assertEquals(self.icon.get_names(), ['open', 'close'])
Index: tests/test_gio.py
===================================================================
--- tests/test_gio.py	(revision 930)
+++ tests/test_gio.py	(working copy)
@@ -568,19 +568,6 @@
         self.failUnless(isinstance(icon, gio.Icon))
 
 
-class TestThemedIcon(unittest.TestCase):
-    def setUp(self):
-        self.icon = gio.ThemedIcon("open")
-
-    def testGetNames(self):
-        self.assertEquals(self.icon.get_names(), ['open'])
-
-    def testAppendName(self):
-        self.assertEquals(self.icon.get_names(), ['open'])
-        self.icon.append_name('close')
-        self.assertEquals(self.icon.get_names(), ['open', 'close'])
-
-
 class TestContentTypeGuess(unittest.TestCase):
     def testFromName(self):
         mime_type = gio.content_type_guess('diagram.svg')
Index: tests/Makefile.am
===================================================================
--- tests/Makefile.am	(revision 930)
+++ tests/Makefile.am	(working copy)
@@ -24,6 +24,7 @@
 tests =  \
 	test_conversion.py \
 	test_enum.py \
+	test_gicon.py \
 	test_gio.py \
 	test_gobject.py \
 	test_gtype.py \
Index: gio/gio.override
===================================================================
--- gio/gio.override	(revision 930)
+++ gio/gio.override	(working copy)
@@ -38,8 +38,6 @@
     PyObject *data;
 } PyGIONotify;
 
-static PyObject *  pygio_do_icon_richcompare(PyGObject *self, PyGObject *other, int op);
-
 static void
 py_decref_callback (gpointer data)
 {
@@ -88,6 +86,7 @@
   gfileattribute.override
   gfileenumerator.override
   gfileinfo.override
+  gicon.override
   ginputstream.override
   goutputstream.override
   gvolume.override
@@ -101,7 +100,6 @@
   g_file_new_from_path
   g_file_new_from_uri
   g_file_hash
-  g_icon_hash
   g_input_stream_read_all
   g_io_error_quark
   g_simple_async_result_new_error
@@ -181,151 +179,6 @@
   return ret;
 }
 %%
-override-slot GIcon.tp_richcompare
-static PyObject *
-pygio_do_icon_richcompare(PyGObject *self, PyGObject *other, int op)
-{
-    PyObject *result;
-
-    if (PyObject_TypeCheck(self, &PyGIcon_Type)
-        && PyObject_TypeCheck(other, &PyGIcon_Type)) {
-        GIcon *icon1 = G_ICON(self->obj);
-        GIcon *icon2 = G_ICON(other->obj);
-
-        switch (op) {
-        case Py_EQ:
-            result = (g_icon_equal(icon1, icon2)
-                      ? Py_True : Py_False);
-            break;
-        case Py_NE:
-            result = (!g_icon_equal(icon1, icon2)
-                      ? Py_True : Py_False);
-            break;
-        default:
-            result = Py_NotImplemented;
-        }
-    }
-    else
-        result = Py_NotImplemented;
-
-    Py_INCREF(result);
-    return result;
-}
-static PyObject *
-_wrap_g_icon_tp_richcompare(PyGObject *self, PyGObject *other, int op)
-{
-    return pygio_do_icon_richcompare(self, other, op);
-}
-%%
-override-slot GIcon.tp_hash
-static long
-_wrap_g_icon_tp_hash(PyGObject *self)
-{
-    return g_icon_hash(G_ICON(self->obj));
-}
-%%
-override-slot GFileIcon.tp_richcompare
-/* We need to duplicate, because GIcon is an interface, not a class. */
-static PyObject *
-_wrap_g_file_icon_tp_richcompare(PyGObject *self, PyGObject *other, int op)
-{
-    return pygio_do_icon_richcompare(self, other, op);
-}
-%%
-override-slot GFileIcon.tp_hash
-/* We need to duplicate, because GIcon is an interface, not a class. */
-static long
-_wrap_g_file_icon_tp_hash(PyGObject *self)
-{
-    return g_icon_hash(G_ICON(self->obj));
-}
-%%
-override-slot GFileIcon.tp_repr
-static PyObject *
-_wrap_g_file_icon_tp_repr(PyGObject *self)
-{
-    GFile *file = g_file_icon_get_file(G_FILE_ICON(self->obj));
-    char *uri = (file ? g_file_get_uri(file) : NULL);
-    gchar *representation;
-    PyObject *result;
-
-    if (uri) {
-	representation = g_strdup_printf("<%s at %p: %s>", self->ob_type->tp_name, self, uri);
-	g_free(uri);
-    }
-    else
-	representation = g_strdup_printf("<%s at %p: UNKNOWN URI>", self->ob_type->tp_name, self);
-
-    result = PyString_FromString(representation);
-    g_free(representation);
-    return result;
-}
-%%
-override g_themed_icon_get_names noargs
-static PyObject *
-_wrap_g_themed_icon_get_names(PyGObject *self)
-{
-    const char * const *names;
-    PyObject *ret;
-
-    names = g_themed_icon_get_names(G_THEMED_ICON(self->obj));
-
-    ret = PyList_New(0);
-    while (names && *names) {
-        PyObject *item = PyString_FromString(names[0]);
-        PyList_Append(ret, item);
-        Py_DECREF(item);
-
-        names++;
-    }
-
-    return ret;
-}
-%%
-override-slot GThemedIcon.tp_richcompare
-/* We need to duplicate, because GIcon is an interface, not a class. */
-static PyObject *
-_wrap_g_themed_icon_tp_richcompare(PyGObject *self, PyGObject *other, int op)
-{
-    return pygio_do_icon_richcompare(self, other, op);
-}
-%%
-override-slot GThemedIcon.tp_hash
-/* We need to duplicate, because GIcon is an interface, not a class. */
-static long
-_wrap_g_themed_icon_tp_hash(PyGObject *self)
-{
-    return g_icon_hash(G_ICON(self->obj));
-}
-%%
-override-slot GThemedIcon.tp_repr
-static PyObject *
-_wrap_g_themed_icon_tp_repr(PyGObject *self)
-{
-    const char * const *names = g_themed_icon_get_names(G_THEMED_ICON(self->obj));
-    GString *representation = g_string_new(NULL);
-    PyObject *result;
-
-    g_string_append_printf(representation, "<%s at %p: ", self->ob_type->tp_name, self);
-
-    if (names) {
-	gboolean first_name = TRUE;
-	while (*names) {
-	    if (!first_name)
-		g_string_append(representation, ", ");
-	    else
-		first_name = FALSE;
-
-	    g_string_append(representation, *names++);
-	}
-    }
-
-    g_string_append(representation, ">");
-    result = PyString_FromString(representation->str);
-    g_string_free(representation, TRUE);
-    return result;
-}
-%%
 override g_content_type_guess kwargs
 static PyObject *
 _wrap_g_content_type_guess(PyGObject *self, PyObject *args, PyObject *kwargs)
Index: gio/gio.defs
===================================================================
--- gio/gio.defs	(revision 930)
+++ gio/gio.defs	(working copy)
@@ -3448,6 +3448,20 @@
 
 (define-method load
   (of-object "GLoadableIcon")
+  (docstring
+  "ICON.load([size, [cancellable]]) -> input stream, type\n"
+  "\n"
+  "Opens a stream of icon data for reading. The result is a tuple of\n"
+  "gio.InputStream and type (either a string or None). The stream can\n"
+  "be read to retrieve icon data.\n"
+  "\n"
+  ;; Note: this is just a guess, GIO docs say nothing at the moment.
+  "Optional size is a hint at desired icon size. Not all implementations\n"
+  "support it and the hint will be just ignored in such cases.\n"
+  "If cancellable is specified, then the operation can be cancelled\n"
+  "by triggering the cancellable object from another thread. See\n"
+  "gio.File.read for details."
+  )
   (c-name "g_loadable_icon_load")
   (return-type "GInputStream*")
   (parameters
@@ -3460,6 +3474,15 @@
 
 (define-method load_async
   (of-object "GLoadableIcon")
+  (docstring
+  "ICON.load_async(callback, [size, [cancellable, [user_data]]]) -> start loading\n"
+  "\n"
+  "For more information, see gio.LoadableIcon.load() which is the\n"
+  "synchronous version of this call. Asynchronously opens icon data for\n"
+  "reading. When the operation is finished, callback will be called.\n"
+  "You can then call gio.LoadableIcon.load_finish() to get the result of\n"
+  "the operation.\n"
+  )
   (c-name "g_loadable_icon_load_async")
   (return-type "none")
   (parameters
@@ -3471,6 +3494,13 @@
 )
 
 (define-method load_finish
+  (docstring
+  "F.load_finish(res) -> start loading\n"
+  "\n"
+  "Finish asynchronous icon loading operation. Must be called from callback\n"
+  "as specified to gio.LoadableIcon.load_async. Returns a tuple of\n"
+  "gio.InputStream and type, just as gio.LoadableIcon.load."
+  )
   (of-object "GLoadableIcon")
   (c-name "g_loadable_icon_load_finish")
   (return-type "GInputStream*")
Index: gio/Makefile.am
===================================================================
--- gio/Makefile.am	(revision 930)
+++ gio/Makefile.am	(working copy)
@@ -42,6 +42,7 @@
 	gfileattribute.override		\
 	gfileenumerator.override	\
 	gfileinfo.override		\
+	gicon.override			\
 	ginputstream.override 		\
 	goutputstream.override 		\
 	gvolume.override		\
Index: gio/gicon.override
===================================================================
--- gio/gicon.override	(revision 0)
+++ gio/gicon.override	(revision 0)
@@ -0,0 +1,341 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * pygtk- Python bindings for the GTK toolkit.
+ * Copyright (C) 2008  Johan Dahlin
+ *
+ *   gicon.override: module overrides for GIcon and related types
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+%%
+headers
+
+static PyObject *  pygio_do_icon_richcompare(PyGObject *self, PyGObject *other, int op);
+
+%%
+ignore-glob
+  g_icon_hash
+  g_themed_icon_new_from_names
+  g_themed_icon_new_with_default_fallbacks
+%%
+override-slot GIcon.tp_richcompare
+static PyObject *
+pygio_do_icon_richcompare(PyGObject *self, PyGObject *other, int op)
+{
+    PyObject *result;
+
+    if (PyObject_TypeCheck(self, &PyGIcon_Type)
+        && PyObject_TypeCheck(other, &PyGIcon_Type)) {
+        GIcon *icon1 = G_ICON(self->obj);
+        GIcon *icon2 = G_ICON(other->obj);
+
+        switch (op) {
+        case Py_EQ:
+            result = (g_icon_equal(icon1, icon2)
+                      ? Py_True : Py_False);
+            break;
+        case Py_NE:
+            result = (!g_icon_equal(icon1, icon2)
+                      ? Py_True : Py_False);
+            break;
+        default:
+            result = Py_NotImplemented;
+        }
+    }
+    else
+        result = Py_NotImplemented;
+
+    Py_INCREF(result);
+    return result;
+}
+static PyObject *
+_wrap_g_icon_tp_richcompare(PyGObject *self, PyGObject *other, int op)
+{
+    return pygio_do_icon_richcompare(self, other, op);
+}
+%%
+override-slot GIcon.tp_hash
+static long
+_wrap_g_icon_tp_hash(PyGObject *self)
+{
+    return g_icon_hash(G_ICON(self->obj));
+}
+%%
+override g_loadable_icon_load kwargs
+static PyObject *
+_wrap_g_loadable_icon_load(PyGObject *self,
+                           PyObject *args,
+                           PyObject *kwargs)
+{
+    static char *kwlist[] = { "size", "cancellable", NULL };
+    int size = 0;
+    char *type = NULL;
+    PyGObject *pycancellable = NULL;
+    GCancellable *cancellable;
+    GError *error = NULL;
+    GInputStream *stream;
+    PyObject *result;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+                                     "|iO:gio.LoadableIcon.load",
+				     kwlist,
+				     &size, &pycancellable))
+        return NULL;
+
+    if (!pygio_check_cancellable(pycancellable, &cancellable))
+	return NULL;
+
+    stream = g_loadable_icon_load(G_LOADABLE_ICON(self->obj), size, &type,
+				  cancellable, &error);
+    if (pyg_error_check(&error))
+        return NULL;
+
+    result = Py_BuildValue("Ns", pygobject_new((GObject *) stream), type);
+    g_free(type);
+    return result;
+}
+%%
+override g_loadable_icon_load_async kwargs
+static PyObject *
+_wrap_g_loadable_icon_load_async(PyGObject *self,
+				 PyObject *args,
+				 PyObject *kwargs)
+{
+    static char *kwlist[] = { "callback", "size", "cancellable", "user_data", NULL };
+    int size = 0;
+    PyGObject *pycancellable = NULL;
+    GCancellable *cancellable;
+    PyGIONotify *notify;
+
+    notify = g_slice_new0(PyGIONotify);
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+                                     "O|iOO:gio.LoadableIcon.load_async",
+				     kwlist,
+				     &notify->callback, &size, &pycancellable, &notify->data))
+	goto error;
+
+    if (!PyCallable_Check(notify->callback)) {
+	PyErr_SetString(PyExc_TypeError, "callback argument not callable");
+	goto error;
+    }
+
+    if (!pygio_check_cancellable(pycancellable, &cancellable))
+	goto error;
+
+    Py_INCREF(notify->callback);
+    Py_XINCREF(notify->data);
+
+    g_loadable_icon_load_async(G_LOADABLE_ICON(self->obj),
+			       size,
+			       cancellable,
+			       (GAsyncReadyCallback) async_result_callback_marshal,
+			       notify);
+    Py_INCREF(Py_None);
+    return Py_None;
+
+ error:
+    g_slice_free(PyGIONotify, notify);
+    return NULL;
+}
+%%
+override g_loadable_icon_load_finish kwargs
+static PyObject *
+_wrap_g_loadable_icon_load_finish(PyGObject *self,
+				  PyObject *args,
+				  PyObject *kwargs)
+{
+    static char *kwlist[] = { "res", NULL };
+    PyGObject *res;
+    char *type = NULL;
+    GError *error = NULL;
+    GInputStream *stream;
+    PyObject *result;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+                                     "O!:gio.LoadableIcon.load_finish",
+				     kwlist,
+				     &PyGAsyncResult_Type, &res))
+        return NULL;
+
+    stream = g_loadable_icon_load_finish(G_LOADABLE_ICON(self->obj),
+					 G_ASYNC_RESULT(res->obj), &type, &error);
+    if (pyg_error_check(&error))
+        return NULL;
+
+    result = Py_BuildValue("Ns", pygobject_new((GObject *) stream), type);
+    g_free(type);
+    return result;
+}
+%%
+override-slot GFileIcon.tp_richcompare
+/* We need to duplicate, because GIcon is an interface, not a class. */
+static PyObject *
+_wrap_g_file_icon_tp_richcompare(PyGObject *self, PyGObject *other, int op)
+{
+    return pygio_do_icon_richcompare(self, other, op);
+}
+%%
+override-slot GFileIcon.tp_hash
+/* We need to duplicate, because GIcon is an interface, not a class. */
+static long
+_wrap_g_file_icon_tp_hash(PyGObject *self)
+{
+    return g_icon_hash(G_ICON(self->obj));
+}
+%%
+override-slot GFileIcon.tp_repr
+static PyObject *
+_wrap_g_file_icon_tp_repr(PyGObject *self)
+{
+    GFile *file = g_file_icon_get_file(G_FILE_ICON(self->obj));
+    char *uri = (file ? g_file_get_uri(file) : NULL);
+    gchar *representation;
+    PyObject *result;
+
+    if (uri) {
+	representation = g_strdup_printf("<%s at %p: %s>", self->ob_type->tp_name, self, uri);
+	g_free(uri);
+    }
+    else
+	representation = g_strdup_printf("<%s at %p: UNKNOWN URI>", self->ob_type->tp_name, self);
+
+    result = PyString_FromString(representation);
+    g_free(representation);
+    return result;
+}
+%%
+new-constructor G_TYPE_THEMED_ICON
+%%
+override g_themed_icon_new kwargs
+static int
+_wrap_g_themed_icon_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "name", "use_default_fallbacks", NULL };
+    PyObject *name;
+    gboolean use_default_fallbacks = FALSE;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:gio.ThemedIcon.__init__",
+				     kwlist, &name, &use_default_fallbacks))
+	return -1;
+
+    if (PyString_Check(name)) {
+	pygobject_construct(self,
+			    "name", PyString_AsString(name),
+			    "use-default-fallbacks", use_default_fallbacks, NULL);
+	return 0;
+    }
+    else if (PySequence_Check(name)) {
+	PyObject *tuple = PySequence_Tuple(name);
+
+	if (tuple) {
+	    int k;
+	    int length = PyTuple_Size(tuple);
+	    char **names = g_new(char *, length + 1);
+
+	    for (k = 0; k < length; k++) {
+		PyObject *str = PyTuple_GetItem(tuple, k);
+		if (str && PyString_Check(str))
+		    names[k] = PyString_AsString(str);
+		else {
+		    Py_DECREF(tuple);
+		    g_free(names);
+		    goto error;
+		}
+	    }
+
+	    names[length] = NULL;
+	    pygobject_construct(self,
+				"names", names,
+				"use-default-fallbacks", use_default_fallbacks, NULL);
+	    Py_DECREF(tuple);
+	    g_free(names);
+	    return 0;
+	}
+    }
+
+ error:
+    if (!PyErr_Occurred()) {
+	PyErr_SetString(PyExc_TypeError,
+			"argument 1 of gio.ThemedIcon.__init__ "
+			"must be either a string or a sequence of strings");
+    }
+    return -1;
+}
+%%
+override g_themed_icon_get_names noargs
+static PyObject *
+_wrap_g_themed_icon_get_names(PyGObject *self)
+{
+    const char * const *names;
+    PyObject *ret;
+
+    names = g_themed_icon_get_names(G_THEMED_ICON(self->obj));
+
+    ret = PyList_New(0);
+    while (names && *names) {
+        PyObject *item = PyString_FromString(names[0]);
+        PyList_Append(ret, item);
+        Py_DECREF(item);
+
+        names++;
+    }
+
+    return ret;
+}
+%%
+override-slot GThemedIcon.tp_richcompare
+/* We need to duplicate, because GIcon is an interface, not a class. */
+static PyObject *
+_wrap_g_themed_icon_tp_richcompare(PyGObject *self, PyGObject *other, int op)
+{
+    return pygio_do_icon_richcompare(self, other, op);
+}
+%%
+override-slot GThemedIcon.tp_hash
+/* We need to duplicate, because GIcon is an interface, not a class. */
+static long
+_wrap_g_themed_icon_tp_hash(PyGObject *self)
+{
+    return g_icon_hash(G_ICON(self->obj));
+}
+%%
+override-slot GThemedIcon.tp_repr
+static PyObject *
+_wrap_g_themed_icon_tp_repr(PyGObject *self)
+{
+    const char * const *names = g_themed_icon_get_names(G_THEMED_ICON(self->obj));
+    GString *representation = g_string_new(NULL);
+    PyObject *result;
+
+    g_string_append_printf(representation, "<%s at %p: ", self->ob_type->tp_name, self);
+
+    if (names) {
+	gboolean first_name = TRUE;
+	while (*names) {
+	    if (!first_name)
+		g_string_append(representation, ", ");
+	    else
+		first_name = FALSE;
+
+	    g_string_append(representation, *names++);
+	}
+    }
+
+    g_string_append(representation, ">");
+    result = PyString_FromString(representation->str);
+    g_string_free(representation, TRUE);
+    return result;
+}
