[seahorse] Refactor deleting, add SeahorseDeleter and SeahorseDeletable types



commit 3b34b0009119313d903566ecd01a21a62500e345
Author: Stef Walter <stefw collabora co uk>
Date:   Thu Dec 1 12:18:34 2011 +0100

    Refactor deleting, add SeahorseDeleter and SeahorseDeletable types
    
     * SeahorseDeletable is an interface implemented by objects that can
       be deletable, with a "deletable" boolean property, and a create_deleter()
       method.
     * SeahorseDeleter is an object that can delete objects, combining them
       if possible.

 gkr/Makefile.am                                    |    2 +
 gkr/seahorse-gkr-actions.c                         |  110 +-------
 gkr/seahorse-gkr-item-deleter.c                    |  271 ++++++++++++++++++++
 gkr/seahorse-gkr-item-deleter.h                    |   42 +++
 gkr/seahorse-gkr-item.c                            |   25 ++-
 gkr/seahorse-gkr-keyring-deleter.c                 |  228 ++++++++++++++++
 gkr/seahorse-gkr-keyring-deleter.h                 |   42 +++
 gkr/seahorse-gkr-keyring.c                         |   22 ++-
 gkr/seahorse-gkr-operation.c                       |  148 -----------
 gkr/seahorse-gkr-operation.h                       |    8 -
 libseahorse/Makefile.am                            |    2 +
 libseahorse/seahorse-deletable.c                   |  185 +++++++++++++
 libseahorse/seahorse-deletable.h                   |   53 ++++
 libseahorse/seahorse-deleter.c                     |  135 ++++++++++
 libseahorse/seahorse-deleter.h                     |   92 +++++++
 libseahorse/seahorse-exportable.c                  |   22 ++-
 libseahorse/seahorse-exportable.h                  |    2 +
 libseahorse/seahorse-object.c                      |   18 ++-
 libseahorse/seahorse-viewer.c                      |   32 +--
 pgp/Makefile.am                                    |    2 +
 pgp/seahorse-gpgme-key-deleter.c                   |  181 +++++++++++++
 pgp/seahorse-gpgme-key-deleter.h                   |   42 +++
 pgp/seahorse-gpgme-key.c                           |   22 ++
 pgp/seahorse-gpgme-secret-deleter.c                |  177 +++++++++++++
 pgp/seahorse-gpgme-secret-deleter.h                |   42 +++
 pgp/seahorse-pgp-actions.c                         |  117 ---------
 pkcs11/Makefile.am                                 |    3 +-
 pkcs11/seahorse-certificate.c                      |   51 ++++-
 pkcs11/seahorse-pkcs11-actions.c                   |   63 -----
 pkcs11/seahorse-pkcs11-deleter.c                   |  235 +++++++++++++++++
 pkcs11/seahorse-pkcs11-deleter.h                   |   55 ++++
 pkcs11/seahorse-pkcs11-key-deleter.c               |  150 +++++++++++
 pkcs11/seahorse-pkcs11-key-deleter.h               |   42 +++
 pkcs11/seahorse-pkcs11-operations.c                |  146 -----------
 pkcs11/seahorse-pkcs11-properties.c                |   67 ++----
 pkcs11/seahorse-private-key.c                      |   29 ++-
 pkcs11/seahorse-token.c                            |   25 ++-
 pkcs11/seahorse-token.h                            |    3 +
 ssh/Makefile.am                                    |    1 +
 ssh/seahorse-ssh-actions.c                         |   44 ----
 ssh/seahorse-ssh-deleter.c                         |  205 +++++++++++++++
 .../seahorse-ssh-deleter.h                         |   38 ++--
 ssh/seahorse-ssh-key.c                             |   20 ++-
 43 files changed, 2476 insertions(+), 723 deletions(-)
---
diff --git a/gkr/Makefile.am b/gkr/Makefile.am
index ef1e392..7fc401b 100644
--- a/gkr/Makefile.am
+++ b/gkr/Makefile.am
@@ -23,8 +23,10 @@ libseahorse_gkr_la_SOURCES = \
 	seahorse-gkr-backend.c seahorse-gkr-backend.h \
 	seahorse-gkr-dialogs.h seahorse-gkr-dialogs.c \
 	seahorse-gkr-item.c seahorse-gkr-item.h \
+	seahorse-gkr-item-deleter.c seahorse-gkr-item-deleter.h \
 	seahorse-gkr-item-properties.c \
 	seahorse-gkr-keyring.c seahorse-gkr-keyring.h \
+	seahorse-gkr-keyring-deleter.c seahorse-gkr-keyring-deleter.h \
 	seahorse-gkr-keyring-properties.c \
 	seahorse-gkr-operation.c seahorse-gkr-operation.h
 
diff --git a/gkr/seahorse-gkr-actions.c b/gkr/seahorse-gkr-actions.c
index 9236188..533a3ad 100644
--- a/gkr/seahorse-gkr-actions.c
+++ b/gkr/seahorse-gkr-actions.c
@@ -25,6 +25,7 @@
 #include "seahorse-gkr-actions.h"
 #include "seahorse-gkr-backend.h"
 #include "seahorse-gkr-keyring.h"
+#include "seahorse-gkr-keyring-deleter.h"
 #include "seahorse-gkr-dialogs.h"
 #include "seahorse-gkr-operation.h"
 
@@ -301,12 +302,14 @@ on_keyring_properties (GtkAction* action,
 }
 
 static void
