]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkrecentmanager.c
Redo the fix for bug #492117, by adding a getter for the private drag
[~andy/gtk] / gtk / gtkrecentmanager.c
index 93545da3596f7043c1d67cd1709be431bf25fb7c..2e21d09c2ba8d44699eede2b7a0c072347990a27 100644 (file)
@@ -48,8 +48,8 @@
 /* the file where we store the recently used items */
 #define GTK_RECENTLY_USED_FILE ".recently-used.xbel"
 
-/* a poll per second should be enough */
-#define POLL_DELTA     1000
+/* a poll approximately every five seconds */
+#define POLL_DELTA      5
 
 /* return all items by default */
 #define DEFAULT_LIMIT  -1
 /* keep in sync with xdgmime */
 #define GTK_RECENT_DEFAULT_MIME        "application/octet-stream"
 
+typedef struct
+{
+  GSourceFunc func;
+  gpointer data;
+  GDestroyNotify notify;
+} ThreadsDispatch;
+
 typedef struct
 {
   gchar *name;
@@ -96,7 +103,6 @@ struct _GtkRecentManagerPrivate
 {
   gchar *filename;
 
-  guint is_screen_singleton : 1;
   guint is_dirty : 1;
   guint write_in_progress : 1;
   guint read_in_progress : 1;
@@ -104,8 +110,6 @@ struct _GtkRecentManagerPrivate
   gint limit;
   gint size;
 
-  GdkScreen *screen;
-  
   GBookmarkFile *recent_items;
   
   time_t last_mtime;
@@ -121,6 +125,7 @@ enum
   PROP_SIZE
 };
 
+static void           gtk_recent_manager_dispose      (GObject               *object);
 static void           gtk_recent_manager_finalize     (GObject               *object);
 
 static void           gtk_recent_manager_set_property (GObject               *object,
@@ -150,22 +155,74 @@ static void           gtk_recent_info_free            (GtkRecentInfo         *re
 
 static guint signal_changed = 0;
 
-G_DEFINE_TYPE (GtkRecentManager, gtk_recent_manager, G_TYPE_OBJECT);
+static GtkRecentManager *recent_manager_singleton = NULL;
 
-void
-filename_warning (const char *format, const char *filename, const char *message)
+G_DEFINE_TYPE (GtkRecentManager, gtk_recent_manager, G_TYPE_OBJECT)
+
+static void
+filename_warning (const gchar *format, 
+                  const gchar *filename, 
+                  const gchar *message)
 {
-  char *utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+  gchar *utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
   g_warning (format, utf8 ? utf8 : "(invalid filename)", message);
   g_free (utf8);
 }
 
+/* Test of haystack has the needle prefix, comparing case
+ * insensitive. haystack may be UTF-8, but needle must
+ * contain only lowercase ascii. */
+static gboolean
+has_case_prefix (const gchar *haystack, 
+                 const gchar *needle)
+{
+  const gchar *h, *n;
+
+  /* Eat one character at a time. */
+  h = haystack;
+  n = needle;
+
+  while (*n && *h && *n == g_ascii_tolower (*h))
+    {
+      n++;
+      h++;
+    }
+
+  return *n == '\0';
+}
+
 GQuark
 gtk_recent_manager_error_quark (void)
 {
   return g_quark_from_static_string ("gtk-recent-manager-error-quark");
 }
 
+static gboolean
+threads_dispatch (gpointer data)
+{
+  ThreadsDispatch *dispatch = data;
+  gboolean res = FALSE;
+
+  GDK_THREADS_ENTER ();
+
+  if (!g_source_is_destroyed (g_main_current_source ()))
+    res = dispatch->func (dispatch->data);
+
+  GDK_THREADS_LEAVE ();
+
+  return res;
+}
+
+static void
+threads_free (gpointer data)
+{
+  ThreadsDispatch *dispatch = data;
+
+  if (dispatch->notify)
+    dispatch->notify (dispatch->data);
+
+  g_slice_free (ThreadsDispatch, dispatch);
+}
 
 static void
 gtk_recent_manager_class_init (GtkRecentManagerClass *klass)
@@ -176,6 +233,7 @@ gtk_recent_manager_class_init (GtkRecentManagerClass *klass)
   
   gobject_class->set_property = gtk_recent_manager_set_property;
   gobject_class->get_property = gtk_recent_manager_get_property;
+  gobject_class->dispose = gtk_recent_manager_dispose;
   gobject_class->finalize = gtk_recent_manager_finalize;
   
   /**
@@ -254,24 +312,32 @@ static void
 gtk_recent_manager_init (GtkRecentManager *manager)
 {
   GtkRecentManagerPrivate *priv;
+  ThreadsDispatch *dispatch;
   
   priv = g_type_instance_get_private ((GTypeInstance *) manager,
                                      GTK_TYPE_RECENT_MANAGER);
   manager->priv = priv;
   
-  priv->filename = g_build_filename (g_get_home_dir (),
-                                    GTK_RECENTLY_USED_FILE,
-                                    NULL);
-  
   priv->limit = DEFAULT_LIMIT;
   priv->size = 0;
   
-  priv->is_screen_singleton = FALSE;
   priv->is_dirty = FALSE;
   priv->write_in_progress = FALSE;
   priv->read_in_progress = FALSE;
 
-  priv->screen = NULL;
+  priv->filename = g_build_filename (g_get_home_dir (),
+                                    GTK_RECENTLY_USED_FILE,
+                                    NULL);
+  
+  dispatch = g_slice_new (ThreadsDispatch);
+  dispatch->func = gtk_recent_manager_poll_timeout;
+  dispatch->data = manager;
+  dispatch->notify = NULL;
+  priv->poll_timeout = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT + 30,
+                                                   POLL_DELTA,
+                                                   threads_dispatch,
+                                                   dispatch,
+                                                   threads_free);
 
   build_recent_items_list (manager);
 }
@@ -324,22 +390,31 @@ gtk_recent_manager_get_property (GObject               *object,
 } 
 
 static void
-gtk_recent_manager_finalize (GObject *object)
+gtk_recent_manager_dispose (GObject *object)
 {
   GtkRecentManager *manager = GTK_RECENT_MANAGER (object);
   GtkRecentManagerPrivate *priv = manager->priv;
 
-  /* remove the poll timeout */
   if (priv->poll_timeout)
-    g_source_remove (priv->poll_timeout);
-  
-  if (priv->filename)
-    g_free (priv->filename);
+    {
+      g_source_remove (priv->poll_timeout);
+      priv->poll_timeout = 0;
+    }
+
+  G_OBJECT_CLASS (gtk_recent_manager_parent_class)->dispose (object);
+}
+
+static void
+gtk_recent_manager_finalize (GObject *object)
+{
+  GtkRecentManager *manager = GTK_RECENT_MANAGER (object);
+  GtkRecentManagerPrivate *priv = manager->priv;
+
+  g_free (priv->filename);
   
   if (priv->recent_items)
     g_bookmark_file_free (priv->recent_items);
-  
-  /* chain up parent's finalize method */  
+
   G_OBJECT_CLASS (gtk_recent_manager_parent_class)->finalize (object);
 }
 
@@ -372,9 +447,7 @@ gtk_recent_manager_real_changed (GtkRecentManager *manager)
        }
 
       write_error = NULL;
-      g_bookmark_file_to_file (priv->recent_items,
-                              priv->filename,
-                              &write_error);
+      g_bookmark_file_to_file (priv->recent_items, priv->filename, &write_error);
       if (write_error)
         {
           filename_warning ("Attempting to store changes into `%s', "
@@ -384,6 +457,8 @@ gtk_recent_manager_real_changed (GtkRecentManager *manager)
          g_error_free (write_error);
        }
 
+      priv->write_in_progress = FALSE;
+         
       /* we have sync'ed our list with the storage file, so we
        * update the file mtime in order to skip the timed check
        * and spare us from a re-read.
@@ -395,8 +470,6 @@ gtk_recent_manager_real_changed (GtkRecentManager *manager)
                            priv->filename,
                            g_strerror (errno));
 
-         priv->write_in_progress = FALSE;
-         
          g_object_thaw_notify (G_OBJECT (manager));
 
          return;
@@ -449,7 +522,7 @@ gtk_recent_manager_poll_timeout (gpointer data)
       return TRUE;
     }
 
-  /* the file didn't change from the last poll(), so we bail out */
+  /* the file didn't change from the last poll, so we bail out */
   if (stat_buf.st_mtime == priv->last_mtime)
     return TRUE;
 
@@ -464,6 +537,7 @@ gtk_recent_manager_set_filename (GtkRecentManager *manager,
                                 const gchar      *filename)
 {
   GtkRecentManagerPrivate *priv;
+  ThreadsDispatch *dispatch;
   
   g_assert (GTK_IS_RECENT_MANAGER (manager));
   priv = manager->priv;
@@ -480,9 +554,16 @@ gtk_recent_manager_set_filename (GtkRecentManager *manager,
     }
 
   priv->filename = g_strdup (filename);
-  priv->poll_timeout = g_timeout_add (POLL_DELTA,
-                                     gtk_recent_manager_poll_timeout,
-                                     manager);
+
+  dispatch = g_slice_new (ThreadsDispatch);
+  dispatch->func = gtk_recent_manager_poll_timeout;
+  dispatch->data = manager;
+  dispatch->notify = NULL;
+  priv->poll_timeout = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT + 30,
+                                                   POLL_DELTA,
+                                                   threads_dispatch,
+                                                   dispatch,
+                                                   threads_free);
 
   /* mark us clean, so that we can re-read the list
    * of recently used resources
@@ -586,9 +667,8 @@ build_recent_items_list (GtkRecentManager *manager)
  * monitors the recently used resources list, and emits the "changed" signal
  * each time something inside the list changes.
  *
- * #GtkRecentManager objects are expansive: be sure to create them only when
- * needed. You should use the gtk_recent_manager_new_for_screen() or the
- * gtk_recent_manager_get_default() functions instead.
+ * #GtkRecentManager objects are expensive: be sure to create them only when
+ * needed. You should use gtk_recent_manager_get_default() instead.
  *
  * Return value: A newly created #GtkRecentManager object.
  *
@@ -603,20 +683,21 @@ gtk_recent_manager_new (void)
 /**
  * gtk_recent_manager_get_default:
  *
- * Gets the recent manager for the default screen. See
- * gtk_recent_manager_get_for_screen().
+ * Gets a unique instance of #GtkRecentManager, that you can share
+ * in your application without caring about memory management. The
+ * returned instance will be freed when you application terminates.
  *
- * Return value: A unique #GtkRecentManager associated with the
- *   default screen. This recent manager is associated to the
- *   screen and can be used as long as the screen is open.
- *   Do no ref or unref it.
+ * Return value: A unique #GtkRecentManager. Do not ref or unref it.
  *
  * Since: 2.10
  */
 GtkRecentManager *
 gtk_recent_manager_get_default (void)
 {
-  return gtk_recent_manager_get_for_screen (gdk_screen_get_default ());
+  if (G_UNLIKELY (!recent_manager_singleton))
+    recent_manager_singleton = gtk_recent_manager_new ();
+
+  return recent_manager_singleton;
 }
 
 /**
@@ -637,70 +718,16 @@ gtk_recent_manager_get_default (void)
  *   and can be used as long as the screen is open. Do not ref or
  *   unref it.
  *
+ * Deprecated: 2.12: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling gtk_recent_manager_get_default().
+ *
  * Since: 2.10
  */
 GtkRecentManager *
 gtk_recent_manager_get_for_screen (GdkScreen *screen)
 {
-  GtkRecentManager *manager;
-
-  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
-  g_return_val_if_fail (!screen->closed, NULL);
-
-  manager = g_object_get_data (G_OBJECT (screen), "gtk-recent-manager-default");
-  if (!manager)
-    {
-      GtkRecentManagerPrivate *priv;
-      
-      manager = gtk_recent_manager_new ();
-      gtk_recent_manager_set_screen (manager, screen);
-
-      priv = manager->priv;
-      priv->is_screen_singleton = TRUE;
-
-      g_object_set_data (G_OBJECT (screen), I_("gtk-recent-manager-default"), manager);
-    }
-
-  return manager;
-}
-
-static void
-display_closed (GdkDisplay       *display,
-               gboolean          is_error,
-               GtkRecentManager *manager)
-{
-  GtkRecentManagerPrivate *priv = manager->priv;
-  GdkScreen *screen = priv->screen;
-  gboolean was_screen_singleton = priv->is_screen_singleton;
-
-  if (was_screen_singleton)
-    {
-      g_object_set_data (G_OBJECT (screen), I_("gtk-recent-manager-default"), NULL);
-      priv->is_screen_singleton = FALSE;
-    }
-
-  gtk_recent_manager_set_screen (manager, NULL);
-
-  if (was_screen_singleton)
-    g_object_unref (manager);
-}
-
-static void
-unset_screen (GtkRecentManager *manager)
-{
-  GtkRecentManagerPrivate *priv = manager->priv;
-  GdkDisplay *display;
-
-  if (priv->screen)
-    {
-      display = gdk_screen_get_display (priv->screen);
-
-      g_signal_handlers_disconnect_by_func (display,
-                                           (gpointer) display_closed,
-                                           manager);
-
-      priv->screen = NULL;
-    }
+  return gtk_recent_manager_get_default ();
 }
 
 /**
@@ -713,30 +740,16 @@ unset_screen (GtkRecentManager *manager)
  * storage.
  * 
  * Since: 2.10
+ *
+ * Deprecated: 2.12: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function has
+ *   no effect.
  */
 void
 gtk_recent_manager_set_screen (GtkRecentManager *manager,
                               GdkScreen        *screen)
 {
-  GtkRecentManagerPrivate *priv;
-  GdkDisplay *display;
-
-  g_return_if_fail (GTK_IS_RECENT_MANAGER (manager));
-  g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
-
-  priv = manager->priv;
-
-  unset_screen (manager);
 
-  if (screen)
-    {
-      display = gdk_screen_get_display (screen);
-
-      priv->screen = screen;
-
-      g_signal_connect (display, "closed",
-                       G_CALLBACK (display_closed), manager);
-    }
 }
 
 /**
@@ -788,16 +801,15 @@ gtk_recent_manager_get_limit (GtkRecentManager *manager)
  * gtk_recent_manager_add_item:
  * @manager: a #GtkRecentManager
  * @uri: a valid URI
- * @error: return location for a #GError, or %NULL
  *
  * Adds a new resource, pointed by @uri, into the recently used
  * resources list.
  *
- * This function automatically retrieving some of the needed
+ * This function automatically retrieves some of the needed
  * metadata and setting other metadata to common default values; it
  * then feeds the data to gtk_recent_manager_add_full().
  *
- * See gtk_recent_manager_add_full() if you want to explicitely
+ * See gtk_recent_manager_add_full() if you want to explicitly
  * define the metadata for the resource pointed by @uri.
  *
  * Return value: %TRUE if the new item was successfully added
@@ -807,63 +819,52 @@ gtk_recent_manager_get_limit (GtkRecentManager *manager)
  */
 gboolean
 gtk_recent_manager_add_item (GtkRecentManager  *manager,
-                            const gchar       *uri,
-                            GError           **error)
+                            const gchar       *uri)
 {
-  GtkRecentData *recent_data;
-  GError *add_error;
+  GtkRecentData recent_data;
   gboolean retval;
   
   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
   g_return_val_if_fail (uri != NULL, FALSE);
 
-  recent_data = g_slice_new (GtkRecentData);
-  
-  recent_data->display_name = NULL;
-  recent_data->description = NULL;
-  
+  recent_data.display_name = NULL;
+  recent_data.description = NULL;
+  recent_data.mime_type = NULL;
+
 #ifdef G_OS_UNIX
-  if (g_str_has_prefix (uri, "file://"))
+  if (has_case_prefix (uri, "file:/"))
     {
       gchar *filename;
       const gchar *mime_type;
       
       filename = g_filename_from_uri (uri, NULL, NULL);
-      mime_type = xdg_mime_get_mime_type_for_file (filename, NULL);
-      if (!mime_type)
-        recent_data->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
-      else
-        recent_data->mime_type = g_strdup (mime_type);
+      if (filename)
+        {
+          mime_type = xdg_mime_get_mime_type_for_file (filename, NULL);
+          if (mime_type)
+            recent_data.mime_type = g_strdup (mime_type);
       
-      g_free (filename);
+          g_free (filename);
+        }
+
+      if (!recent_data.mime_type)
+        recent_data.mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
     }
   else
 #endif
-    recent_data->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
-  
-  recent_data->app_name = g_strdup (g_get_application_name ());
-  recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
+    recent_data.mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
   
-  recent_data->groups = NULL;
+  recent_data.app_name = g_strdup (g_get_application_name ());
+  recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
+  recent_data.groups = NULL;
+  recent_data.is_private = FALSE;
   
-  recent_data->is_private = FALSE;
+  retval = gtk_recent_manager_add_full (manager, uri, &recent_data);
   
-  add_error = NULL;
-  retval = gtk_recent_manager_add_full (manager, uri, recent_data, &add_error);
-  
-  g_free (recent_data->mime_type);
-  g_free (recent_data->app_name);
-  g_free (recent_data->app_exec);
+  g_free (recent_data.mime_type);
+  g_free (recent_data.app_name);
+  g_free (recent_data.app_exec);
 
-  g_slice_free (GtkRecentData, recent_data);
-  
-  if (!retval)
-    {
-      g_propagate_error (error, add_error);
-      
-      return FALSE;
-    }
-  
   return retval;
 }
 
@@ -872,7 +873,6 @@ gtk_recent_manager_add_item (GtkRecentManager  *manager,
  * @manager: a #GtkRecentManager
  * @uri: a valid URI
  * @recent_data: metadata of the resource
- * @error: return location for a #GError, or %NULL
  *
  * Adds a new resource, pointed by @uri, into the recently used
  * resources list, using the metadata specified inside the #GtkRecentData
@@ -902,8 +902,7 @@ gtk_recent_manager_add_item (GtkRecentManager  *manager,
 gboolean
 gtk_recent_manager_add_full (GtkRecentManager     *manager,
                             const gchar          *uri,
-                            const GtkRecentData  *data,
-                            GError              **error)
+                            const GtkRecentData  *data)
 {
   GtkRecentManagerPrivate *priv;
   
@@ -915,53 +914,47 @@ gtk_recent_manager_add_full (GtkRecentManager     *manager,
   if ((data->display_name) &&
       (!g_utf8_validate (data->display_name, -1, NULL)))
     {
-      g_set_error  (error, GTK_RECENT_MANAGER_ERROR,
-                   GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING,
-                   _("The display name of the recently used resource "
-                     "must be a valid UTF-8 encoded string."));
+      g_warning ("Attempting to add `%s' to the list of recently used "
+                "resources, but the display name is not a valid UTF-8 "
+                "encoded string",
+                uri);
       return FALSE;
     }
   
   if ((data->description) &&
       (!g_utf8_validate (data->description, -1, NULL)))
     {
-      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
-                  GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING,
-                  _("The description of the recently used resource "
-                    "must by a valid UTF-8 encoded string."));
+      g_warning ("Attempting to add `%s' to the list of recently used "
+                "resources, but the description is not a valid UTF-8 "
+                "encoded string",
+                uri);
       return FALSE;
     }
 
  
   if (!data->mime_type)
     {
-      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
-                   GTK_RECENT_MANAGER_ERROR_INVALID_MIME,
-                  _("You must specify the MIME type of the "
-                    "resource pointed by `%s'"),
-                  uri);
+      g_warning ("Attempting to add `%s' to the list of recently used "
+                "resources, but not MIME type was defined",
+                uri);
       return FALSE;
     }
   
   if (!data->app_name)
     {
-      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
-                  GTK_RECENT_MANAGER_ERROR_NOT_REGISTERED,
-                  _("You must specify the name of the application "
-                    "that is registering the recently used resource "
-                    "pointed by `%s'"),
-                  uri);
+      g_warning ("Attempting to add `%s' to the list of recently used "
+                "resources, but no name of the application that is "
+                "registering it was defined",
+                uri);
       return FALSE;
     }
   
   if (!data->app_exec)
     {
-      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
-                   GTK_RECENT_MANAGER_ERROR_BAD_EXEC_STRING,
-                  _("You must specify a command line to "
-                    "be used when launching the resource "
-                    "pointed by `%s'"),
-                  uri);
+      g_warning ("Attempting to add `%s' to the list of recently used "
+                "resources, but no command line for the application "
+                "that is registering it was defined",
+                uri);
       return FALSE;
     }
   
