Index: gio/giotypes.h
===================================================================
--- gio/giotypes.h	(revision 7593)
+++ gio/giotypes.h	(working copy)
@@ -112,6 +112,14 @@ typedef struct _GVolume                       GVol
 typedef struct _GVolumeMonitor                GVolumeMonitor;
 
 /**
+ * GBookmark:
+ *
+ * A handle to an object implementing the #GBookmarkIface interface.
+ **/
+typedef struct _GBookmark                        GBookmark;
+typedef struct _GBookmarkMonitor                 GBookmarkMonitor;
+
+/**
  * GAsyncReadyCallback:
  * @source_object: the object the asynchronous operation was started with.
  * @res: a #GAsyncResult.
Index: gio/ChangeLog
===================================================================
--- gio/ChangeLog	(revision 7594)
+++ gio/ChangeLog	(working copy)
@@ -1,5 +1,17 @@
 2008-10-12  David Zeuthen  <davidz@redhat.com>
 
+	Add GIcon serialization code (#555740).
+
+	* gio/gicon.[ch]: Add g_icon_to_string() and
+	g_icon_new_for_string(). Originally written by Matthias Clasen for
+	gvfs.
+
+	* gio/tests/Makefile.am:
+	* gio/tests/g-icon.c:
+	test cases for GIcon serialization
+
+2008-10-12  David Zeuthen  <davidz@redhat.com>
+
 	* gio.symbols:
 	* gunionvolumemonitor.c:
 	* gvolumemonitor.h:
Index: gio/gio.h
===================================================================
--- gio/gio.h	(revision 7593)
+++ gio/gio.h	(working copy)
@@ -29,6 +29,8 @@
 
 #include <gio/gappinfo.h>
 #include <gio/gasyncresult.h>
+#include <gio/gbookmark.h>
+#include <gio/gbookmarkmonitor.h>
 #include <gio/gbufferedinputstream.h>
 #include <gio/gbufferedoutputstream.h>
 #include <gio/gcancellable.h>
Index: gio/tests/Makefile.am
===================================================================
--- gio/tests/Makefile.am	(revision 7593)
+++ gio/tests/Makefile.am	(working copy)
@@ -22,7 +22,8 @@ TEST_PROGS +=	 		\
 	g-file 			\
 	g-file-info 		\
 	data-input-stream 	\
-	data-output-stream 
+	data-output-stream 	\
+	g-icon
 
 if OS_UNIX
 TEST_PROGS += live-g-file unix-streams desktop-app-info
@@ -46,6 +47,9 @@ data_input_stream_LDADD		= $(progs_ldadd)
 data_output_stream_SOURCES	= data-output-stream.c
 data_output_stream_LDADD	= $(progs_ldadd)
 
+g_icon_SOURCES	= g-icon.c
+g_icon_LDADD	= $(progs_ldadd)
+
 live_g_file_SOURCES	  = live-g-file.c
 live_g_file_LDADD	  = $(progs_ldadd)
 
Index: gio/giomodule.c
===================================================================
--- gio/giomodule.c	(revision 7593)
+++ gio/giomodule.c	(working copy)
@@ -29,6 +29,7 @@
 #include "glocalfilemonitor.h"
 #include "glocaldirectorymonitor.h"
 #include "gnativevolumemonitor.h"
+#include "gnativebookmarkmonitor.h"
 #include "gvfs.h"
 #ifdef G_OS_UNIX
 #include "gdesktopappinfo.h"
@@ -290,6 +291,7 @@ extern GType _g_fen_file_monitor_get_type (void);
 extern GType _g_inotify_directory_monitor_get_type (void);
 extern GType _g_inotify_file_monitor_get_type (void);
 extern GType _g_unix_volume_monitor_get_type (void);
+extern GType _g_gtk_bookmark_monitor_get_type (void);
 extern GType _g_local_vfs_get_type (void);
 
 extern GType _g_win32_volume_monitor_get_type (void);
@@ -326,6 +328,12 @@ _g_io_modules_ensure_loaded (void)
       
       ep = g_io_extension_point_register (G_NATIVE_VOLUME_MONITOR_EXTENSION_POINT_NAME);
       g_io_extension_point_set_required_type (ep, G_TYPE_NATIVE_VOLUME_MONITOR);
+
+      ep = g_io_extension_point_register (G_BOOKMARK_MONITOR_EXTENSION_POINT_NAME);
+      g_io_extension_point_set_required_type (ep, G_TYPE_BOOKMARK_MONITOR);
+
+      ep = g_io_extension_point_register (G_NATIVE_BOOKMARK_MONITOR_EXTENSION_POINT_NAME);
+      g_io_extension_point_set_required_type (ep, G_TYPE_NATIVE_BOOKMARK_MONITOR);
       
       ep = g_io_extension_point_register (G_VFS_EXTENSION_POINT_NAME);
       g_io_extension_point_set_required_type (ep, G_TYPE_VFS);
@@ -364,6 +372,7 @@ _g_io_modules_ensure_loaded (void)
 #endif
 #ifdef G_OS_UNIX
       _g_unix_volume_monitor_get_type ();
+      _g_gtk_bookmark_monitor_get_type ();
 #endif
 #ifdef G_OS_WIN32
       _g_winhttp_vfs_get_type ();
Index: gio/gioenums.h
===================================================================
--- gio/gioenums.h	(revision 7593)
+++ gio/gioenums.h	(working copy)
@@ -1,6 +1,6 @@
 /* GIO - GLib Input, Output and Streaming Library
  *
- * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -462,7 +462,40 @@ typedef enum  {
   G_EMBLEM_ORIGIN_TAG
 } GEmblemOrigin;
 
+/**
+ * GBookmarkKind:
+ * @G_BOOKMARK_KIND_UNKNOWN: Bookmark of unknown kind.
+ * @G_BOOKMARK_KIND_SHORTCUT: Bookmark is a shortcut.
+ * @G_BOOKMARK_KIND_RECENT_FILE: Bookmark is a recently used file.
+ *
+ * The kind of a #GBookmark.
+ *
+ * Since: 2.20
+ */
+typedef enum {
+  G_BOOKMARK_KIND_UNKNOWN,
+  G_BOOKMARK_KIND_SHORTCUT,
+  G_BOOKMARK_KIND_RECENT_FILE,
+} GBookmarkKind;
 
+/**
+ * GBookmarkFlags:
+ * @G_BOOKMARK_FLAG_NONE: No flags are set.
+ * @G_BOOKMARK_FLAG_USER: Bookmark is owned by user. If this flag is not set, the bookmark is provided by the system.
+ * @G_BOOKMARK_FLAG_WRITABLE: Bookmark is writable.
+ * @G_BOOKMARK_FLAG_PRIVATE: Bookmark is private.
+ *
+ * Flags for #GBookmark.
+ *
+ * Since: 2.20
+ */
+typedef enum {
+  G_BOOKMARK_FLAG_NONE      = 0,          /*< nick=none >*/
+  G_BOOKMARK_FLAG_USER      = (1 << 0),   /*< nick=user >*/
+  G_BOOKMARK_FLAG_WRITABLE  = (1 << 1),   /*< nick=writable >*/
+  G_BOOKMARK_FLAG_PRIVATE   = (1 << 2)    /*< nick=private >*/
+} GBookmarkFlags;
+
 G_END_DECLS
 
 #endif /* __GIO_ENUMS_H__ */
Index: gio/Makefile.am
===================================================================
--- gio/Makefile.am	(revision 7593)
+++ gio/Makefile.am	(working copy)
@@ -128,6 +128,10 @@ appinfo_sources += gdesktopappinfo.c gdesktopappin
 platform_libadd += xdgmime/libxdgmime.la
 platform_deps += xdgmime/libxdgmime.la
 unix_sources = \
+	ggtkbookmark.c	 	\
+	ggtkbookmark.h 		\
+	ggtkbookmarkmonitor.c 	\
+	ggtkbookmarkmonitor.h 	\
 	gunixmount.c		\
 	gunixmount.h		\
 	gunixmounts.c 		\
@@ -169,6 +173,10 @@ libgio_2_0_la_SOURCES =		\
 	gasynchelper.c 		\
 	gasynchelper.h 		\
 	gasyncresult.c 		\
+	gbookmark.c	        \
+	gbookmark.h	        \
+	gbookmarkmonitor.c	\
+	gbookmarkmonitor.h	\
 	gbufferedinputstream.c 	\
 	gbufferedoutputstream.c \
 	gcancellable.c 		\
@@ -207,6 +215,8 @@ libgio_2_0_la_SOURCES =		\
 	gmemoryinputstream.c 	\
 	gmemoryoutputstream.c 	\
 	gmountoperation.c 	\
+	gnativebookmarkmonitor.c \
+	gnativebookmarkmonitor.h \
 	gnativevolumemonitor.c 	\
 	gnativevolumemonitor.h 	\
 	goutputstream.c 	\
@@ -215,6 +225,8 @@ libgio_2_0_la_SOURCES =		\
 	gseekable.c 		\
 	gsimpleasyncresult.c 	\
 	gthemedicon.c 		\
+	gunionbookmarkmonitor.c	\
+	gunionbookmarkmonitor.h	\
 	gunionvolumemonitor.c 	\
 	gunionvolumemonitor.h 	\
 	gvfs.c 			\