-on_delete_objects_complete (GObject *source, GAsyncResult *result, gpointer user_data)
+on_delete_keyring_complete (GObject *source,
+                            GAsyncResult *result,
+                            gpointer user_data)
 {
 	GtkWindow *parent = GTK_WINDOW (user_data);
 	GError *error = NULL;
 
-	if (!seahorse_gkr_delete_finish (result, &error)) {
+	if (!seahorse_deleter_delete_finish (SEAHORSE_DELETER (source), result, &error)) {
 		seahorse_util_show_error (parent, _("Couldn't delete keyring"), error->message);
 		g_error_free (error);
 	}
@@ -318,33 +321,16 @@ static void
 on_keyrings_delete (GtkAction* action,
                     gpointer user_data)
 {
-	GList *objects = user_data;
-	GCancellable *cancellable;
-	GtkWindow *parent;
-	gchar *prompt;
-	gboolean ret;
-
-	if (!objects)
-		return;
+	SeahorseGkrKeyring *keyring = SEAHORSE_GKR_KEYRING (user_data);
+	GtkWindow *parent = seahorse_action_get_window (action);
+	SeahorseDeleter *deleter;
 
-	prompt = g_strdup_printf (_ ("Are you sure you want to delete the password keyring '%s'?"),
-	                          seahorse_object_get_label (objects->data));
-	parent = seahorse_action_get_window (action);
+	deleter = seahorse_gkr_keyring_deleter_new (keyring);
 
-	ret = seahorse_delete_dialog_prompt (parent, prompt);
-	if (ret) {
-		cancellable = g_cancellable_new ();
-		seahorse_gkr_delete_async (objects, cancellable,
-		                           on_delete_objects_complete,
-		                           g_object_ref (parent));
-		seahorse_progress_show (cancellable, ngettext ("Deleting keyring", "Deleting keyrings",
-		                                               g_list_length (objects)), TRUE);
-		g_object_unref (cancellable);
-	} else {
-		g_cancellable_cancel (g_cancellable_get_current ());
-	}
-
-	g_free (prompt);
+	if (seahorse_deleter_prompt (deleter, parent))
+		seahorse_deleter_delete_async (deleter, NULL, on_delete_keyring_complete,
+		                               g_object_ref (parent));
+	g_object_unref (deleter);
 }
 
 static const GtkActionEntry KEYRINGS_ACTIONS[] = {
@@ -352,8 +338,6 @@ static const GtkActionEntry KEYRINGS_ACTIONS[] = {
 	  N_("Lock the password storage keyring so a master password is required to unlock it."), G_CALLBACK (on_keyrings_lock) },
 	{ "keyring-unlock", NULL, N_("_Unlock"), "",
 	  N_("Unlock the password storage keyring with a master password so it is available for use."), G_CALLBACK (on_keyrings_unlock) },
-	{ "keyring-delete", GTK_STOCK_DELETE, NULL, NULL,
-	  N_("Delete the keyring."), G_CALLBACK (on_keyrings_delete) },
 };
 
 static const GtkActionEntry KEYRING_ACTIONS[] = {
@@ -363,6 +347,8 @@ static const GtkActionEntry KEYRING_ACTIONS[] = {
 	  N_("Change the unlock password of the password storage keyring"), G_CALLBACK (on_keyring_password) },
 	{ "keyring-properties", GTK_STOCK_PROPERTIES, NULL, NULL,
 	  N_("Properties of the keyring."), G_CALLBACK (on_keyring_properties) },
+	{ "keyring-delete", GTK_STOCK_DELETE, NULL, NULL,
+	  N_("Delete the keyring."), G_CALLBACK (on_keyrings_delete) },
 
 	/* Generic actions used by the sidebar for the current selection */
 	{ "lock", NULL, NULL, "",
@@ -496,73 +482,11 @@ on_password_properties (GtkAction *action,
 	                                   seahorse_action_get_window (action));
 }
 
-static void
-on_delete_objects (GObject *source,
-                   GAsyncResult *result,
-                   gpointer user_data)
-{
-	GtkWidget *parent = GTK_WIDGET (user_data);
-	GError *error = NULL;
-
-	if (!seahorse_gkr_delete_finish (result, &error)) {
-		seahorse_util_show_error (parent, _("Couldn't delete item"), error->message);
-		g_error_free (error);
-	}
-
-	g_object_unref (parent);
-}
-
-static void
-on_delete_passwords (GtkAction *action,
-                     gpointer user_data)
-{
-	GCancellable *cancellable;
-	GtkWindow *parent;
-	GList *objects;
-	gchar *prompt;
-	gboolean ret;
-	guint num;
-
-	objects = user_data;
-	if (objects == NULL)
-		return;
-
-	num = g_list_length (objects);
-	if (num == 1) {
-		prompt = g_strdup_printf (_ ("Are you sure you want to delete the password '%s'?"),
-		                          seahorse_object_get_label (SEAHORSE_OBJECT (objects->data)));
-	} else {
-		prompt = g_strdup_printf (ngettext ("Are you sure you want to delete %d password?",
-		                                    "Are you sure you want to delete %d passwords?",
-		                                    num), num);
-	}
-
-	parent = seahorse_action_get_window (action);
-	ret = seahorse_delete_dialog_prompt (parent, prompt);
-
-	if (ret) {
-		cancellable = g_cancellable_new ();
-		seahorse_gkr_delete_async (objects, cancellable, on_delete_objects, g_object_ref (parent));
-		seahorse_progress_show (cancellable, ngettext ("Deleting item", "Deleting items", num), TRUE);
-		g_object_unref (cancellable);
-	} else {
-		g_cancellable_cancel (g_cancellable_get_current ());
-
-	}
-
-	g_free (prompt);
-}
-
 static const GtkActionEntry ITEM_ACTIONS[] = {
 	{ "properties", GTK_STOCK_PROPERTIES, NULL, NULL,
 	  N_("Properties of the password."), G_CALLBACK (on_password_properties) },
 };
 
-static const GtkActionEntry ITEMS_ACTIONS[] = {
-	{ "delete", GTK_STOCK_DELETE, NULL, NULL,
-	  N_("Delete the password."), G_CALLBACK (on_delete_passwords) },
-};
-
 static void
 seahorse_gkr_item_actions_init (SeahorseGkrItemActions *self)
 {
@@ -577,10 +501,6 @@ seahorse_gkr_item_actions_clone_for_objects (SeahorseActions *actions,
 
 	cloned = gtk_action_group_new ("KeyringItem");
 	gtk_action_group_set_translation_domain (cloned, GETTEXT_PACKAGE);
-	gtk_action_group_add_actions_full (cloned, ITEMS_ACTIONS,
-	                                   G_N_ELEMENTS (ITEMS_ACTIONS),
-	                                   seahorse_object_list_copy (objects),
-	                                   seahorse_object_list_free);
 
 	if (!objects->next)
 		gtk_action_group_add_actions_full (cloned, ITEM_ACTIONS,
diff --git a/gkr/seahorse-gkr-item-deleter.c b/gkr/seahorse-gkr-item-deleter.c
new file mode 100644
index 0000000..441a58f
--- /dev/null
+++ b/gkr/seahorse-gkr-item-deleter.c
@@ -0,0 +1,271 @@
+/* 
+ * Seahorse
+ * 
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "seahorse-gkr-backend.h"
+#include "seahorse-gkr-item-deleter.h"
+#include "seahorse-gkr-operation.h"
+
+#include "seahorse-delete-dialog.h"
+#include "seahorse-object.h"
+
+#include <gnome-keyring.h>
+
+#include <glib/gi18n.h>
+
+#define SEAHORSE_GKR_ITEM_DELETER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), SEAHORSE_TYPE_GKR_ITEM_DELETER, SeahorseGkrItemDeleterClass))
+#define SEAHORSE_IS_GKR_ITEM_DELETER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAHORSE_TYPE_GKR_ITEM_DELETER))
+#define SEAHORSE_GKR_ITEM_DELETER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAHORSE_TYPE_GKR_ITEM_DELETER, SeahorseGkrItemDeleterClass))
+
+typedef struct _SeahorseGkrItemDeleterClass SeahorseGkrItemDeleterClass;
+
+struct _SeahorseGkrItemDeleter {
+	SeahorseDeleter parent;
+	GList *items;
+};
+
+struct _SeahorseGkrItemDeleterClass {
+	SeahorseDeleterClass parent_class;
+};
+
+static void     delete_one_item                    (GSimpleAsyncResult *res);
+
+G_DEFINE_TYPE (SeahorseGkrItemDeleter, seahorse_gkr_item_deleter, SEAHORSE_TYPE_DELETER);
+
+static void
+seahorse_gkr_item_deleter_init (SeahorseGkrItemDeleter *self)
+{
+
+}
+
+static void
+seahorse_gkr_item_deleter_finalize (GObject *obj)
+{
+	SeahorseGkrItemDeleter *self = SEAHORSE_GKR_ITEM_DELETER (obj);
+
+	g_list_free_full (self->items, g_object_unref);
+
+	G_OBJECT_CLASS (seahorse_gkr_item_deleter_parent_class)->finalize (obj);
+}
+
+static GtkDialog *
+seahorse_gkr_item_deleter_create_confirm (SeahorseDeleter *deleter,
+                                          GtkWindow *parent)
+{
+	SeahorseGkrItemDeleter *self = SEAHORSE_GKR_ITEM_DELETER (deleter);
+	GtkDialog *dialog;
+	gchar *prompt;
+	guint num;
+
+	num = g_list_length (self->items);
+	if (num == 1) {
+		prompt = g_strdup_printf (_ ("Are you sure you want to delete the password '%s'?"),
+		                          seahorse_object_get_label (SEAHORSE_OBJECT (self->items->data)));
+	} else {
+		prompt = g_strdup_printf (ngettext ("Are you sure you want to delete %d password?",
+		                                    "Are you sure you want to delete %d passwords?",
+		                                    num), num);
+	}
+
+
+	dialog = seahorse_delete_dialog_new (parent, prompt);
+	g_free (prompt);
+
+	return dialog;
+}
+
+static GList *
+seahorse_gkr_item_deleter_get_objects (SeahorseDeleter *deleter)
+{
+	SeahorseGkrItemDeleter *self = SEAHORSE_GKR_ITEM_DELETER (deleter);
+	return self->items;
+}
+
+static gboolean
+seahorse_gkr_item_deleter_add_object (SeahorseDeleter *deleter,
+                                      GObject *object)
+{
+	SeahorseGkrItemDeleter *self = SEAHORSE_GKR_ITEM_DELETER (deleter);
+
+	if (!SEAHORSE_IS_GKR_ITEM (object))
+		return FALSE;
+	self->items = g_list_append (self->items, g_object_ref (object));
+	return TRUE;
+}
+
+typedef struct {
+	SeahorseGkrItem *current;
+	GCancellable *cancellable;
+	GQueue queue;
+	gpointer request;
+	gulong cancelled_sig;
+} DeleteClosure;
+
+static void
+delete_closure_free (gpointer data)
+{
+	DeleteClosure *closure = data;
+	SeahorseGkrItem *item;
+
+	g_clear_object (&closure->current);
+	while ((item = g_queue_pop_head (&closure->queue)))
+		g_object_unref (item);
+	g_queue_clear (&closure->queue);
+	if (closure->cancellable && closure->cancelled_sig)
+		g_signal_handler_disconnect (closure->cancellable,
+		                             closure->cancelled_sig);
+	g_clear_object (&closure->cancellable);
+	g_assert (!closure->request);
+	g_free (closure);
+}
+
+static void
+on_delete_gkr_complete (GnomeKeyringResult result,
+                        gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+	SeahorseGkrItemDeleter *self = SEAHORSE_GKR_ITEM_DELETER (g_async_result_get_source_object (user_data));
+	SeahorseGkrKeyring *keyring;
+	GError *error = NULL;
+	guint32 item_id;
+
+	closure->request = NULL;
+
+	if (seahorse_gkr_propagate_error (result, &error)) {
+		g_simple_async_result_take_error (res, error);
+		g_simple_async_result_complete_in_idle (res);
+
+	} else {
+		g_object_get (closure->current, "place", &keyring, NULL);
+		item_id = seahorse_gkr_item_get_item_id (closure->current);
+		seahorse_gkr_keyring_remove_item (keyring, item_id);
+		g_object_unref (keyring);
+		delete_one_item (res);
+	}
+
+	g_object_unref (self);
+}
+
+static void
+delete_one_item (GSimpleAsyncResult *res)
+{
+	DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+	const gchar *name;
+	guint32 item_id;
+
+	g_clear_object (&closure->current);
+	closure->current = g_queue_pop_head (&closure->queue);
+	if (closure->current == NULL) {
+		g_simple_async_result_complete_in_idle (res);
+
+	} else {
+		name = seahorse_gkr_item_get_keyring_name (closure->current);
+		item_id = seahorse_gkr_item_get_item_id (closure->current);
+		gnome_keyring_item_delete (name, item_id, on_delete_gkr_complete,
+		                           g_object_ref (res), g_object_unref);
+	}
+}
+
+static void
+on_delete_gkr_cancelled (GCancellable *cancellable,
+                         gpointer user_data)
+{
+	DeleteClosure *closure = user_data;
+
+	if (closure->request)
+		gnome_keyring_cancel_request (closure->request);
+}
+
+static void
+seahorse_gkr_item_deleter_delete_async (SeahorseDeleter *deleter,
+                                        GCancellable *cancellable,
+                                        GAsyncReadyCallback callback,
+                                        gpointer user_data)
+{
+	SeahorseGkrItemDeleter *self = SEAHORSE_GKR_ITEM_DELETER (deleter);
+	GSimpleAsyncResult *res;
+	DeleteClosure *closure;
+	GList *l;
+
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                 seahorse_gkr_item_deleter_delete_async);
+	closure = g_new0 (DeleteClosure, 1);
+	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	g_queue_init (&closure->queue);
+	for (l = self->items; l != NULL; l = g_list_next (l))
+		g_queue_push_tail (&closure->queue, g_object_ref (l->data));
+	g_simple_async_result_set_op_res_gpointer (res, closure, delete_closure_free);
+
+	if (cancellable)
+		closure->cancelled_sig = g_cancellable_connect (cancellable,
+		                                                G_CALLBACK (on_delete_gkr_cancelled),
+		                                                closure, NULL);
+
+	delete_one_item (res);
+	g_object_unref (res);
+}
+
+static gboolean
+seahorse_gkr_item_deleter_delete_finish (SeahorseDeleter *deleter,
+                                            GAsyncResult *result,
+                                            GError **error)
+{
+	SeahorseGkrItemDeleter *self = SEAHORSE_GKR_ITEM_DELETER (deleter);
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	                      seahorse_gkr_item_deleter_delete_async), FALSE);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+seahorse_gkr_item_deleter_class_init (SeahorseGkrItemDeleterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	SeahorseDeleterClass *deleter_class = SEAHORSE_DELETER_CLASS (klass);
+
+	gobject_class->finalize = seahorse_gkr_item_deleter_finalize;
+
+	deleter_class->add_object = seahorse_gkr_item_deleter_add_object;
+	deleter_class->create_confirm = seahorse_gkr_item_deleter_create_confirm;
+	deleter_class->delete_async = seahorse_gkr_item_deleter_delete_async;
+	deleter_class->delete_finish = seahorse_gkr_item_deleter_delete_finish;
+	deleter_class->get_objects = seahorse_gkr_item_deleter_get_objects;
+}
+
+SeahorseDeleter *
+seahorse_gkr_item_deleter_new (SeahorseGkrItem *item)
+{
+	SeahorseDeleter *deleter;
+
+	deleter = g_object_new (SEAHORSE_TYPE_GKR_ITEM_DELETER, NULL);
+	if (!seahorse_deleter_add_object (deleter, G_OBJECT (item)))
+		g_assert_not_reached ();
+
+	return deleter;
+}
diff --git a/gkr/seahorse-gkr-item-deleter.h b/gkr/seahorse-gkr-item-deleter.h
new file mode 100644
index 0000000..1d493ac
--- /dev/null
+++ b/gkr/seahorse-gkr-item-deleter.h
@@ -0,0 +1,42 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __SEAHORSE_GKR_ITEM_DELETER_H__
+#define __SEAHORSE_GKR_ITEM_DELETER_H__
+
+#include <glib-object.h>
+
+#include "seahorse-deleter.h"
+#include "seahorse-gkr-item.h"
+
+#define SEAHORSE_TYPE_GKR_ITEM_DELETER               (seahorse_gkr_item_deleter_get_type ())
+#define SEAHORSE_GKR_ITEM_DELETER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAHORSE_TYPE_GKR_ITEM_DELETER, SeahorseGkrItemDeleter))
+#define SEAHORSE_IS_GKR_ITEM_DELETER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAHORSE_TYPE_GKR_ITEM_DELETER))
+
+typedef struct _SeahorseGkrItemDeleter SeahorseGkrItemDeleter;
+
+GType              seahorse_gkr_item_deleter_get_type   (void) G_GNUC_CONST;
+
+SeahorseDeleter *  seahorse_gkr_item_deleter_new        (SeahorseGkrItem *item);
+
+#endif /* __SEAHORSE_GKR_ITEM_DELETER_H__ */
diff --git a/gkr/seahorse-gkr-item.c b/gkr/seahorse-gkr-item.c
index 8c9e1ea..4e5486d 100644
--- a/gkr/seahorse-gkr-item.c
+++ b/gkr/seahorse-gkr-item.c
@@ -29,9 +29,11 @@
 #include "seahorse-gkr.h"
 #include "seahorse-gkr-actions.h"
 #include "seahorse-gkr-item.h"
+#include "seahorse-gkr-item-deleter.h"
 #include "seahorse-gkr-keyring.h"
 #include "seahorse-gkr-operation.h"
 
+#include "seahorse-deletable.h"
 #include "seahorse-icons.h"
 #include "seahorse-place.h"
 #include "seahorse-util.h"
@@ -472,9 +474,13 @@ struct _SeahorseGkrItemPrivate {
 	DisplayInfo *display_info;
 };
 
-static gboolean require_item_attrs (SeahorseGkrItem *self);
+static gboolean  require_item_attrs                  (SeahorseGkrItem *self);
 
-G_DEFINE_TYPE (SeahorseGkrItem, seahorse_gkr_item, SEAHORSE_TYPE_OBJECT);
+static void      seahorse_gkr_item_deletable_iface   (SeahorseDeletableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (SeahorseGkrItem, seahorse_gkr_item, SEAHORSE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_DELETABLE, seahorse_gkr_item_deletable_iface);
+);
 
 /* -----------------------------------------------------------------------------
  * INTERNAL HELPERS
@@ -907,9 +913,18 @@ seahorse_gkr_item_class_init (SeahorseGkrItemClass *klass)
 	                              FALSE, G_PARAM_READABLE));
 }
 
-/* -----------------------------------------------------------------------------
- * PUBLIC 
- */
+static SeahorseDeleter *
+seahorse_gkr_item_create_deleter (SeahorseDeletable *deletable)
+{
+	SeahorseGkrItem *self = SEAHORSE_GKR_ITEM (deletable);
+	return seahorse_gkr_item_deleter_new (self);
+}
+
+static void
+seahorse_gkr_item_deletable_iface (SeahorseDeletableIface *iface)
+{
+	iface->create_deleter = seahorse_gkr_item_create_deleter;
+}
 
 SeahorseGkrItem *
 seahorse_gkr_item_new (SeahorseGkrKeyring *keyring,
diff --git a/gkr/seahorse-gkr-keyring-deleter.c b/gkr/seahorse-gkr-keyring-deleter.c
new file mode 100644
index 0000000..a304225
--- /dev/null
+++ b/gkr/seahorse-gkr-keyring-deleter.c
@@ -0,0 +1,228 @@
+/* 
+ * Seahorse
+ * 
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "seahorse-gkr-backend.h"
+#include "seahorse-gkr-keyring-deleter.h"
+#include "seahorse-gkr-operation.h"
+
+#include "seahorse-delete-dialog.h"
+#include "seahorse-object.h"
+
+#include <gnome-keyring.h>
+
+#include <glib/gi18n.h>
+
+#define SEAHORSE_GKR_KEYRING_DELETER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), SEAHORSE_TYPE_GKR_KEYRING_DELETER, SeahorseGkrKeyringDeleterClass))
+#define SEAHORSE_IS_GKR_KEYRING_DELETER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAHORSE_TYPE_GKR_KEYRING_DELETER))
+#define SEAHORSE_GKR_KEYRING_DELETER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAHORSE_TYPE_GKR_KEYRING_DELETER, SeahorseGkrKeyringDeleterClass))
+
+typedef struct _SeahorseGkrKeyringDeleterClass SeahorseGkrKeyringDeleterClass;
+
+struct _SeahorseGkrKeyringDeleter {
+	SeahorseDeleter parent;
+	SeahorseGkrKeyring *keyring;
+	GList *objects;
+};
+
+struct _SeahorseGkrKeyringDeleterClass {
+	SeahorseDeleterClass parent_class;
+};
+
+G_DEFINE_TYPE (SeahorseGkrKeyringDeleter, seahorse_gkr_keyring_deleter, SEAHORSE_TYPE_DELETER);
+
+static void
+seahorse_gkr_keyring_deleter_init (SeahorseGkrKeyringDeleter *self)
+{
+
+}
+
+static void
+seahorse_gkr_keyring_deleter_finalize (GObject *obj)
+{
+	SeahorseGkrKeyringDeleter *self = SEAHORSE_GKR_KEYRING_DELETER (obj);
+
+	g_object_unref (self->keyring);
+	g_list_free (self->objects);
+
+	G_OBJECT_CLASS (seahorse_gkr_keyring_deleter_parent_class)->finalize (obj);
+}
+
+static GtkDialog *
+seahorse_gkr_keyring_deleter_create_confirm (SeahorseDeleter *deleter,
+                                             GtkWindow *parent)
+{
+	SeahorseGkrKeyringDeleter *self = SEAHORSE_GKR_KEYRING_DELETER (deleter);
+	GtkDialog *dialog;
+
+	dialog = seahorse_delete_dialog_new (parent,
+	                                     _("Are you sure you want to delete the password keyring '%s'?"),
+	                                     seahorse_object_get_label (SEAHORSE_OBJECT (self->keyring)));
+
+	seahorse_delete_dialog_set_check_label (SEAHORSE_DELETE_DIALOG (dialog), _("I understand that all items will be permanently deleted."));
+	seahorse_delete_dialog_set_check_require (SEAHORSE_DELETE_DIALOG (dialog), TRUE);
+
+	return dialog;
+}
+
+static GList *
+seahorse_gkr_keyring_deleter_get_objects (SeahorseDeleter *deleter)
+{
+	SeahorseGkrKeyringDeleter *self = SEAHORSE_GKR_KEYRING_DELETER (deleter);
+	return self->objects;
+}
+
+static gboolean
+seahorse_gkr_keyring_deleter_add_object (SeahorseDeleter *deleter,
+                                         GObject *object)
+{
+	SeahorseGkrKeyringDeleter *self = SEAHORSE_GKR_KEYRING_DELETER (deleter);
+	if (self->keyring)
+		return FALSE;
+	if (!SEAHORSE_IS_GKR_KEYRING (object))
+		return FALSE;
+	self->keyring = g_object_ref (object);
+	self->objects = g_list_append (self->objects, self->keyring);
+	return TRUE;
+}
+
+typedef struct {
+	GCancellable *cancellable;
+	gpointer request;
+	gulong cancelled_sig;
+} DeleteClosure;
+
+static void
+delete_closure_free (gpointer data)
+{
+	DeleteClosure *closure = data;
+	if (closure->cancellable && closure->cancelled_sig)
+		g_signal_handler_disconnect (closure->cancellable,
+		                             closure->cancelled_sig);
+	g_clear_object (&closure->cancellable);
+	g_assert (!closure->request);
+	g_free (closure);
+}
+
+static void
+on_delete_gkr_complete (GnomeKeyringResult result,
+                        gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+	SeahorseGkrKeyringDeleter *self = SEAHORSE_GKR_KEYRING_DELETER (g_async_result_get_source_object (user_data));
+	GError *error = NULL;
+
+	closure->request = NULL;
+
+	if (seahorse_gkr_propagate_error (result, &error))
+		g_simple_async_result_take_error (res, error);
+	else
+		seahorse_gkr_backend_remove_keyring (NULL, self->keyring);
+
+	g_simple_async_result_complete_in_idle (res);
+	g_object_unref (self);
+}
+
+static void
+on_delete_gkr_cancelled (GCancellable *cancellable,
+                         gpointer user_data)
+{
+	DeleteClosure *closure = user_data;
+
+	if (closure->request)
+		gnome_keyring_cancel_request (closure->request);
+}
+
+static void
+seahorse_gkr_keyring_deleter_delete_async (SeahorseDeleter *deleter,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data)
+{
+	SeahorseGkrKeyringDeleter *self = SEAHORSE_GKR_KEYRING_DELETER (deleter);
+	GSimpleAsyncResult *res;
+	DeleteClosure *closure;
+	const gchar *name;
+
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                 seahorse_gkr_keyring_deleter_delete_async);
+	closure = g_new0 (DeleteClosure, 1);
+	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	g_simple_async_result_set_op_res_gpointer (res, closure, delete_closure_free);
+
+	name = seahorse_gkr_keyring_get_name (self->keyring);
+	closure->request = gnome_keyring_delete (name, on_delete_gkr_complete,
+	                                         g_object_ref (res), g_object_unref);
+
+	if (cancellable)
+		closure->cancelled_sig = g_cancellable_connect (cancellable,
+		                                                G_CALLBACK (on_delete_gkr_cancelled),
+		                                                closure, NULL);
+
+	g_object_unref (res);
+}
+
+static gboolean
+seahorse_gkr_keyring_deleter_delete_finish (SeahorseDeleter *deleter,
+                                            GAsyncResult *result,
+                                            GError **error)
+{
+	SeahorseGkrKeyringDeleter *self = SEAHORSE_GKR_KEYRING_DELETER (deleter);
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	                      seahorse_gkr_keyring_deleter_delete_async), FALSE);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+seahorse_gkr_keyring_deleter_class_init (SeahorseGkrKeyringDeleterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	SeahorseDeleterClass *deleter_class = SEAHORSE_DELETER_CLASS (klass);
+
+	gobject_class->finalize = seahorse_gkr_keyring_deleter_finalize;
+
+	deleter_class->add_object = seahorse_gkr_keyring_deleter_add_object;
+	deleter_class->create_confirm = seahorse_gkr_keyring_deleter_create_confirm;
+	deleter_class->delete_async = seahorse_gkr_keyring_deleter_delete_async;
+	deleter_class->delete_finish = seahorse_gkr_keyring_deleter_delete_finish;
+	deleter_class->get_objects = seahorse_gkr_keyring_deleter_get_objects;
+}
+
+SeahorseDeleter *
+seahorse_gkr_keyring_deleter_new (SeahorseGkrKeyring *keyring)
+{
+	SeahorseDeleter *deleter;
+
+	deleter = g_object_new (SEAHORSE_TYPE_GKR_KEYRING_DELETER, NULL);
+	if (!seahorse_deleter_add_object (deleter, G_OBJECT (keyring)))
+		g_assert_not_reached ();
+
+	return deleter;
+}
diff --git a/gkr/seahorse-gkr-keyring-deleter.h b/gkr/seahorse-gkr-keyring-deleter.h
new file mode 100644
index 0000000..75c23d2
--- /dev/null
+++ b/gkr/seahorse-gkr-keyring-deleter.h
@@ -0,0 +1,42 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __SEAHORSE_GKR_KEYRING_DELETER_H__
+#define __SEAHORSE_GKR_KEYRING_DELETER_H__
+
+#include <glib-object.h>
+
+#include "seahorse-deleter.h"
+#include "seahorse-gkr-keyring.h"
+
+#define SEAHORSE_TYPE_GKR_KEYRING_DELETER               (seahorse_gkr_keyring_deleter_get_type ())
+#define SEAHORSE_GKR_KEYRING_DELETER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAHORSE_TYPE_GKR_KEYRING_DELETER, SeahorseGkrKeyringDeleter))
+#define SEAHORSE_IS_GKR_KEYRING_DELETER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAHORSE_TYPE_GKR_KEYRING_DELETER))
+
+typedef struct _SeahorseGkrKeyringDeleter SeahorseGkrKeyringDeleter;
+
+GType              seahorse_gkr_keyring_deleter_get_type   (void) G_GNUC_CONST;
+
+SeahorseDeleter *  seahorse_gkr_keyring_deleter_new        (SeahorseGkrKeyring *keyring);
+
+#endif /* __SEAHORSE_GKR_KEYRING_DELETER_H__ */
diff --git a/gkr/seahorse-gkr-keyring.c b/gkr/seahorse-gkr-keyring.c
index ee917b2..b309e6f 100644
--- a/gkr/seahorse-gkr-keyring.c
+++ b/gkr/seahorse-gkr-keyring.c
@@ -23,11 +23,13 @@
 #include "config.h"
 
 #include "seahorse-gkr.h"
+#include "seahorse-gkr-keyring-deleter.h"
 #include "seahorse-gkr-keyring.h"
 #include "seahorse-gkr-operation.h"
 #include "seahorse-gkr-actions.h"
 
 #include "seahorse-action.h"
+#include "seahorse-deletable.h"
 #include "seahorse-progress.h"
 #include "seahorse-util.h"
 
@@ -59,11 +61,15 @@ struct _SeahorseGkrKeyringPrivate {
 };
 
 static void     seahorse_keyring_place_iface        (SeahorsePlaceIface *iface);
+
 static void     seahorse_keyring_collection_iface   (GcrCollectionIface *iface);
 
+static void     seahorse_keyring_deletable_iface    (SeahorseDeletableIface *iface);
+
 G_DEFINE_TYPE_WITH_CODE (SeahorseGkrKeyring, seahorse_gkr_keyring, SEAHORSE_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (GCR_TYPE_COLLECTION, seahorse_keyring_collection_iface);
                          G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_PLACE, seahorse_keyring_place_iface);
+                         G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_DELETABLE, seahorse_keyring_deletable_iface);
 );
 
 static GType
@@ -520,9 +526,19 @@ seahorse_keyring_collection_iface (GcrCollectionIface *iface)
 	iface->contains = seahorse_gkr_keyring_contains;
 }
 
-/* -----------------------------------------------------------------------------
- * PUBLIC 
- */
+static SeahorseDeleter *
+seahorse_gkr_keyring_create_deleter (SeahorseDeletable *deletable)
+{
+	SeahorseGkrKeyring *self = SEAHORSE_GKR_KEYRING (deletable);
+	return seahorse_gkr_keyring_deleter_new (self);
+}
+
+static void
+seahorse_keyring_deletable_iface (SeahorseDeletableIface *iface)
+{
+	iface->create_deleter = seahorse_gkr_keyring_create_deleter;
+}
+
 
 SeahorseGkrKeyring*
 seahorse_gkr_keyring_new (const gchar *keyring_name)
diff --git a/gkr/seahorse-gkr-operation.c b/gkr/seahorse-gkr-operation.c
index 46d7252..bc45636 100644
--- a/gkr/seahorse-gkr-operation.c
+++ b/gkr/seahorse-gkr-operation.c
@@ -360,151 +360,3 @@ seahorse_gkr_update_description_finish (SeahorseGkrItem *item,
 
 	return TRUE;
 }
-
-typedef struct {
-	gpointer request;
-	GQueue *objects;
-	GCancellable *cancellable;
-	gulong cancelled_sig;
-} delete_gkr_closure;
-
-static void
-delete_gkr_free (gpointer data)
-{
-	delete_gkr_closure *closure = data;
-	g_queue_foreach (closure->objects, (GFunc)g_object_unref, NULL);
-	g_queue_free (closure->objects);
-	if (closure->cancellable && closure->cancelled_sig)
-		g_signal_handler_disconnect (closure->cancellable,
-		                             closure->cancelled_sig);
-	g_clear_object (&closure->cancellable);
-	g_assert (!closure->request);
-	g_free (closure);
-}
-
-static void            delete_gkr_one_object         (GSimpleAsyncResult *res);
-
-static void 
-on_delete_gkr_complete (GnomeKeyringResult result,
-                        gpointer user_data)
-{
-	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
-	delete_gkr_closure *closure = g_simple_async_result_get_op_res_gpointer (res);
-	GError *error = NULL;
-	GObject *object;
-	SeahorseGkrKeyring *keyring = NULL;
-	SeahorseGkrItem *item;
-
-	closure->request = NULL;
-	object = g_queue_pop_head (closure->objects);
-	seahorse_progress_end (closure->cancellable, object);
-
-	if (seahorse_gkr_propagate_error (result, &error)) {
-		g_simple_async_result_take_error (res, error);
-		g_simple_async_result_complete_in_idle (res);
-
-	} else if (SEAHORSE_IS_GKR_ITEM (object)) {
-		g_object_get (object, "place", &keyring, NULL);
-		item = SEAHORSE_GKR_ITEM (object);
-		seahorse_gkr_keyring_remove_item (keyring, seahorse_gkr_item_get_item_id (item));
-
-		delete_gkr_one_object (res);
-	} else if (SEAHORSE_IS_GKR_KEYRING (object)) {
-		keyring = SEAHORSE_GKR_KEYRING (object);
-		seahorse_gkr_backend_remove_keyring (NULL, keyring);
-
-		delete_gkr_one_object (res);
-	}
-
-	g_object_unref (object);
-}
-
-static void
-on_delete_gkr_cancelled (GCancellable *cancellable,
-                         gpointer user_data)
-{
-	delete_gkr_closure *closure = user_data;
-
-	if (closure->request)
-		gnome_keyring_cancel_request (closure->request);
-}
-
-static void
-delete_gkr_one_object (GSimpleAsyncResult *res)
-{
-	delete_gkr_closure *closure = g_simple_async_result_get_op_res_gpointer (res);
-	GObject *object;
-	const gchar *keyring;
-	guint32 item;
-
-	if (g_queue_is_empty (closure->objects)) {
-		g_simple_async_result_complete_in_idle (res);
-		return;
-	}
-
-	g_assert (!closure->request);
-	object = g_queue_peek_head (closure->objects);
-
-	seahorse_progress_begin (closure->cancellable, object);
-	if (SEAHORSE_IS_GKR_ITEM (object)) {
-		keyring = seahorse_gkr_item_get_keyring_name (SEAHORSE_GKR_ITEM (object));
-		item = seahorse_gkr_item_get_item_id (SEAHORSE_GKR_ITEM (object));
-		closure->request = gnome_keyring_item_delete (keyring, item,
-		                                              on_delete_gkr_complete,
-		                                              g_object_ref (res), g_object_unref);
-	} else if (SEAHORSE_IS_GKR_KEYRING (object)) {
-		keyring = seahorse_gkr_keyring_get_name (SEAHORSE_GKR_KEYRING (object));
-		closure->request = gnome_keyring_delete (keyring,
-		                                         on_delete_gkr_complete,
-		                                         g_object_ref (res), g_object_unref);
-	} else {
-		g_assert_not_reached ();
-	}
-}
-
-void
-seahorse_gkr_delete_async (GList *objects,
-                           GCancellable *cancellable,
-                           GAsyncReadyCallback callback,
-                           gpointer user_data)
-{
-	GSimpleAsyncResult *res;
-	delete_gkr_closure *closure;
-	GList *l;
-
-	res = g_simple_async_result_new (NULL, callback, user_data,
-	                                 seahorse_gkr_delete_async);
-
-	closure = g_new0 (delete_gkr_closure, 1);
-	closure->objects = g_queue_new ();
-	closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-	g_simple_async_result_set_op_res_gpointer (res, closure, delete_gkr_free);
-
-	for (l = objects; l != NULL; l = g_list_next (l)) {
-		g_return_if_fail (SEAHORSE_IS_GKR_ITEM (l->data) || SEAHORSE_IS_GKR_KEYRING (l->data));
-		g_queue_push_tail (closure->objects, g_object_ref (l->data));
-		seahorse_progress_prep (cancellable, l->data, NULL);
-	}
-
-	delete_gkr_one_object (res);
-
-	if (cancellable)
-		closure->cancelled_sig = g_cancellable_connect (cancellable,
-		                                                G_CALLBACK (on_delete_gkr_cancelled),
-		                                                closure, NULL);
-
-	g_object_unref (res);
-}
-
-gboolean
-seahorse_gkr_delete_finish (GAsyncResult *result,
-                            GError **error)
-{
-	g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
-	                      seahorse_gkr_delete_async), FALSE);
-
-	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
-		return FALSE;
-
-	return TRUE;
-}
diff --git a/gkr/seahorse-gkr-operation.h b/gkr/seahorse-gkr-operation.h
index 3f398ed..2172383 100644
--- a/gkr/seahorse-gkr-operation.h
+++ b/gkr/seahorse-gkr-operation.h
@@ -49,12 +49,4 @@ gboolean      seahorse_gkr_update_secret_finish      (SeahorseGkrItem *item,
                                                       GAsyncResult *result,
                                                       GError **error);
 
-void          seahorse_gkr_delete_async              (GList *objects,
-                                                      GCancellable *cancellable,
-                                                      GAsyncReadyCallback callback,
-                                                      gpointer user_data);
-
-gboolean      seahorse_gkr_delete_finish             (GAsyncResult *result,
-                                                      GError **error);
-
 #endif /* __SEAHORSE_GKR_OPERATION_H__ */
diff --git a/libseahorse/Makefile.am b/libseahorse/Makefile.am
index d1a4b15..ae6d681 100644
--- a/libseahorse/Makefile.am
+++ b/libseahorse/Makefile.am
@@ -37,6 +37,8 @@ libseahorse_la_SOURCES = \
 	seahorse-context.c seahorse-context.h \
 	seahorse-debug.c seahorse-debug.h \
 	seahorse-delete-dialog.c seahorse-delete-dialog.h \
+	seahorse-deletable.c seahorse-deletable.h \
+	seahorse-deleter.c seahorse-deleter.h \
 	seahorse-exportable.c seahorse-exportable.h \
 	seahorse-exporter.c seahorse-exporter.h \
 	seahorse-icons.c seahorse-icons.h \
diff --git a/libseahorse/seahorse-deletable.c b/libseahorse/seahorse-deletable.c
new file mode 100644
index 0000000..1dcd98e
--- /dev/null
+++ b/libseahorse/seahorse-deletable.c
@@ -0,0 +1,185 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2004,2005 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "seahorse-deletable.h"
+#include "seahorse-deleter.h"
+
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+typedef SeahorseDeletableIface SeahorseDeletableInterface;
+
+G_DEFINE_INTERFACE (SeahorseDeletable, seahorse_deletable, G_TYPE_OBJECT);
+
+SeahorseDeleter *
+seahorse_deletable_create_deleter (SeahorseDeletable *deletable)
+{
+	SeahorseDeletableIface *iface;
+
+	g_return_val_if_fail (SEAHORSE_IS_DELETABLE (deletable), NULL);
+
+	iface = SEAHORSE_DELETABLE_GET_INTERFACE (deletable);
+	g_return_val_if_fail (iface->create_deleter, NULL);
+
+	return iface->create_deleter (deletable);
+}
+
+gboolean
+seahorse_deletable_can_delete (gpointer object)
+{
+	gboolean can = FALSE;
+
+	if (!SEAHORSE_IS_DELETABLE (object))
+		return FALSE;
+
+	g_object_get (object, "deletable", &can, NULL);
+	return can;
+}
+
+static void
+seahorse_deletable_default_init (SeahorseDeletableIface *iface)
+{
+	static gboolean initialized = FALSE;
+	if (!initialized) {
+		initialized = TRUE;
+		g_object_interface_install_property (iface,
+		                g_param_spec_boolean ("deletable", "Deletable", "Is actually deletable",
+		                                      FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+	}
+}
+
+typedef struct {
+	GMainLoop *loop;
+	GAsyncResult *result;
+} WaitClosure;
+
+static WaitClosure *
+wait_closure_new (void)
+{
+	WaitClosure *closure;
+
+	closure = g_slice_new0 (WaitClosure);
+	closure->loop = g_main_loop_new (NULL, FALSE);
+
+	return closure;
+}
+
+static void
+wait_closure_free (WaitClosure *closure)
+{
+	g_clear_object (&closure->result);
+	g_main_loop_unref (closure->loop);
+	g_slice_free (WaitClosure, closure);
+}
+
+static void
+on_wait_complete (GObject *source,
+                  GAsyncResult *result,
+                  gpointer user_data)
+{
+	WaitClosure *closure = user_data;
+	g_assert (closure->result == NULL);
+	closure->result = g_object_ref (result);
+	g_main_loop_quit (closure->loop);
+}
+
+guint
+seahorse_deletable_delete_with_prompt_wait (GList *objects,
+                                            GtkWindow *parent,
+                                            GError **error)
+{
+	SeahorseDeleter *deleter;
+	WaitClosure *closure;
+	GHashTable *pending;
+	guint count = 0;
+	gboolean ret;
+	GList *l, *x;
+	GList *deleted;
+
+	g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), 0);
+	g_return_val_if_fail (error == NULL || *error == NULL, 0);
+
+	/* A table for monitoring which objects are still pending */
+	pending = g_hash_table_new (g_direct_hash, g_direct_equal);
+	for (l = objects; l != NULL; l = g_list_next (l))
+		g_hash_table_replace (pending, l->data, l->data);
+
+	closure = wait_closure_new ();
+
+	for (l = objects; l != NULL; l = g_list_next (l)) {
+		if (!g_hash_table_lookup (pending, l->data))
+			continue;
+
+		if (!seahorse_deletable_can_delete (l->data)) {
+			g_hash_table_remove (pending, l->data);
+			continue;
+		}
+
+		deleter = seahorse_deletable_create_deleter (SEAHORSE_DELETABLE (l->data));
+		if (!deleter)
+			continue;
+
+		/* Now go and add all pending to each exporter */
+		for (x = objects; x != NULL; x = g_list_next (x)) {
+			if (x->data == l->data)
+				continue;
+			if (g_hash_table_lookup (pending, x->data))
+				seahorse_deleter_add_object (deleter, x->data);
+		}
+
+		/* Now show a prompt choosing between the exporters */
+		ret = seahorse_deleter_prompt (deleter, parent);
+
+		if (!ret) {
+			g_object_unref (deleter);
+			break;
+		}
+
+		seahorse_deleter_delete_async (deleter, NULL, on_wait_complete, closure);
+
+		g_main_loop_run (closure->loop);
+
+		ret = seahorse_deleter_delete_finish (deleter, closure->result, error);
+		g_object_unref (closure->result);
+		closure->result = NULL;
+
+		if (ret) {
+			deleted = seahorse_deleter_get_objects (deleter);
+			for (x = deleted; x != NULL; x = g_list_next (x)) {
+				g_hash_table_remove (pending, x->data);
+				count++;
+			}
+		}
+
+		g_object_unref (deleter);
+
+		if (!ret)
+			break;
+	}
+
+	wait_closure_free (closure);
+	g_hash_table_destroy (pending);
+	return count;
+}
diff --git a/libseahorse/seahorse-deletable.h b/libseahorse/seahorse-deletable.h
new file mode 100644
index 0000000..28b2ab3
--- /dev/null
+++ b/libseahorse/seahorse-deletable.h
@@ -0,0 +1,53 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Deletable, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __SEAHORSE_DELETABLE_H__
+#define __SEAHORSE_DELETABLE_H__
+
+#include "seahorse-deleter.h"
+
+#define SEAHORSE_TYPE_DELETABLE                (seahorse_deletable_get_type ())
+#define SEAHORSE_DELETABLE(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAHORSE_TYPE_DELETABLE, SeahorseDeletable))
+#define SEAHORSE_IS_DELETABLE(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAHORSE_TYPE_DELETABLE))
+#define SEAHORSE_DELETABLE_GET_INTERFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SEAHORSE_TYPE_DELETABLE, SeahorseDeletableIface))
+
+typedef struct _SeahorseDeletable SeahorseDeletable;
+typedef struct _SeahorseDeletableIface SeahorseDeletableIface;
+
+struct _SeahorseDeletableIface {
+	GTypeInterface parent;
+
+	SeahorseDeleter *   (* create_deleter)      (SeahorseDeletable *deletable);
+};
+
+GType             seahorse_deletable_get_type                (void) G_GNUC_CONST;
+
+SeahorseDeleter * seahorse_deletable_create_deleter          (SeahorseDeletable *deletable);
+
+gboolean          seahorse_deletable_can_delete              (gpointer object);
+
+guint             seahorse_deletable_delete_with_prompt_wait (GList *objects,
+                                                              GtkWindow *parent,
+                                                              GError **error);
+
+#endif /* __SEAHORSE_DELETABLE_H__ */
diff --git a/libseahorse/seahorse-deleter.c b/libseahorse/seahorse-deleter.c
new file mode 100644
index 0000000..1b5b519
--- /dev/null
+++ b/libseahorse/seahorse-deleter.c
@@ -0,0 +1,135 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "seahorse-deleter.h"
+
+G_DEFINE_TYPE (SeahorseDeleter, seahorse_deleter, G_TYPE_OBJECT);
+
+static void
+seahorse_deleter_init (SeahorseDeleter *deleter)
+{
+
+}
+
+static void
+seahorse_deleter_class_init (SeahorseDeleterClass *klass)
+{
+
+}
+
+GtkDialog *
+seahorse_deleter_create_confirm (SeahorseDeleter *deleter,
+                                 GtkWindow *parent)
+{
+	SeahorseDeleterClass *class;
+
+	g_return_val_if_fail (SEAHORSE_IS_DELETER (deleter), NULL);
+
+	class = SEAHORSE_DELETER_GET_CLASS (deleter);
+	g_return_val_if_fail (class->create_confirm != NULL, NULL);
+
+	return (class->create_confirm) (deleter, parent);
+}
+
+GList *
+seahorse_deleter_get_objects (SeahorseDeleter *deleter)
+{
+	SeahorseDeleterClass *class;
+
+	g_return_val_if_fail (SEAHORSE_IS_DELETER (deleter), NULL);
+
+	class = SEAHORSE_DELETER_GET_CLASS (deleter);
+	g_return_val_if_fail (class->get_objects != NULL, NULL);
+
+	return (class->get_objects) (deleter);
+}
+
+gboolean
+seahorse_deleter_add_object (SeahorseDeleter *deleter,
+                             GObject *object)
+{
+	SeahorseDeleterClass *class;
+
+	g_return_val_if_fail (SEAHORSE_IS_DELETER (deleter), FALSE);
+	g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
+
+	class = SEAHORSE_DELETER_GET_CLASS (deleter);
+	g_return_val_if_fail (class->add_object != NULL, FALSE);
+
+	return (class->add_object) (deleter, object);
+}
+
+void
+seahorse_deleter_delete_async (SeahorseDeleter *deleter,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+	SeahorseDeleterClass *class;
+
+	g_return_if_fail (SEAHORSE_IS_DELETER (deleter));
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+	class = SEAHORSE_DELETER_GET_CLASS (deleter);
+	g_return_if_fail (class->delete_async != NULL);
+
+	(class->delete_async) (deleter, cancellable, callback, user_data);
+}
+
+gboolean
+seahorse_deleter_delete_finish (SeahorseDeleter *deleter,
+                                GAsyncResult *result,
+                                GError **error)
+{
+	SeahorseDeleterClass *class;
+
+	g_return_val_if_fail (SEAHORSE_IS_DELETER (deleter), FALSE);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+	class = SEAHORSE_DELETER_GET_CLASS (deleter);
+	g_return_val_if_fail (class->delete_finish != NULL, FALSE);
+
+	return (class->delete_finish) (deleter, result, error);
+}
+
+gboolean
+seahorse_deleter_prompt (SeahorseDeleter *deleter,
+                         GtkWindow *parent)
+{
+	GtkDialog *prompt;
+	gint res;
+
+	g_return_val_if_fail (SEAHORSE_IS_DELETER (deleter), FALSE);
+	g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), FALSE);
+
+	prompt = seahorse_deleter_create_confirm (deleter, parent);
+	g_return_val_if_fail (prompt != NULL, FALSE);
+
+	res = gtk_dialog_run (prompt);
+	gtk_widget_destroy (GTK_WIDGET (prompt));
+
+	return res == GTK_RESPONSE_OK || res == GTK_RESPONSE_ACCEPT;
+}
diff --git a/libseahorse/seahorse-deleter.h b/libseahorse/seahorse-deleter.h
new file mode 100644
index 0000000..5b261ec
--- /dev/null
+++ b/libseahorse/seahorse-deleter.h
@@ -0,0 +1,92 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Deleter, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __SEAHORSE_DELETER_H__
+#define __SEAHORSE_DELETER_H__
+
+#include "seahorse-types.h"
+
+#include <gio/gio.h>
+
+#include <gtk/gtk.h>
+
+#define SEAHORSE_TYPE_DELETER             (seahorse_deleter_get_type ())
+#define SEAHORSE_DELETER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAHORSE_TYPE_DELETER, SeahorseDeleter))
+#define SEAHORSE_DELETER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), SEAHORSE_TYPE_DELETER, SeahorseDeleterClass))
+#define SEAHORSE_IS_DELETER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAHORSE_TYPE_DELETER))
+#define SEAHORSE_IS_DELETER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAHORSE_TYPE_DELETER))
+#define SEAHORSE_DELETER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAHORSE_TYPE_DELETER, SeahorseDeleterClass))
+
+typedef struct _SeahorseDeleter SeahorseDeleter;
+typedef struct _SeahorseDeleterClass SeahorseDeleterClass;
+
+struct _SeahorseDeleter {
+	GObject parent;
+};
+
+struct _SeahorseDeleterClass {
+	GObjectClass parent;
+
+	/* virtual methods ------------------------------------------------- */
+
+	GtkDialog *     (* create_confirm)           (SeahorseDeleter *deleter,
+	                                              GtkWindow *parent);
+
+	GList *         (* get_objects)              (SeahorseDeleter *deleter);
+
+	gboolean        (* add_object)               (SeahorseDeleter *deleter,
+	                                              GObject *object);
+
+	void            (* delete_async)             (SeahorseDeleter *deleter,
+	                                              GCancellable *cancellable,
+	                                              GAsyncReadyCallback callback,
+	                                              gpointer user_data);
+
+	gboolean        (* delete_finish)            (SeahorseDeleter *deleter,
+	                                              GAsyncResult *result,
+	                                              GError **error);
+};
+
+GType            seahorse_deleter_get_type           (void) G_GNUC_CONST;
+
+GtkDialog *      seahorse_deleter_create_confirm     (SeahorseDeleter *deleter,
+                                                      GtkWindow *parent);
+
+GList *          seahorse_deleter_get_objects        (SeahorseDeleter *deleter);
+
+gboolean         seahorse_deleter_add_object         (SeahorseDeleter *deleter,
+                                                      GObject *object);
+
+void             seahorse_deleter_delete_async       (SeahorseDeleter *deleter,
+                                                      GCancellable *cancellable,
+                                                      GAsyncReadyCallback callback,
+                                                      gpointer user_data);
+
+gboolean         seahorse_deleter_delete_finish      (SeahorseDeleter *deleter,
+                                                      GAsyncResult *result,
+                                                      GError **error);
+
+gboolean         seahorse_deleter_prompt             (SeahorseDeleter *deleter,
+                                                      GtkWindow *parent);
+
+#endif /* __SEAHORSE_DELETER_H__ */
diff --git a/libseahorse/seahorse-exportable.c b/libseahorse/seahorse-exportable.c
index ad4bad5..b41c4fd 100644
--- a/libseahorse/seahorse-exportable.c
+++ b/libseahorse/seahorse-exportable.c
@@ -47,6 +47,19 @@ seahorse_exportable_create_exporters (SeahorseExportable *exportable,
 	return iface->create_exporters (exportable, type);
 }
 