@@ -1093,7 +1086,7 @@ gtk_recent_manager_has_item (GtkRecentManager *manager,
   return g_bookmark_file_has_item (priv->recent_items, uri);
 }
 
-static gboolean
+static void
 build_recent_info (GBookmarkFile  *bookmarks,
                   GtkRecentInfo  *info)
 {
@@ -1147,14 +1140,11 @@ build_recent_info (GBookmarkFile  *bookmarks,
       app_info->count = count;
       app_info->stamp = stamp;
       
-      info->applications = g_slist_append (info->applications,
-                                          app_info);
+      info->applications = g_slist_prepend (info->applications, app_info);
       g_hash_table_replace (info->apps_lookup, app_info->name, app_info);
     }
   
   g_strfreev (apps);
-  
-  return TRUE; 
 }
 
 /**
@@ -1171,7 +1161,9 @@ build_recent_info (GBookmarkFile  *bookmarks,
  *   about the resource pointed by @uri, or %NULL if the URI was
  *   not registered in the recently used resources list.  Free with
  *   gtk_recent_info_unref().
- **/
+ *
+ * Since: 2.10
+ */
 GtkRecentInfo *
 gtk_recent_manager_lookup_item (GtkRecentManager  *manager,
                                const gchar       *uri,
@@ -1179,7 +1171,6 @@ gtk_recent_manager_lookup_item (GtkRecentManager  *manager,
 {
   GtkRecentManagerPrivate *priv;
   GtkRecentInfo *info = NULL;
-  gboolean res;
   
   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
   g_return_val_if_fail (uri != NULL, NULL);
@@ -1214,15 +1205,9 @@ gtk_recent_manager_lookup_item (GtkRecentManager  *manager,
   /* fill the RecentInfo structure with the data retrieved by our
    * parser object from the storage file 
    */
-  res = build_recent_info (priv->recent_items, info);
-  if (!res)
-    {
-      gtk_recent_info_free (info);
-      
-      return NULL;
-    }
-  return gtk_recent_info_ref (info);
+  build_recent_info (priv->recent_items, info);
+
+  return info;
 }
 
 /**
@@ -1309,47 +1294,25 @@ gtk_recent_manager_get_items (GtkRecentManager *manager)
   priv = manager->priv;
   if (!priv->recent_items)
     return NULL;
+
+  if (priv->limit == 0)
+    return NULL;
   
   uris = g_bookmark_file_get_uris (priv->recent_items, &uris_len);
   for (i = 0; i < uris_len; i++)
     {
       GtkRecentInfo *info;
-      gboolean res;
+      
+      if (priv->limit != -1 && i == priv->limit)
+        break;
       
       info = gtk_recent_info_new (uris[i]);
-      res = build_recent_info (priv->recent_items, info);
-      if (!res)
-        {
-          g_warning ("Unable to create a RecentInfo object for "
-                     "item with URI `%s'",
-                     uris[i]);
-          gtk_recent_info_free (info);
-         
-          continue;
-        }
+      build_recent_info (priv->recent_items, info);
       
       retval = g_list_prepend (retval, info);
     }
   
   g_strfreev (uris);
-    
-  /* clamp the list, if a limit is present */
-  if ((priv->limit != -1) &&
-      (g_list_length (retval) > priv->limit))
-    {
-      GList *clamp, *l;
-      
-      clamp = g_list_nth (retval, priv->limit - 1);
-      
-      if (!clamp)
-        return retval;
-      
-      l = clamp->next;
-      clamp->next = NULL;
-      
-      g_list_foreach (l, (GFunc) gtk_recent_info_free, NULL);
-      g_list_free (l);
-    }
   
   return retval;
 }
@@ -1426,7 +1389,7 @@ gtk_recent_info_get_type (void)
   static GType info_type = 0;
   
   if (!info_type)
-    info_type = g_boxed_type_register_static ("GtkRecentInfo",
+    info_type = g_boxed_type_register_static (I_("GtkRecentInfo"),
                                              (GBoxedCopyFunc) gtk_recent_info_ref,
                                              (GBoxedFreeFunc) gtk_recent_info_unref);
   return info_type;
@@ -1721,11 +1684,9 @@ recent_app_info_free (RecentAppInfo *app_info)
   if (!app_info)
     return;
   
-  if (app_info->name)
-    g_free (app_info->name);
+  g_free (app_info->name);
   
-  if (app_info->exec)
-    g_free (app_info->exec);
+  g_free (app_info->exec);
   
   g_free (app_info);
 }
@@ -1736,7 +1697,7 @@ recent_app_info_free (RecentAppInfo *app_info)
  * @app_name: the name of the application that has registered this item
  * @app_exec: return location for the string containing the command line
  * @count: return location for the number of times this item was registered
- * @time: return location for the timestamp this item was last registered
+ * @time_: return location for the timestamp this item was last registered
  *    for this application
  *
  * Gets the data regarding the application that has registered the resource
@@ -1756,7 +1717,7 @@ gtk_recent_info_get_application_info (GtkRecentInfo  *info,
                                      const gchar    *app_name,
                                      gchar         **app_exec,
                                      guint          *count,
-                                     time_t         *time)
+                                     time_t         *time_)
 {
   RecentAppInfo *ai;
   
@@ -1780,8 +1741,8 @@ gtk_recent_info_get_application_info (GtkRecentInfo  *info,
   if (count)
     *count = ai->count;
   
-  if (time)
-    *time = ai->stamp;
+  if (time_)
+    *time_ = ai->stamp;
 
   return TRUE;
 }
@@ -1894,106 +1855,56 @@ gtk_recent_info_last_application (GtkRecentInfo  *info)
   return g_strdup (name);
 }
 
-typedef struct
-{
-  gint size;
-  GdkPixbuf *pixbuf;
-} IconCacheElement;
-
-static void
-icon_cache_element_free (IconCacheElement *element)
-{
-  if (element->pixbuf)
-    g_object_unref (element->pixbuf);
-  g_free (element);
-}
-
-static void
-icon_theme_changed (GtkIconTheme     *icon_theme)
-{
-  GHashTable *cache;
-
-  /* Difference from the initial creation is that we don't
-   * reconnect the signal
-   */
-  cache = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                (GDestroyNotify)g_free,
-                                (GDestroyNotify)icon_cache_element_free);
-  g_object_set_data_full (G_OBJECT (icon_theme), "gtk-recent-icon-cache",
-                         cache, (GDestroyNotify)g_hash_table_destroy);
-}
-
-/* TODO: use the GtkFileChooser's icon cache instead of our own to reduce
- * the memory footprint
- */
-static GdkPixbuf *
-get_cached_icon (const gchar *name,
-                gint         pixel_size)
-{
-  GtkIconTheme *icon_theme;
-  GHashTable *cache;
-  IconCacheElement *element;
-
-  icon_theme = gtk_icon_theme_get_default ();
-  cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-recent-icon-cache");
-
-  if (!cache)
-    {
-      cache = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                    (GDestroyNotify)g_free,
-                                    (GDestroyNotify)icon_cache_element_free);
-
-      g_object_set_data_full (G_OBJECT (icon_theme), "gtk-recent-icon-cache",
-                             cache, (GDestroyNotify)g_hash_table_destroy);
-      g_signal_connect (icon_theme, "changed",
-                       G_CALLBACK (icon_theme_changed), NULL);
-    }
-
-  element = g_hash_table_lookup (cache, name);
-  if (!element)
-    {
-      element = g_new0 (IconCacheElement, 1);
-      g_hash_table_insert (cache, g_strdup (name), element);
-    }
-
-  if (element->size != pixel_size)
-    {
-      if (element->pixbuf)
-       g_object_unref (element->pixbuf);
-
-      element->size = pixel_size;
-      element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
-                                                 pixel_size, 0, NULL);
-    }
-
-  return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
-}
-
-
 static GdkPixbuf *
 get_icon_for_mime_type (const char *mime_type,
                        gint        pixel_size)
 {
+  GtkIconTheme *icon_theme;
   const char *separator;
   GString *icon_name;
   GdkPixbuf *pixbuf;
 
   separator = strchr (mime_type, '/');
   if (!separator)
-    return NULL; /* maybe we should return a GError with "invalid MIME-type" */
+    return NULL;
+
+  icon_theme = gtk_icon_theme_get_default ();
+
+  /* try with the three icon name variants for MIME types */
+
+  /* canonicalize MIME type: foo/x-bar -> foo-x-bar */
+  icon_name = g_string_new (NULL);
+  g_string_append_len (icon_name, mime_type, separator - mime_type);
+  g_string_append_c (icon_name, '-');
+  g_string_append (icon_name, separator + 1);
+  pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str,
+                                     pixel_size,
+                                     0,
+                                     NULL);
+  g_string_free (icon_name, TRUE);
+  if (pixbuf)
+    return pixbuf;
 