@@ -278,6 +290,8 @@ libgio_2_0_la_DEPENDENCIES = $(gio_def) $(platform
 gio_headers =			\
 	gappinfo.h 		\
 	gasyncresult.h 		\
+	gbookmark.h		\
+	gbookmarkmonitor.h	\
 	gbufferedinputstream.h 	\
 	gbufferedoutputstream.h \
 	gcancellable.h 		\
@@ -311,6 +325,7 @@ gio_headers =			\
 	gmemoryinputstream.h 	\
 	gmemoryoutputstream.h 	\
 	gmountoperation.h 	\
+	gnativebookmarkmonitor.h \
 	gnativevolumemonitor.h 	\
 	goutputstream.h 	\
 	gseekable.h 		\
@@ -363,3 +378,11 @@ gioenumtypes.c: $(gio_headers) gioenumtypes.c.temp
 
 gio-2.0.lib: libgio-2.0.la gio.def
 	lib -machine:@LIB_EXE_MACHINE_FLAG@ -name:libgio-2.0-$(LT_CURRENT_MINUS_AGE).dll -def:gio.def -out:$@
+
+#
+# programs to compile and install
+#
+bin_PROGRAMS = gio-bookmark
+
+gio_bookmark_SOURCES = gio-bookmark.c
+gio_bookmark_LDADD = $(top_builddir)/gio/libgio-2.0.la
Index: gio/gicon.c
===================================================================
--- gio/gicon.c	(revision 7593)
+++ gio/gicon.c	(working copy)
@@ -21,7 +21,15 @@
  */
 
 #include "config.h"
+#include <stdlib.h>
+#include <string.h>
+
 #include "gicon.h"
+#include "gthemedicon.h"
+#include "gfileicon.h"
+#include "gemblemedicon.h"
+#include "gfile.h"
+#include "gioerror.h"
 
 #include "glibintl.h"
 
@@ -140,5 +148,335 @@ g_icon_equal (GIcon *icon1,
   return (* iface->equal) (icon1, icon2);
 }
 
+static char *
+_g_icon_to_string_tokenized (GIcon *icon)
+{
+  char *ret;
+
+  g_return_val_if_fail (icon != NULL, NULL);
+  g_return_val_if_fail (G_IS_ICON (icon), NULL);
+
+  /* We encode the icon as a series of tab-separated tokens.  The
+   * second token is the type of the icon. To help decoding, the
+   * number of tokens is the first token (not included in the count).
+   */
+  if (G_IS_FILE_ICON (icon))
+    {
+      GFileIcon *file_icon = G_FILE_ICON (icon);
+      GFile *file;
+      char *uri;
+      char *escaped_uri;
+
+      file = g_file_icon_get_file (file_icon);
+      uri = g_file_get_uri (file);
+      escaped_uri = g_uri_escape_string (uri, NULL, TRUE);
+
+      ret = g_strdup_printf ("2 GFileIcon %s", escaped_uri);
+
+      g_free (uri);
+      g_free (escaped_uri);
+    }
+  else if (G_IS_THEMED_ICON (icon))
+    {
+      GThemedIcon *themed_icon = G_THEMED_ICON (icon);
+      char *escaped_name;
+      char **names;
+      GString *s;
+
+      g_object_get (themed_icon,
+                    "names", &names,
+                    NULL);
+
+      s = g_string_new (0);
+      g_string_append_printf (s, "%d GThemedIcon", g_strv_length (names) + 1);
+
+      if (names != NULL)
+        {
+          int n;
+          for (n = 0; names[n] != NULL; n++)
+            {
+              escaped_name = g_uri_escape_string (names[n], NULL, TRUE);
+              g_string_append_c (s, ' ');
+              g_string_append (s, escaped_name);
+              g_free (escaped_name);
+            }
+        }
+
+      ret = g_string_free (s, FALSE);
+
+      g_strfreev (names);
+    }
+  else if (G_IS_EMBLEMED_ICON (icon))
+    {
+      char *base, *s;
+      GList *emblems, *e;
+      int n;
+      GString *str;
+
+      /* GEmblemedIcons are encoded as
+       *
+       *   <num_tokens> GEmblemedIcon <num_emblems> [<origin> <encoded_icon> ]*
+       */
+      str = g_string_new ("");
+      base = _g_icon_to_string_tokenized (g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon)));
+      emblems = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
+      g_string_append_printf (str, "GEmblemedIcon %s %d", base, g_list_length (emblems));
+      n = atoi (base) + 2;
+      g_free (base);
+      for (e = emblems; e; e = e->next)
+        {
+          s = _g_icon_to_string_tokenized (g_emblem_get_icon (G_EMBLEM (e->data)));
+          g_string_append_printf (str, " %d %s", g_emblem_get_origin (G_EMBLEM (e->data)), s);
+          n += atoi (s) + 2;
+          g_free (s);
+        }
+
+      s = g_string_free (str, FALSE);
+      ret = g_strdup_printf ("%d %s", n + 1, s);
+      g_free (s);
+    }
+  else
+    {
+      ret = NULL;
+      g_warning ("unknown icon type; please add support");
+    }
+
+  return ret;
+}
+
+/**
+ * g_icon_to_string:
+ * @icon: a #GIcon.
+ *
+ * Generates a textual representation of @icon that can be used for
+ * serialization such as when passing @icon to a different process or
+ * saving it to persistent storage. Use g_icon_new_for_string() to
+ * get @icon back from the returned string.
+ *
+ * The encoding of the returned string is proprietary to #GIcon except
+ * in the following two cases
+ *
+ * <itemizedlist>
+ * <listitem>
+ *   If @icon is a #GFileIcon, the returned string is a native path
+ *   (such as <literal>/path/to/icon.png</literal>) without escaping
+ *   if the #GFile for @icon is a native file.  If the file is not
+ *   native, the returned string is the result of g_file_get_uri().
+ * </listitem>
+ * <listitem>
+ *  If @icon is a #GThemedIcon with exactly one name, the encoding is
+ *  simply the name (such as <literal>network-server</literal>).
+ * </listitem>
+ * </itemizedlist>
+ *
+ * Returns: A newly allocated NUL-terminated string. Use g_free() to
+ * free.
+ *
+ * Since: 2.20
+ */
+gchar *
+g_icon_to_string (GIcon *icon)
+{
+  gchar *ret;
+
+  g_return_val_if_fail (icon != NULL, NULL);
+  g_return_val_if_fail (G_IS_ICON (icon), NULL);
+
+  ret = NULL;
+
+  if (G_IS_FILE_ICON (icon))
+    {
+      GFile *file;
+
+      file = g_file_icon_get_file (G_FILE_ICON (icon));
+      if (g_file_is_native (file))
+        ret = g_file_get_path (file);
+      else
+        ret = g_file_get_uri (file);
+    }
+  else if (G_IS_THEMED_ICON (icon))
+    {
+      gchar **names;
+
+      g_object_get (icon,
+                    "names", &names,
+                    NULL);
+
+      if (names != NULL)
+        {
+          if (g_strv_length (names) == 1)
+            {
+              ret = g_strdup (names[0]);
+            }
+          g_strfreev (names);
+        }
+    }
+
+  if (ret == NULL)
+    ret = _g_icon_to_string_tokenized (icon);
+
+  return ret;
+}
+
+static GIcon *
+_g_icon_new_from_tokens (char **tokens, int num_tokens)
+{
+  GIcon *icon;
+
+  icon = NULL;
+
+  if (strcmp (tokens[0], "GFileIcon") == 0)
+    {
+      GFile *file;
+      gchar *unescaped_uri;
+
+      if (num_tokens != 2)
+        goto out;
+
+      unescaped_uri = g_uri_unescape_string (tokens[1], NULL);
+      file = g_file_new_for_uri (unescaped_uri);
+      icon = g_file_icon_new (file);
+      g_object_unref (file);
+      g_free (unescaped_uri);
+    }
+  else if (strcmp (tokens[0], "GThemedIcon") == 0)
+    {
+      int n;
+
+      for (n = 1; n < num_tokens; n++)
+        {
+          gchar *unescaped_name;
+
+          unescaped_name = g_uri_unescape_string (tokens[n], NULL);
+          if (icon == NULL)
+            icon = g_themed_icon_new (unescaped_name);
+          else
+            g_themed_icon_append_name (G_THEMED_ICON (icon), unescaped_name);
+          g_free (unescaped_name);
+        }
+    }
+  else if (strcmp (tokens[0], "GEmblemedIcon") == 0)
+    {
+       int n, m, i;
+       GIcon *base, *e_icon;
+       GEmblem *emblem;
+       GList *emblems, *e;
+       GEmblemOrigin origin;
+       gchar **t = tokens;
+
+       t++;
+       n = atoi (*t);
+       t++;
+       if (t - tokens >= num_tokens)
+         goto out;
+       base = _g_icon_new_from_tokens (t, n);
+       if (base == NULL)
+         goto out;
+
+       t = t + n;
+       m = atoi (*t);
+       t++;
+       emblems = NULL;
+       for (i = 0; i < m; i++)
+         {
+           origin = atoi (*t);
+           t++;
+           n = atoi (*t);
+           t++;
+           if (t - tokens >= num_tokens)
+             goto cleanup;
+           e_icon = _g_icon_new_from_tokens (t, n);
+           t += n;
+           if (e_icon == NULL)
+             goto cleanup;
+           emblem = g_emblem_new_with_origin (e_icon, origin);
+           emblems = g_list_append (emblems, emblem);
+         }
+       icon = g_emblemed_icon_new (base, (GEmblem*)emblems->data);
+       for (e = emblems->next; e; e = e->next)
+         g_emblemed_icon_add_emblem (G_EMBLEMED_ICON (icon), (GEmblem*)e->data);
+
+ cleanup:
+       g_object_unref (base);
+       g_list_foreach (emblems, (GFunc)g_object_unref, NULL);
+       g_list_free (emblems);
+    }
+
+ out:
+   return icon;
+}
+
+/**
+ * g_icon_new_for_string:
+ * @str: A string obtained via g_icon_to_string().
+ * @error: Return location for error.
+ *
+ * Generate a #GIcon instance from @str. This function can fail if
+ * @str is not valid - see g_icon_to_string() for discussion.
+ *
+ * Returns: An object implementing the #GIcon interface or %NULL if
+ * @error is set.
+ *
+ * Since: 2.20
+ **/
+GIcon *
+g_icon_new_for_string (const gchar   *str,
+                       GError       **error)
+{
+  GIcon *icon;
+
+  g_return_val_if_fail (str != NULL, NULL);
+
+  icon = NULL;
+
+  /* handle special GFileIcon and GThemedIcon cases; only these never start with digits */
+  if (!g_ascii_isdigit (str[0]))
+    {
+      gchar *scheme;
+
+      scheme = g_uri_parse_scheme (str);
+      if (scheme != NULL || str[0] == '/')
+        {
+          GFile *location;
+
+          location = g_file_new_for_commandline_arg (str);
+          icon = g_file_icon_new (location);
+          g_object_unref (location);
+          g_free (scheme);
+        }
+      else
+        {
+          icon = g_themed_icon_new (str);
+        }
+    }
+  else
+    {
+      gchar **tokens;
+
+      tokens = g_strsplit (str, " ", 0);
+
+      /* handle tokenized encoding */
+      if (g_strv_length (tokens) >= 3)
+        {
+          gint num_tokens;
+          num_tokens = atoi (tokens[0]);
+          icon = _g_icon_new_from_tokens (tokens + 1, num_tokens);
+        }
+
+      g_strfreev (tokens);
+    }
+
+  if (icon == NULL)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_ARGUMENT,
+                   _("Malformed GIcon data \"%s\""), str);
+    }
+
+  return icon;
+}
+
+
 #define __G_ICON_C__
 #include "gioaliasdef.c"
Index: gio/gicon.h
===================================================================
--- gio/gicon.h	(revision 7593)
+++ gio/gicon.h	(working copy)
@@ -59,17 +59,21 @@ struct _GIconIface
 
   /* Virtual Table */
 
-  guint    (* hash)  (GIcon *icon);
-  gboolean (* equal) (GIcon *icon1,
-                      GIcon *icon2);
+  guint       (* hash)     (GIcon *icon);
+  gboolean    (* equal)    (GIcon *icon1,
+                            GIcon *icon2);
 };
 
-GType    g_icon_get_type (void) G_GNUC_CONST;
+GType    g_icon_get_type  (void) G_GNUC_CONST;
 
-guint    g_icon_hash     (gconstpointer  icon);
-gboolean g_icon_equal    (GIcon         *icon1,
-                          GIcon         *icon2);
+guint    g_icon_hash      (gconstpointer  icon);
+gboolean g_icon_equal     (GIcon         *icon1,
+                           GIcon         *icon2);
 
+gchar   *g_icon_to_string       (GIcon         *icon);
+GIcon   *g_icon_new_for_string  (const gchar   *str,
+                                 GError       **error);
+
 G_END_DECLS
 
 #endif /* __G_ICON_H__ */
Index: gio/gio.symbols
===================================================================
--- gio/gio.symbols	(revision 7594)
+++ gio/gio.symbols	(working copy)
@@ -469,6 +469,8 @@ g_filter_output_stream_get_base_stream
 g_icon_get_type  G_GNUC_CONST
 g_icon_hash 
 g_icon_equal 
+g_icon_to_string
+g_icon_new_for_string
 #endif
 #endif
 
Index: docs/reference/glib/tmpl/iochannels.sgml
===================================================================
--- docs/reference/glib/tmpl/iochannels.sgml	(revision 7593)
+++ docs/reference/glib/tmpl/iochannels.sgml	(working copy)
@@ -395,7 +395,7 @@ error domains.
 
 @channel: 
 @condition: 
-@Returns:
+@Returns: 
 
 
 <!-- ##### FUNCTION g_io_add_watch ##### -->
@@ -404,7 +404,7 @@ error domains.
 </para>
 
 @channel: 
-@condition:
+@condition: 
 @func: 
 @user_data: 
 @Returns: 
@@ -417,9 +417,9 @@ error domains.
 
 @channel: 
 @priority: 
-@condition:
+@condition: 
 @func: 
-@user_data:
+@user_data: 
 @notify: 
 @Returns: 
 
Index: docs/reference/gio/gio-sections.txt
===================================================================
--- docs/reference/gio/gio-sections.txt	(revision 7593)
+++ docs/reference/gio/gio-sections.txt	(working copy)
@@ -351,6 +351,8 @@ GIcon
 GIconIface
 g_icon_hash
 g_icon_equal
