Bug 545350 – GAppInfo deletion Bug 545351 – Reset associations for
authorMatthias Clasen <mclasen@redhat.com>
Fri, 26 Sep 2008 19:57:36 +0000 (19:57 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Fri, 26 Sep 2008 19:57:36 +0000 (19:57 +0000)
2008-09-26  Matthias Clasen  <mclasen@redhat.com>

        Bug 545350 – GAppInfo deletion
        Bug 545351 – Reset associations for content type

        * gio.symbols:
        * gappinfo.[hc]: New functions g_app_info_can_delete,
        g_app_info_delete and g_app_info_reset_type_associations.

        * gdesktopappinfo.c:
        * gwin32appinfo.c: Implementations of these.

        * tests/Makefile.am:
        * tests/desktop-app-info.c: Tests for GAppInfo functionality.

svn path=/trunk/; revision=7554

gio/ChangeLog
gio/gappinfo.c
gio/gappinfo.h
gio/gdesktopappinfo.c
gio/gio.symbols
gio/gwin32appinfo.c
gio/tests/Makefile.am
gio/tests/desktop-app-info.c [new file with mode: 0644]

index 2f6aa06..e99b804 100644 (file)
@@ -1,3 +1,18 @@
+2008-09-26  Matthias Clasen  <mclasen@redhat.com>
+       
+       Bug 545350 – GAppInfo deletion
+       Bug 545351 – Reset associations for content type
+
+       * gio.symbols:
+       * gappinfo.[hc]: New functions g_app_info_can_delete,
+       g_app_info_delete and g_app_info_reset_type_associations.
+
+       * gdesktopappinfo.c:
+       * gwin32appinfo.c: Implementations of these.
+
+       * tests/Makefile.am:
+       * tests/desktop-app-info.c: Tests for GAppInfo functionality.
+
 2008-09-26  Dan Winship  <danw@gnome.org>
 
        Bug 505361 – gunixinputstream.c assumes poll() available
index 5352aea..e32c9ba 100644 (file)
@@ -574,6 +574,62 @@ g_app_info_launch_default_for_uri (const char         *uri,
   return res;
 }
 
+/**
+ * g_app_info_can_delete:
+ * @appinfo: a #GAppInfo
+ *
+ * Obtains the information whether the GAppInfo can be deleted.
+ * See g_app_info_delete().
+ *
+ * Returns: %TRUE if @appinfo can be deleted
+ *
+ * Since: 2.20
+ */
+gboolean
+g_app_info_can_delete (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  if (iface->can_delete)
+    return (* iface->can_delete) (appinfo);
+  return FALSE; 
+}
+
+
+/**
+ * g_app_info_delete:
+ * @appinfo: a #GAppInfo
+ *
+ * Tries to delete an #GAppInfo. 
+ *
+ * On some platforms, there may be a difference between user-defined
+ * #GAppInfo<!-- -->s which can be deleted, and system-wide ones which
+ * cannot. See g_app_info_can_delete().
+ * 
+ * Returns: %TRUE if @appinfo has been deleted
+ *
+ * Since: 2.20
+ */
+gboolean
+g_app_info_delete (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  if (iface->do_delete)
+    return (* iface->do_delete) (appinfo);
+  return FALSE; 
+}
+
 
 G_DEFINE_TYPE (GAppLaunchContext, g_app_launch_context, G_TYPE_OBJECT);
 
@@ -690,5 +746,6 @@ g_app_launch_context_launch_failed (GAppLaunchContext *context,
     class->launch_failed (context, startup_notify_id);
 }
 
+
 #define __G_APP_INFO_C__
 #include "gioaliasdef.c"
index 74464aa..14ac96a 100644 (file)
@@ -75,6 +75,8 @@ typedef struct _GAppLaunchContextPrivate GAppLaunchContextPrivate;
  * @add_supports_type: Adds to the #GAppInfo information about supported file types.
  * @can_remove_supports_type: Checks for support for removing supported file types from a #GAppInfo.
  * @remove_supports_type: Removes a supported application type from a #GAppInfo.
+ * @can_delete: Checks if a #GAppInfo can be deleted. Since 2.20
+ * @do_delete: Deletes a #GAppInfo. Since 2.20
  *
  * Application Information interface, for operating system portability.
  */
@@ -120,6 +122,8 @@ struct _GAppInfoIface
   gboolean     (* remove_supports_type)         (GAppInfo           *appinfo,
                                                  const char         *content_type,
                                                  GError            **error);
+  gboolean     (* can_delete)                   (GAppInfo           *appinfo);
+  gboolean     (* do_delete)                    (GAppInfo           *appinfo);
 };
 
 GType       g_app_info_get_type                     (void) G_GNUC_CONST;
