7ae463615df906cbf5ae226c3173302fd70a2502
diff --git a/gtk/gtkfilesystemunix.c b/gtk/gtkfilesystemunix.c
index 38a6cd0..c890ca6 100644
--- a/gtk/gtkfilesystemunix.c
+++ b/gtk/gtkfilesystemunix.c
@@ -46,7 +46,8 @@
 #include <stdio.h>
 #include <time.h>
 
-#define BOOKMARKS_FILENAME ".gtk-bookmarks"
+#define BOOKMARKS_OLD_FILENAME  ".gtk-bookmarks"
+#define BOOKMARKS_FILENAME      "gtk-bookmarks.xbel"
 
 #define HIDDEN_FILENAME ".hidden"
 
@@ -78,6 +79,9 @@ struct _GtkFileSystemUnix
   guint execute_callbacks_idle_id;
   GSList *callbacks;
 
+  GBookmarkFile *bookmarks;
+  time_t bookmarks_last_mtime;
+
   guint have_afs : 1;
   guint have_net : 1;
 };
@@ -358,6 +362,9 @@ gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
 
   system_unix->execute_callbacks_idle_id = 0;
   system_unix->callbacks = NULL;
+
+  system_unix->bookmarks = NULL;
+  system_unix->bookmarks_last_mtime = (time_t) -1;
 }
 
 static void
@@ -1769,17 +1776,6 @@ get_icon_name_for_mime_type (const char *mime_type)
 #endif
 }
 
-static void
-bookmark_list_free (GSList *list)
-{
-  GSList *l;
-
-  for (l = list; l; l = l->next)
-    g_free (l->data);
-
-  g_slist_free (list);
-}
-
 /* Returns whether a URI is a local file:// */
 static gboolean
 is_local_uri (const char *uri)
@@ -1799,81 +1795,174 @@ is_local_uri (const char *uri)
   return result;
 }
 
-static char *
-bookmark_get_filename (void)
+static gchar *
+bookmark_get_filename (gboolean old_format)
 {
-  char *filename;
+  gchar *filename;
+
+  if (!old_format)
+    {
+      filename = g_build_filename (g_get_user_data_dir (),
+                                   "places",
+                                   BOOKMARKS_FILENAME,
+                                   NULL);
+    }
+  else
+    {
+      filename = g_build_filename (g_get_home_dir (),
+                                   BOOKMARKS_OLD_FILENAME,
+                                   NULL);
+    }
 
-  filename = g_build_filename (g_get_home_dir (), 
-			       BOOKMARKS_FILENAME, NULL);
   g_assert (filename != NULL);
   return filename;
 }
 
 static gboolean