+g_icon_to_string
+g_icon_new_for_string
 <SUBSECTION Standard>
 G_ICON
 G_IS_ICON
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/gbookmark.c	2008-10-12 02:38:36.000000000 -0400
@@ -0,0 +1,430 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include "config.h"
+#include "gbookmark.h"
+#include "gfile.h"
+#include "gicon.h"
+#include "gasyncresult.h"
+#include "gsimpleasyncresult.h"
+#include "gioerror.h"
+#include "glibintl.h"
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gbookmark
+ * @short_description: Bookmark management
+ * @include: gio/gio.h
+ *
+ * The #GBookmark interface represents bookmarks. See the
+ * #GBookmarkMonitor class for details on how to obtain various kinds
+ * of bookmarks.
+ **/
+
+static void g_bookmark_base_init (gpointer g_class);
+static void g_bookmark_class_init (gpointer g_class,
+                                   gpointer class_data);
+
+GType
+g_bookmark_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      const GTypeInfo bookmark_info =
+      {
+        sizeof (GBookmarkIface), /* class_size */
+        g_bookmark_base_init,   /* base_init */
+        NULL,		/* base_finalize */
+        g_bookmark_class_init,
+        NULL,		/* class_finalize */
+        NULL,		/* class_data */
+        0,
+        0,              /* n_preallocs */
+        NULL
+      };
+      GType g_define_type_id =
+        g_type_register_static (G_TYPE_INTERFACE, I_("GBookmark"),
+                                &bookmark_info, 0);
+
+      g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+static void
+g_bookmark_class_init (gpointer g_class,
+                     gpointer class_data)
+{
+}
+
+static void
+g_bookmark_base_init (gpointer g_class)
+{
+  static gboolean initialized = FALSE;
+
+  if (! initialized)
+    {
+     /**
+      * GBookmark::changed:
+      *
+      * Emitted when the bookmark has been changed.
+      **/
+      g_signal_new (I_("changed"),
+                    G_TYPE_BOOKMARK,
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GBookmarkIface, changed),
+                    NULL, NULL,
+                    g_cclosure_marshal_VOID__VOID,
+                    G_TYPE_NONE, 0);
+
+     /**
+      * GBookmark::removed:
+      *
+      * This signal is emitted when the #GBookmark have been removed. If
+      * the recipient is holding references to the object they should
+      * release them so the object can be finalized.
+      **/
+      g_signal_new (I_("removed"),
+                    G_TYPE_BOOKMARK,
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GBookmarkIface, removed),
+                    NULL, NULL,
+                    g_cclosure_marshal_VOID__VOID,
+                    G_TYPE_NONE, 0);
+
+      initialized = TRUE;
+    }
+}
+
+/**
+ * g_bookmark_get_kind:
+ * @bookmark: a #GBookmark.
+ *
+ * Gets the kind of @bookmark.
+ *
+ * Returns: the kind for the given @bookmark.
+ **/
+GBookmarkKind
+g_bookmark_get_kind (GBookmark *bookmark)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), G_BOOKMARK_KIND_UNKNOWN);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->get_kind) (bookmark);
+}
+
+/**
+ * g_bookmark_get_flags:
+ * @bookmark: a #GBookmark.
+ *
+ * Get flags for @bookmark.
+ *
+ * Returns: Flags from #GBookmarkFlags for @bookmark.
+ **/
+GBookmarkFlags
+g_bookmark_get_flags (GBookmark *bookmark)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), FALSE);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->get_flags) (bookmark);
+}
+
+/**
+ * g_bookmark_get_location:
+ * @bookmark: a #GBookmark.
+ *
+ * Gets the location that @bookmark points to.
+ *
+ * Returns: the location for the given @bookmark. The returned object
+ * should be unreffed with g_object_unref() when no longer needed.
+ **/
+GFile *
+g_bookmark_get_location (GBookmark *bookmark)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), NULL);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->get_location) (bookmark);
+}
+
+/**
+ * g_bookmark_get_title:
+ * @bookmark: a #GBookmark.
+ *
+ * Gets the title of @bookmark.
+ *
+ * Returns: the title for the given @bookmark. The returned string
+ * should be freed with g_free() when no longer needed.
+ **/
+char *
+g_bookmark_get_title (GBookmark *bookmark)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), NULL);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->get_title) (bookmark);
+}
+
+/**
+ * g_bookmark_get_description:
+ * @bookmark: a #GBookmark.
+ *
+ * Gets the description of @bookmark.
+ *
+ * Returns: the description for the given @bookmark. The returned string
+ * should be freed with g_free() when no longer needed.
+ **/
+char *
+g_bookmark_get_description (GBookmark *bookmark)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), NULL);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->get_description) (bookmark);
+}
+
+
+/**
+ * g_bookmark_get_position:
+ * @bookmark: a #GBookmark.
+ *
+ * Gets the position of @bookmark.
+ *
+ * Returns: the position for the given @bookmark.
+ **/
+gint
+g_bookmark_get_position (GBookmark *bookmark)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), -1);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->get_position) (bookmark);
+}
+
+/**
+ * g_bookmark_get_icon:
+ * @bookmark: a #GBookmark.
+ *
+ * Gets the icon of @bookmark.
+ *
+ * Returns: the icon for the given @bookmark. The returned object
+ * should be unreffed with g_object_unref() when no longer needed.
+ **/
+GIcon *
+g_bookmark_get_icon (GBookmark *bookmark)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), NULL);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->get_icon) (bookmark);
+}
+
+
+/**
+ * g_bookmark_set_location:
+ * @bookmark: a #GBookmark.
+ * @location: Location to set.
+ * @error: Return location for error.
+ *
+ * Sets new @location for @bookmark.
+ *
+ * Returns: %TRUE if operation succeeded, %FALSE if @error is set.
+ **/
+gboolean
+g_bookmark_set_location (GBookmark   *bookmark,
+                         GFile       *location,
+                         GError     **error)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), FALSE);
+  g_return_val_if_fail (G_IS_FILE (location), FALSE);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->set_location) (bookmark,
+                                  location,
+                                  error);
+}
+
+/**
+ * g_bookmark_set_title:
+ * @bookmark: a #GBookmark.
+ * @title: Title to set.
+ * @error: Return location for error.
+ *
+ * Sets new @title for @bookmark.
+ *
+ * Returns: %TRUE if operation succeeded, %FALSE if @error is set.
+ **/
+gboolean
+g_bookmark_set_title (GBookmark   *bookmark,
+                      const char  *title,
+                      GError     **error)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), FALSE);
+  g_return_val_if_fail (title != NULL, FALSE);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->set_title) (bookmark,
+                               title,
+                               error);
+}
+
+/**
+ * g_bookmark_set_description:
+ * @bookmark: a #GBookmark.
+ * @description: Description to set.
+ * @error: Return location for error.
+ *
+ * Sets new @description for @bookmark.
+ *
+ * Returns: %TRUE if operation succeeded, %FALSE if @error is set.
+ **/
+gboolean
+g_bookmark_set_description (GBookmark   *bookmark,
+                            const char  *description,
+                            GError     **error)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), FALSE);
+  g_return_val_if_fail (description != NULL, FALSE);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->set_description) (bookmark,
+                                     description,
+                                     error);
+}
+
+/**
+ * g_bookmark_set_position:
+ * @bookmark: a #GBookmark.
+ * @position: Position of bookmark.
+ * @error: Return location for error.
+ *
+ * Sets new @position for @bookmark.
+ *
+ * Returns: %TRUE if operation succeeded, %FALSE if @error is set.
+ **/
+gboolean
+g_bookmark_set_position (GBookmark   *bookmark,
+                         gint         position,
+                         GError     **error)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), FALSE);
+  g_return_val_if_fail (position >= 0, FALSE);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->set_position) (bookmark,
+                                  position,
+                                  error);
+}
+
+/**
+ * g_bookmark_set_icon:
+ * @bookmark: a #GBookmark.
+ * @icon: Icon to set.
+ * @error: Return location for error.
+ *
+ * Sets new @icon for @bookmark.
+ *
+ * Returns: %TRUE if operation succeeded, %FALSE if @error is set.
+ **/
+gboolean
+g_bookmark_set_icon (GBookmark   *bookmark,
+                     GIcon       *icon,
+                     GError     **error)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), FALSE);
+  g_return_val_if_fail (G_IS_ICON (icon), FALSE);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->set_icon) (bookmark,
+                              icon,
+                              error);
+}
+
+/**
+ * g_bookmark_delete:
+ * @bookmark: a #GBookmark.
+ * @error: Return location for error.
+ *
+ * Delete @bookmark.
+ *
+ * Returns: %TRUE if operation succeeded, %FALSE if @error is set.
+ **/
+gboolean
+g_bookmark_delete (GBookmark   *bookmark,
+                   GError     **error)
+{
+  GBookmarkIface *iface;
+
+  g_return_val_if_fail (G_IS_BOOKMARK (bookmark), FALSE);
+
+  iface = G_BOOKMARK_GET_IFACE (bookmark);
+
+  return (* iface->delete) (bookmark,
+                            error);
+}
+
+
+#define __G_BOOKMARK_C__
+#include "gioaliasdef.c"
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/gbookmark.h	2008-10-12 02:50:47.000000000 -0400
@@ -0,0 +1,122 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_BOOKMARK_H__
+#define __G_BOOKMARK_H__
+
+#include <gio/giotypes.h>
+#include <gio/gioenums.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_BOOKMARK            (g_bookmark_get_type ())
+#define G_BOOKMARK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_BOOKMARK, GBookmark))
+#define G_IS_BOOKMARK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_BOOKMARK))
+#define G_BOOKMARK_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_BOOKMARK, GBookmarkIface))
+
+/**
+ * GBookmarkIface:
+ * @g_iface: The parent interface.
+ * @changed: Changed signal that is emitted when the bookmark's state has changed.
+ * @removed: The removed signal that is emitted when the #GBookmark have been removed. If the recipient is holding references to the object they should release them so the object can be finalized.
+ * TODO: other fields
+ *
+ * Interface for implementing operations for bookmarks.
+ **/
+typedef struct _GBookmarkIface    GBookmarkIface;
+
+struct _GBookmarkIface
+{
+  GTypeInterface g_iface;
+
+  /* signals */
+
+  void              (* changed)          (GBookmark *bookmark);
+  void              (* removed)          (GBookmark *bookmark);
+
+  /* Virtual Table */
+  GBookmarkKind      (* get_kind)        (GBookmark   *bookmark);
+  GBookmarkFlags     (* get_flags)       (GBookmark   *bookmark);
+
+  GFile           *  (* get_location)    (GBookmark   *bookmark);
+  char            *  (* get_title)       (GBookmark   *bookmark);
+  char            *  (* get_description) (GBookmark   *bookmark);
+  gint               (* get_position)    (GBookmark   *bookmark);
+  GIcon           *  (* get_icon)        (GBookmark   *bookmark);
+
+  gboolean           (* set_location)    (GBookmark   *bookmark,
+                                          GFile       *location,
+                                          GError     **error);
+  gboolean           (* set_title)       (GBookmark   *bookmark,
+                                          const char  *title,
+                                          GError     **error);
+  gboolean           (* set_description) (GBookmark   *bookmark,
+                                          const char  *description,
+                                          GError     **error);
+  gboolean           (* set_position)    (GBookmark   *bookmark,
+                                          gint         position,
+                                          GError     **error);
+  gboolean           (* set_icon)        (GBookmark   *bookmark,
+                                          GIcon       *icon,
+                                          GError     **error);
+
+  gboolean           (* delete)          (GBookmark   *bookmark,
+                                          GError     **error);
+};
+
+GType            g_bookmark_get_type        (void) G_GNUC_CONST;
+GBookmarkKind    g_bookmark_get_kind        (GBookmark *bookmark);
+GBookmarkFlags   g_bookmark_get_flags       (GBookmark *bookmark);
+
+GFile           *g_bookmark_get_location    (GBookmark *bookmark);
+char            *g_bookmark_get_title       (GBookmark *bookmark);
+char            *g_bookmark_get_description (GBookmark *bookmark);
+gint             g_bookmark_get_position    (GBookmark *bookmark);
+GIcon           *g_bookmark_get_icon        (GBookmark *bookmark);
+
+gboolean         g_bookmark_set_location    (GBookmark   *bookmark,
+                                             GFile       *location,
+                                             GError     **error);
+gboolean         g_bookmark_set_title       (GBookmark   *bookmark,
+                                             const char  *title,
+                                             GError     **error);
+gboolean         g_bookmark_set_description (GBookmark   *bookmark,
+                                             const char  *title,
+                                             GError     **error);
+gboolean         g_bookmark_set_position    (GBookmark   *bookmark,
+                                             gint         position,
+                                             GError     **error);
+gboolean         g_bookmark_set_icon        (GBookmark   *bookmark,
+                                             GIcon       *icon,
+                                             GError     **error);
+
+gboolean         g_bookmark_delete          (GBookmark   *bookmark,
+                                             GError     **error);
+/* TODO: add more */
+
+G_END_DECLS
+
+#endif /* __G_BOOKMARK_H__ */
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/gbookmarkmonitor.c	2008-10-12 00:53:16.000000000 -0400
@@ -0,0 +1,145 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include "config.h"
+#include "gbookmarkmonitor.h"
+#include "gbookmark.h"
+#include "glibintl.h"
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gbookmarkmonitor
+ * @short_description: Bookmark Monitor
+ * @include: gio/gio.h
+ * @see_also: #GFileMonitor, #GVolumeMonitor
+ *
+ * #GBookmarkMonitor is for listing bookmarks on the system.
+**/
+
+G_DEFINE_TYPE (GBookmarkMonitor, g_bookmark_monitor, G_TYPE_OBJECT);
+
+enum {
+  BOOKMARK_ADDED,
+  BOOKMARK_REMOVED,
+  BOOKMARK_CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+g_bookmark_monitor_finalize (GObject *object)
+{
+  GBookmarkMonitor *monitor;
+
+  monitor = G_BOOKMARK_MONITOR (object);
+
+  G_OBJECT_CLASS (g_bookmark_monitor_parent_class)->finalize (object);
+}
+
+static void
+g_bookmark_monitor_class_init (GBookmarkMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_bookmark_monitor_finalize;
+
+  /**
+   * GBookmarkMonitor::bookmark-added:
+   * @bookmark_monitor: The bookmark monitor emitting the signal.
+   * @bookmark: a #GBookmark that was added.
+   *
+   * Emitted when a bookmark is added to the system.
+   **/
+  signals[BOOKMARK_ADDED] = g_signal_new (I_("bookmark-added"),
+                                          G_TYPE_BOOKMARK_MONITOR,
+                                          G_SIGNAL_RUN_LAST,
+                                          G_STRUCT_OFFSET (GBookmarkMonitorClass, bookmark_added),
+                                          NULL, NULL,
+                                          g_cclosure_marshal_VOID__OBJECT,
+                                          G_TYPE_NONE, 1, G_TYPE_BOOKMARK);
+
+  /**
+   * GBookmarkMonitor::bookmark-removed:
+   * @bookmark_monitor: The bookmark monitor emitting the signal.
+   * @bookmark: a #GBookmark that was removed.
+   *
+   * Emitted when a bookmark is removed from the system.
+   **/
+  signals[BOOKMARK_REMOVED] = g_signal_new (I_("bookmark-removed"),
+                                            G_TYPE_BOOKMARK_MONITOR,
+                                            G_SIGNAL_RUN_LAST,
+                                            G_STRUCT_OFFSET (GBookmarkMonitorClass, bookmark_removed),
+                                            NULL, NULL,
+                                            g_cclosure_marshal_VOID__OBJECT,
+                                            G_TYPE_NONE, 1, G_TYPE_BOOKMARK);
+
+  /**
+   * GBookmarkMonitor::bookmark-changed:
+   * @bookmark_monitor: The bookmark monitor emitting the signal.
+   * @bookmark: a #GBookmark that changed.
+   *
+   * Emitted when bookmark is changed.
+   **/
+  signals[BOOKMARK_CHANGED] = g_signal_new (I_("bookmark-changed"),
+                                            G_TYPE_BOOKMARK_MONITOR,
+                                            G_SIGNAL_RUN_LAST,
+                                            G_STRUCT_OFFSET (GBookmarkMonitorClass, bookmark_changed),
+                                            NULL, NULL,
+                                            g_cclosure_marshal_VOID__OBJECT,
+                                            G_TYPE_NONE, 1, G_TYPE_BOOKMARK);
+}
+
+static void
+g_bookmark_monitor_init (GBookmarkMonitor *monitor)
+{
+}
+
+/**
+ * g_bookmark_monitor_get_bookmarks:
+ * @bookmark_monitor: a #GBookmarkMonitor.
+ *
+ * Gets a list of the bookmarks on the system.
+ *
+ * The returned list should be freed with g_list_free(), after
+ * its elements have been unreffed with g_object_unref().
+ *
+ * Returns: a #GList of #GBookmark objects.
+ **/
+GList *
+g_bookmark_monitor_get_bookmarks (GBookmarkMonitor *bookmark_monitor)
+{
+  GBookmarkMonitorClass *class;
+
+  g_return_val_if_fail (G_IS_BOOKMARK_MONITOR (bookmark_monitor), NULL);
+
+  class = G_BOOKMARK_MONITOR_GET_CLASS (bookmark_monitor);
+
+  return class->get_bookmarks (bookmark_monitor);
+}
+
+#define __G_BOOKMARK_MONITOR_C__
+#include "gioaliasdef.c"
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/gbookmarkmonitor.h	2008-10-11 21:43:58.000000000 -0400
@@ -0,0 +1,104 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_BOOKMARK_MONITOR_H__
+#define __G_BOOKMARK_MONITOR_H__
+
+#include <gio/giotypes.h>
+#include <gio/gbookmark.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_BOOKMARK_MONITOR         (g_bookmark_monitor_get_type ())
+#define G_BOOKMARK_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_BOOKMARK_MONITOR, GBookmarkMonitor))
+#define G_BOOKMARK_MONITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_BOOKMARK_MONITOR, GBookmarkMonitorClass))
+#define G_BOOKMARK_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_BOOKMARK_MONITOR, GBookmarkMonitorClass))
+#define G_IS_BOOKMARK_MONITOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_BOOKMARK_MONITOR))
+#define G_IS_BOOKMARK_MONITOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_BOOKMARK_MONITOR))
+
+/**
+ * G_BOOKMARK_MONITOR_EXTENSION_POINT_NAME:
+ *
+ * Extension point for bookmark monitor functionality.
+ * See <link linkend="extending-gio">Extending GIO</link>.
+ */
+#define G_BOOKMARK_MONITOR_EXTENSION_POINT_NAME "gio-bookmark-monitor"
+
+/**
+ * GBookmarkMonitor:
+ * @parent_instance: The parent instance.
+ *
+ * A Bookmark Monitor that watches for bookmark events.
+ **/
+typedef struct _GBookmarkMonitorClass GBookmarkMonitorClass;
+
+struct _GBookmarkMonitor
+{
+  GObject parent_instance;
+
+  /*< private >*/
+  gpointer priv;
+};
+
+struct _GBookmarkMonitorClass
+{
+  GObjectClass parent_class;
+
+  /*< public >*/
+
+  /* signals */
+  void      (* bookmark_added)         (GBookmarkMonitor *bookmark_monitor,
+                                        GBookmark        *bookmark);
+  void      (* bookmark_removed)       (GBookmarkMonitor *bookmark_monitor,
+                                        GBookmark        *bookmark);
+  void      (* bookmark_changed)       (GBookmarkMonitor *bookmark_monitor,
+                                        GBookmark        *bookmark);
+
+  /* Vtable */
+  gboolean               (* is_supported)   (void);
+  GList                * (* get_bookmarks)  (GBookmarkMonitor *bookmark_monitor);
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_g_reserved0) (void);
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+};
+
+GType                  g_bookmark_monitor_get_type      (void) G_GNUC_CONST;
+GBookmarkMonitor      *g_bookmark_monitor_get           (void);
+GList *                g_bookmark_monitor_get_bookmarks (GBookmarkMonitor     *bookmark_monitor);
+
+G_END_DECLS
+
+#endif /* __G_BOOKMARK_MONITOR_H__ */
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/gnativebookmarkmonitor.c	2008-10-11 22:07:51.000000000 -0400
@@ -0,0 +1,34 @@
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+#include "gnativebookmarkmonitor.h"
+
+#include "gioalias.h"
+
+G_DEFINE_ABSTRACT_TYPE (GNativeBookmarkMonitor, g_native_bookmark_monitor, G_TYPE_BOOKMARK_MONITOR);
+
+static void
+g_native_bookmark_monitor_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (g_native_bookmark_monitor_parent_class)->finalize (object);
+}
+
+
+static void
+g_native_bookmark_monitor_class_init (GNativeBookmarkMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_native_bookmark_monitor_finalize;
+}
+
+
+static void
+g_native_bookmark_monitor_init (GNativeBookmarkMonitor *native_monitor)
+{
+}
+
+#define __G_NATIVE_BOOKMARK_MONITOR_C__
+#include "gioaliasdef.c"
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/gnativebookmarkmonitor.h	2008-10-11 22:08:07.000000000 -0400
@@ -0,0 +1,43 @@
+#ifndef __G_NATIVE_BOOKMARK_MONITOR_H__
+#define __G_NATIVE_BOOKMARK_MONITOR_H__
+
+#include <gio/gbookmarkmonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_NATIVE_BOOKMARK_MONITOR        (g_native_bookmark_monitor_get_type ())
+#define G_NATIVE_BOOKMARK_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NATIVE_BOOKMARK_MONITOR, GNativeBookmarkMonitor))
+#define G_NATIVE_BOOKMARK_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NATIVE_BOOKMARK_MONITOR, GNativeBookmarkMonitorClass))
+#define G_IS_NATIVE_BOOKMARK_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NATIVE_BOOKMARK_MONITOR))
+#define G_IS_NATIVE_BOOKMARK_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_NATIVE_BOOKMARK_MONITOR))
+
+#define G_NATIVE_BOOKMARK_MONITOR_EXTENSION_POINT_NAME "gio-native-bookmark-monitor"
+
+typedef struct _GNativeBookmarkMonitor      GNativeBookmarkMonitor;
+typedef struct _GNativeBookmarkMonitorClass GNativeBookmarkMonitorClass;
+
+struct _GNativeBookmarkMonitor
+{
+  GBookmarkMonitor parent_instance;
+};
+
+struct _GNativeBookmarkMonitorClass
+{
+  GBookmarkMonitorClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_g_reserved0) (void);
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+};
+
+GType g_native_bookmark_monitor_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_NATIVE_BOOKMARK_MONITOR_H__ */
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/gunionbookmarkmonitor.c	2008-10-12 00:55:17.000000000 -0400
@@ -0,0 +1,344 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+#include "gunionbookmarkmonitor.h"
+#include "giomodule-priv.h"
+#include "gnativebookmarkmonitor.h"
+
+#include "glibintl.h"
+
+#include "gioalias.h"
+
+struct _GUnionBookmarkMonitor {
+  GBookmarkMonitor parent;
+
+  GList *monitors;
+};
+
+static void g_union_bookmark_monitor_remove_monitor (GUnionBookmarkMonitor *union_monitor,
+                                                   GBookmarkMonitor *child_monitor);
+
+
+#define g_union_bookmark_monitor_get_type _g_union_bookmark_monitor_get_type
+G_DEFINE_TYPE (GUnionBookmarkMonitor, g_union_bookmark_monitor, G_TYPE_BOOKMARK_MONITOR);
+
+static GStaticRecMutex the_bookmark_monitor_mutex = G_STATIC_REC_MUTEX_INIT;
+
+static GUnionBookmarkMonitor *the_bookmark_monitor = NULL;
+
+static void
+g_union_bookmark_monitor_finalize (GObject *object)
+{
+  GUnionBookmarkMonitor *monitor;
+  GBookmarkMonitor *child_monitor;
+
+  monitor = G_UNION_BOOKMARK_MONITOR (object);
+
+  while (monitor->monitors != NULL)
+    {
+      child_monitor = monitor->monitors->data;
+      g_union_bookmark_monitor_remove_monitor (monitor,
+                                             child_monitor);
+      g_object_unref (child_monitor);
+    }
+
+  G_OBJECT_CLASS (g_union_bookmark_monitor_parent_class)->finalize (object);
+}
+
+static void
+g_union_bookmark_monitor_dispose (GObject *object)
+{
+  GUnionBookmarkMonitor *monitor;
+
+  monitor = G_UNION_BOOKMARK_MONITOR (object);
+
+  g_static_rec_mutex_lock (&the_bookmark_monitor_mutex);
+  the_bookmark_monitor = NULL;
+  g_static_rec_mutex_unlock (&the_bookmark_monitor_mutex);
+
+  G_OBJECT_CLASS (g_union_bookmark_monitor_parent_class)->dispose (object);
+}
+
+static GList *
+get_bookmarks (GBookmarkMonitor *bookmark_monitor)
+{
+  GUnionBookmarkMonitor *monitor;
+  GBookmarkMonitor *child_monitor;
+  GList *res;
+  GList *l;
+
+  monitor = G_UNION_BOOKMARK_MONITOR (bookmark_monitor);
+
+  res = NULL;
+
+  g_static_rec_mutex_lock (&the_bookmark_monitor_mutex);
+
+  for (l = monitor->monitors; l != NULL; l = l->next)
+    {
+      child_monitor = l->data;
+
+      res = g_list_concat (res, g_bookmark_monitor_get_bookmarks (child_monitor));
+    }
+
+  g_static_rec_mutex_unlock (&the_bookmark_monitor_mutex);
+
+  return res;
+}
+
+static void
+g_union_bookmark_monitor_class_init (GUnionBookmarkMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GBookmarkMonitorClass *monitor_class = G_BOOKMARK_MONITOR_CLASS (klass);
+
+  gobject_class->finalize = g_union_bookmark_monitor_finalize;
+  gobject_class->dispose = g_union_bookmark_monitor_dispose;
+
+  monitor_class->get_bookmarks = get_bookmarks;
+}
+
+static void
+child_bookmark_added (GBookmarkMonitor      *child_monitor,
+                      GBookmark             *child_bookmark,
+                      GUnionBookmarkMonitor *union_monitor)
+{
+  g_signal_emit_by_name (union_monitor,
+                         "bookmark-added",
+                         child_bookmark);
+}
+
+static void
+child_bookmark_removed (GBookmarkMonitor      *child_monitor,
+                        GBookmark             *child_bookmark,
+                        GUnionBookmarkMonitor *union_monitor)
+{
+  g_signal_emit_by_name (union_monitor,
+                         "bookmark-removed",
+                         child_bookmark);
+}
+
+static void
+child_bookmark_changed (GBookmarkMonitor      *child_monitor,
+                        GBookmark             *child_bookmark,
+                        GUnionBookmarkMonitor *union_monitor)
+{
+  g_signal_emit_by_name (union_monitor,
+                         "bookmark-changed",
+                         child_bookmark);
+}
+
+static void
+g_union_bookmark_monitor_add_monitor (GUnionBookmarkMonitor *union_monitor,
+                                    GBookmarkMonitor      *bookmark_monitor)
+{
+  if (g_list_find (union_monitor->monitors, bookmark_monitor))
+    return;
+
+  union_monitor->monitors = g_list_prepend (union_monitor->monitors, g_object_ref (bookmark_monitor));
+
+  g_signal_connect (bookmark_monitor, "bookmark-added", (GCallback) child_bookmark_added, union_monitor);
+  g_signal_connect (bookmark_monitor, "bookmark-removed", (GCallback) child_bookmark_removed, union_monitor);
+  g_signal_connect (bookmark_monitor, "bookmark-changed", (GCallback) child_bookmark_changed, union_monitor);
+}
+
+static void
+g_union_bookmark_monitor_remove_monitor (GUnionBookmarkMonitor *union_monitor,
+                                       GBookmarkMonitor      *child_monitor)
+{
+  GList *l;
+
+  l = g_list_find (union_monitor->monitors, child_monitor);
+  if (l == NULL)
+    return;
+
+  union_monitor->monitors = g_list_delete_link (union_monitor->monitors, l);
+
+  g_signal_handlers_disconnect_by_func (child_monitor, child_bookmark_added, union_monitor);
+  g_signal_handlers_disconnect_by_func (child_monitor, child_bookmark_removed, union_monitor);
+  g_signal_handlers_disconnect_by_func (child_monitor, child_bookmark_changed, union_monitor);
+}
+
+static GType
+get_default_native_class (gpointer data)
+{
+  GNativeBookmarkMonitorClass *klass, *native_class, **native_class_out;
+  const char *use_this;
+  GIOExtensionPoint *ep;
+  GIOExtension *extension;
+  GList *l;
+
+  native_class_out = data;
+
+  use_this = g_getenv ("GIO_USE_BOOKMARK_MONITOR");
+
+  /* Ensure vfs in modules loaded */
+  _g_io_modules_ensure_loaded ();
+
+  ep = g_io_extension_point_lookup (G_NATIVE_BOOKMARK_MONITOR_EXTENSION_POINT_NAME);
+  native_class = NULL;
+  if (use_this)
+    {
+      extension = g_io_extension_point_get_extension_by_name (ep, use_this);
+      if (extension)
+        {
+          klass = G_NATIVE_BOOKMARK_MONITOR_CLASS (g_io_extension_ref_class (extension));
+          if (G_BOOKMARK_MONITOR_CLASS (klass)->is_supported())
+            native_class = klass;
+          else
+            g_type_class_unref (klass);
+        }
+    }
+
+  if (native_class == NULL)
+    {
+      for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
+        {
+          extension = l->data;
+          klass = G_NATIVE_BOOKMARK_MONITOR_CLASS (g_io_extension_ref_class (extension));
+          if (G_BOOKMARK_MONITOR_CLASS (klass)->is_supported())
+            {
+              native_class = klass;
+              break;
+            }
+          else
+            g_type_class_unref (klass);
+        }
+    }
+
+  if (native_class)
+    {
+      *native_class_out = native_class;
+      return G_TYPE_FROM_CLASS (native_class);
+    }
+  else
+    return G_TYPE_INVALID;
+}
+
+/* We return the class, with a ref taken.
+ * This way we avoid unloading the class/module
+ * between selecting the type and creating the
+ * instance on the first call.
+ */
+static GNativeBookmarkMonitorClass *
+get_native_class (void)
+{
+  static GOnce once_init = G_ONCE_INIT;
+  GTypeClass *type_class;
+
+  type_class = NULL;
+  g_once (&once_init, (GThreadFunc)get_default_native_class, &type_class);
+
+  if (type_class == NULL && once_init.retval != GUINT_TO_POINTER(G_TYPE_INVALID))
+    type_class = g_type_class_ref ((GType)once_init.retval);
+
+  return (GNativeBookmarkMonitorClass *)type_class;
+}
+
+static void
+g_union_bookmark_monitor_init (GUnionBookmarkMonitor *union_monitor)
+{
+}
+
+static void
+populate_union_monitor (GUnionBookmarkMonitor *union_monitor)
+{
+  GBookmarkMonitor *monitor;
+  GNativeBookmarkMonitorClass *native_class;
+  GBookmarkMonitorClass *klass;
+  GIOExtensionPoint *ep;
+  GIOExtension *extension;
+  GList *l;
+
+  native_class = get_native_class ();
+
+  if (native_class != NULL)
+    {
+      monitor = g_object_new (G_TYPE_FROM_CLASS (native_class), NULL);
+      g_union_bookmark_monitor_add_monitor (union_monitor, monitor);
+      g_object_unref (monitor);
+      g_type_class_unref (native_class);
+    }
+
+  ep = g_io_extension_point_lookup (G_BOOKMARK_MONITOR_EXTENSION_POINT_NAME);
+  for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
+    {
+      extension = l->data;
+
+      klass = G_BOOKMARK_MONITOR_CLASS (g_io_extension_ref_class (extension));
+      if (klass->is_supported == NULL || klass->is_supported())
+        {
+          monitor = g_object_new (g_io_extension_get_type (extension), NULL);
+          g_union_bookmark_monitor_add_monitor (union_monitor, monitor);
+          g_object_unref (monitor);
+        }
+      g_type_class_unref (klass);
+    }
+}
+
+static GUnionBookmarkMonitor *
+g_union_bookmark_monitor_new (void)
+{
+  GUnionBookmarkMonitor *monitor;
+
+  monitor = g_object_new (G_TYPE_UNION_BOOKMARK_MONITOR, NULL);
+
+  return monitor;
+}
+
+/**
+ * g_bookmark_monitor_get:
+ *
+ * Gets the bookmark monitor used by gio.
+ *
+ * Returns: a reference to the #GBookmarkMonitor used by gio. Call
+ *    g_object_unref() when done with it.
+ **/
+GBookmarkMonitor *
+g_bookmark_monitor_get (void)
+{
+  GBookmarkMonitor *monitor;
+
+  g_static_rec_mutex_lock (&the_bookmark_monitor_mutex);
+
+  if (the_bookmark_monitor)
+    monitor = G_BOOKMARK_MONITOR (g_object_ref (the_bookmark_monitor));
+  else
+    {
+      the_bookmark_monitor = g_union_bookmark_monitor_new ();
+      populate_union_monitor (the_bookmark_monitor);
+      monitor = G_BOOKMARK_MONITOR (the_bookmark_monitor);
+    }
+
+  g_static_rec_mutex_unlock (&the_bookmark_monitor_mutex);
+
+  return monitor;
+}
+
+#define __G_UNION_BOOKMARK_MONITOR_C__
+#include "gioaliasdef.c"
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/gunionbookmarkmonitor.h	2008-10-11 22:02:42.000000000 -0400
@@ -0,0 +1,48 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#ifndef __G_UNION_BOOKMARK_MONITOR_H__
+#define __G_UNION_BOOKMARK_MONITOR_H__
+
+#include <gio/gbookmarkmonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_UNION_BOOKMARK_MONITOR        (_g_union_bookmark_monitor_get_type ())
+#define G_UNION_BOOKMARK_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNION_BOOKMARK_MONITOR, GUnionBookmarkMonitor))
+#define G_UNION_BOOKMARK_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_UNION_BOOKMARK_MONITOR, GUnionBookmarkMonitorClass))
+#define G_IS_UNION_BOOKMARK_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNION_BOOKMARK_MONITOR))
+#define G_IS_UNION_BOOKMARK_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_UNION_BOOKMARK_MONITOR))
+
+typedef struct _GUnionBookmarkMonitor      GUnionBookmarkMonitor;
+typedef struct _GUnionBookmarkMonitorClass GUnionBookmarkMonitorClass;
+
+struct _GUnionBookmarkMonitorClass
+{
+  GBookmarkMonitorClass parent_class;
+};
+
+GType _g_union_bookmark_monitor_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_UNION_BOOKMARK_MONITOR_H__ */
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/ggtkbookmark.c	2008-10-12 13:42:23.000000000 -0400
@@ -0,0 +1,363 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include "ggtkbookmark.h"
+#include "gbookmark.h"
+#include "gfile.h"
+#include "gioerror.h"
+
+#include "gioalias.h"
+
+struct _GGtkBookmark {
+  GObject parent;
+
+  GGtkBookmarkMonitor *monitor;
+
+  gint position;
+  char *uri;
+  char *title;
+  char *description;
+  GIcon *icon;
+};
+
+static void g_gtk_bookmark_bookmark_iface_init (GBookmarkIface *iface);
+
+#define g_gtk_bookmark_get_type _g_gtk_bookmark_get_type
+G_DEFINE_TYPE_WITH_CODE (GGtkBookmark, g_gtk_bookmark, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_BOOKMARK,
+                                                g_gtk_bookmark_bookmark_iface_init))
+
+static void
+g_gtk_bookmark_finalize (GObject *object)
+{
+  GGtkBookmark *bookmark;
+
+  bookmark = G_GTK_BOOKMARK (object);
+
+  if (bookmark->monitor != NULL)
+    g_object_unref (bookmark->monitor);
+
+  g_free (bookmark->uri);
+  g_free (bookmark->title);
+  g_free (bookmark->description);
+  if (bookmark->icon != NULL)
+    g_object_unref (bookmark->icon);
+
+  G_OBJECT_CLASS (g_gtk_bookmark_parent_class)->finalize (object);
+}
+
+static void
+g_gtk_bookmark_class_init (GGtkBookmarkClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_gtk_bookmark_finalize;
+}
+
+static void
+g_gtk_bookmark_init (GGtkBookmark *gtk_bookmark)
+{
+}
+
+GGtkBookmark *
+_g_gtk_bookmark_new (GGtkBookmarkMonitor *monitor,
+                     gint                 position,
+                     const char          *uri,
+                     const char          *title,
+                     const char          *description,
+                     GIcon               *icon)
+{
+  GGtkBookmark *bookmark;
+
+  bookmark = g_object_new (G_TYPE_GTK_BOOKMARK, NULL);
+  bookmark->monitor = G_GTK_BOOKMARK_MONITOR (g_object_ref (monitor));
+  bookmark->position = position;
+  bookmark->uri = g_strdup (uri);
+  bookmark->title = g_strdup (title);
+  bookmark->description = g_strdup (description);
+  bookmark->icon = icon != NULL ? g_object_ref (icon) : NULL;
+
+  return bookmark;
+}
+
+static GBookmarkKind
+g_gtk_bookmark_get_kind (GBookmark *bookmark)
+{
+  return G_BOOKMARK_KIND_SHORTCUT;
+}
+
+static GBookmarkFlags
+g_gtk_bookmark_get_flags (GBookmark *bookmark)
+{
+  return G_BOOKMARK_FLAG_USER | G_BOOKMARK_FLAG_WRITABLE;
+}
+
+
+static GFile *
+g_gtk_bookmark_get_location (GBookmark *bookmark)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return g_file_new_for_uri (gtk_bookmark->uri);
+}
+
+static gint
+g_gtk_bookmark_get_position (GBookmark *bookmark)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return gtk_bookmark->position;
+}
+
+static char *
+g_gtk_bookmark_get_title (GBookmark *bookmark)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return g_strdup (gtk_bookmark->title);
+}
+
+static char *
+g_gtk_bookmark_get_description (GBookmark *bookmark)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return g_strdup (gtk_bookmark->description);
+}
+
+static GIcon *
+g_gtk_bookmark_get_icon (GBookmark *bookmark)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  if (gtk_bookmark->icon != NULL)
+    return g_object_ref (gtk_bookmark->icon);
+  else
+    return NULL;
+}
+
+static gboolean
+g_gtk_bookmark_set_location (GBookmark   *bookmark,
+                             GFile       *location,
+                             GError     **error)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return _g_gtk_bookmark_monitor_set_location (gtk_bookmark->monitor,
+                                               gtk_bookmark,
+                                               location,
+                                               error);
+}
+
+static gboolean
+g_gtk_bookmark_set_title (GBookmark   *bookmark,
+                          const char  *title,
+                          GError     **error)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return _g_gtk_bookmark_monitor_set_title (gtk_bookmark->monitor,
+                                            gtk_bookmark,
+                                            title,
+                                            error);
+}
+
+static gboolean
+g_gtk_bookmark_set_description (GBookmark   *bookmark,
+                                const char  *description,
+                                GError     **error)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return _g_gtk_bookmark_monitor_set_description (gtk_bookmark->monitor,
+                                                  gtk_bookmark,
+                                                  description,
+                                                  error);
+}
+
+static gboolean
+g_gtk_bookmark_set_position (GBookmark   *bookmark,
+                             gint         position,
+                             GError     **error)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return _g_gtk_bookmark_monitor_set_position (gtk_bookmark->monitor,
+                                               gtk_bookmark,
+                                               position,
+                                               error);
+}
+
+static gboolean
+g_gtk_bookmark_set_icon (GBookmark   *bookmark,
+                         GIcon       *icon,
+                         GError     **error)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return _g_gtk_bookmark_monitor_set_icon (gtk_bookmark->monitor,
+                                           gtk_bookmark,
+                                           icon,
+                                           error);
+}
+
+static gboolean
+g_gtk_bookmark_delete (GBookmark   *bookmark,
+                       GError     **error)
+{
+  GGtkBookmark *gtk_bookmark = G_GTK_BOOKMARK (bookmark);
+  return _g_gtk_bookmark_monitor_delete (gtk_bookmark->monitor,
+                                         gtk_bookmark,
+                                         error);
+}
+
+static void
+g_gtk_bookmark_bookmark_iface_init (GBookmarkIface *iface)
+{
+  iface->get_kind        = g_gtk_bookmark_get_kind;
+  iface->get_flags       = g_gtk_bookmark_get_flags;
+
+  iface->get_location    = g_gtk_bookmark_get_location;
+  iface->get_title       = g_gtk_bookmark_get_title;
+  iface->get_description = g_gtk_bookmark_get_description;
+  iface->get_position    = g_gtk_bookmark_get_position;
+  iface->get_icon        = g_gtk_bookmark_get_icon;
+
+  iface->set_location    = g_gtk_bookmark_set_location;
+  iface->set_title       = g_gtk_bookmark_set_title;
+  iface->set_description = g_gtk_bookmark_set_description;
+  iface->set_position    = g_gtk_bookmark_set_position;
+  iface->set_icon        = g_gtk_bookmark_set_icon;
+
+  iface->delete          = g_gtk_bookmark_delete;
+}
+
+gint
+_g_gtk_bookmark_get_position (GGtkBookmark *bookmark)
+{
+  return bookmark->position;
+}
+
+const char *
+_g_gtk_bookmark_get_uri (GGtkBookmark *bookmark)
+{
+  return bookmark->uri;
+}
+
+const char *
+_g_gtk_bookmark_get_title (GGtkBookmark *bookmark)
+{
+  return bookmark->title;
+}
+
+const char *
+_g_gtk_bookmark_get_description (GGtkBookmark *bookmark)
+{
+  return bookmark->description;
+}
+
+GIcon *
+_g_gtk_bookmark_get_icon (GGtkBookmark *bookmark)
+{
+  return bookmark->icon;
+}
+
+static int
+safe_strcmp (const char *a, const char *b)
+{
+  if (a == NULL && b == NULL)
+    return 0;
+  else if (a == NULL && b != NULL)
+    return 1;
+  else if (a != NULL && b == NULL)
+    return -1;
+  else
+    return strcmp (a, b);
+}
+
+gboolean
+_g_gtk_bookmark_equal (GGtkBookmark *a,
+                       GGtkBookmark *b)
+{
+  gboolean res;
+
+  res = FALSE;
+
+  if (strcmp (a->uri, b->uri) != 0)
+    goto out;
+
+  if (safe_strcmp (a->title, b->title) != 0)
+    goto out;
+
+  if (safe_strcmp (a->description, b->description) != 0)
+    goto out;
+
+  if (!g_icon_equal (a->icon, b->icon))
+    goto out;
+
+  res = TRUE;
+
+ out:
+  return res;
+}
+
+void _g_gtk_bookmark_copy (GGtkBookmark *destination,
+                           GGtkBookmark *source)
+{
+  _g_gtk_bookmark_set_uri (destination, source->uri);
+  _g_gtk_bookmark_set_title (destination, source->title);
+  _g_gtk_bookmark_set_description (destination, source->description);
+  _g_gtk_bookmark_set_icon (destination, source->icon);
+  destination->position = source->position;
+}
+
+void
+_g_gtk_bookmark_set_uri (GGtkBookmark        *bookmark,
+                         const char          *uri)
+{
+  g_free (bookmark->uri);
+  bookmark->uri = g_strdup (uri);
+}
+
+void
+_g_gtk_bookmark_set_title (GGtkBookmark        *bookmark,
+                           const char          *title)
+{
+  g_free (bookmark->title);
+  bookmark->title = g_strdup (title);
+}
+
+void
+_g_gtk_bookmark_set_description (GGtkBookmark        *bookmark,
+                                 const char          *description)
+{
+  g_free (bookmark->description);
+  bookmark->description = g_strdup (description);
+}
+
+void
+_g_gtk_bookmark_set_icon (GGtkBookmark        *bookmark,
+                          GIcon               *icon)
+{
+  if (bookmark->icon != NULL)
+    g_object_unref (bookmark->icon);
+  bookmark->icon = icon != NULL ? g_object_ref (icon) : NULL;;
+}
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/ggtkbookmark.h	2008-10-12 13:33:00.000000000 -0400
@@ -0,0 +1,73 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#ifndef __G_GTK_BOOKMARK_H__
+#define __G_GTK_BOOKMARK_H__
+
+#include <gio/giotypes.h>
+#include <gio/ggtkbookmarkmonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_GTK_BOOKMARK        (_g_gtk_bookmark_get_type ())
+#define G_GTK_BOOKMARK(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GTK_BOOKMARK, GGtkBookmark))
+#define G_GTK_BOOKMARK_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GTK_BOOKMARK, GGtkBookmarkClass))
+#define G_IS_GTK_BOOKMARK(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GTK_BOOKMARK))
+#define G_IS_GTK_BOOKMARK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GTK_BOOKMARK))
+
+typedef struct _GGtkBookmarkClass GGtkBookmarkClass;
+
+struct _GGtkBookmarkClass
+{
+  GObjectClass parent_class;
+};
+
+GType         _g_gtk_bookmark_get_type         (void) G_GNUC_CONST;
+GGtkBookmark *_g_gtk_bookmark_new              (GGtkBookmarkMonitor *monitor,
+                                                gint                 position,
+                                                const char          *uri,
+                                                const char          *title,
+                                                const char          *description,
+                                                GIcon               *icon);
+gint          _g_gtk_bookmark_get_position     (GGtkBookmark        *bookmark);
+const char   *_g_gtk_bookmark_get_uri          (GGtkBookmark        *bookmark);
+const char   *_g_gtk_bookmark_get_title        (GGtkBookmark        *bookmark);
+const char   *_g_gtk_bookmark_get_description  (GGtkBookmark        *bookmark);
+GIcon        *_g_gtk_bookmark_get_icon         (GGtkBookmark        *bookmark);
+
+void          _g_gtk_bookmark_set_uri          (GGtkBookmark        *bookmark,
+                                                const char          *uri);
+void          _g_gtk_bookmark_set_title        (GGtkBookmark        *bookmark,
+                                                const char          *title);
+void          _g_gtk_bookmark_set_description  (GGtkBookmark        *bookmark,
+                                                const char          *description);
+void          _g_gtk_bookmark_set_icon         (GGtkBookmark        *bookmark,
+                                                GIcon               *icon);
+
+gboolean      _g_gtk_bookmark_equal            (GGtkBookmark        *a,
+                                                GGtkBookmark        *b);
+void          _g_gtk_bookmark_copy             (GGtkBookmark        *destination,
+                                                GGtkBookmark        *source);
+
+G_END_DECLS
+
+#endif /* __G_GTK_BOOKMARK_H__ */
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/ggtkbookmarkmonitor.c	2008-10-12 13:49:48.000000000 -0400
@@ -0,0 +1,776 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+#include "ggtkbookmarkmonitor.h"
+#include "ggtkbookmark.h"
+#include "gfile.h"
+#include "gicon.h"
+#include "gioerror.h"
+#include "giomodule.h"
+
+#include "glibintl.h"
+
+#include "gioalias.h"
+
+struct _GGtkBookmarkMonitor {
+  GNativeBookmarkMonitor parent;
+
+  GFileMonitor *file_monitor;
+
+  GList *bookmarks;
+};
+
+static void update_bookmarks (GGtkBookmarkMonitor *monitor,
+                              gboolean             emit_signals);
+
+#define g_gtk_bookmark_monitor_get_type _g_gtk_bookmark_monitor_get_type
+G_DEFINE_TYPE_WITH_CODE (GGtkBookmarkMonitor, g_gtk_bookmark_monitor, G_TYPE_NATIVE_BOOKMARK_MONITOR,
+                         g_io_extension_point_implement (G_NATIVE_BOOKMARK_MONITOR_EXTENSION_POINT_NAME,
+                                                         g_define_type_id,
+                                                         "gtk",
+                                                         0));
+
+static void
+g_gtk_bookmark_monitor_finalize (GObject *object)
+{
+  GGtkBookmarkMonitor *monitor;
+
+  monitor = G_GTK_BOOKMARK_MONITOR (object);
+
+  g_list_foreach (monitor->bookmarks, (GFunc) g_object_unref, NULL);
+  g_list_free (monitor->bookmarks);
+
+  if (monitor->file_monitor != NULL)
+    g_object_unref (monitor->file_monitor);
+
+  G_OBJECT_CLASS (g_gtk_bookmark_monitor_parent_class)->finalize (object);
+}
+
+static int
+compare_bookmark_by_position (GGtkBookmark *a, GGtkBookmark *b)
+{
+  return _g_gtk_bookmark_get_position (a) - _g_gtk_bookmark_get_position (b);
+}
+
+static int
+compare_bookmark_by_uri (GGtkBookmark *a, GGtkBookmark *b)
+{
+  const char *ua;
+  const char *ub;
+
+  ua = _g_gtk_bookmark_get_uri (a);
+  ub = _g_gtk_bookmark_get_uri (b);
+
+  return strcmp (ua, ub);
+}
+
+static GList *
+get_bookmarks (GBookmarkMonitor *bookmark_monitor)
+{
+  GGtkBookmarkMonitor *monitor;
+  GList *l;
+
+  monitor = G_GTK_BOOKMARK_MONITOR (bookmark_monitor);
+
+  l = g_list_copy (monitor->bookmarks);
+  l = g_list_sort (l, (GCompareFunc) compare_bookmark_by_position);
+  g_list_foreach (l, (GFunc) g_object_ref, NULL);
+
+  return l;
+}
+
+static gboolean
+is_supported (void)
+{
+  return TRUE;
+}
+
+static void
+g_gtk_bookmark_monitor_class_init (GGtkBookmarkMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GBookmarkMonitorClass *monitor_class = G_BOOKMARK_MONITOR_CLASS (klass);
+
+  gobject_class->finalize = g_gtk_bookmark_monitor_finalize;
+
+  monitor_class->get_bookmarks = get_bookmarks;
+  monitor_class->is_supported = is_supported;
+}
+
+static GFile *
+get_gtk_bookmarks_file (void)
+{
+  char *s;
+  GFile *f;
+
+  s = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
+  f = g_file_new_for_path (s);
+  g_free (s);
+
+  return f;
+}
+
+static GFile *
+get_gtk_bookmarks_sidecar_file (void)
+{
+  char *s;
+  GFile *f;
+
+  s = g_build_filename (g_get_home_dir (), ".gtk-bookmarks-sidecar", NULL);
+  f = g_file_new_for_path (s);
+  g_free (s);
+
+  return f;
+}
+
+static void
+bookmarks_file_changed (GFileMonitor        *monitor,
+                        GFile               *file,
+                        GFile               *other_file,
+                        GFileMonitorEvent    event_type,
+                        GGtkBookmarkMonitor *bookmark_monitor)
+{
+  update_bookmarks (bookmark_monitor, TRUE);
+}
+
+static void
+g_gtk_bookmark_monitor_init (GGtkBookmarkMonitor *monitor)
+{
+  GError *error;
+  GFile *f;
+
+  f = get_gtk_bookmarks_file ();
+  error = NULL;
+  monitor->file_monitor = g_file_monitor_file (f, G_FILE_MONITOR_NONE, NULL, &error);
+  if (monitor->file_monitor == NULL)
+    {
+      g_warning ("Cannot monitor gtk bookmarks file: %s", error->message);
+      g_error_free (error);
+    }
+  else
+    {
+      g_signal_connect (monitor->file_monitor,
+                        "changed",
+                        G_CALLBACK (bookmarks_file_changed),
+                        monitor);
+    }
+  g_object_unref (f);
+
+  /* don't emit signals on construction */
+  update_bookmarks (monitor, FALSE);
+}
+
+GBookmarkMonitor *
+_g_gtk_bookmark_monitor_new (void)
+{
+  GGtkBookmarkMonitor *monitor;
+
+  monitor = g_object_new (G_TYPE_GTK_BOOKMARK_MONITOR,
+                          NULL);
+
+  return G_BOOKMARK_MONITOR (monitor);
+}
+
+static GList *
+parse_bookmark_file (GGtkBookmarkMonitor  *monitor,
+                     GError              **error)
+{
+  GList *ret;
+  int n;
+  GFile *bookmarks_file;
+  GFile *bookmarks_sidecar_file;
+  GError *local_error;
+  char *contents;
+  char **lines;
+  char *sidecar_contents;
+  GKeyFile *sidecar_key_file;
+
+  ret = NULL;
+  contents = NULL;
+  sidecar_contents = NULL;
+  sidecar_key_file = NULL;
+
+  bookmarks_file = get_gtk_bookmarks_file ();
+  bookmarks_sidecar_file = get_gtk_bookmarks_sidecar_file ();
+
+  local_error = NULL;
+  if (!g_file_load_contents (bookmarks_file, NULL, &contents, NULL, NULL, &local_error))
+    {
+      if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+        {
+          g_error_free (local_error);
+        }
+      else
+        {
+          g_propagate_error (error, local_error);
+        }
+      goto out;
+  }
+
+  local_error = NULL;
+  if (!g_file_load_contents (bookmarks_sidecar_file, NULL, &sidecar_contents, NULL, NULL, &local_error))
+    {
+      if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+        {
+          g_error_free (local_error);
+        }
+      else
+        {
+          /* this is not fatal */
+          g_warning ("Cannot load gtk bookmarks sidecar: %s", local_error->message);
+          g_error_free (local_error);
+        }
+  }
+
+  if (sidecar_contents != NULL && strlen (sidecar_contents) > 0)
+    {
+      sidecar_key_file = g_key_file_new ();
+      local_error = NULL;
+      if (!g_key_file_load_from_data (sidecar_key_file,
+                                      sidecar_contents,
+                                      strlen (sidecar_contents),
+                                      G_KEY_FILE_NONE,
+                                      &local_error))
+        {
+          if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+            {
+              g_error_free (local_error);
+            }
+          else
+            {
+              /* this is not fatal */
+              g_warning ("Cannot parse gtk bookmarks sidecar: %s", local_error->message);
+              g_error_free (local_error);
+            }
+        }
+    }
+
+  lines = g_strsplit (contents, "\n", -1);
+  for (n = 0; lines[n] != NULL; n++)
+    {
+      char **tokens;
+
+      tokens = g_strsplit (lines[n], " ", 2);
+      if (tokens[0] != NULL)
+        {
+          GGtkBookmark *bookmark;
+          const char *uri;
+          const char *title;
+          char *description;
+          char *icon_str;
+          GIcon *icon;
+
+          description = NULL;
+          icon_str = NULL;
+          icon = NULL;
+
+          uri = tokens[0];
+          title = tokens[1];
+
+          if (sidecar_key_file != NULL)
+            {
+              description = g_key_file_get_string (sidecar_key_file,
+                                                   uri,
+                                                   "Description",
+                                                   NULL);
+
+              icon_str = g_key_file_get_string (sidecar_key_file,
+                                                uri,
+                                                "GIcon",
+                                                NULL);
+              if (icon_str != NULL)
+                {
+                  GError *local_error;
+                  local_error = NULL; /* non-fatal if the GIcon string is malformed */
+                  icon = g_icon_new_for_string (icon_str, &local_error);
+                  if (icon == NULL)
+                    {
+                      g_warning ("Error parsing GIcon for bookmark with URI %s: %s", uri, local_error->message);
+                      g_error_free (local_error);
+                    }
+                }
+            }
+
+
+          bookmark = _g_gtk_bookmark_new (monitor,
+                                          n,
+                                          uri,
+                                          title,
+                                          description,
+                                          icon);
+
+          ret = g_list_prepend (ret, bookmark);
+
+          g_free (description);
+          g_free (icon_str);
+          if (icon != NULL)
+            g_object_unref (icon);
+        }
+
+      g_strfreev (tokens);
+    }
+  g_strfreev (lines);
+
+ out:
+  if (sidecar_key_file != NULL)
+    g_key_file_free (sidecar_key_file);
+  g_free (contents);
+  g_free (sidecar_contents);
+  g_object_unref (bookmarks_file);
+  g_object_unref (bookmarks_sidecar_file);
+
+  ret = g_list_reverse (ret);
+
+  return ret;
+}
+
+static gboolean
+save_bookmarks_file (GGtkBookmarkMonitor  *monitor,
+                     GList                *bookmarks,
+                     GError              **error)
+{
+  GList *l;
+  GFile *bookmarks_file;
+  GFile *bookmarks_sidecar_file;
+  GString *str;
+  char *contents;
+  char *sidecar_contents;
+  gboolean ret;
+  GKeyFile *sidecar_key_file;
+
+  ret = FALSE;
+  bookmarks_file = NULL;
+  bookmarks_sidecar_file = NULL;
+  contents = NULL;
+  sidecar_contents = NULL;
+
+  str = g_string_new (NULL);
+  sidecar_key_file = g_key_file_new ();
+  for (l = bookmarks; l != NULL; l = l->next)
+    {
+      GGtkBookmark *bookmark = G_GTK_BOOKMARK (l->data);
+      const char *uri;
+      const char *title;
+      const char *description;
+      GIcon *icon;
+
+      uri = _g_gtk_bookmark_get_uri (bookmark);
+      title = _g_gtk_bookmark_get_title (bookmark);
+      description = _g_gtk_bookmark_get_description (bookmark);
+      icon = _g_gtk_bookmark_get_icon (bookmark);
+
+      if (title != NULL && strlen (title) > 0)
+        g_string_append_printf (str, "%s %s\n", uri, title);
+      else
+        g_string_append_printf (str, "%s\n", uri);
+
+      if (description != NULL)
+        g_key_file_set_string (sidecar_key_file,
+                               uri,
+                               "Description",
+                               description);
+
+      if (icon != NULL)
+        {
+          char *icon_str;
+          icon_str = g_icon_to_string (icon);
+          g_key_file_set_string (sidecar_key_file,
+                                 uri,
+                                 "GIcon",
+                                 icon_str);
+          g_free (icon_str);
+        }
+    }
+  contents = g_string_free (str, FALSE);
+  sidecar_contents = g_key_file_to_data (sidecar_key_file, NULL, NULL);
+  g_key_file_free (sidecar_key_file);
+
+  bookmarks_file = get_gtk_bookmarks_file ();
+  bookmarks_sidecar_file = get_gtk_bookmarks_sidecar_file ();
+
+  /* TODO: Maybe use CREATE_PRIVATE? */
+  if (!g_file_replace_contents (bookmarks_sidecar_file,
+                                sidecar_contents,
+                                strlen (sidecar_contents),
+                                NULL,
+                                FALSE,
+                                G_FILE_CREATE_NONE,
+                                NULL,
+                                NULL,
+                                error))
+    goto out;
+  if (!g_file_replace_contents (bookmarks_file,
+                                contents,
+                                strlen (contents),
+                                NULL,
+                                FALSE,
+                                G_FILE_CREATE_NONE,
+                                NULL,
+                                NULL,
+                                error))
+    goto out;
+
+  ret = TRUE;
+
+  /* TODO: reload bookmarks? */
+
+ out:
+  g_free (contents);
+  g_free (sidecar_contents);
+  if (sidecar_key_file != NULL)
+  if (bookmarks_sidecar_file != NULL)
+    g_object_unref (bookmarks_sidecar_file);
+  if (bookmarks_file != NULL)
+    g_object_unref (bookmarks_file);
+
+  return ret;
+}
+
+static void
+diff_sorted_lists (GList         *list1,
+                   GList         *list2,
+                   GCompareFunc   compare,
+                   GList        **added,
+                   GList        **removed,
+                   GList        **same)
+{
+  int order;
+
+  *added = *removed = *same = NULL;
+
+  while (list1 != NULL &&
+         list2 != NULL)
+    {
+      order = (*compare) (list1->data, list2->data);
+      if (order < 0)
+        {
+          *removed = g_list_prepend (*removed, list1->data);
+          list1 = list1->next;
+        }
+      else if (order > 0)
+        {
+          *added = g_list_prepend (*added, list2->data);
+          list2 = list2->next;
+        }
+      else
+        { /* same item */
+          *same = g_list_prepend (*same, list2->data);
+          *same = g_list_prepend (*same, list1->data);
+          list1 = list1->next;
+          list2 = list2->next;
+        }
+    }
+
+  while (list1 != NULL)
+    {
+      *removed = g_list_prepend (*removed, list1->data);
+      list1 = list1->next;
+    }
+  while (list2 != NULL)
+    {
+      *added = g_list_prepend (*added, list2->data);
+      list2 = list2->next;
+    }
+}
+
+static void
+update_bookmarks (GGtkBookmarkMonitor *monitor, gboolean emit_signals)
+{
+  GList *new_bookmarks;
+  GList *added;
+  GList *removed;
+  GList *same;
+  GList *l;
+
+  new_bookmarks = parse_bookmark_file (monitor, NULL);
+  new_bookmarks = g_list_sort (new_bookmarks, (GCompareFunc) compare_bookmark_by_uri);
+  monitor->bookmarks = g_list_sort (monitor->bookmarks, (GCompareFunc) compare_bookmark_by_uri);
+
+  diff_sorted_lists (monitor->bookmarks,
+                     new_bookmarks,
+                     (GCompareFunc) compare_bookmark_by_uri,
+                     &added,
+                     &removed,
+                     &same);
+
+  for (l = removed; l != NULL; l = l->next)
+    {
+      GGtkBookmark *bookmark = G_GTK_BOOKMARK (l->data);
+
+      monitor->bookmarks = g_list_remove (monitor->bookmarks, bookmark);
+      if (emit_signals)
+        {
+          g_signal_emit_by_name (monitor, "bookmark-removed", bookmark);
+          g_signal_emit_by_name (bookmark, "removed");
+        }
+      g_object_unref (bookmark);
+    }
+
+  for (l = added; l != NULL; l = l->next)
+    {
+      GGtkBookmark *bookmark = G_GTK_BOOKMARK (l->data);
+
+      monitor->bookmarks = g_list_prepend (monitor->bookmarks, g_object_ref (bookmark));
+      if (emit_signals)
+        {
+          g_signal_emit_by_name (monitor, "bookmark-added", bookmark);
+        }
+    }
+
+  for (l = same; l != NULL; l = l->next)
+    {
+      GGtkBookmark *existing_bookmark;
+      GGtkBookmark *bookmark;
+
+      existing_bookmark = G_GTK_BOOKMARK (l->data);
+      l = l->next;
+      g_assert (l != NULL);
+      bookmark = G_GTK_BOOKMARK (l->data);
+
+      if (!_g_gtk_bookmark_equal (existing_bookmark, bookmark))
+        {
+          _g_gtk_bookmark_copy (existing_bookmark, bookmark);
+          g_signal_emit_by_name (monitor, "bookmark-changed", bookmark);
+          g_signal_emit_by_name (bookmark, "changed");
+        }
+    }
+
+  g_list_foreach (new_bookmarks, (GFunc) g_object_unref, NULL);
+  g_list_free (new_bookmarks);
+  g_list_free (removed);
+  g_list_free (added);
+  g_list_free (same);
+}
+
+typedef void (*ModifyFunction) (GGtkBookmarkMonitor  *monitor,
+                                GList               **bookmarks,
+                                GGtkBookmark         *bookmark,
+                                gpointer              user_data);
+
+/* generic modifier function */
+static gboolean
+modify_bookmark (GGtkBookmarkMonitor *monitor,
+                 GGtkBookmark        *bookmark,
+                 ModifyFunction       mod_func,
+                 gpointer             mod_user_data,
+                 GError             **error)
+{
+  GList *l, *new_bookmarks;
+  gboolean ret;
+  GError *local_error;
+
+  ret = FALSE;
+  new_bookmarks = NULL;
+
+  local_error = NULL;
+  new_bookmarks = parse_bookmark_file (monitor, &local_error);
+  if (local_error != NULL)
+    {
+      g_propagate_error (error, local_error);
+      goto out;
+    }
+
+  for (l = new_bookmarks; l != NULL; l = l->next)
+    {
+      GGtkBookmark *b = G_GTK_BOOKMARK (l->data);
+      if (strcmp (_g_gtk_bookmark_get_uri (bookmark),
+                  _g_gtk_bookmark_get_uri (b)) == 0)
+        {
+          mod_func (monitor, &new_bookmarks, b, mod_user_data);
+          break;
+        }
+    }
+
+  if (l == NULL)
+    {
+      /* Didn't find bookmark in list */
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_FOUND,
+                           _("Bookmark not found"));
+      goto out;
+    }
+
+  if (!save_bookmarks_file (monitor, new_bookmarks, error))
+    goto out;
+
+  update_bookmarks (monitor, TRUE);
+  ret = TRUE;
+
+ out:
+  g_list_foreach (new_bookmarks, (GFunc) g_object_unref, NULL);
+  g_list_free (new_bookmarks);
+  return ret;
+}
+
+
+static void
+mod_func_set_uri (GGtkBookmarkMonitor  *monitor,
+                  GList               **bookmarks,
+                  GGtkBookmark         *bookmark,
+                  gpointer              user_data)
+{
+  const char *uri = (const char *) user_data;
+  _g_gtk_bookmark_set_uri (bookmark, uri);
+}
+
+gboolean
+_g_gtk_bookmark_monitor_set_location (GGtkBookmarkMonitor *monitor,
+                                      GGtkBookmark        *bookmark,
+                                      GFile               *location,
+                                      GError             **error)
+{
+  char *uri;
+  gboolean ret;
+  uri = g_file_get_uri (location);
+  ret = modify_bookmark (monitor,
+                         bookmark,
+                         mod_func_set_uri,
+                         uri,
+                         error);
+  g_free (uri);
+  return ret;
+}
+
+static void
+mod_func_set_title (GGtkBookmarkMonitor  *monitor,
+                    GList               **bookmarks,
+                    GGtkBookmark         *bookmark,
+                    gpointer              user_data)
+{
+  const char *title = (const char *) user_data;
+  _g_gtk_bookmark_set_title (bookmark, title);
+}
+
+gboolean
+_g_gtk_bookmark_monitor_set_title (GGtkBookmarkMonitor *monitor,
+                                   GGtkBookmark        *bookmark,
+                                   const char          *title,
+                                   GError             **error)
+{
+  return modify_bookmark (monitor,
+                          bookmark,
+                          mod_func_set_title,
+                          (gpointer) title,
+                          error);
+}
+
+static void
+mod_func_set_description (GGtkBookmarkMonitor  *monitor,
+                    GList               **bookmarks,
+                    GGtkBookmark         *bookmark,
+                    gpointer              user_data)
+{
+  const char *description = (const char *) user_data;
+  _g_gtk_bookmark_set_description (bookmark, description);
+}
+
+gboolean
+_g_gtk_bookmark_monitor_set_description (GGtkBookmarkMonitor *monitor,
+                                         GGtkBookmark        *bookmark,
+                                         const char          *description,
+                                         GError             **error)
+{
+  return modify_bookmark (monitor,
+                          bookmark,
+                          mod_func_set_description,
+                          (gpointer) description,
+                          error);
+}
+
+static void
+mod_func_set_position (GGtkBookmarkMonitor  *monitor,
+                       GList               **bookmarks,
+                       GGtkBookmark         *bookmark,
+                       gpointer              user_data)
+{
+  gint position = GPOINTER_TO_INT (user_data);
+  *bookmarks = g_list_remove (*bookmarks, bookmark);
+  *bookmarks = g_list_insert (*bookmarks, bookmark, position);
+}
+
+gboolean
+_g_gtk_bookmark_monitor_set_position (GGtkBookmarkMonitor *monitor,
+                                      GGtkBookmark        *bookmark,
+                                      gint                 position,
+                                      GError             **error)
+{
+  return modify_bookmark (monitor,
+                          bookmark,
+                          mod_func_set_position,
+                          GINT_TO_POINTER (position),
+                          error);
+}
+
+
+static void
+mod_func_set_icon (GGtkBookmarkMonitor  *monitor,
+                   GList               **bookmarks,
+                   GGtkBookmark         *bookmark,
+                   gpointer              user_data)
+{
+  GIcon *icon = G_ICON (user_data);
+  _g_gtk_bookmark_set_icon (bookmark, icon);
+}
+
+gboolean
+_g_gtk_bookmark_monitor_set_icon (GGtkBookmarkMonitor *monitor,
+                                  GGtkBookmark        *bookmark,
+                                  GIcon               *icon,
+                                  GError             **error)
+{
+  return modify_bookmark (monitor,
+                          bookmark,
+                          mod_func_set_icon,
+                          icon,
+                          error);
+}
+
+
+static void
+mod_func_delete (GGtkBookmarkMonitor  *monitor,
+                 GList               **bookmarks,
+                 GGtkBookmark         *bookmark,
+                 gpointer              user_data)
+{
+  *bookmarks = g_list_remove (*bookmarks, bookmark);
+  g_object_unref (bookmark);
+}
+
+gboolean
+_g_gtk_bookmark_monitor_delete (GGtkBookmarkMonitor *monitor,
+                                GGtkBookmark        *bookmark,
+                                GError             **error)
+{
+  return modify_bookmark (monitor,
+                          bookmark,
+                          mod_func_delete,
+                          NULL,
+                          error);
+}
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/ggtkbookmarkmonitor.h	2008-10-12 02:48:51.000000000 -0400
@@ -0,0 +1,81 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#ifndef __G_GTK_BOOKMARK_MONITOR_H__
+#define __G_GTK_BOOKMARK_MONITOR_H__
+
+#include <gio/gnativebookmarkmonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_GTK_BOOKMARK_MONITOR        (_g_gtk_bookmark_monitor_get_type ())
+#define G_GTK_BOOKMARK_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GTK_BOOKMARK_MONITOR, GGtkBookmarkMonitor))
+#define G_GTK_BOOKMARK_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GTK_BOOKMARK_MONITOR, GGtkBookmarkMonitorClass))
+#define G_IS_GTK_BOOKMARK_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GTK_BOOKMARK_MONITOR))
+#define G_IS_GTK_BOOKMARK_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GTK_BOOKMARK_MONITOR))
+
+typedef struct _GGtkBookmarkMonitor      GGtkBookmarkMonitor;
+typedef struct _GGtkBookmarkMonitorClass GGtkBookmarkMonitorClass;
+
+/* forward declaration */
+typedef struct _GGtkBookmark GGtkBookmark;
+
+struct _GGtkBookmarkMonitorClass
+{
+  GNativeBookmarkMonitorClass parent_class;
+};
+
+GType              _g_gtk_bookmark_monitor_get_type (void) G_GNUC_CONST;
+GBookmarkMonitor * _g_gtk_bookmark_monitor_new      (void);
+
+gboolean           _g_gtk_bookmark_monitor_set_location (GGtkBookmarkMonitor *monitor,
+                                                         GGtkBookmark        *bookmark,
+                                                         GFile               *location,
+                                                         GError             **error);
+
+gboolean           _g_gtk_bookmark_monitor_set_title (GGtkBookmarkMonitor *monitor,
+                                                      GGtkBookmark        *bookmark,
+                                                      const char          *title,
+                                                      GError             **error);
+
+gboolean           _g_gtk_bookmark_monitor_set_description (GGtkBookmarkMonitor *monitor,
+                                                            GGtkBookmark        *bookmark,
+                                                            const char          *description,
+                                                            GError             **error);
+
+gboolean           _g_gtk_bookmark_monitor_set_position (GGtkBookmarkMonitor *monitor,
+                                                         GGtkBookmark        *bookmark,
+                                                         gint                 position,
+                                                         GError             **error);
+
+gboolean           _g_gtk_bookmark_monitor_set_icon (GGtkBookmarkMonitor *monitor,
+                                                     GGtkBookmark        *bookmark,
+                                                     GIcon               *icon,
+                                                     GError             **error);
+
+gboolean           _g_gtk_bookmark_monitor_delete (GGtkBookmarkMonitor *monitor,
+                                                   GGtkBookmark        *bookmark,
+                                                   GError             **error);
+
+G_END_DECLS
+
+#endif /* __G_GTK_BOOKMARK_MONITOR_H__ */
--- /dev/null	2008-10-04 13:13:26.878056362 -0400
+++ gio/gio-bookmark.c	2008-10-12 13:48:16.000000000 -0400
@@ -0,0 +1,420 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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 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.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include "config.h"
+#include <gio/gio.h>
+
+static GMainLoop *main_loop;
+static GBookmarkMonitor *monitor;
+
+static gboolean opt_list = FALSE;
+static gboolean opt_extra_detail = FALSE;
+static gboolean opt_monitor = FALSE;
+static char *opt_delete = NULL;
+static char *opt_modify = NULL;
+static char *opt_uri = NULL;
+static char *opt_title = NULL;
+static char *opt_description = NULL;
+static gint opt_position = -1;
+static char *opt_icon = NULL;
+
+static const GOptionEntry entries[] =
+{
+  { "list", 'l', 0, G_OPTION_ARG_NONE, &opt_list, "List bookmarks", NULL},
+  { "detail", 'i', 0, G_OPTION_ARG_NONE, &opt_extra_detail, "Show extra information for bookmarks", NULL},
+  { "monitor", 'o', 0, G_OPTION_ARG_NONE, &opt_monitor, "Monitor events from bookmark monitor", NULL},
+  { "delete", 'd', 0, G_OPTION_ARG_STRING, &opt_delete, "Delete bookmark(s) with given URI", NULL},
+  { "modify", 'm', 0, G_OPTION_ARG_STRING, &opt_modify, "Modify bookmark(s) with given URI", NULL},
+  { "uri", 'u', 0, G_OPTION_ARG_STRING, &opt_uri, "Set location", NULL},
+  { "title", 't', 0, G_OPTION_ARG_STRING, &opt_title, "Set title", NULL},
+  { "description", 'e', 0, G_OPTION_ARG_STRING, &opt_description, "Set description", NULL},
+  { "position", 'p', 0, G_OPTION_ARG_INT, &opt_position, "Set position", NULL},
+  { "icon", 'c', 0, G_OPTION_ARG_STRING, &opt_icon, "Set icon", NULL},
+  { NULL }
+};
+
+static const char *
+get_kind_string (GBookmarkKind kind)
+{
+  const char *ret;
+
+  switch (kind)
+    {
+    case G_BOOKMARK_KIND_SHORTCUT:
+      ret = "Shortcut";
+      break;
+    case G_BOOKMARK_KIND_RECENT_FILE:
+      ret = "Recent File";
+      break;
+    default:
+    case G_BOOKMARK_KIND_UNKNOWN:
+      ret = "Unknown";
+      break;
+    }
+
+  return ret;
+}
+
+static void
+print_bookmark (GBookmark *bookmark, int indent)
+{
+  GFile *location;
+  char *uri;
+
+  location = g_bookmark_get_location (bookmark);
+  uri = g_file_get_uri (location);
+
+  if (indent > 0)
+    g_print ("%*s", indent, " ");
+  g_print ("%s\n", uri);
+
+  if (opt_extra_detail)
+    {
+      gint position;
+      char *title;
+      char *description;
+      GBookmarkFlags flags;
+      GBookmarkKind kind;
+      GIcon *icon;
+      char *icon_str;
+      gboolean print_comma;
+
+      position = g_bookmark_get_position (bookmark);
+      kind = g_bookmark_get_kind (bookmark);
+      flags = g_bookmark_get_flags (bookmark);
+      title = g_bookmark_get_title (bookmark);
+      description = g_bookmark_get_description (bookmark);
+      icon = g_bookmark_get_icon (bookmark);
+
+      icon_str = NULL;
+      if (icon != NULL)
+        icon_str = g_icon_to_string (icon);
+
+      g_print ("%*sType:        %s\n", indent + 2, " ", g_type_name (G_TYPE_FROM_INSTANCE (bookmark)));
+      g_print ("%*sKind:        %s\n", indent + 2, " ", get_kind_string (kind));
+      g_print ("%*sPosition:    %d\n", indent + 2, " ", position);
+      g_print ("%*sTitle:       %s\n", indent + 2, " ", title != NULL ? title : "(not set)");
+      g_print ("%*sDescription: %s\n", indent + 2, " ", description != NULL ? description : "(not set)");
+      g_print ("%*sIcon:        %s\n", indent + 2, " ", icon_str != NULL ? icon_str : "(not set)");
+      g_print ("%*sFlags:       ", indent + 2, " ");
+      print_comma = FALSE;
+      if (flags & G_BOOKMARK_FLAG_USER)
+        {
+          g_print ("User");
+          print_comma = TRUE;
+        }
+      if (flags & G_BOOKMARK_FLAG_WRITABLE)
+        {
+          if (print_comma)
+            g_print (", ");
+          g_print ("Writable");
+        }
+      g_print ("\n");
+
+      g_print ("\n");
+
+      g_free (title);
+      g_free (description);
+      g_free (icon_str);
+      if (icon != NULL)
+        g_object_unref (icon);
+    }
+
+  g_free (uri);
+  g_object_unref (location);
+}
+
+static gboolean
+list_bookmarks (void)
+{
+  GList *l, *bookmarks;
+
+  bookmarks = g_bookmark_monitor_get_bookmarks (monitor);
+  for (l = bookmarks; l != NULL; l = l->next)
+    {
+      GBookmark *bookmark = G_BOOKMARK (l->data);
+      print_bookmark (bookmark, 0);
+    }
+  g_list_foreach (bookmarks, (GFunc) g_object_unref, NULL);
+  g_list_free (bookmarks);
+
+  return TRUE;
+}
+
+static void
+monitor_bookmark_added (GBookmarkMonitor *bookmark_monitor,
+                        GBookmark        *bookmark,
+                        gpointer          user_data)
+{
+  g_print ("Bookmark added\n");
+  print_bookmark (bookmark, 2);
+}
+
+static void
+monitor_bookmark_removed (GBookmarkMonitor *bookmark_monitor,
+                          GBookmark        *bookmark,
+                          gpointer          user_data)
+{
+  g_print ("Bookmark removed\n");
+  print_bookmark (bookmark, 2);
+}
+
+static void
+monitor_bookmark_changed (GBookmarkMonitor *bookmark_monitor,
+                          GBookmark        *bookmark,
+                          gpointer          user_data)
+{
+  g_print ("Bookmark changed\n");
+  print_bookmark (bookmark, 2);
+}
+
+static void
+bookmarks_monitor (void)
+{
+  GMainLoop *loop;
+
+  loop = g_main_loop_new (NULL, FALSE);
+
+  g_signal_connect (monitor, "bookmark-added", (GCallback) monitor_bookmark_added, NULL);
+  g_signal_connect (monitor, "bookmark-removed", (GCallback) monitor_bookmark_removed, NULL);
+  g_signal_connect (monitor, "bookmark-changed", (GCallback) monitor_bookmark_changed, NULL);
+
+  g_print ("Monitoring events. Press Ctrl+C to quit.\n");
+
+  g_main_loop_run (main_loop);
+}
+
+static gboolean
+delete_bookmark (const char *uri)
+{
+  GList *l, *bookmarks;
+  GFile *location;
+  gboolean ret;
+  int num_matches;
+
+  ret = TRUE;
+  num_matches = 0;
+
+  location = g_file_new_for_commandline_arg (uri);
+  bookmarks = g_bookmark_monitor_get_bookmarks (monitor);
+  for (l = bookmarks; l != NULL; l = l->next)
+    {
+      GBookmark *bookmark = G_BOOKMARK (l->data);
+      GFile *bookmark_location;
+
+      bookmark_location = g_bookmark_get_location (bookmark);
+      if (g_file_equal (bookmark_location, location))
+        {
+          GError *error;
+          error = NULL;
+          if (!g_bookmark_delete (bookmark, &error))
+            {
+              g_printerr ("Error deleting bookmark for URI %s: %s\n", uri, error->message);
+              g_error_free (error);
+              ret = FALSE;
+            }
+
+          num_matches++;
+        }
+      g_object_unref (bookmark_location);
+    }
+
+  g_object_unref (location);
+
+  g_list_foreach (bookmarks, (GFunc) g_object_unref, NULL);
+  g_list_free (bookmarks);
+
+  if (ret)
+    {
+      if (num_matches == 0)
+        {
+          g_printerr ("No bookmarks matching URI %s\n", uri);
+          ret = FALSE;
+        }
+    }
+
+  return ret;
+}
+
+static gboolean
+modify_bookmark (const char *uri)
+{
+  GList *l, *bookmarks;
+  GFile *location;
+  gboolean ret;
+  int num_matches;
+
+  ret = TRUE;
+  num_matches = 0;
+
+  location = g_file_new_for_commandline_arg (uri);
+  bookmarks = g_bookmark_monitor_get_bookmarks (monitor);
+  for (l = bookmarks; l != NULL; l = l->next)
+    {
+      GBookmark *bookmark = G_BOOKMARK (l->data);
+      GFile *bookmark_location;
+
+      bookmark_location = g_bookmark_get_location (bookmark);
+      if (g_file_equal (bookmark_location, location))
+        {
+          GError *error;
+
+          if (opt_uri != NULL)
+            {
+              GFile *new_location;
+              new_location = g_file_new_for_commandline_arg (opt_uri);
+              error = NULL;
+              if (!g_bookmark_set_location (bookmark, new_location, &error))
+                {
+                  g_printerr ("Error setting location for bookmark with URI %s: %s\n", uri, error->message);
+                  g_error_free (error);
+                  ret = FALSE;
+                }
+              g_object_unref (new_location);
+            }
+
+          if (opt_title != NULL)
+            {
+              error = NULL;
+              if (!g_bookmark_set_title (bookmark, opt_title, &error))
+                {
+                  g_printerr ("Error setting title for bookmark with URI %s: %s\n", uri, error->message);
+                  g_error_free (error);
+                  ret = FALSE;
+                }
+            }
+
+          if (opt_description != NULL)
+            {
+              error = NULL;
+              if (!g_bookmark_set_description (bookmark, opt_description, &error))
+                {
+                  g_printerr ("Error setting description for bookmark with URI %s: %s\n", uri, error->message);
+                  g_error_free (error);
+                  ret = FALSE;
+                }
+            }
+
+          if (opt_position != -1)
+            {
+              error = NULL;
+              if (!g_bookmark_set_position (bookmark, opt_position, &error))
+                {
+                  g_printerr ("Error setting position for bookmark with URI %s: %s\n", uri, error->message);
+                  g_error_free (error);
+                  ret = FALSE;
+                }
+            }
+
+          if (opt_icon != NULL)
+            {
+              GIcon *icon;
+
+              error = NULL;
+              icon = g_icon_new_for_string (opt_icon, &error);
+              if (icon == NULL)
+                {
+                  g_warning ("Malformed icon data passed: %s", error->message);
+                  g_error_free (error);
+                  ret = FALSE;
+                }
+              else
+                {
+                  error = NULL;
+                  if (!g_bookmark_set_icon (bookmark, icon, &error))
+                    {
+                      g_printerr ("Error setting position for bookmark with URI %s: %s\n", uri, error->message);
+                      g_error_free (error);
+                      ret = FALSE;
+                    }
+                  g_object_unref (icon);
+                }
+            }
+
+          num_matches++;
+        }
+      g_object_unref (bookmark_location);
+    }
+
+  g_object_unref (location);
+
+  g_list_foreach (bookmarks, (GFunc) g_object_unref, NULL);
+  g_list_free (bookmarks);
+
+  if (ret)
+    {
+      if (num_matches == 0)
+        {
+          g_printerr ("No bookmarks matching URI %s\n", uri);
+          ret = FALSE;
+        }
+    }
+
+  return ret;
+}
+
+int
+main (int argc, char *argv[])
+{
+  gboolean ret;
+  GError *error;
+  GOptionContext *context;
+
+  ret = FALSE;
+
+  g_type_init ();
+
+  error = NULL;
+  context = g_option_context_new ("- bookmarks");
+  g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+  g_option_context_parse (context, &argc, &argv, &error);
+  g_option_context_free (context);
+
+  main_loop = g_main_loop_new (NULL, FALSE);
+  monitor = g_bookmark_monitor_get ();
+
+  if (opt_list)
+    {
+      ret = list_bookmarks ();
+    }
+  else if (opt_monitor)
+    {
+      bookmarks_monitor ();
+    }
+  else if (opt_delete != NULL)
+    {
+      ret = delete_bookmark (opt_delete);
+    }
+  else if (opt_modify != NULL)
+    {
+      ret = modify_bookmark (opt_modify);
+    }
+  else
+    {
+      /* TODO: print usage */
+    }
+
+  g_object_unref (monitor);
+  g_main_loop_unref (main_loop);
+
+  return ret ? 0 : 1;
+}