@@ -160,9 +164,12 @@ gboolean    g_app_info_can_remove_supports_type     (GAppInfo             *appin
 gboolean    g_app_info_remove_supports_type         (GAppInfo             *appinfo,
                                                     const char           *content_type,
                                                     GError              **error);
+gboolean    g_app_info_can_delete                   (GAppInfo   *appinfo);
+gboolean    g_app_info_delete                       (GAppInfo   *appinfo);
 
 GList *   g_app_info_get_all                     (void);
 GList *   g_app_info_get_all_for_type            (const char  *content_type);
+void      g_app_info_reset_type_associations     (const char  *content_type);
 GAppInfo *g_app_info_get_default_for_type        (const char  *content_type,
                                                  gboolean     must_support_uris);
 GAppInfo *g_app_info_get_default_for_uri_scheme  (const char  *uri_scheme);
index b86e420..866bc35 100644 (file)
@@ -1171,7 +1171,8 @@ update_mimeapps_list (const char  *desktop_id,
   char **list;
   gsize length, data_size;
   char *data;
-  int i, j;
+  int i, j, k;
+  char **content_types;
 
   /* Don't add both at start and end */
   g_assert (!(add_at_start && add_at_end));
@@ -1191,75 +1192,109 @@ update_mimeapps_list (const char  *desktop_id,
       key_file = g_key_file_new ();
     }
 
-  /* Add to the right place in the list */
-  
-  length = 0;
-  old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
-                                        content_type, &length, NULL);
-
-  list = g_new (char *, 1 + length + 1);
-
-  i = 0;
-  if (add_at_start)
-    list[i++] = g_strdup (desktop_id);
-  if (old_list)
+  if (content_type)
     {
-      for (j = 0; old_list[j] != NULL; j++)
-       {
-         if (strcmp (old_list[j], desktop_id) != 0)
-           list[i++] = g_strdup (old_list[j]);
-       }
+      content_types = g_new (char *, 2);
+      content_types[0] = g_strdup (content_type);
+      content_types[1] = NULL;
+    }
+  else
+    {
+      content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
     }
-  if (add_at_end)
-    list[i++] = g_strdup (desktop_id);
-  list[i] = NULL;
-  
-  g_strfreev (old_list);
 
-  g_key_file_set_string_list (key_file,
-                             ADDED_ASSOCIATIONS_GROUP,
-                             content_type,
-                             (const char * const *)list, i);
+  for (k = 0; content_types && content_types[k]; k++)
+    { 
+      /* Add to the right place in the list */
+  
+      length = 0;
+      old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
+                                            content_types[k], &length, NULL);
 
-  g_strfreev (list);
+      list = g_new (char *, 1 + length + 1);
 
-  /* Remove from removed associations group (unless remove) */
+      i = 0;
+      if (add_at_start)
+        list[i++] = g_strdup (desktop_id);
+      if (old_list)
+        {
+          for (j = 0; old_list[j] != NULL; j++)
+           {
+             if (g_strcmp0 (old_list[j], desktop_id) != 0)
+               list[i++] = g_strdup (old_list[j]);
+           }
+        }
+      if (add_at_end)
+        list[i++] = g_strdup (desktop_id);
+      list[i] = NULL;
   
-  length = 0;
-  old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
-                                        content_type, &length, NULL);
+      g_strfreev (old_list);
 
-  list = g_new (char *, 1 + length + 1);
-
-  i = 0;
-  if (remove)
-    list[i++] = g_strdup (desktop_id);
-  if (old_list)
+      if (list[0] == NULL || desktop_id == NULL)
+        g_key_file_remove_key (key_file,
+                              ADDED_ASSOCIATIONS_GROUP,
+                              content_types[k],
+                              NULL);
+      else
+        g_key_file_set_string_list (key_file,
+                                   ADDED_ASSOCIATIONS_GROUP,
+                                   content_types[k],
+                                   (const char * const *)list, i);
+   
+      g_strfreev (list);
+    }
+  
+  if (content_type)
     {
-      for (j = 0; old_list[j] != NULL; j++)
-       {
-         if (strcmp (old_list[j], desktop_id) != 0)
-           list[i++] = g_strdup (old_list[j]);
-       }
+      /* reuse the list from above */
     }