-bookmark_list_read (GSList **bookmarks, GError **error)
+bookmark_list_read (GtkFileSystemUnix  *system_unix,
+                    GError            **error)
 {
   gchar *filename;
   gchar *contents;
+  GBookmarkFile *bookmark;
+  struct stat stat_buf;
+  time_t now;
+  gboolean try_old_format = FALSE;
   gboolean result = FALSE;
 
-  filename = bookmark_get_filename ();
-  *bookmarks = NULL;
+  if (!system_unix->bookmarks)
+    {
+      bookmark = g_bookmark_file_new ();
+      system_unix->bookmarks = bookmark;
+    }
+  else
+    bookmark = system_unix->bookmarks;
 
-  if (g_file_get_contents (filename, &contents, NULL, error))
+  filename = g_build_filename (g_get_user_data_dir (), "places", NULL);
+  if (g_mkdir_with_parents (filename, 0700) == -1)
     {
-      gchar **lines = g_strsplit (contents, "\n", -1);
-      int i;
-      GHashTable *table;
+      if (errno != EEXIST)
+        {
+          g_free (filename);
+          g_set_error (error, GTK_FILE_SYSTEM_ERROR,
+                       GTK_FILE_SYSTEM_ERROR_FAILED,
+                       _("Unable to create bookmark directory: %s"),
+                       g_strerror (errno));
+          return FALSE;
+        }
+    }
 
-      table = g_hash_table_new (g_str_hash, g_str_equal);
+  g_free (filename);
 
-      for (i = 0; lines[i]; i++)
-	{
-	  if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
-	    {
-	      *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
-	      g_hash_table_insert (table, lines[i], lines[i]);
-	    }
-	}
+  time (&now);
 
-      g_free (contents);
-      g_hash_table_destroy (table);
-      g_strfreev (lines);
+  filename = bookmark_get_filename (FALSE);
+  if (stat (filename, &stat_buf) == -1)
+    {
+      if (errno != ENOENT)
+        {
+          g_set_error (error, GTK_FILE_SYSTEM_ERROR,
+                       GTK_FILE_SYSTEM_ERROR_FAILED,
+                       _("Bookmark loading failed: %s"),
+                       g_strerror (errno));
+          g_free (filename);
+          return FALSE;
+        }
+      else
+        {
+          /* force a read, and load the old format first */
+          stat_buf.st_mtime = now;
+          system_unix->bookmarks_last_mtime = (time_t) -1;
+          try_old_format = TRUE;
+        }
+    }
 
-      *bookmarks = g_slist_reverse (*bookmarks);
-      result = TRUE;
+  /* read the bookmarks only if the on disk representation
+   * has been changed, or if we didn't load them already
+   */
+  if (stat_buf.st_mtime > system_unix->bookmarks_last_mtime)
+    {
+      /* load new format if we found the file */
+      if (!try_old_format)
+        result = g_bookmark_file_load_from_file (bookmark, filename, error);
+       
+      /* merge with the old one */
+      g_free (filename);
+      filename = bookmark_get_filename (TRUE);
+      if (g_file_get_contents (filename, &contents, NULL, error))
+        {
+          gchar **lines = g_strsplit (contents, "\n", -1);
+          int i;
+
+          for (i = 0; lines[i]; i++)
+            {
+              gchar *uri, *label;
+
+              uri = lines[i];
+              if (!uri || *uri == '\0')
+                break;
+
+              label = strchr (lines[i], ' ');
+              if (label && *label != '\0')
+                {
+                  *label = '\0';
+                  label += 1;
+                }
+
+              if (!g_bookmark_file_has_item (bookmark, uri))
+                {
+                  if (label && *label)
+                    g_bookmark_file_set_title (bookmark, uri, label);
+
+                  /* use what gnome-vfs uses for MIME type */
+                  g_bookmark_file_set_mime_type (bookmark, uri,
+                                                 "x-directory/normal");
+                  g_bookmark_file_add_application (bookmark, uri,
+                                                   NULL, NULL);
+                }
+            }
+
+          g_free (contents);
+          g_strfreev (lines);
+
+          result = TRUE;
+        }
     }
+  else
+    result = TRUE;
 
   g_free (filename);
 
+  if (result)
+    system_unix->bookmarks_last_mtime = stat_buf.st_mtime;
+
   return result;
 }
 
 static gboolean