+
+gboolean
+seahorse_exportable_can_export (gpointer object)
+{
+	gboolean can = FALSE;
+
+	if (!SEAHORSE_IS_EXPORTABLE (object))
+		return FALSE;
+
+	g_object_get (object, "exportable", &can, NULL);
+	return can;
+}
+
 typedef struct {
 	GMainLoop *loop;
 	GAsyncResult *result;
@@ -88,6 +101,9 @@ seahorse_exportable_default_init (SeahorseExportableIface *iface)
 	static gboolean initialized = FALSE;
 	if (!initialized) {
 		initialized = TRUE;
+		g_object_interface_install_property (iface,
+		               g_param_spec_boolean ("exportable", "Exportable", "Is actually exportable",
+		                                      FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 	}
 }
 
@@ -112,7 +128,7 @@ seahorse_exportable_export_to_directory_wait (GList *objects,
 	closure = wait_closure_new ();
 
 	for (l = objects; l != NULL; l = g_list_next (l)) {
-		if (!SEAHORSE_IS_EXPORTABLE (l->data))
+		if (!seahorse_exportable_can_export (l->data))
 			continue;
 
 		exporters = seahorse_exportable_create_exporters (SEAHORSE_EXPORTABLE (l->data),
@@ -168,7 +184,7 @@ seahorse_exportable_export_to_text_wait (GList *objects,
 	g_return_val_if_fail (size != NULL, 0);
 
 	for (l = objects; l != NULL; l = g_list_next (l)) {
-		if (!SEAHORSE_IS_EXPORTABLE (l->data))
+		if (!seahorse_exportable_can_export (l->data))
 			continue;
 
 		/* If we've already found exporters, then add to those */
@@ -244,7 +260,7 @@ seahorse_exportable_export_to_prompt_wait (GList *objects,
 		if (!g_hash_table_lookup (pending, l->data))
 			continue;
 
-		if (!SEAHORSE_IS_EXPORTABLE (l->data)) {
+		if (!seahorse_exportable_can_export (l->data)) {
 			g_hash_table_remove (pending, l->data);
 			continue;
 		}
diff --git a/libseahorse/seahorse-exportable.h b/libseahorse/seahorse-exportable.h
index 4474151..b6e910d 100644
--- a/libseahorse/seahorse-exportable.h
+++ b/libseahorse/seahorse-exportable.h
@@ -50,6 +50,8 @@ GType       seahorse_exportable_get_type                    (void) G_GNUC_CONST;
 GList *     seahorse_exportable_create_exporters            (SeahorseExportable *exportable,
                                                              SeahorseExporterType type);
 
+gboolean    seahorse_exportable_can_export                  (gpointer object);
+
 guint       seahorse_exportable_export_to_directory_wait    (GList *objects,
                                                              const gchar *directory,
                                                              GError **error);
diff --git a/libseahorse/seahorse-object.c b/libseahorse/seahorse-object.c
index 18c49bb..5fa9930 100644
--- a/libseahorse/seahorse-object.c
+++ b/libseahorse/seahorse-object.c
@@ -40,7 +40,9 @@ enum {
 	PROP_ICON,
 	PROP_IDENTIFIER,
 	PROP_USAGE,
-	PROP_FLAGS
+	PROP_FLAGS,
+	PROP_DELETABLE,
+	PROP_EXPORTABLE
 };
 
 /**
@@ -274,6 +276,12 @@ seahorse_object_get_property (GObject *obj, guint prop_id, GValue *value,
 	case PROP_FLAGS:
 		g_value_set_uint (value, seahorse_object_get_flags (self));
 		break;
+	case PROP_DELETABLE:
+		g_value_set_boolean (value, seahorse_object_get_flags (self) & SEAHORSE_FLAG_DELETABLE ? TRUE : FALSE);
+		break;
+	case PROP_EXPORTABLE:
+		g_value_set_boolean (value, seahorse_object_get_flags (self) & SEAHORSE_FLAG_EXPORTABLE ? TRUE : FALSE);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -414,6 +422,14 @@ seahorse_object_class_init (SeahorseObjectClass *klass)
 	g_object_class_install_property (gobject_class, PROP_FLAGS,
 	           g_param_spec_uint ("flags", "Object Flags", "This object's flags.", 
 	                              0, G_MAXUINT, 0, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_DELETABLE,
+	           g_param_spec_boolean ("deletable", "Deletable", "Object is deletable.",
+	                                 FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property (gobject_class, PROP_EXPORTABLE,
+	           g_param_spec_boolean ("exportable", "Exportable", "Object is exportable.",
+	                                 FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 }
 
 /* -----------------------------------------------------------------------------
diff --git a/libseahorse/seahorse-viewer.c b/libseahorse/seahorse-viewer.c
index ae3893b..8930dbf 100644
--- a/libseahorse/seahorse-viewer.c
+++ b/libseahorse/seahorse-viewer.c
@@ -25,6 +25,7 @@
 #include "seahorse-action.h"
 #include "seahorse-actions.h"
 #include "seahorse-backend.h"
+#include "seahorse-deletable.h"
 #include "seahorse-exportable.h"
 #include "seahorse-object.h"
 #include "seahorse-preferences.h"
@@ -177,27 +178,18 @@ on_object_delete (GtkAction *action,
                   gpointer user_data)
 {
 	SeahorseViewer *self = SEAHORSE_VIEWER (user_data);
-	GCancellable *cancellable;
-	GtkAction *delete_action;
+	GError *error = NULL;
 	GtkWindow *window;
-	GList *l;
-
-	cancellable = g_cancellable_new ();
+	GList *objects;
 
-	g_cancellable_push_current (cancellable);
 	window = seahorse_viewer_get_window (self);
+	objects = seahorse_viewer_get_selected_objects (self);
+	seahorse_deletable_delete_with_prompt_wait (objects, window, &error);
 
-	/* Now go through and clone for the selection */
-	for (l = self->pv->selection_actions;
-	     l != NULL && !g_cancellable_is_cancelled (cancellable);
-	     l = g_list_next (l)) {
-		delete_action = gtk_action_group_get_action (l->data, "delete");
-		if (delete_action != NULL && gtk_action_is_sensitive (delete_action))
-			seahorse_action_activate_with_window (delete_action, window);
-	}
+	if (error != NULL)
+		seahorse_util_handle_error (&error, window, _("Cannot delete"));
 
-	g_cancellable_pop_current (cancellable);
-	g_object_unref (cancellable);
+	g_list_free (objects);
 }
 
 static void
@@ -340,10 +332,12 @@ seahorse_viewer_real_selection_changed (SeahorseViewer *self)
 
 	objects = seahorse_viewer_get_selected_objects (self);
 	for (l = objects; l != NULL; l = g_list_next (l)) {
-		if (SEAHORSE_IS_EXPORTABLE (l->data)) {
+		if (seahorse_exportable_can_export (l->data))
 			can_export = TRUE;
+		if (seahorse_deletable_can_delete (l->data))
+			can_delete = TRUE;
+		if (can_export && can_delete)
 			break;
-		}
 	}
 
 	groups = lookup_actions_for_objects (self, objects);
@@ -353,8 +347,6 @@ seahorse_viewer_real_selection_changed (SeahorseViewer *self)
 	for (l = groups; l != NULL; l = g_list_next (l)) {
 		if (gtk_action_group_get_action (l->data, "properties"))
 			can_properties = TRUE;
-		if (gtk_action_group_get_action (l->data, "delete"))
-			can_delete = TRUE;
 	}
 
 	gtk_action_set_sensitive (self->pv->properties_object, can_properties);
diff --git a/pgp/Makefile.am b/pgp/Makefile.am
index d7c260f..b1ddb75 100644
--- a/pgp/Makefile.am
+++ b/pgp/Makefile.am
@@ -49,11 +49,13 @@ libseahorse_pgp_la_SOURCES = \
 	seahorse-gpgme-exporter.c seahorse-gpgme-exporter.h \
 	seahorse-gpgme-generate.c \
 	seahorse-gpgme-key.c seahorse-gpgme-key.h \
+	seahorse-gpgme-key-deleter.c seahorse-gpgme-key-deleter.h \
 	seahorse-gpgme-key-op.c seahorse-gpgme-key-op.h \
 	seahorse-gpgme-keyring.c seahorse-gpgme-keyring.h \
 	seahorse-gpgme-photo.c seahorse-gpgme-photo.h \
 	seahorse-gpgme-photos.c \
 	seahorse-gpgme-revoke.c \
+	seahorse-gpgme-secret-deleter.c seahorse-gpgme-secret-deleter.h \
 	seahorse-gpgme-sign.c \
 	seahorse-gpgme-subkey.c seahorse-gpgme-subkey.h \
 	seahorse-gpgme-uid.c seahorse-gpgme-uid.h \
diff --git a/pgp/seahorse-gpgme-key-deleter.c b/pgp/seahorse-gpgme-key-deleter.c
new file mode 100644
index 0000000..ea4cd13
--- /dev/null
+++ b/pgp/seahorse-gpgme-key-deleter.c
@@ -0,0 +1,181 @@
+/* 
+ * Seahorse
+ * 
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "seahorse-gpgme.h"
+#include "seahorse-gpgme-key-deleter.h"
+#include "seahorse-gpgme-key-op.h"
+
+#include "seahorse-delete-dialog.h"
+
+#include <glib/gi18n.h>
+
+#define SEAHORSE_GPGME_KEY_DELETER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), SEAHORSE_TYPE_GPGME_KEY_DELETER, SeahorseGpgmeKeyDeleterClass))
+#define SEAHORSE_IS_GPGME_KEY_DELETER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAHORSE_TYPE_GPGME_KEY_DELETER))
+#define SEAHORSE_GPGME_KEY_DELETER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAHORSE_TYPE_GPGME_KEY_DELETER, SeahorseGpgmeKeyDeleterClass))
+
+typedef struct _SeahorseGpgmeKeyDeleterClass SeahorseGpgmeKeyDeleterClass;
+
+struct _SeahorseGpgmeKeyDeleter {
+	SeahorseDeleter parent;
+	GList *keys;
+};
+
+struct _SeahorseGpgmeKeyDeleterClass {
+	SeahorseDeleterClass parent_class;
+};
+
+G_DEFINE_TYPE (SeahorseGpgmeKeyDeleter, seahorse_gpgme_key_deleter, SEAHORSE_TYPE_DELETER);
+
+static void
+seahorse_gpgme_key_deleter_init (SeahorseGpgmeKeyDeleter *self)
+{
+
+}
+
+static void
+seahorse_gpgme_key_deleter_finalize (GObject *obj)
+{
+	SeahorseGpgmeKeyDeleter *self = SEAHORSE_GPGME_KEY_DELETER (obj);
+
+	g_list_free_full (self->keys, g_object_unref);
+
+	G_OBJECT_CLASS (seahorse_gpgme_key_deleter_parent_class)->finalize (obj);
+}
+
+static GtkDialog *
+seahorse_gpgme_key_deleter_create_confirm (SeahorseDeleter *deleter,
+                                           GtkWindow *parent)
+{
+	SeahorseGpgmeKeyDeleter *self = SEAHORSE_GPGME_KEY_DELETER (deleter);
+	GtkDialog *dialog;
+	gchar *prompt;
+	guint num;
+
+	num = g_list_length (self->keys);
+	if (num == 1) {
+		prompt = g_strdup_printf (_("Are you sure you want to permanently delete %s?"),
+		                          seahorse_object_get_label (SEAHORSE_OBJECT (self->keys->data)));
+	} else {
+		prompt = g_strdup_printf (ngettext ("Are you sure you want to permanently delete %d keys?",
+		                                    "Are you sure you want to permanently delete %d keys?",
+		                                    num),
+		                          num);
+	}
+
+	dialog = seahorse_delete_dialog_new (parent, prompt);
+	g_free (prompt);
+
+	return dialog;
+}
+
+static GList *
+seahorse_gpgme_key_deleter_get_objects (SeahorseDeleter *deleter)
+{
+	SeahorseGpgmeKeyDeleter *self = SEAHORSE_GPGME_KEY_DELETER (deleter);
+	return self->keys;
+}
+
+static gboolean
+seahorse_gpgme_key_deleter_add_object (SeahorseDeleter *deleter,
+                                      GObject *object)
+{
+	SeahorseGpgmeKeyDeleter *self = SEAHORSE_GPGME_KEY_DELETER (deleter);
+
+	if (!SEAHORSE_IS_GPGME_KEY (object))
+		return FALSE;
+	self->keys = g_list_append (self->keys, g_object_ref (object));
+	return TRUE;
+}
+
+static void
+seahorse_gpgme_key_deleter_delete_async (SeahorseDeleter *deleter,
+                                         GCancellable *cancellable,
+                                         GAsyncReadyCallback callback,
+                                         gpointer user_data)
+{
+	SeahorseGpgmeKeyDeleter *self = SEAHORSE_GPGME_KEY_DELETER (deleter);
+	GSimpleAsyncResult *res;
+	GError *error = NULL;
+	gpgme_error_t gerr;
+	GList *l;
+
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                 seahorse_gpgme_key_deleter_delete_async);
+
+	for (l = self->keys; l != NULL && !g_cancellable_is_cancelled (cancellable);
+	     l = g_list_next (l)) {
+		gerr = seahorse_gpgme_key_op_delete (l->data);
+		if (seahorse_gpgme_propagate_error (gerr, &error)) {
+			g_simple_async_result_take_error (res, error);
+			break;
+		}
+	}
+
+	g_simple_async_result_complete_in_idle (res);
+	g_object_unref (res);
+}
+
+static gboolean
+seahorse_gpgme_key_deleter_delete_finish (SeahorseDeleter *deleter,
+                                          GAsyncResult *result,
+                                          GError **error)
+{
+	SeahorseGpgmeKeyDeleter *self = SEAHORSE_GPGME_KEY_DELETER (deleter);
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	                      seahorse_gpgme_key_deleter_delete_async), FALSE);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+seahorse_gpgme_key_deleter_class_init (SeahorseGpgmeKeyDeleterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	SeahorseDeleterClass *deleter_class = SEAHORSE_DELETER_CLASS (klass);
+
+	gobject_class->finalize = seahorse_gpgme_key_deleter_finalize;
+
+	deleter_class->add_object = seahorse_gpgme_key_deleter_add_object;
+	deleter_class->create_confirm = seahorse_gpgme_key_deleter_create_confirm;
+	deleter_class->delete_async = seahorse_gpgme_key_deleter_delete_async;
+	deleter_class->delete_finish = seahorse_gpgme_key_deleter_delete_finish;
+	deleter_class->get_objects = seahorse_gpgme_key_deleter_get_objects;
+}
+
+SeahorseDeleter *
+seahorse_gpgme_key_deleter_new (SeahorseGpgmeKey *item)
+{
+	SeahorseDeleter *deleter;
+
+	deleter = g_object_new (SEAHORSE_TYPE_GPGME_KEY_DELETER, NULL);
+	if (!seahorse_deleter_add_object (deleter, G_OBJECT (item)))
+		g_assert_not_reached ();
+
+	return deleter;
+}
diff --git a/pgp/seahorse-gpgme-key-deleter.h b/pgp/seahorse-gpgme-key-deleter.h
new file mode 100644
index 0000000..2dce664
--- /dev/null
+++ b/pgp/seahorse-gpgme-key-deleter.h
@@ -0,0 +1,42 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __SEAHORSE_GPGME_KEY_DELETER_H__
+#define __SEAHORSE_GPGME_KEY_DELETER_H__
+
+#include <glib-object.h>
+
+#include "seahorse-deleter.h"
+#include "seahorse-gpgme-key.h"
+
+#define SEAHORSE_TYPE_GPGME_KEY_DELETER       (seahorse_gpgme_key_deleter_get_type ())
+#define SEAHORSE_GPGME_KEY_DELETER(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAHORSE_TYPE_GPGME_KEY_DELETER, SeahorseGpgmeKeyDeleter))
+#define SEAHORSE_IS_GPGME_KEY_DELETER(obj)    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAHORSE_TYPE_GPGME_KEY_DELETER))
+
+typedef struct _SeahorseGpgmeKeyDeleter SeahorseGpgmeKeyDeleter;
+
+GType              seahorse_gpgme_key_deleter_get_type   (void) G_GNUC_CONST;
+
+SeahorseDeleter *  seahorse_gpgme_key_deleter_new        (SeahorseGpgmeKey *key);
+
+#endif /* __SEAHORSE_GPGME_KEY_DELETER_H__ */
diff --git a/pgp/seahorse-gpgme-key.c b/pgp/seahorse-gpgme-key.c
index b90c0d6..5cc7603 100644
--- a/pgp/seahorse-gpgme-key.c
+++ b/pgp/seahorse-gpgme-key.c
@@ -24,13 +24,16 @@
 #include "seahorse-gpgme.h"
 #include "seahorse-gpgme-exporter.h"
 #include "seahorse-gpgme-key-op.h"
+#include "seahorse-gpgme-key-deleter.h"
 #include "seahorse-gpgme-photo.h"
 #include "seahorse-gpgme-keyring.h"
+#include "seahorse-gpgme-secret-deleter.h"
 #include "seahorse-gpgme-uid.h"
 #include "seahorse-pgp-actions.h"
 #include "seahorse-pgp-backend.h"
 #include "seahorse-pgp-key.h"
 
+#include "seahorse-deletable.h"
 #include "seahorse-exportable.h"
 #include "seahorse-icons.h"
 #include "seahorse-predicate.h"
@@ -50,10 +53,13 @@ enum {
 	PROP_TRUST
 };
 
+static void       seahorse_gpgme_key_deletable_iface       (SeahorseDeletableIface *iface);
+
 static void       seahorse_gpgme_key_exportable_iface      (SeahorseExportableIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (SeahorseGpgmeKey, seahorse_gpgme_key, SEAHORSE_TYPE_PGP_KEY,
                          G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_EXPORTABLE, seahorse_gpgme_key_exportable_iface);
+                         G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_DELETABLE, seahorse_gpgme_key_deletable_iface);
 );
 
 struct _SeahorseGpgmeKeyPrivate {
@@ -550,6 +556,22 @@ seahorse_gpgme_key_class_init (SeahorseGpgmeKeyClass *klass)
         g_object_class_override_property (gobject_class, PROP_TRUST, "trust");
 }
 
+static SeahorseDeleter *
+seahorse_gpgme_key_create_deleter (SeahorseDeletable *deletable)
+{
+	SeahorseGpgmeKey *self = SEAHORSE_GPGME_KEY (deletable);
+	if (self->pv->seckey)
+		return seahorse_gpgme_secret_deleter_new (self);
+	else
+		return seahorse_gpgme_key_deleter_new (self);
+}
+
+static void
+seahorse_gpgme_key_deletable_iface (SeahorseDeletableIface *iface)
+{
+	iface->create_deleter = seahorse_gpgme_key_create_deleter;
+}
+
 static GList *
 seahorse_gpgme_key_create_exporters (SeahorseExportable *exportable,
                                      SeahorseExporterType type)
diff --git a/pgp/seahorse-gpgme-secret-deleter.c b/pgp/seahorse-gpgme-secret-deleter.c
new file mode 100644
index 0000000..3a2bcfa
--- /dev/null
+++ b/pgp/seahorse-gpgme-secret-deleter.c
@@ -0,0 +1,177 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "seahorse-gpgme.h"
+#include "seahorse-gpgme-key-op.h"
+#include "seahorse-gpgme-secret-deleter.h"
+
+#include "seahorse-delete-dialog.h"
+
+#include <glib/gi18n.h>
+
+#define SEAHORSE_GPGME_SECRET_DELETER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), SEAHORSE_TYPE_GPGME_SECRET_DELETER, SeahorseGpgmeSecretDeleterClass))
+#define SEAHORSE_IS_GPGME_SECRET_DELETER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAHORSE_TYPE_GPGME_SECRET_DELETER))
+#define SEAHORSE_GPGME_SECRET_DELETER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAHORSE_TYPE_GPGME_SECRET_DELETER, SeahorseGpgmeSecretDeleterClass))
+
+typedef struct _SeahorseGpgmeSecretDeleterClass SeahorseGpgmeSecretDeleterClass;
+
+struct _SeahorseGpgmeSecretDeleter {
+	SeahorseDeleter parent;
+	SeahorseGpgmeKey *key;
+	GList *keys;
+};
+
+struct _SeahorseGpgmeSecretDeleterClass {
+	SeahorseDeleterClass parent_class;
+};
+
+G_DEFINE_TYPE (SeahorseGpgmeSecretDeleter, seahorse_gpgme_secret_deleter, SEAHORSE_TYPE_DELETER);
+
+static void
+seahorse_gpgme_secret_deleter_init (SeahorseGpgmeSecretDeleter *self)
+{
+
+}
+
+static void
+seahorse_gpgme_secret_deleter_finalize (GObject *obj)
+{
+	SeahorseGpgmeSecretDeleter *self = SEAHORSE_GPGME_SECRET_DELETER (obj);
+
+	g_clear_object (&self->key);
+	g_list_free (self->keys);
+
+	G_OBJECT_CLASS (seahorse_gpgme_secret_deleter_parent_class)->finalize (obj);
+}
+
+static GtkDialog *
+seahorse_gpgme_secret_deleter_create_confirm (SeahorseDeleter *deleter,
+                                           GtkWindow *parent)
+{
+	SeahorseGpgmeSecretDeleter *self = SEAHORSE_GPGME_SECRET_DELETER (deleter);
+	GtkDialog *dialog;
+	gchar *prompt;
+
+	prompt = g_strdup_printf (_("Are you sure you want to permanently delete %s?"),
+	                          seahorse_object_get_label (SEAHORSE_OBJECT (self->key)));
+
+	dialog = seahorse_delete_dialog_new (parent, prompt);
+
+	seahorse_delete_dialog_set_check_label (SEAHORSE_DELETE_DIALOG (dialog), _("I understand that this secret key will be permanently deleted."));
+	seahorse_delete_dialog_set_check_require (SEAHORSE_DELETE_DIALOG (dialog), TRUE);
+
+	g_free (prompt);
+	return dialog;
+}
+
+static GList *
+seahorse_gpgme_secret_deleter_get_objects (SeahorseDeleter *deleter)
+{
+	SeahorseGpgmeSecretDeleter *self = SEAHORSE_GPGME_SECRET_DELETER (deleter);
+	return self->keys;
+}
+
+static gboolean
+seahorse_gpgme_secret_deleter_add_object (SeahorseDeleter *deleter,
+                                          GObject *object)
+{
+	SeahorseGpgmeSecretDeleter *self = SEAHORSE_GPGME_SECRET_DELETER (deleter);
+
+	if (!SEAHORSE_IS_GPGME_KEY (object))
+		return FALSE;
+	if (self->key)
+		return FALSE;
+	if (seahorse_object_get_usage (SEAHORSE_OBJECT (object)) != SEAHORSE_USAGE_PRIVATE_KEY)
+		return FALSE;
+	self->key = g_object_ref (object);
+	self->keys = g_list_append (self->keys, object);
+	return TRUE;
+}
+
+static void
+seahorse_gpgme_secret_deleter_delete_async (SeahorseDeleter *deleter,
+                                            GCancellable *cancellable,
+                                            GAsyncReadyCallback callback,
+                                            gpointer user_data)
+{
+	SeahorseGpgmeSecretDeleter *self = SEAHORSE_GPGME_SECRET_DELETER (deleter);
+	GSimpleAsyncResult *res;
+	GError *error = NULL;
+	gpgme_error_t gerr;
+
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                 seahorse_gpgme_secret_deleter_delete_async);
+
+	gerr = seahorse_gpgme_key_op_delete_pair (self->key);
+	if (seahorse_gpgme_propagate_error (gerr, &error))
+		g_simple_async_result_take_error (res, error);
+
+	g_simple_async_result_complete_in_idle (res);
+	g_object_unref (res);
+}
+
+static gboolean
+seahorse_gpgme_secret_deleter_delete_finish (SeahorseDeleter *deleter,
+                                             GAsyncResult *result,
+                                             GError **error)
+{
+	SeahorseGpgmeSecretDeleter *self = SEAHORSE_GPGME_SECRET_DELETER (deleter);
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	                      seahorse_gpgme_secret_deleter_delete_async), FALSE);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+
+	return TRUE;
+}
+
+
+static void
+seahorse_gpgme_secret_deleter_class_init (SeahorseGpgmeSecretDeleterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	SeahorseDeleterClass *deleter_class = SEAHORSE_DELETER_CLASS (klass);
+
+	gobject_class->finalize = seahorse_gpgme_secret_deleter_finalize;
+
+	deleter_class->add_object = seahorse_gpgme_secret_deleter_add_object;
+	deleter_class->create_confirm = seahorse_gpgme_secret_deleter_create_confirm;
+	deleter_class->delete_async = seahorse_gpgme_secret_deleter_delete_async;
+	deleter_class->delete_finish = seahorse_gpgme_secret_deleter_delete_finish;
+	deleter_class->get_objects = seahorse_gpgme_secret_deleter_get_objects;
+}
+
+SeahorseDeleter *
+seahorse_gpgme_secret_deleter_new (SeahorseGpgmeKey *key)
+{
+	SeahorseDeleter *deleter;
+
+	deleter = g_object_new (SEAHORSE_TYPE_GPGME_SECRET_DELETER, NULL);
+	if (!seahorse_deleter_add_object (deleter, G_OBJECT (key)))
+		g_assert_not_reached ();
+
+	return deleter;
+}
diff --git a/pgp/seahorse-gpgme-secret-deleter.h b/pgp/seahorse-gpgme-secret-deleter.h
new file mode 100644
index 0000000..766010a
--- /dev/null
+++ b/pgp/seahorse-gpgme-secret-deleter.h
@@ -0,0 +1,42 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __SEAHORSE_GPGME_SECRET_DELETER_H__
+#define __SEAHORSE_GPGME_SECRET_DELETER_H__
+
+#include <glib-object.h>
+
+#include "seahorse-deleter.h"
+#include "seahorse-gpgme-key.h"
+
+#define SEAHORSE_TYPE_GPGME_SECRET_DELETER       (seahorse_gpgme_secret_deleter_get_type ())
+#define SEAHORSE_GPGME_SECRET_DELETER(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAHORSE_TYPE_GPGME_SECRET_DELETER, SeahorseGpgmeSecretDeleter))
+#define SEAHORSE_IS_GPGME_SECRET_DELETER(obj)    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAHORSE_TYPE_GPGME_SECRET_DELETER))
+
+typedef struct _SeahorseGpgmeSecretDeleter SeahorseGpgmeSecretDeleter;
+
+GType              seahorse_gpgme_secret_deleter_get_type   (void) G_GNUC_CONST;
+
+SeahorseDeleter *  seahorse_gpgme_secret_deleter_new        (SeahorseGpgmeKey *key);
+
+#endif /* __SEAHORSE_GPGME_SECRET_DELETER_H__ */
diff --git a/pgp/seahorse-pgp-actions.c b/pgp/seahorse-pgp-actions.c
index 21998cf..522f92d 100644
--- a/pgp/seahorse-pgp-actions.c
+++ b/pgp/seahorse-pgp-actions.c
@@ -197,112 +197,6 @@ on_show_properties (GtkAction *action,
 	                                  seahorse_action_get_window (action));
 }
 
-static void
-on_delete_objects (GtkAction *action,
-                   gpointer user_data)
-{
-	guint num;
-	gint num_keys;
-	gint num_identities;
-	char* message;
-	SeahorseObject *obj;
-	GList* to_delete, *l;
-	GtkWindow *parent;
-	gpgme_error_t gerr;
-	guint length;
-	GError *error = NULL;
-	GList *objects;
-
-	objects = user_data;
-	num = g_list_length (objects);
-	if (num == 0)
-		return;
-
-	num_keys = 0;
-	num_identities = 0;
-	message = NULL;
-
-	/*
-	 * Go through and validate all what we have to delete,
-	 * removing UIDs where the parent Key is also on the
-	 * chopping block.
-	 */
-	to_delete = NULL;
-
-	for (l = objects; l; l = g_list_next (l)) {
-		obj = SEAHORSE_OBJECT (l->data);
-		if (SEAHORSE_IS_PGP_UID (obj)) {
-			if (g_list_find (objects, seahorse_pgp_uid_get_parent (SEAHORSE_PGP_UID (obj))) == NULL) {
-				to_delete = g_list_prepend (to_delete, obj);
-				++num_identities;
-			}
-		} else if (G_OBJECT_TYPE (obj) == SEAHORSE_TYPE_GPGME_KEY) {
-			to_delete = g_list_prepend (to_delete, obj);
-			++num_keys;
-		}
-	}
-
-	/* Figure out a good prompt message */
-	length = g_list_length (to_delete);
-	switch (length) {
-	case 0:
-		return;
-	case 1:
-		message = g_strdup_printf (_ ("Are you sure you want to permanently delete %s?"),
-		                           seahorse_object_get_label (to_delete->data));
-		break;
-	default:
-		if (num_keys > 0 && num_identities > 0) {
-			message = g_strdup_printf (ngettext (_("Are you sure you want to permanently delete %d keys and identities?"),
-			                                     _("Are you sure you want to permanently delete %d keys and identities?"),
-			                                     length),
-			                           length);
-		} else if (num_keys > 0) {
-			message = g_strdup_printf (ngettext (_("Are you sure you want to permanently delete %d keys?"),
-			                                     _("Are you sure you want to permanently delete %d keys?"),
-			                                     length),
-			                           length);
-		} else if (num_identities > 0){
-			message = g_strdup_printf (ngettext (_("Are you sure you want to permanently delete %d identities?"),
-			                                     _("Are you sure you want to permanently delete %d identities?"),
-			                                     length),
-			                           length);
-		} else {
-			g_assert_not_reached ();
-		}
-		break;
-	}
-
-	parent = seahorse_action_get_window (action);
-	if (!seahorse_delete_dialog_prompt (parent, message)) {
-		g_free (message);
-		g_cancellable_cancel (g_cancellable_get_current ());
-		return;
-	}
-
-	gerr = 0;
-	for (l = objects; l; l = g_list_next (l)) {
-		obj = SEAHORSE_OBJECT (l->data);
-		if (SEAHORSE_IS_GPGME_UID (obj)) {
-			gerr = seahorse_gpgme_key_op_del_uid (SEAHORSE_GPGME_UID (obj));
-			message = _("Couldn't delete user ID");
-		} else if (SEAHORSE_IS_GPGME_KEY (obj)) {
-			if (seahorse_object_get_usage (obj) == SEAHORSE_USAGE_PRIVATE_KEY) {
-				gerr = seahorse_gpgme_key_op_delete_pair (SEAHORSE_GPGME_KEY (obj));
-				message = _("Couldn't delete private key");
-			} else {
-				gerr = seahorse_gpgme_key_op_delete (SEAHORSE_GPGME_KEY (obj));
-				message = _("Couldn't delete public key");
-			}
-		}
-
-		if (seahorse_gpgme_propagate_error (gerr, &error)) {
-			seahorse_util_handle_error (&error, parent, "%s", message);
-			return;
-		}
-	}
-}
-
 static const GtkActionEntry KEY_ACTIONS[] = {
 	{ "key-sign", GTK_STOCK_INDEX, N_("_Sign Key..."), "",
 	  N_("Sign public key"), G_CALLBACK (on_key_sign) },
@@ -310,11 +204,6 @@ static const GtkActionEntry KEY_ACTIONS[] = {
 	  N_("Properties of the key."), G_CALLBACK (on_show_properties) },
 };
 
-static const GtkActionEntry KEYS_ACTIONS[] = {
-	{ "delete", GTK_STOCK_DELETE, NULL, NULL,
-	  N_("Delete the key."), G_CALLBACK (on_delete_objects) },
-};
-
 static void
 seahorse_gpgme_key_actions_init (SeahorseGpgmeKeyActions *self)
 {
@@ -322,8 +211,6 @@ seahorse_gpgme_key_actions_init (SeahorseGpgmeKeyActions *self)
 	gtk_action_group_set_translation_domain (actions, GETTEXT_PACKAGE);
 	gtk_action_group_add_actions (actions, KEY_ACTIONS,
 	                              G_N_ELEMENTS (KEY_ACTIONS), NULL);
-	gtk_action_group_add_actions (actions, KEYS_ACTIONS,
-	                              G_N_ELEMENTS (KEYS_ACTIONS), NULL);
 	gtk_action_group_set_visible (actions, FALSE);
 	seahorse_actions_register_definition (SEAHORSE_ACTIONS (self), KEY_DEFINITION);
 
@@ -338,10 +225,6 @@ seahorse_gpgme_key_actions_clone_for_objects (SeahorseActions *actions,
 	g_return_val_if_fail (objects != NULL, NULL);
 
 	cloned = gtk_action_group_new ("GpgmeKey");
-	gtk_action_group_add_actions_full (cloned, KEYS_ACTIONS,
-	                                   G_N_ELEMENTS (KEYS_ACTIONS),
-	                                   seahorse_object_list_copy (objects),
-	                                   seahorse_object_list_free);
 	gtk_action_group_add_actions_full (cloned, SYNC_ACTIONS,
 	                                   G_N_ELEMENTS (SYNC_ACTIONS),
 	                                   seahorse_object_list_copy (objects),
diff --git a/pkcs11/Makefile.am b/pkcs11/Makefile.am
index 7cdce84..7c07782 100644
--- a/pkcs11/Makefile.am
+++ b/pkcs11/Makefile.am
@@ -20,9 +20,10 @@ libseahorse_pkcs11_la_SOURCES = \
 	seahorse-interaction.c seahorse-interaction.h \
 	seahorse-pkcs11-actions.c seahorse-pkcs11-actions.h \
 	seahorse-pkcs11-backend.c seahorse-pkcs11-backend.h \
+	seahorse-pkcs11-deleter.c seahorse-pkcs11-deleter.h \
 	seahorse-pkcs11-helpers.c seahorse-pkcs11-helpers.h \
 	seahorse-pkcs11-generate.c seahorse-pkcs11-generate.h \
-	seahorse-pkcs11-operations.c seahorse-pkcs11-operations.h \
+	seahorse-pkcs11-key-deleter.c seahorse-pkcs11-key-deleter.h \
 	seahorse-pkcs11-properties.c seahorse-pkcs11-properties.h \
 	seahorse-pkcs11-request.c seahorse-pkcs11-request.h \
 	seahorse-pkcs11.c seahorse-pkcs11.h \
diff --git a/pkcs11/seahorse-certificate.c b/pkcs11/seahorse-certificate.c
index 00091aa..2fc470c 100644
--- a/pkcs11/seahorse-certificate.c
+++ b/pkcs11/seahorse-certificate.c
@@ -26,11 +26,13 @@
 #include "seahorse-certificate-der-exporter.h"
 #include "seahorse-pkcs11.h"
 #include "seahorse-pkcs11-actions.h"
+#include "seahorse-pkcs11-deleter.h"
 #include "seahorse-pkcs11-helpers.h"
 #include "seahorse-private-key.h"
 #include "seahorse-token.h"
 #include "seahorse-types.h"
 
+#include "seahorse-deletable.h"
 #include "seahorse-exportable.h"
 #include "seahorse-util.h"
 #include "seahorse-validity.h"
@@ -45,7 +47,8 @@ static const gulong REQUIRED_ATTRS[] = {
 	CKA_ID,
 	CKA_LABEL,
 	CKA_CLASS,
-	CKA_CERTIFICATE_CATEGORY
+	CKA_CERTIFICATE_CATEGORY,
+	CKA_MODIFIABLE
 };
 
 enum {
@@ -56,6 +59,8 @@ enum {
 	PROP_ACTIONS,
 	PROP_PARTNER,
 
+	PROP_EXPORTABLE,
+	PROP_DELETABLE,
 	PROP_ICON,
 	PROP_DESCRIPTION
 };
@@ -74,12 +79,15 @@ static void   seahorse_certificate_certificate_iface           (GcrCertificateIf
 
 static void   seahorse_certificate_object_attributes_iface     (GckObjectAttributesIface *iface);
 
+static void   seahorse_certificate_deletable_iface             (SeahorseDeletableIface *iface);
+
 static void   seahorse_certificate_exportable_iface            (SeahorseExportableIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (SeahorseCertificate, seahorse_certificate, GCK_TYPE_OBJECT,
                          GCR_CERTIFICATE_MIXIN_IMPLEMENT_COMPARABLE ();
                          G_IMPLEMENT_INTERFACE (GCR_TYPE_CERTIFICATE, seahorse_certificate_certificate_iface);
                          G_IMPLEMENT_INTERFACE (GCK_TYPE_OBJECT_ATTRIBUTES, seahorse_certificate_object_attributes_iface);
+                         G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_DELETABLE, seahorse_certificate_deletable_iface);
                          G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_EXPORTABLE, seahorse_certificate_exportable_iface);
 );
 
@@ -213,6 +221,12 @@ seahorse_certificate_get_property (GObject *obj,
 	case PROP_DESCRIPTION:
 		g_value_set_string (value, seahorse_certificate_get_description (self));
 		break;
+	case PROP_EXPORTABLE:
+		g_value_set_boolean (value, gck_attributes_find (self->pv->attributes, CKA_VALUE) != NULL);
+		break;
+	case PROP_DELETABLE:
+		g_value_set_boolean (value, seahorse_token_is_deletable (self->pv->token, GCK_OBJECT (self)));
+		break;
 	default:
 		gcr_certificate_mixin_get_property (obj, prop_id, value, pspec);
 		break;
@@ -286,6 +300,10 @@ seahorse_certificate_class_init (SeahorseCertificateClass *klass)
 
 	g_object_class_override_property (gobject_class, PROP_ATTRIBUTES, "attributes");
 
+	g_object_class_override_property (gobject_class, PROP_DELETABLE, "deletable");
+
+	g_object_class_override_property (gobject_class, PROP_EXPORTABLE, "exportable");
+
 	g_object_class_override_property (gobject_class, PROP_ICON, "icon");
 	g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description");
 
@@ -322,6 +340,12 @@ seahorse_certificate_create_exporters (SeahorseExportable *exportable,
                                        SeahorseExporterType type)
 {
 	SeahorseExporter *exporter;
+	gboolean can_export = FALSE;
+
+	g_object_get (exportable, "exportable", &can_export, NULL);
+	if (!can_export)
+		return NULL;
+
 	exporter = seahorse_certificate_der_exporter_new (SEAHORSE_CERTIFICATE (exportable));
 	return g_list_append (NULL, exporter);
 }
@@ -332,6 +356,31 @@ seahorse_certificate_exportable_iface (SeahorseExportableIface *iface)
 	iface->create_exporters = seahorse_certificate_create_exporters;
 }
 
+static SeahorseDeleter *
+seahorse_certificate_create_deleter (SeahorseDeletable *deletable)
+{
+	SeahorseCertificate *self = SEAHORSE_CERTIFICATE (deletable);
+	SeahorseDeleter *deleter = NULL;
+
+	if (self->pv->private_key)
+		deleter = seahorse_deletable_create_deleter (SEAHORSE_DELETABLE (self->pv->private_key));
+
+	if (seahorse_token_is_deletable (self->pv->token, GCK_OBJECT (self))) {
+		if (deleter == NULL)
+			deleter = seahorse_pkcs11_deleter_new (self);
+		else if (!seahorse_deleter_add_object (deleter, G_OBJECT (self)))
+			g_return_val_if_reached (NULL);
+	}
+
+	return deleter;
+}
+
+static void
+seahorse_certificate_deletable_iface (SeahorseDeletableIface *iface)
+{
+	iface->create_deleter = seahorse_certificate_create_deleter;
+}
+
 GIcon *
 seahorse_certificate_get_icon (SeahorseCertificate *self)
 {
diff --git a/pkcs11/seahorse-pkcs11-actions.c b/pkcs11/seahorse-pkcs11-actions.c
index 809822c..dcabea4 100644
--- a/pkcs11/seahorse-pkcs11-actions.c
+++ b/pkcs11/seahorse-pkcs11-actions.c
@@ -28,7 +28,6 @@
 #include "seahorse-interaction.h"
 #include "seahorse-pkcs11.h"
 #include "seahorse-pkcs11-properties.h"
-#include "seahorse-pkcs11-operations.h"
 #include "seahorse-token.h"
 
 #include "seahorse-action.h"
@@ -206,70 +205,12 @@ on_show_properties (GtkAction *action,
 	gtk_widget_show (GTK_WIDGET (window));
 }
 
-static void
-on_delete_completed (GObject *source,
-                     GAsyncResult *result,
-                     gpointer user_data)
-{
-	GtkWindow *parent = GTK_WINDOW (user_data);
-	GError *error = NULL;
-
-	if (!seahorse_pkcs11_delete_finish (result, &error))
-		seahorse_util_handle_error (&error, parent, _("Couldn't delete"));
-
-	g_object_unref (parent);
-}
-
-static void
-on_delete_objects (GtkAction *action,
-                   gpointer user_data)
-{
-	GCancellable *cancellable;
-	gchar *prompt;
-	gchar *display;
-	GtkWindow *parent;
-	gboolean ret;
-	guint num;
-	GList *objects;
-
-	objects = user_data;
-	num = g_list_length (objects);
-	if (num == 1) {
-		g_object_get (objects->data, "label", &display, NULL);
-		prompt = g_strdup_printf (_("Are you sure you want to delete the certificate '%s'?"), display);
-		g_free (display);
-	} else {
-		prompt = g_strdup_printf (ngettext (
-				"Are you sure you want to delete %d certificate?",
-				"Are you sure you want to delete %d certificates?",
-				num), num);
-	}
-
-	parent = seahorse_action_get_window (action);
-	ret = seahorse_delete_dialog_prompt (parent, prompt);
-	g_free (prompt);
-
-	if (ret) {
-		cancellable = g_cancellable_new ();
-		seahorse_pkcs11_delete_async (objects, cancellable,
-		                              on_delete_completed, g_object_ref (parent));
-		seahorse_progress_show (cancellable, _("Deleting"), TRUE);
-		g_object_unref (cancellable);
-	} else {
-		g_cancellable_cancel (g_cancellable_get_current ());
-	}
-}
 
 static const GtkActionEntry CERTIFICATE_ACTIONS[] = {
 	{ "properties", GTK_STOCK_PROPERTIES, NULL, NULL,
 	  N_("Properties of the certificate."), G_CALLBACK (on_show_properties) },
 };
 
-static const GtkActionEntry CERTIFICATES_ACTIONS[] = {
-	{ "delete", GTK_STOCK_DELETE, NULL, NULL,
-	  N_("Delete the certificate."), G_CALLBACK (on_delete_objects) },
-};
-
 static void
 seahorse_pkcs11_object_actions_init (SeahorsePkcs11ObjectActions *self)
 {
@@ -286,10 +227,6 @@ seahorse_pkcs11_object_actions_clone_for_objects (SeahorseActions *actions,
 
 	cloned = gtk_action_group_new ("Pkcs11Object");
 	gtk_action_group_set_translation_domain (cloned, GETTEXT_PACKAGE);
-	gtk_action_group_add_actions_full (cloned, CERTIFICATES_ACTIONS,
-	                                   G_N_ELEMENTS (CERTIFICATES_ACTIONS),
-	                                   seahorse_object_list_copy (objects),
-	                                   seahorse_object_list_free);
 
 	/* Only one object? */
 	if (!objects->next)
diff --git a/pkcs11/seahorse-pkcs11-deleter.c b/pkcs11/seahorse-pkcs11-deleter.c
new file mode 100644
index 0000000..30d7b3c
--- /dev/null
+++ b/pkcs11/seahorse-pkcs11-deleter.c
@@ -0,0 +1,235 @@
+/* 
+ * Seahorse
+ * 
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "seahorse-pkcs11-deleter.h"
+#include "seahorse-token.h"
+
+#include "seahorse-delete-dialog.h"
+
+#include <gck/gck.h>
+
+#include <glib/gi18n.h>
+
+G_DEFINE_TYPE (SeahorsePkcs11Deleter, seahorse_pkcs11_deleter, SEAHORSE_TYPE_DELETER);
+
+static void
+seahorse_pkcs11_deleter_init (SeahorsePkcs11Deleter *self)
+{
+
+}
+
+static void
+seahorse_pkcs11_deleter_finalize (GObject *obj)
+{
+	SeahorsePkcs11Deleter *self = SEAHORSE_PKCS11_DELETER (obj);
+
+	g_list_free_full (self->objects, g_object_unref);
+
+	G_OBJECT_CLASS (seahorse_pkcs11_deleter_parent_class)->finalize (obj);
+}
+
+static GtkDialog *
+seahorse_pkcs11_deleter_create_confirm (SeahorseDeleter *deleter,
+                                        GtkWindow *parent)
+{
+	SeahorsePkcs11Deleter *self = SEAHORSE_PKCS11_DELETER (deleter);
+	GtkDialog *dialog;
+	gchar *prompt;
+	gchar *label;
+	guint num;
+
+	num = g_list_length (self->objects);
+	if (num == 1) {
+		g_object_get (self->objects->data, "label", &label, NULL);
+		prompt = g_strdup_printf (_("Are you sure you want to permanently delete %s?"), label);
+		g_free (label);
+	} else {
+		prompt = g_strdup_printf (ngettext ("Are you sure you want to permanently delete %d certificate?",
+		                                    "Are you sure you want to permanently delete %d certificates?",
+		                                    num),
+		                          num);
+	}
+
+	dialog = seahorse_delete_dialog_new (parent, prompt);
+	g_free (prompt);
+
+	return dialog;
+}
+
+static GList *
+seahorse_pkcs11_deleter_get_objects (SeahorseDeleter *deleter)
+{
+	SeahorsePkcs11Deleter *self = SEAHORSE_PKCS11_DELETER (deleter);
+	return self->objects;
+}
+
+static gboolean
+seahorse_pkcs11_deleter_add_object (SeahorseDeleter *deleter,
+                                    GObject *object)
+{
+	SeahorsePkcs11Deleter *self = SEAHORSE_PKCS11_DELETER (deleter);
+
+	if (!SEAHORSE_IS_CERTIFICATE (object))
+		return FALSE;
+
+	self->objects = g_list_append (self->objects, g_object_ref (object));
+	return TRUE;
+}
+
+typedef struct {
+	GQueue *objects;
+	GCancellable *cancellable;
+} DeleteClosure;
+
+
+static void
+delete_closure_free (gpointer data)
+{
+	DeleteClosure *closure = data;
+	g_queue_foreach (closure->objects, (GFunc)g_object_unref, NULL);
+	g_queue_free (closure->objects);
+	g_clear_object (&closure->cancellable);
+	g_free (closure);
+}
+
+static void pkcs11_delete_one_object (GSimpleAsyncResult *res);
+
+static void
+on_delete_object_completed (GObject *source,
+                            GAsyncResult *result,
+                            gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+	GError *error = NULL;
+	GckObject *object;
+	SeahorseToken *pkcs11_token;
+
+	object = g_queue_pop_head (closure->objects);
+
+	if (!gck_object_destroy_finish (GCK_OBJECT (source), result, &error)) {
+
+		/* Ignore objects that have gone away */
+		if (g_error_matches (error, GCK_ERROR, CKR_OBJECT_HANDLE_INVALID)) {
+			g_clear_error (&error);
+		} else {
+			g_simple_async_result_take_error (res, error);
+			g_simple_async_result_complete (res);
+		}
+
+	}
+
+	if (error == NULL) {
+		g_object_get (object, "place", &pkcs11_token, NULL);
+		g_return_if_fail (SEAHORSE_IS_TOKEN (pkcs11_token));
+		seahorse_token_remove_object (pkcs11_token, GCK_OBJECT (object));
+		pkcs11_delete_one_object (res);
+	}
+
+	g_object_unref (object);
+	g_object_unref (res);
+}
+
+static void
+pkcs11_delete_one_object (GSimpleAsyncResult *res)
+{
+	DeleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+	GckObject *object;
+
+	if (g_queue_is_empty (closure->objects)) {
+		g_simple_async_result_complete_in_idle (res);
+		return;
+	}
+
+	object = g_queue_peek_head (closure->objects);
+
+	gck_object_destroy_async (object, closure->cancellable,
+	                          on_delete_object_completed, g_object_ref (res));
+}
+
+static void
+seahorse_pkcs11_deleter_delete_async (SeahorseDeleter *deleter,
+                                      GCancellable *cancellable,
+                                      GAsyncReadyCallback callback,
+                                      gpointer user_data)
+{
+	SeahorsePkcs11Deleter *self = SEAHORSE_PKCS11_DELETER (deleter);
+	GSimpleAsyncResult *res;
+	DeleteClosure *closure;
+	GList *l;
+
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                 seahorse_pkcs11_deleter_delete_async);
+	closure = g_new0 (DeleteClosure, 1);
+	closure->objects = g_queue_new ();
+	for (l = self->objects; l != NULL; l = g_list_next (l))
+		g_queue_push_tail (closure->objects, g_object_ref (l->data));
+	g_simple_async_result_set_op_res_gpointer (res, closure, delete_closure_free);
+
+	pkcs11_delete_one_object (res);
+
+	g_object_unref (res);
+}
+
+static gboolean
+seahorse_pkcs11_deleter_delete_finish (SeahorseDeleter *deleter,
+                                       GAsyncResult *result,
+                                       GError **error)
+{
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (deleter),
+	                      seahorse_pkcs11_deleter_delete_async), FALSE);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+seahorse_pkcs11_deleter_class_init (SeahorsePkcs11DeleterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	SeahorseDeleterClass *deleter_class = SEAHORSE_DELETER_CLASS (klass);
+
+	gobject_class->finalize = seahorse_pkcs11_deleter_finalize;
+
+	deleter_class->add_object = seahorse_pkcs11_deleter_add_object;
+	deleter_class->create_confirm = seahorse_pkcs11_deleter_create_confirm;
+	deleter_class->delete_async = seahorse_pkcs11_deleter_delete_async;
+	deleter_class->delete_finish = seahorse_pkcs11_deleter_delete_finish;
+	deleter_class->get_objects = seahorse_pkcs11_deleter_get_objects;
+}
+
+SeahorseDeleter *
+seahorse_pkcs11_deleter_new (SeahorseCertificate *certificate)
+{
+	SeahorseDeleter *deleter;
+
+	deleter = g_object_new (SEAHORSE_TYPE_PKCS11_DELETER, NULL);
+	if (!seahorse_deleter_add_object (deleter, G_OBJECT (certificate)))
+		g_assert_not_reached ();
+
+	return deleter;
+}
diff --git a/pkcs11/seahorse-pkcs11-deleter.h b/pkcs11/seahorse-pkcs11-deleter.h
new file mode 100644
index 0000000..bdb6b81
--- /dev/null
+++ b/pkcs11/seahorse-pkcs11-deleter.h
@@ -0,0 +1,55 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __SEAHORSE_PKCS11_DELETER_H__
+#define __SEAHORSE_PKCS11_DELETER_H__
+
+#include <glib-object.h>
+
+#include "seahorse-deleter.h"
+#include "seahorse-certificate.h"
+
+#define SEAHORSE_TYPE_PKCS11_DELETER       (seahorse_pkcs11_deleter_get_type ())
+#define SEAHORSE_PKCS11_DELETER(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAHORSE_TYPE_PKCS11_DELETER, SeahorsePkcs11Deleter))
+#define SEAHORSE_IS_PKCS11_DELETER(obj)    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAHORSE_TYPE_PKCS11_DELETER))
+#define SEAHORSE_PKCS11_DELETER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), SEAHORSE_TYPE_PKCS11_DELETER, SeahorsePkcs11DeleterClass))
+#define SEAHORSE_IS_PKCS11_DELETER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAHORSE_TYPE_PKCS11_DELETER))
+#define SEAHORSE_PKCS11_DELETER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAHORSE_TYPE_PKCS11_DELETER, SeahorsePkcs11DeleterClass))
+
+typedef struct _SeahorsePkcs11Deleter SeahorsePkcs11Deleter;
+typedef struct _SeahorsePkcs11DeleterClass SeahorsePkcs11DeleterClass;
+
+struct _SeahorsePkcs11Deleter {
+	SeahorseDeleter parent;
+	GList *objects;
+};
+
+struct _SeahorsePkcs11DeleterClass {
+	SeahorseDeleterClass parent_class;
+};
+
+GType              seahorse_pkcs11_deleter_get_type   (void) G_GNUC_CONST;
+
+SeahorseDeleter *  seahorse_pkcs11_deleter_new        (SeahorseCertificate *cert);
+
+#endif /* __SEAHORSE_PKCS11_DELETER_H__ */
diff --git a/pkcs11/seahorse-pkcs11-key-deleter.c b/pkcs11/seahorse-pkcs11-key-deleter.c
new file mode 100644
index 0000000..8b1d024
--- /dev/null
+++ b/pkcs11/seahorse-pkcs11-key-deleter.c
@@ -0,0 +1,150 @@
+/* 
+ * Seahorse
+ * 
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "seahorse-certificate.h"
+#include "seahorse-pkcs11-deleter.h"
+#include "seahorse-pkcs11-key-deleter.h"
+#include "seahorse-private-key.h"
+#include "seahorse-token.h"
+
+#include "seahorse-delete-dialog.h"
+
+#include <gck/gck.h>
+
+#include <glib/gi18n.h>
+
+#define SEAHORSE_PKCS11_KEY_DELETER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), SEAHORSE_TYPE_PKCS11_KEY_DELETER, SeahorsePkcs11KeyDeleterClass))
+#define SEAHORSE_IS_PKCS11_KEY_DELETER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAHORSE_TYPE_PKCS11_KEY_DELETER))
+#define SEAHORSE_PKCS11_KEY_DELETER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAHORSE_TYPE_PKCS11_KEY_DELETER, SeahorsePkcs11KeyDeleterClass))
+
+typedef struct _SeahorsePkcs11KeyDeleterClass SeahorsePkcs11KeyDeleterClass;
+
+struct _SeahorsePkcs11KeyDeleter {
+	SeahorsePkcs11Deleter parent;
+	SeahorseCertificate *cert;
+	SeahorsePrivateKey *key;
+	gchar *label;
+};
+
+struct _SeahorsePkcs11KeyDeleterClass {
+	SeahorsePkcs11DeleterClass parent_class;
+};
+
+G_DEFINE_TYPE (SeahorsePkcs11KeyDeleter, seahorse_pkcs11_key_deleter, SEAHORSE_TYPE_PKCS11_DELETER);
+
+static void
+seahorse_pkcs11_key_deleter_init (SeahorsePkcs11KeyDeleter *self)
+{
+
+}
+
+static void
+seahorse_pkcs11_key_deleter_finalize (GObject *obj)
+{
+	SeahorsePkcs11KeyDeleter *self = SEAHORSE_PKCS11_KEY_DELETER (obj);
+
+	g_free (self->label);
+
+	G_OBJECT_CLASS (seahorse_pkcs11_key_deleter_parent_class)->finalize (obj);
+}
+
+static GtkDialog *
+seahorse_pkcs11_key_deleter_create_confirm (SeahorseDeleter *deleter,
+                                            GtkWindow *parent)
+{
+	SeahorsePkcs11KeyDeleter *self = SEAHORSE_PKCS11_KEY_DELETER (deleter);
+	GtkDialog *dialog;
+	gchar *prompt;
+
+	prompt = g_strdup_printf (_("Are you sure you want to permanently delete %s?"), self->label);
+
+	dialog = seahorse_delete_dialog_new (parent, prompt);
+
+	seahorse_delete_dialog_set_check_label (SEAHORSE_DELETE_DIALOG (dialog), _("I understand that this key will be permanently deleted."));
+	seahorse_delete_dialog_set_check_require (SEAHORSE_DELETE_DIALOG (dialog), TRUE);
+
+	g_free (prompt);
+	return dialog;
+}
+
+static gboolean
+seahorse_pkcs11_key_deleter_add_object (SeahorseDeleter *deleter,
+                                        GObject *object)
+{
+	SeahorsePkcs11KeyDeleter *self = SEAHORSE_PKCS11_KEY_DELETER (deleter);
+	SeahorsePkcs11Deleter *base = SEAHORSE_PKCS11_DELETER (deleter);
+	gpointer partner = NULL;
+
+	if (SEAHORSE_IS_PRIVATE_KEY (object)) {
+		if (self->key != NULL)
+			return FALSE;
+		if (self->cert != NULL) {
+			partner = seahorse_certificate_get_partner (SEAHORSE_CERTIFICATE (self->cert));
+			if (partner != object)
+				return FALSE;
+		}
+		self->key = SEAHORSE_PRIVATE_KEY (object);
+		base->objects = g_list_prepend (base->objects, g_object_ref (self->key));
+
+	} else if (SEAHORSE_IS_CERTIFICATE (object)) {
+		if (self->cert != NULL)
+			return FALSE;
+		if (self->key != NULL) {
+			partner = seahorse_private_key_get_partner (SEAHORSE_PRIVATE_KEY (self->key));
+			if (partner != object)
+				return FALSE;
+		}
+		self->cert = SEAHORSE_CERTIFICATE (object);
+		base->objects = g_list_append (base->objects, g_object_ref (self->cert));
+	}
+
+	if (self->label == NULL)
+		g_object_get (object, "label", &self->label, NULL);
+	return TRUE;
+}
+
+static void
+seahorse_pkcs11_key_deleter_class_init (SeahorsePkcs11KeyDeleterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	SeahorseDeleterClass *deleter_class = SEAHORSE_DELETER_CLASS (klass);
+
+	deleter_class->add_object = seahorse_pkcs11_key_deleter_add_object;
+	deleter_class->create_confirm = seahorse_pkcs11_key_deleter_create_confirm;
+
+	gobject_class->finalize = seahorse_pkcs11_key_deleter_finalize;
+}
+
+SeahorseDeleter *
+seahorse_pkcs11_key_deleter_new (GObject *cert_or_key)
+{
+	SeahorseDeleter *deleter;
+
+	deleter = g_object_new (SEAHORSE_TYPE_PKCS11_KEY_DELETER, NULL);
+	if (!seahorse_deleter_add_object (deleter, cert_or_key))
+		g_assert_not_reached ();
+
+	return deleter;
+}
diff --git a/pkcs11/seahorse-pkcs11-key-deleter.h b/pkcs11/seahorse-pkcs11-key-deleter.h
new file mode 100644
index 0000000..e93308c
--- /dev/null
+++ b/pkcs11/seahorse-pkcs11-key-deleter.h
@@ -0,0 +1,42 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __SEAHORSE_PKCS11_KEY_DELETER_H__
+#define __SEAHORSE_PKCS11_KEY_DELETER_H__
+
+#include <glib-object.h>
+
+#include "seahorse-deleter.h"
+#include "seahorse-certificate.h"
+
+#define SEAHORSE_TYPE_PKCS11_KEY_DELETER       (seahorse_pkcs11_key_deleter_get_type ())
+#define SEAHORSE_PKCS11_KEY_DELETER(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAHORSE_TYPE_PKCS11_KEY_DELETER, SeahorsePkcs11KeyDeleter))
+#define SEAHORSE_IS_PKCS11_KEY_DELETER(obj)    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAHORSE_TYPE_PKCS11_KEY_DELETER))
+
+typedef struct _SeahorsePkcs11KeyDeleter SeahorsePkcs11KeyDeleter;
+
+GType              seahorse_pkcs11_key_deleter_get_type   (void) G_GNUC_CONST;
+
+SeahorseDeleter *  seahorse_pkcs11_key_deleter_new        (GObject *cert_or_key);
+
+#endif /* __SEAHORSE_PKCS11_KEY_DELETER_H__ */
diff --git a/pkcs11/seahorse-pkcs11-properties.c b/pkcs11/seahorse-pkcs11-properties.c
index fd00890..e45508a 100644
--- a/pkcs11/seahorse-pkcs11-properties.c
+++ b/pkcs11/seahorse-pkcs11-properties.c
@@ -22,13 +22,14 @@
 #include "config.h"
 
 #include "seahorse-certificate.h"
+#include "seahorse-pkcs11-deleter.h"
+#include "seahorse-pkcs11-key-deleter.h"
 #include "seahorse-pkcs11-properties.h"
-#include "seahorse-pkcs11-operations.h"
 #include "seahorse-pkcs11-request.h"
 #include "seahorse-private-key.h"
 
 #include "seahorse-action.h"
-#include "seahorse-delete-dialog.h"
+#include "seahorse-deleter.h"
 #include "seahorse-exportable.h"
 #include "seahorse-progress.h"
 #include "seahorse-util.h"
@@ -177,7 +178,7 @@ on_delete_complete (GObject *source,
 	SeahorsePkcs11Properties *self = SEAHORSE_PKCS11_PROPERTIES (user_data);
 	GError *error = NULL;
 
-	if (seahorse_pkcs11_delete_finish (result, &error))
+	if (seahorse_deleter_delete_finish (SEAHORSE_DELETER (source), result, &error))
 		gtk_widget_destroy (GTK_WIDGET (self));
 
 	else
@@ -191,58 +192,25 @@ on_delete_objects (GtkAction *action,
                    gpointer user_data)
 {
 	SeahorsePkcs11Properties *self = SEAHORSE_PKCS11_PROPERTIES (user_data);
-	gboolean with_partner = FALSE;
+	SeahorseDeleter *deleter;
 	GObject *partner = NULL;
-	GtkDialog *dialog = NULL;
-	GCancellable *cancellable;
-	GList *objects = NULL;
-	gchar *label;
-
-	g_object_get (self->object,
-	              "label", &label,
-	              "partner", &partner,
-	              NULL);
-
-	if (SEAHORSE_IS_CERTIFICATE (self->object)) {
-		objects = g_list_prepend (objects, g_object_ref (self->object));
-		dialog = seahorse_delete_dialog_new (GTK_WINDOW (self),
-		                                     _("Are you sure you want to delete the certificate '%s'?"),
-		                                     label);
-
-		if (SEAHORSE_IS_PRIVATE_KEY (partner)) {
-			seahorse_delete_dialog_set_check_value (SEAHORSE_DELETE_DIALOG (dialog), FALSE);
-			seahorse_delete_dialog_set_check_label (SEAHORSE_DELETE_DIALOG (dialog),
-			                                        _("Also delete matching private key"));
-			with_partner = TRUE;
-		}
 
-	} else if (SEAHORSE_IS_PRIVATE_KEY (self->object)) {
-		objects = g_list_prepend (objects, g_object_ref (self->object));
-		dialog = seahorse_delete_dialog_new (GTK_WINDOW (self),
-		                                     _("Are you sure you want to delete the private key '%s'?"),
-		                                     label);
+	g_object_get (self->object, "partner", &partner, NULL);
 
-		seahorse_delete_dialog_set_check_value (SEAHORSE_DELETE_DIALOG (dialog), FALSE);
-		seahorse_delete_dialog_set_check_require (SEAHORSE_DELETE_DIALOG (dialog), TRUE);
-		seahorse_delete_dialog_set_check_label (SEAHORSE_DELETE_DIALOG (dialog),
-		                                        _("I understand that this action cannot be undone"));
+	if (partner || SEAHORSE_IS_PRIVATE_KEY (self->object)) {
+		deleter = seahorse_pkcs11_key_deleter_new (self->object);
+		if (!seahorse_deleter_add_object (SEAHORSE_DELETER (deleter), partner))
+			g_assert_not_reached ();
+	} else {
+		deleter = seahorse_pkcs11_deleter_new (SEAHORSE_CERTIFICATE (self->object));
 	}
 
-	if (dialog && gtk_dialog_run (dialog) == GTK_RESPONSE_OK) {
-		if (with_partner)
-			objects = g_list_prepend (objects, partner);
-
-		cancellable = g_cancellable_new ();
-		seahorse_pkcs11_delete_async (objects, cancellable,
-		                              on_delete_complete, g_object_ref (self));
-		seahorse_progress_show (cancellable, _("Deleting"), TRUE);
-		g_object_unref (cancellable);
+	if (seahorse_deleter_prompt (deleter, GTK_WINDOW (self))) {
+		seahorse_deleter_delete_async (deleter, NULL, on_delete_complete, g_object_ref (self));
 	}
 
-	gtk_widget_destroy (GTK_WIDGET (dialog));
-	g_list_free_full (objects, g_object_unref);
+	g_object_unref (deleter);
 	g_clear_object (&partner);
-	g_free (label);
 }
 
 static void
@@ -340,12 +308,17 @@ seahorse_pkcs11_properties_constructed (GObject *obj)
 	GError *error = NULL;
 	GList *exporters = NULL;
 	GtkAction *request;
+	GtkAction *action;
 
 	G_OBJECT_CLASS (seahorse_pkcs11_properties_parent_class)->constructed (obj);
 
 	self->actions = gtk_action_group_new ("Pkcs11Actions");
 	gtk_action_group_add_actions (self->actions, UI_ACTIONS, G_N_ELEMENTS (UI_ACTIONS), self);
 	gtk_action_group_set_translation_domain (self->actions, GETTEXT_PACKAGE);
+	action = gtk_action_group_get_action (self->actions, "delete-object");
+	g_object_bind_property (self->object, "deletable", action, "sensitive", G_BINDING_SYNC_CREATE);
+	action = gtk_action_group_get_action (self->actions, "export-object");
+	g_object_bind_property (self->object, "exportable", action, "sensitive", G_BINDING_SYNC_CREATE);
 	request = gtk_action_group_get_action (self->actions, "request-certificate");
 	gtk_action_set_is_important (request, TRUE);
 	gtk_action_set_visible (request, FALSE);
diff --git a/pkcs11/seahorse-private-key.c b/pkcs11/seahorse-private-key.c
index 4206657..04ceb5a 100644
--- a/pkcs11/seahorse-private-key.c
+++ b/pkcs11/seahorse-private-key.c
@@ -26,9 +26,11 @@
 #include "seahorse-pkcs11.h"
 #include "seahorse-pkcs11-actions.h"
 #include "seahorse-pkcs11-helpers.h"
+#include "seahorse-pkcs11-key-deleter.h"
 #include "seahorse-token.h"
 #include "seahorse-types.h"
 
+#include "seahorse-deletable.h"
 #include "seahorse-util.h"
 
 #include <gcr/gcr.h>
@@ -43,6 +45,7 @@ static const gulong REQUIRED_ATTRS[] = {
 	CKA_LABEL,
 	CKA_CLASS,
 	CKA_KEY_TYPE,
+	CKA_MODIFIABLE,
 };
 
 enum {
@@ -53,6 +56,7 @@ enum {
 	PROP_ACTIONS,
 	PROP_PARTNER,
 
+	PROP_DELETABLE,
 	PROP_LABEL,
 	PROP_MARKUP,
 	PROP_DESCRIPTION,
@@ -67,10 +71,13 @@ struct _SeahorsePrivateKeyPrivate {
 	GIcon *icon;
 };
 
+static void seahorse_private_key_deletable_iface (SeahorseDeletableIface *iface);
+
 static void seahorse_private_key_object_attributes_iface (GckObjectAttributesIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (SeahorsePrivateKey, seahorse_private_key, GCK_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (GCK_TYPE_OBJECT_ATTRIBUTES, seahorse_private_key_object_attributes_iface)
+                         G_IMPLEMENT_INTERFACE (GCK_TYPE_OBJECT_ATTRIBUTES, seahorse_private_key_object_attributes_iface);
+                         G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_DELETABLE, seahorse_private_key_deletable_iface);
 );
 
 static void
@@ -156,6 +163,9 @@ seahorse_private_key_get_property (GObject *obj,
 	case PROP_MARKUP:
 		g_value_take_string (value, calculate_markup (self));
 		break;
+	case PROP_DELETABLE:
+		g_value_set_boolean (value, seahorse_token_is_deletable (self->pv->token, GCK_OBJECT (self)));
+		break;
 	case PROP_DESCRIPTION:
 		g_value_set_string (value, _("Private key"));
 		break;
@@ -244,6 +254,8 @@ seahorse_private_key_class_init (SeahorsePrivateKeyClass *klass)
 	                                 G_TYPE_ICON, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
 
 	g_object_class_override_property (gobject_class, PROP_ATTRIBUTES, "attributes");
+
+	g_object_class_override_property (gobject_class, PROP_DELETABLE, "deletable");
 }
 
 static void
@@ -253,6 +265,21 @@ seahorse_private_key_object_attributes_iface (GckObjectAttributesIface *iface)
 	iface->n_attribute_types = G_N_ELEMENTS (REQUIRED_ATTRS);
 }
 
+static SeahorseDeleter *
+seahorse_private_key_create_deleter (SeahorseDeletable *deletable)
+{
+	SeahorsePrivateKey *self = SEAHORSE_PRIVATE_KEY (deletable);
+	if (!seahorse_token_is_deletable (self->pv->token, GCK_OBJECT (self)))
+		return NULL;
+	return seahorse_pkcs11_key_deleter_new (G_OBJECT (self));
+}
+
+static void
+seahorse_private_key_deletable_iface (SeahorseDeletableIface *iface)
+{
+	iface->create_deleter = seahorse_private_key_create_deleter;
+}
+
 SeahorseCertificate *
 seahorse_private_key_get_partner (SeahorsePrivateKey *self)
 {
diff --git a/pkcs11/seahorse-token.c b/pkcs11/seahorse-token.c
index 890955f..a47775c 100644
--- a/pkcs11/seahorse-token.c
+++ b/pkcs11/seahorse-token.c
@@ -32,7 +32,6 @@
 #include "seahorse-pkcs11.h"
 #include "seahorse-pkcs11-actions.h"
 #include "seahorse-pkcs11-helpers.h"
-#include "seahorse-pkcs11-operations.h"
 #include "seahorse-private-key.h"
 #include "seahorse-token.h"
 
@@ -1071,3 +1070,27 @@ seahorse_token_unlock_finish (SeahorseToken *self,
 
 	return TRUE;
 }
+
+gboolean
+seahorse_token_is_deletable (SeahorseToken *self,
+                             GckObject *object)
+{
+	GckAttributes *attrs;
+	GckTokenInfo *info;
+	gboolean ret;
+
+	g_return_val_if_fail (SEAHORSE_IS_TOKEN (self), FALSE);
+	g_return_val_if_fail (GCK_IS_OBJECT (object), FALSE);
+
+	info = seahorse_token_get_info (self);
+	if (info->flags & CKF_WRITE_PROTECTED)
+		return FALSE;
+
+	g_object_get (object, "attributes", &attrs, NULL);
+
+	if (!gck_attributes_find_boolean (attrs, CKA_MODIFIABLE, &ret))
+		ret = TRUE;
+
+	gck_attributes_unref (attrs);
+	return ret;
+}
diff --git a/pkcs11/seahorse-token.h b/pkcs11/seahorse-token.h
index 273479b..0b25027 100644
--- a/pkcs11/seahorse-token.h
+++ b/pkcs11/seahorse-token.h
@@ -70,6 +70,9 @@ gboolean               seahorse_token_has_mechanism     (SeahorseToken *self,
 void                   seahorse_token_remove_object     (SeahorseToken *self,
                                                          GckObject *object);
 
+gboolean               seahorse_token_is_deletable      (SeahorseToken *self,
+                                                         GckObject *object);
+
 void                   seahorse_token_lock_async        (SeahorseToken *self,
                                                          GTlsInteraction *interaction,
                                                          GCancellable *cancellable,
diff --git a/ssh/Makefile.am b/ssh/Makefile.am
index af3a760..f2e6921 100644
--- a/ssh/Makefile.am
+++ b/ssh/Makefile.am
@@ -20,6 +20,7 @@ libseahorse_ssh_la_SOURCES = \
 	seahorse-ssh.h seahorse-ssh.c \
 	seahorse-ssh-actions.c seahorse-ssh-actions.h \
 	seahorse-ssh-backend.c seahorse-ssh-backend.h \
+	seahorse-ssh-deleter.c seahorse-ssh-deleter.h \
 	seahorse-ssh-dialogs.h \
 	seahorse-ssh-exporter.c seahorse-ssh-exporter.h \
 	seahorse-ssh-generate.c \
diff --git a/ssh/seahorse-ssh-actions.c b/ssh/seahorse-ssh-actions.c
index 0ba6ade..2087ca7 100644
--- a/ssh/seahorse-ssh-actions.c
+++ b/ssh/seahorse-ssh-actions.c
@@ -90,54 +90,10 @@ on_show_properties (GtkAction *action,
 	                                  seahorse_action_get_window (action));
 }
 
-static void
-on_delete_objects (GtkAction *action,
-                   gpointer user_data)
-{
-	guint num;
-	gchar* prompt;
-	GList *l;
-	GtkWindow *parent;
-	GError *error = NULL;
-	GList* objects;
-
-	objects = user_data;
-	num = g_list_length (objects);
-	if (num == 0) {
-		return;
-
-	} else if (num == 1) {
-		prompt = g_strdup_printf (_("Are you sure you want to delete the secure shell key '%s'?"),
-		                          seahorse_object_get_label (objects->data));
-
-	} else {
-		prompt = g_strdup_printf (ngettext (_("Are you sure you want to delete %d secure shell keys?"),
-		                                    _("Are you sure you want to delete %d secure shell keys?"),
-		                                    num),
-		                          num);
-	}
-
-	parent = seahorse_action_get_window (action);
-	if (seahorse_delete_dialog_prompt (parent, prompt)) {
-		for (l = objects; l != NULL; l = g_list_next (l)) {
-			if (!seahorse_ssh_op_delete_sync (l->data, &error)) {
-				seahorse_util_handle_error (&error, parent, _("Couldn't delete key"));
-			}
-		}
-	} else {
-		g_cancellable_cancel (g_cancellable_get_current ());
-	}
-
-	g_free (prompt);
-
-}
-
 static const GtkActionEntry KEYS_ACTIONS[] = {
 	{ "remote-ssh-upload", NULL, N_ ("Configure Key for _Secure Shell..."), "",
 		N_ ("Send public Secure Shell key to another machine, and enable logins using that key."),
 		G_CALLBACK (on_ssh_upload) },
-	{ "delete", GTK_STOCK_DELETE, NULL, NULL,
-	  N_("Delete the key."), G_CALLBACK (on_delete_objects) },
 };
 
 static const GtkActionEntry KEY_ACTIONS[] = {
diff --git a/ssh/seahorse-ssh-deleter.c b/ssh/seahorse-ssh-deleter.c
new file mode 100644
index 0000000..69eedb3
--- /dev/null
+++ b/ssh/seahorse-ssh-deleter.c
@@ -0,0 +1,205 @@
+/* 
+ * Seahorse
+ * 
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "seahorse-ssh-key.h"
+#include "seahorse-ssh-deleter.h"
+#include "seahorse-ssh-operation.h"
+
+#include "seahorse-delete-dialog.h"
+
+#include <glib/gi18n.h>
+
+#define SEAHORSE_SSH_DELETER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), SEAHORSE_TYPE_SSH_DELETER, SeahorseSshDeleterClass))
+#define SEAHORSE_IS_SSH_DELETER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAHORSE_TYPE_SSH_DELETER))
+#define SEAHORSE_SSH_DELETER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAHORSE_TYPE_SSH_DELETER, SeahorseSshDeleterClass))
+
+typedef struct _SeahorseSshDeleterClass SeahorseSshDeleterClass;
+
+struct _SeahorseSshDeleter {
+	SeahorseDeleter parent;
+	gboolean have_private;
+	GList *keys;
+};
+
+struct _SeahorseSshDeleterClass {
+	SeahorseDeleterClass parent_class;
+};
+
+G_DEFINE_TYPE (SeahorseSshDeleter, seahorse_ssh_deleter, SEAHORSE_TYPE_DELETER);
+
+static void
+seahorse_ssh_deleter_init (SeahorseSshDeleter *self)
+{
+
+}
+
+static void
+seahorse_ssh_deleter_finalize (GObject *obj)
+{
+	SeahorseSshDeleter *self = SEAHORSE_SSH_DELETER (obj);
+
+	g_list_free_full (self->keys, g_object_unref);
+
+	G_OBJECT_CLASS (seahorse_ssh_deleter_parent_class)->finalize (obj);
+}
+
+static GtkDialog *
+seahorse_ssh_deleter_create_confirm (SeahorseDeleter *deleter,
+                                           GtkWindow *parent)
+{
+	SeahorseSshDeleter *self = SEAHORSE_SSH_DELETER (deleter);
+	const gchar *confirm;
+	GtkDialog *dialog;
+	gchar *prompt;
+	guint num;
+
+	num = g_list_length (self->keys);
+	if (self->have_private) {
+		g_return_val_if_fail (num == 1, NULL);
+		prompt = g_strdup_printf (_("Are you sure you want to delete the secure shell key '%s'?"),
+		                          seahorse_object_get_label (SEAHORSE_OBJECT (self->keys->data)));
+		confirm = _("I understand that this secret key will be permanently deleted.");
+
+	} else if (num == 1) {
+		prompt = g_strdup_printf (_("Are you sure you want to delete the secure shell key '%s'?"),
+		                          seahorse_object_get_label (SEAHORSE_OBJECT (self->keys->data)));
+		confirm = NULL;
+
+	} else {
+		prompt = g_strdup_printf (ngettext (_("Are you sure you want to delete %d secure shell keys?"),
+		                                    _("Are you sure you want to delete %d secure shell keys?"),
+		                                    num),
+		                          num);
+		confirm = NULL;
+	}
+
+	dialog = seahorse_delete_dialog_new (parent, prompt);
+	g_free (prompt);
+
+	if (confirm) {
+		seahorse_delete_dialog_set_check_label (SEAHORSE_DELETE_DIALOG (dialog), confirm);
+		seahorse_delete_dialog_set_check_require (SEAHORSE_DELETE_DIALOG (dialog), TRUE);
+	}
+
+	return dialog;
+}
+
+static GList *
+seahorse_ssh_deleter_get_objects (SeahorseDeleter *deleter)
+{
+	SeahorseSshDeleter *self = SEAHORSE_SSH_DELETER (deleter);
+	return self->keys;
+}
+
+static gboolean
+seahorse_ssh_deleter_add_object (SeahorseDeleter *deleter,
+                                 GObject *object)
+{
+	SeahorseSshDeleter *self = SEAHORSE_SSH_DELETER (deleter);
+	SeahorseUsage usage;
+
+	if (!SEAHORSE_IS_SSH_KEY (object))
+		return FALSE;
+	if (self->have_private)
+		return FALSE;
+	usage = seahorse_object_get_usage (SEAHORSE_OBJECT (object));
+	if (usage == SEAHORSE_USAGE_PRIVATE_KEY) {
+		if (self->keys != NULL)
+			return FALSE;
+		self->have_private = TRUE;
+	}
+
+	self->keys = g_list_append (self->keys, g_object_ref (object));
+	return TRUE;
+}
+
+static void
+seahorse_ssh_deleter_delete_async (SeahorseDeleter *deleter,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+	SeahorseSshDeleter *self = SEAHORSE_SSH_DELETER (deleter);
+	GSimpleAsyncResult *res;
+	GError *error = NULL;
+	GList *l;
+
+	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                 seahorse_ssh_deleter_delete_async);
+
+	for (l = self->keys; l != NULL && !g_cancellable_is_cancelled (cancellable);
+	     l = g_list_next (l)) {
+		if (!seahorse_ssh_op_delete_sync (l->data, &error)) {
+			g_simple_async_result_take_error (res, error);
+			break;
+		}
+	}
+
+	g_simple_async_result_complete_in_idle (res);
+	g_object_unref (res);
+}
+
+static gboolean
+seahorse_ssh_deleter_delete_finish (SeahorseDeleter *deleter,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+	SeahorseSshDeleter *self = SEAHORSE_SSH_DELETER (deleter);
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+	                      seahorse_ssh_deleter_delete_async), FALSE);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+seahorse_ssh_deleter_class_init (SeahorseSshDeleterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	SeahorseDeleterClass *deleter_class = SEAHORSE_DELETER_CLASS (klass);
+
+	gobject_class->finalize = seahorse_ssh_deleter_finalize;
+
+	deleter_class->add_object = seahorse_ssh_deleter_add_object;
+	deleter_class->create_confirm = seahorse_ssh_deleter_create_confirm;
+	deleter_class->delete_async = seahorse_ssh_deleter_delete_async;
+	deleter_class->delete_finish = seahorse_ssh_deleter_delete_finish;
+	deleter_class->get_objects = seahorse_ssh_deleter_get_objects;
+}
+
+SeahorseDeleter *
+seahorse_ssh_deleter_new (SeahorseSSHKey *item)
+{
+	SeahorseDeleter *deleter;
+
+	deleter = g_object_new (SEAHORSE_TYPE_SSH_DELETER, NULL);
+	if (!seahorse_deleter_add_object (deleter, G_OBJECT (item)))
+		g_assert_not_reached ();
+
+	return deleter;
+}
diff --git a/pkcs11/seahorse-pkcs11-operations.h b/ssh/seahorse-ssh-deleter.h
similarity index 50%
rename from pkcs11/seahorse-pkcs11-operations.h
rename to ssh/seahorse-ssh-deleter.h
index a1b94e7..70d29e8 100644
--- a/pkcs11/seahorse-pkcs11-operations.h
+++ b/ssh/seahorse-ssh-deleter.h
@@ -1,36 +1,42 @@
-/* 
+/*
  * Seahorse
- * 
- * Copyright (C) 2008 Stefan Walter
+ *
  * Copyright (C) 2011 Collabora Ltd.
  *
- * This program is free software; you can redistribute it and/or modify 
+ * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as
  * published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
- *  
+ *
  * This program 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 program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.  
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
  */
 
-#ifndef __SEAHORSE_PKCS11_OPERATIONS_H__
-#define __SEAHORSE_PKCS11_OPERATIONS_H__
+#ifndef __SEAHORSE_SSH_DELETER_H__
+#define __SEAHORSE_SSH_DELETER_H__
 
 #include <glib-object.h>
 
-void          seahorse_pkcs11_delete_async     (GList *objects,
-                                                GCancellable *cancellable,
-                                                GAsyncReadyCallback callback,
-                                                gpointer user_data);
+#include "seahorse-deleter.h"
+#include "seahorse-ssh-key.h"
+
+#define SEAHORSE_TYPE_SSH_DELETER       (seahorse_ssh_deleter_get_type ())
+#define SEAHORSE_SSH_DELETER(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAHORSE_TYPE_SSH_DELETER, SeahorseSshDeleter))
+#define SEAHORSE_IS_SSH_DELETER(obj)    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAHORSE_TYPE_SSH_DELETER))
+
+typedef struct _SeahorseSshDeleter SeahorseSshDeleter;
+
+GType              seahorse_ssh_deleter_get_type   (void) G_GNUC_CONST;
 
-gboolean      seahorse_pkcs11_delete_finish    (GAsyncResult *result,
-                                                GError **error);
+SeahorseDeleter *  seahorse_ssh_deleter_new        (SeahorseSSHKey *key);
 
-#endif /* __SEAHORSE_PKCS11_TOKEN_H__ */
+#endif /* __SEAHORSE_SSH_DELETER_H__ */
diff --git a/ssh/seahorse-ssh-key.c b/ssh/seahorse-ssh-key.c
index 27913d0..a20f91a 100644
--- a/ssh/seahorse-ssh-key.c
+++ b/ssh/seahorse-ssh-key.c
@@ -23,11 +23,13 @@
 #include "config.h"
 
 #include "seahorse-ssh-actions.h"
+#include "seahorse-ssh-deleter.h"
 #include "seahorse-ssh-exporter.h"
 #include "seahorse-ssh-key.h"
 #include "seahorse-ssh-operation.h"
 #include "seahorse-ssh-source.h"
 
+#include "seahorse-deletable.h"
 #include "seahorse-exportable.h"
 #include "seahorse-icons.h"
 #include "seahorse-place.h"
@@ -53,10 +55,13 @@ enum {
     PROP_LENGTH
 };
 
+static void       seahorse_ssh_key_deletable_iface       (SeahorseDeletableIface *iface);
+
 static void       seahorse_ssh_key_exportable_iface      (SeahorseExportableIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (SeahorseSSHKey, seahorse_ssh_key, SEAHORSE_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_EXPORTABLE, seahorse_ssh_key_exportable_iface);
+                         G_IMPLEMENT_INTERFACE (SEAHORSE_TYPE_DELETABLE, seahorse_ssh_key_deletable_iface);
 );
 
 /* -----------------------------------------------------------------------------
@@ -313,9 +318,18 @@ seahorse_ssh_key_exportable_iface (SeahorseExportableIface *iface)
 	iface->create_exporters = seahorse_ssh_key_create_exporters;
 }
 
-/* -----------------------------------------------------------------------------
- * PUBLIC METHODS
- */
+static SeahorseDeleter *
+seahorse_ssh_key_create_deleter (SeahorseDeletable *deletable)
+{
+	SeahorseSSHKey *self = SEAHORSE_SSH_KEY (deletable);
+	return seahorse_ssh_deleter_new (self);
+}
+
+static void
+seahorse_ssh_key_deletable_iface (SeahorseDeletableIface *iface)
+{
+	iface->create_deleter = seahorse_ssh_key_create_deleter;
+}
 
 SeahorseSSHKey* 
 seahorse_ssh_key_new (SeahorsePlace *place,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]