-  list[i] = NULL;
+  else
+    {
+      g_strfreev (content_types);
+      content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
+    }
+
+  for (k = 0; content_types && content_types[k]; k++) 
+    {
+      /* Remove from removed associations group (unless remove) */
   
-  g_strfreev (old_list);
+      length = 0;
+      old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
+                                            content_types[k], &length, NULL);
 
-  if (list[0] == NULL)
-    g_key_file_remove_key (key_file,
-                          REMOVED_ASSOCIATIONS_GROUP,
-                          content_type,
-                          NULL);
-  else
-    g_key_file_set_string_list (key_file,
-                               REMOVED_ASSOCIATIONS_GROUP,
-                               content_type,
-                               (const char * const *)list, i);
+      list = g_new (char *, 1 + length + 1);
 
-  g_strfreev (list);
+      i = 0;
+      if (remove)
+        list[i++] = g_strdup (desktop_id);
+      if (old_list)
+        {
+          for (j = 0; old_list[j] != NULL; j++)
+           {
+             if (g_strcmp0 (old_list[j], desktop_id) != 0)
+               list[i++] = g_strdup (old_list[j]);
+           }
+        }
+      list[i] = NULL;
+  
+      g_strfreev (old_list);
+
+      if (list[0] == NULL || desktop_id == NULL)
+        g_key_file_remove_key (key_file,
+                              REMOVED_ASSOCIATIONS_GROUP,
+                              content_types[k],
+                              NULL);
+      else
+        g_key_file_set_string_list (key_file,
+                                   REMOVED_ASSOCIATIONS_GROUP,
+                                   content_types[k],
+                                   (const char * const *)list, i);
 
+      g_strfreev (list);
+    }
   
+  g_strfreev (content_types);  
+
   data = g_key_file_to_data (key_file, &data_size, error);
   g_key_file_free (key_file);
   
@@ -1513,6 +1548,40 @@ g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
   return TRUE;
 }
 