-bookmark_list_write (GSList  *bookmarks, 
-		     GError **error)
+bookmark_list_write (GtkFileSystemUnix  *system_unix,
+		     GError            **error)
 {
-  GSList *l;
-  GString *string;
-  char *filename;
+  gchar *data;
+  gchar *filename;
+  gsize len;
   GError *tmp_error = NULL;
   gboolean result;
 
-  string = g_string_new ("");
+  filename = g_build_filename (g_get_user_data_dir (), "places", NULL);
+  g_mkdir_with_parents (filename, 0700);
+  g_free (filename);
 
-  for (l = bookmarks; l; l = l->next)
-    {
-      g_string_append (string, l->data);
-      g_string_append_c (string, '\n');
-    }
+  filename = bookmark_get_filename (FALSE);
 
-  filename = bookmark_get_filename ();
+  if (!system_unix->bookmarks)
+    system_unix->bookmarks = g_bookmark_file_new ();
 
-  result = g_file_set_contents (filename, string->str, -1, &tmp_error);
+  data = g_bookmark_file_to_data (system_unix->bookmarks, &len, NULL);
+  result = g_file_set_contents (filename, data, len, &tmp_error);
 
+  g_free (data);
   g_free (filename);
-  g_string_free (string, TRUE);
 
   if (!result)
     {
@@ -1886,6 +1975,9 @@ bookmark_list_write (GSList  *bookmarks,
       g_error_free (tmp_error);
     }
 
+  /* force a reload next time we read the list */
+  system_unix->bookmarks_last_mtime = (time_t) -1;
+
   return result;
 }
 
@@ -1895,63 +1987,51 @@ gtk_file_system_unix_insert_bookmark (GtkFileSystem     *file_system,
 				      gint               position,
 				      GError           **error)
 {
-  GSList *bookmarks;
-  int num_bookmarks;
-  GSList *l;
-  char *uri;
+  GtkFileSystemUnix *system_unix;
+  gint num_bookmarks;
+  gchar *uri;
   gboolean result;
   GError *err;
 
+  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+
   err = NULL;
-  if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
+  if (!bookmark_list_read (system_unix, &err) &&
+      err->domain == G_FILE_ERROR &&
+      err->code != G_FILE_ERROR_NOENT)
     {
       g_propagate_error (error, err);
       return FALSE;
     }
 
-  num_bookmarks = g_slist_length (bookmarks);
+  num_bookmarks = g_bookmark_file_get_size (system_unix->bookmarks);
   g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
 
   result = FALSE;
-
   uri = gtk_file_system_unix_path_to_uri (file_system, path);
-
-  for (l = bookmarks; l; l = l->next)
+  if (!g_bookmark_file_has_item (system_unix->bookmarks, uri))
     {
-      char *bookmark, *space;
-
-      bookmark = l->data;
-      
-      space = strchr (bookmark, ' ');
-      if (space)
-	*space = '\0';
-      if (strcmp (bookmark, uri) != 0)
-	{
-	  if (space)
-	    *space = ' ';
-	}
-      else
-	{
-	  g_set_error (error,
-		       GTK_FILE_SYSTEM_ERROR,
-		       GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
-		       _("'%s' already exists in the bookmarks list"),
-		       uri);
-	  goto out;
-	}
+      g_bookmark_file_set_mime_type (system_unix->bookmarks, uri, "x-directory/normal");
+      g_bookmark_file_add_application (system_unix->bookmarks, uri, NULL, NULL);
+    }
+  else
+    {
+      g_set_error (error,
+                   GTK_FILE_SYSTEM_ERROR,
+		   GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
+		   _("'%s' already exists in the bookmarks list"),
+		   uri);
+      goto out;
     }
 
-  bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
-  if (bookmark_list_write (bookmarks, error))
+  if (bookmark_list_write (system_unix, error))
     {
       result = TRUE;
       g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
     }
 
- out:
-
+out:
   g_free (uri);
-  bookmark_list_free (bookmarks);
 
   return result;
 }
@@ -1961,59 +2041,37 @@ gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
 				      const GtkFilePath *path,
 				      GError           **error)
 {
-  GSList *bookmarks;
-  char *uri;
-  GSList *l;
+  GtkFileSystemUnix *system_unix;
+  gchar *uri;
   gboolean result;
 
-  if (!bookmark_list_read (&bookmarks, error))
+  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+  
+  if (!bookmark_list_read (system_unix, error))
     return FALSE;
 
   result = FALSE;
 
   uri = gtk_file_system_path_to_uri (file_system, path);
 
-  for (l = bookmarks; l; l = l->next)
+  if (!g_bookmark_file_remove_item (system_unix->bookmarks, uri, NULL))
     {
-      char *bookmark, *space;
-
-      bookmark = (char *)l->data;
-      space = strchr (bookmark, ' ');
-      if (space)
-	*space = '\0';
-
-      if (strcmp (bookmark, uri) != 0)
-	{
-	  if (space)
-	    *space = ' ';
-	}
-      else
-	{
-	  g_free (l->data);
-	  bookmarks = g_slist_remove_link (bookmarks, l);
-	  g_slist_free_1 (l);
-
-	  if (bookmark_list_write (bookmarks, error))
-	    {
-	      result = TRUE;
-
-	      g_signal_emit_by_name (file_system, "bookmarks-changed", 0);	      
-	    }
-
-	  goto out;
-	}
+      g_set_error (error,
+                   GTK_FILE_SYSTEM_ERROR,
+                   GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
+                   _("'%s' does not exist in the bookmarks list"),
+                   uri);
+    }
+  else
+    {
+      if (bookmark_list_write (system_unix, error))
+        {
+          result = TRUE;
+          g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
+        }
     }
-
-  g_set_error (error,
-	       GTK_FILE_SYSTEM_ERROR,
-	       GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
-	       _("'%s' does not exist in the bookmarks list"),
-	       uri);
-
- out:
 
   g_free (uri);
-  bookmark_list_free (bookmarks);
 
   return result;
 }