+  /* canonicalize MIME type, and prepend "gnome-mime-" */
   icon_name = g_string_new ("gnome-mime-");
   g_string_append_len (icon_name, mime_type, separator - mime_type);
   g_string_append_c (icon_name, '-');
   g_string_append (icon_name, separator + 1);
-  pixbuf = get_cached_icon (icon_name->str, pixel_size);
+  pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str,
+                                     pixel_size,
+                                     0,
+                                     NULL);
   g_string_free (icon_name, TRUE);
   if (pixbuf)
     return pixbuf;
 
+  /* try the MIME family icon */
   icon_name = g_string_new ("gnome-mime-");
   g_string_append_len (icon_name, mime_type, separator - mime_type);
-  pixbuf = get_cached_icon (icon_name->str, pixel_size);
+  pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str,
+                                     pixel_size,
+                                     0,
+                                     NULL);
   g_string_free (icon_name, TRUE);
 
   return pixbuf;
@@ -2024,7 +1935,8 @@ get_icon_fallback (const gchar *icon_name,
  *
  * Retrieves the icon of size @size associated to the resource MIME type.
  *
- * Return value: a #GdkPixbuf containing the icon, or %NULL.
+ * Return value: a #GdkPixbuf containing the icon, or %NULL. Use
+ *   g_object_unref() when finished using the icon.
  *
  * Since: 2.10
  */
@@ -2039,9 +1951,15 @@ gtk_recent_info_get_icon (GtkRecentInfo *info,
   if (info->mime_type)
     retval = get_icon_for_mime_type (info->mime_type, size);
 
-  /* this should never fail */  
+  /* this function should never fail */  
   if (!retval)
-    retval = get_icon_fallback (GTK_STOCK_FILE, size);
+    {
+      if (info->mime_type &&
+          strcmp (info->mime_type, "x-directory/normal") == 0)
+        retval = get_icon_fallback (GTK_STOCK_DIRECTORY, size);
+      else
+        retval = get_icon_fallback (GTK_STOCK_FILE, size);
+    }
   
   return retval;
 }
@@ -2062,7 +1980,7 @@ gtk_recent_info_is_local (GtkRecentInfo *info)
 {
   g_return_val_if_fail (info != NULL, FALSE);
   
-  return g_str_has_prefix (info->uri, "file://");
+  return has_case_prefix (info->uri, "file:/");
 }
 
 /**
@@ -2203,13 +2121,13 @@ get_uri_shortname_for_display (const gchar *uri)
   gchar *name = NULL;
   gboolean validated = FALSE;
 
-  if (g_str_has_prefix (uri, "file://"))
+  if (has_case_prefix (uri, "file:/"))
     {
       gchar *local_file;
       
       local_file = g_filename_from_uri (uri, NULL, NULL);
       
-      if (local_file != NULL)
+      if (local_file)
         {
           name = g_filename_display_basename (local_file);
           validated = TRUE;
@@ -2217,7 +2135,8 @@ get_uri_shortname_for_display (const gchar *uri)
                
       g_free (local_file);
     } 
-  else
+  
+  if (!name)
     {
       gchar *method;
       gchar *local_file;
@@ -2226,7 +2145,7 @@ get_uri_shortname_for_display (const gchar *uri)
       rest = get_method_string (uri, &method);
       local_file = g_filename_display_basename (rest);
       
-      name = g_strdup_printf ("%s: %s", method, local_file);
+      name = g_strconcat (method, ": ", local_file, NULL);
       
       g_free (local_file);
       g_free (method);
@@ -2279,27 +2198,40 @@ gtk_recent_info_get_short_name (GtkRecentInfo *info)
  * gtk_recent_info_get_uri_display:
  * @info: a #GtkRecentInfo
  *
- * Gets a displayable version of the resource's URI.
+ * Gets a displayable version of the resource's URI.  If the resource
+ * is local, it returns a local path; if the resource is not local,
+ * it returns the UTF-8 encoded content of gtk_recent_info_get_uri().
  *
- * Return value: a UTF-8 string containing the resource's URI or %NULL
+ * Return value: a newly allocated UTF-8 string containing the
+ *   resource's URI or %NULL. Use g_free() when done using it.
  *
  * Since: 2.10
  */
 gchar *
 gtk_recent_info_get_uri_display (GtkRecentInfo *info)
 {
-  gchar *filename, *filename_utf8;
+  gchar *retval;
   
   g_return_val_if_fail (info != NULL, NULL);
-  
-  filename = g_filename_from_uri (info->uri, NULL, NULL);
-  if (!filename)
-    return NULL;
+
+  retval = NULL;
+  if (gtk_recent_info_is_local (info))
+    {
+      gchar *filename;
+
+      filename = g_filename_from_uri (info->uri, NULL, NULL);
+      if (!filename)
+        return NULL;
       
-  filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
-  g_free (filename);
+      retval = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+      g_free (filename);
+    }
+  else
+    {
+      retval = make_valid_utf8 (info->uri);
+    }
 
-  return filename_utf8;
+  return retval;
 }
 
 /**
@@ -2420,5 +2352,21 @@ gtk_recent_info_has_group (GtkRecentInfo *info,
   return FALSE;
 }
 
+/*
+ * _gtk_recent_manager_sync:
+ * 
+ * Private function for synchronising the recent manager singleton.
+ */
+void
+_gtk_recent_manager_sync (void)
+{
+  if (recent_manager_singleton)
+    {
+      /* force a dump of the contents of the recent manager singleton */
+      recent_manager_singleton->priv->is_dirty = TRUE;
+      gtk_recent_manager_real_changed (recent_manager_singleton);
+    }
+}
+
 #define __GTK_RECENT_MANAGER_C__
 #include "gtkaliasdef.c"