+static gboolean
+g_desktop_app_info_can_delete (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+  if (info->filename)
+    return g_access (info->filename, W_OK) == 0;
+
+  return FALSE;
+}
+
+static gboolean
+g_desktop_app_info_delete (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  
+  if (info->filename)
+    { 
+      if (g_remove (info->filename) == 0)
+        {
+          update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
+
+          g_free (info->filename);
+          info->filename = NULL;
+          g_free (info->desktop_id);
+          info->desktop_id = NULL;
+
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
 /**
  * g_app_info_create_from_commandline:
  * @commandline: the commandline to use
@@ -1586,6 +1655,8 @@ g_desktop_app_info_iface_init (GAppInfoIface *iface)
   iface->add_supports_type = g_desktop_app_info_add_supports_type;
   iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
   iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
+  iface->can_delete = g_desktop_app_info_can_delete;
+  iface->do_delete = g_desktop_app_info_delete;
 }
 
 static gboolean
@@ -1643,6 +1714,22 @@ g_app_info_get_all_for_type (const char *content_type)
   return g_list_reverse (infos);
 }
 
+/**
+ * g_app_info_reset_type_associations:
+ * content_type: a content type 
+ *
+ * Removes all changes to the type associations done by
+ * g_app_info_set_as_default_for_type(), 
+ * g_app_info_set_as_default_for_extension(), 
+ * g_app_info_add_supports_type() of g_app_info_remove_supports_type().
+ *
+ * Since: 2.20
+ */
+void
+g_app_info_reset_type_associations (const char *content_type)
+{
+  update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
+}
 
 /**
  * g_app_info_get_default_for_type:
index 85a30b1..2e49f53 100644 (file)
@@ -48,6 +48,8 @@ g_app_info_add_supports_type
 g_app_info_can_remove_supports_type
 g_app_info_remove_supports_type
 g_app_info_launch_default_for_uri
+g_app_info_can_delete
+g_app_info_delete
 g_app_launch_context_new
 g_app_launch_context_get_display
 g_app_launch_context_get_startup_notify_id
@@ -60,6 +62,7 @@ g_app_info_get_all
 g_app_info_get_all_for_type
 g_app_info_get_default_for_type
 g_app_info_get_default_for_uri_scheme
+g_app_info_reset_type_associations
 #endif
 #endif
 
index 0043258..3e669c6 100644 (file)
@@ -661,3 +661,9 @@ g_app_info_get_all (void)
 
   return g_list_reverse (infos);
 }
+
+void
+g_app_info_reset_type_associations (const char *content_type)
+{
+  /* nothing to do */
+}
index 8e08c34..510396f 100644 (file)
@@ -22,7 +22,8 @@ TEST_PROGS +=                 \
        g-file                  \
        g-file-info             \
        data-input-stream       \
-       data-output-stream
+       data-output-stream      \
+       desktop-app-info
 
 if OS_UNIX
 TEST_PROGS += live-g-file unix-streams
@@ -49,7 +50,9 @@ data_output_stream_LDADD      = $(progs_ldadd)
 live_g_file_SOURCES      = live-g-file.c
 live_g_file_LDADD        = $(progs_ldadd)
 
+desktop_app_info_SOURCES  = desktop-app-info.c
+desktop_app_info_LDADD   = $(progs_ldadd)
+
 unix_streams_SOURCES     = unix-streams.c
 unix_streams_LDADD       = $(progs_ldadd) \
        $(top_builddir)/gthread/libgthread-2.0.la
-
diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c
new file mode 100644 (file)
index 0000000..f954e6e
--- /dev/null
@@ -0,0 +1,253 @@
+/* 
+ * 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: Matthias Clasen
+ */
+
+#include <glib/glib.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *basedir;
+
+static GAppInfo * 
+create_app_info (const char *name)
+{
+  GError *error;
+  GAppInfo *info;
+
+  error = NULL;
+  info = g_app_info_create_from_commandline ("/usr/bin/true blah",
+                                             name,
+                                             G_APP_INFO_CREATE_NONE,
+                                             &error);
+  g_assert (error == NULL);
+
+  /* this is necessary to ensure that the info is saved */
+  g_app_info_set_as_default_for_type (info, "application/x-blah", &error);
+  g_assert (error == NULL);
+  g_app_info_remove_supports_type (info, "application/x-blah", &error);
+  g_assert (error == NULL);
+  g_app_info_reset_type_associations ("application/x-blah");
+  
+  return info;
+}
+
+static void
+test_delete (void)
+{
+  GAppInfo *info;
+
+  const char *id;
+  char *filename;
+  gboolean res;
+
+  info = create_app_info ("Blah");
+  id = g_app_info_get_id (info);
+  g_assert (id != NULL);
+
+  filename = g_build_filename (basedir, "applications", id, NULL);
+
+  res = g_file_test (filename, G_FILE_TEST_EXISTS);
+  g_assert (res);
+
+  res = g_app_info_can_delete (info);
+  g_assert (res);
+
+  res = g_app_info_delete (info);
+  g_assert (res);
+
+  res = g_file_test (filename, G_FILE_TEST_EXISTS);
+  g_assert (!res);
+
+  g_object_unref (info);
+
+  if (g_file_test ("/usr/share/applications/gedit.desktop", G_FILE_TEST_EXISTS))
+    {
+      info = (GAppInfo*)g_desktop_app_info_new ("gedit.desktop");
+      g_assert (info);
+     
+      res = g_app_info_can_delete (info);
+      g_assert (!res);
+      res = g_app_info_delete (info);
+      g_assert (!res);
+    }
+}
+
+static void
+test_default (void)
+{
+  GAppInfo *info, *info1, *info2, *info3;
+  GList *list;
+  GError *error = NULL;  
+
+  info1 = create_app_info ("Blah1");
+  info2 = create_app_info ("Blah2");
+  info3 = create_app_info ("Blah3");
+
+  g_app_info_set_as_default_for_type (info1, "application/x-test", &error);
+  g_assert (error == NULL);
+
+  g_app_info_set_as_default_for_type (info2, "application/x-test", &error);
+  g_assert (error == NULL);
+
+  list = g_app_info_get_all_for_type ("application/x-test");
+  g_assert (g_list_length (list) == 2);
+  
+  /* check that both are in the list, info2 before info1 */
+  info = (GAppInfo *)list->data;
+  g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info2)) == 0);
+
+  info = (GAppInfo *)list->next->data;
+  g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info1)) == 0);
+
+  g_list_foreach (list, (GFunc)g_object_unref, NULL);
+  g_list_free (list);
+
+  /* now try adding something at the end */
+  g_app_info_add_supports_type (info3, "application/x-test", &error);
+  g_assert (error == NULL);
+
+  list = g_app_info_get_all_for_type ("application/x-test");
+  g_assert (g_list_length (list) == 3);
+  
+  /* check that all are in the list, info2, info1, info3 */
+  info = (GAppInfo *)list->data;
+  g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info2)) == 0);
+
+  info = (GAppInfo *)list->next->data;
+  g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info1)) == 0);
+
+  info = (GAppInfo *)list->next->next->data;
+  g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info3)) == 0);
+
+  g_list_foreach (list, (GFunc)g_object_unref, NULL);
+  g_list_free (list);
+
+  /* now remove info1 again */
+  g_app_info_remove_supports_type (info1, "application/x-test", &error);
+  g_assert (error == NULL);
+
+  list = g_app_info_get_all_for_type ("application/x-test");
+  g_assert (g_list_length (list) == 2);
+  
+  /* check that both are in the list, info2 before info3 */
+  info = (GAppInfo *)list->data;
+  g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info2)) == 0);
+
+  info = (GAppInfo *)list->next->data;
+  g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info3)) == 0);
+
+  g_list_foreach (list, (GFunc)g_object_unref, NULL);
+  g_list_free (list);
+
+  /* now clean it all up */
+  g_app_info_reset_type_associations ("application/x-test");
+
+  list = g_app_info_get_all_for_type ("application/x-test");
+  g_assert (list == NULL);
+  
+  g_app_info_delete (info1);
+  g_app_info_delete (info2);
+  g_app_info_delete (info3);
+
+  g_object_unref (info1);
+  g_object_unref (info2);
+}
+
+static void
+cleanup_dir_recurse (GFile *parent, GFile *root)
+{
+  gboolean res;
+  GError *error;
+  GFileEnumerator *enumerator;
+  GFileInfo *info;
+  GFile *descend;
+  char *relative_path;
+
+  g_assert (root != NULL);
+
+  error = NULL;
+  enumerator =
+    g_file_enumerate_children (parent, "*",
+                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL,
+                               &error);
+  if (! enumerator)
+          return;
+  error = NULL;
+  info = g_file_enumerator_next_file (enumerator, NULL, &error);
+  while ((info) && (!error))
+    {
+      descend = g_file_get_child (parent, g_file_info_get_name (info));
+      g_assert (descend != NULL);
+      relative_path = g_file_get_relative_path (root, descend);
+      g_assert (relative_path != NULL);
+
+      if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+          cleanup_dir_recurse (descend, root);
+
+      error = NULL;
+      res = g_file_delete (descend, NULL, &error);
+      g_assert_cmpint (res, ==, TRUE);
+
+      g_object_unref (descend);
+      error = NULL;
+      info = g_file_enumerator_next_file (enumerator, NULL, &error);
+    }
+  g_assert (error == NULL);
+
+  error = NULL;
+  res = g_file_enumerator_close (enumerator, NULL, &error);
+  g_assert_cmpint (res, ==, TRUE);
+  g_assert (error == NULL);
+}
+
+static void
+cleanup_subdirs (const char *basedir)
+{
+  GFile *base, *file;
+  base = g_file_new_for_path (basedir);  
+  file = g_file_get_child (base, "applications");
+  cleanup_dir_recurse (file, file);
+  g_object_unref (file);
+  file = g_file_get_child (base, "mime");
+  cleanup_dir_recurse (file, file);
+  g_object_unref (file);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+  
+  basedir = g_get_current_dir ();
+  g_setenv ("XDG_DATA_HOME", basedir, TRUE);
+  cleanup_subdirs (basedir);
+  
+  g_test_add_func ("/desktop-app-info/delete", test_delete);
+  g_test_add_func ("/desktop-app-info/default", test_default);
+
+  return g_test_run();
+}