@@ -2021,63 +2079,54 @@ gtk_file_system_unix_remove_bookmark (GtkFileSystem     *file_system,
 static GSList *
 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
 {
-  GSList *bookmarks;
+  GtkFileSystemUnix *system_unix;
   GSList *result;
-  GSList *l;
 
-  if (!bookmark_list_read (&bookmarks, NULL))
+  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+  if (!bookmark_list_read (system_unix, NULL))
     return NULL;
 
   result = NULL;
 
-  for (l = bookmarks; l; l = l->next)
+  if (system_unix->bookmarks)
     {
-      char *bookmark, *space;
+      gchar **uris;
+      gsize n_uris, i;
 
-      bookmark = (char *)l->data;
-      space = strchr (bookmark, ' ');
-      if (space)
-	*space = '\0';
+      uris = g_bookmark_file_get_uris (system_unix->bookmarks, &n_uris);
+      for (i = 0; i < n_uris; i++)
+        {
+          if (is_local_uri (uris[i]))
+            {
+              GtkFilePath *path;
 
-      if (is_local_uri (bookmark))
-	result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, bookmark));
+              path = gtk_file_system_unix_uri_to_path (file_system, uris[i]);
+              result = g_slist_prepend (result, path);
+            }
+        }
+      
+      g_strfreev (uris);
     }
 
-  bookmark_list_free (bookmarks);
-
-  result = g_slist_reverse (result);
-  return result;
+  return g_slist_reverse (result);
 }
 
 static gchar *
 gtk_file_system_unix_get_bookmark_label (GtkFileSystem     *file_system,
 					 const GtkFilePath *path)
 {
-  GSList *labels;
-  gchar *label;
-  GSList *l;
-  char *bookmark, *space, *uri;
-  
-  labels = NULL;
-  label = NULL;
-
-  uri = gtk_file_system_path_to_uri (file_system, path);
-  bookmark_list_read (&labels, NULL);
-
-  for (l = labels; l && !label; l = l->next) 
-    {
-      bookmark = (char *)l->data;
-      space = strchr (bookmark, ' ');
-      if (!space)
-	continue;
+  GtkFileSystemUnix *system_unix;
+  gchar *uri, *label;
 
-      *space = '\0';
+  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+  if (!bookmark_list_read (system_unix, NULL))
+    return NULL;
 
-      if (strcmp (uri, bookmark) == 0)
-	label = g_strdup (space + 1);
-    }
+  label = NULL;
+  
+  uri = gtk_file_system_path_to_uri (file_system, path);
+  label = g_bookmark_file_get_title (system_unix->bookmarks, uri, NULL);
 
-  bookmark_list_free (labels);
   g_free (uri);
 
   return label;
@@ -2088,50 +2137,24 @@ gtk_file_system_unix_set_bookmark_label (GtkFileSystem     *file_system,
 					 const GtkFilePath *path,
 					 const gchar       *label)
 {
-  GSList *labels;
-  GSList *l;
-  gchar *bookmark, *space, *uri;
-  gboolean found;
+  GtkFileSystemUnix *system_unix;
+  gchar *uri;
 
-  labels = NULL;
+  system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+  
+  if (!bookmark_list_read (system_unix, NULL))
+    return;
 
   uri = gtk_file_system_path_to_uri (file_system, path);
-  bookmark_list_read (&labels, NULL);
 
-  found = FALSE;
-  for (l = labels; l && !found; l = l->next) 
+  if (g_bookmark_file_has_item (system_unix->bookmarks, uri))
     {
-      bookmark = (gchar *)l->data;
-      space = strchr (bookmark, ' ');
-      if (space)
-	*space = '\0';
+      g_bookmark_file_set_title (system_unix->bookmarks, uri, label);
 
-      if (strcmp (bookmark, uri) != 0)
-	{
-	  if (space)
-	    *space = ' ';
-	}
-      else
-	{
-	  g_free (bookmark);
-	  
-	  if (label && *label)
-	    l->data = g_strdup_printf ("%s %s", uri, label);
-	  else
-	    l->data = g_strdup (uri);
-
-	  found = TRUE;
-	  break;
-	}
+      if (bookmark_list_write (system_unix, NULL))
+        g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
     }
 
-  if (found)
-    {
-      if (bookmark_list_write (labels, NULL))
-	g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
-    }
-  
-  bookmark_list_free (labels);
   g_free (uri);
 }
 
