[seahorse/pgp/uids-listbox] WIP




commit 73cfd200705040a53f82bb3434d14c4e8e828e3a
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Wed Jul 28 23:08:46 2021 +0200

    WIP

 common/util.vala                           |   9 +
 data/seahorse.gresource.xml                |   1 +
 pgp/meson.build                            |   1 +
 pgp/seahorse-pgp-key-properties.c          | 618 +----------------------------
 pgp/seahorse-pgp-key.c                     |  25 +-
 pgp/seahorse-pgp-key.h                     |   2 +
 pgp/seahorse-pgp-private-key-properties.ui | 183 +--------
 pgp/seahorse-pgp-public-key-properties.ui  |  89 +----
 pgp/seahorse-pgp-uid-list-box-row.ui       | 115 ++++++
 pgp/seahorse-pgp-uid-list-box.c            | 544 +++++++++++++++++++++++++
 pgp/seahorse-pgp-uid-list-box.h            |  36 ++
 pgp/seahorse-pgp-uid.c                     |  30 +-
 12 files changed, 766 insertions(+), 887 deletions(-)
---
diff --git a/common/util.vala b/common/util.vala
index 44f29bba..dfbb1064 100644
--- a/common/util.vala
+++ b/common/util.vala
@@ -75,4 +75,13 @@ namespace Seahorse.Util {
 
         return false;
     }
+
+    public void toggle_action (GLib.SimpleAction action,
+                                                          GLib.Variant? variant,
+                                                          void *user_data)
+       {
+               var old_state = action.get_state();
+               var new_state = new GLib.Variant.boolean(!old_state.get_boolean());
+               action.change_state(new_state);
+       }
 }
diff --git a/data/seahorse.gresource.xml b/data/seahorse.gresource.xml
index 32aa3b2d..ec3b45e2 100644
--- a/data/seahorse.gresource.xml
+++ b/data/seahorse.gresource.xml
@@ -39,6 +39,7 @@
     <file alias="seahorse-pgp-private-key-properties.ui" 
preprocess="xml-stripblanks">../pgp/seahorse-pgp-private-key-properties.ui</file>
     <file alias="seahorse-pgp-public-key-properties.ui" 
preprocess="xml-stripblanks">../pgp/seahorse-pgp-public-key-properties.ui</file>
     <file alias="seahorse-pgp-subkey-list-box-row.ui" 
preprocess="xml-stripblanks">../pgp/seahorse-pgp-subkey-list-box-row.ui</file>
+    <file alias="seahorse-pgp-uid-list-box-row.ui" 
preprocess="xml-stripblanks">../pgp/seahorse-pgp-uid-list-box-row.ui</file>
 
     <!-- PKCS#11 -->
     <file alias="seahorse-pkcs11-generate.ui" 
preprocess="xml-stripblanks">../pkcs11/seahorse-pkcs11-generate.ui</file>
diff --git a/pgp/meson.build b/pgp/meson.build
index 3346c5d6..9a9bf076 100644
--- a/pgp/meson.build
+++ b/pgp/meson.build
@@ -31,6 +31,7 @@ pgp_sources = files(
   'seahorse-pgp-subkey.c',
   'seahorse-pgp-subkey-list-box.c',
   'seahorse-pgp-uid.c',
+  'seahorse-pgp-uid-list-box.c',
   'seahorse-transfer.c',
   'seahorse-unknown.c',
   'seahorse-unknown-source.c',
diff --git a/pgp/seahorse-pgp-key-properties.c b/pgp/seahorse-pgp-key-properties.c
index 42b770e7..09979af1 100644
--- a/pgp/seahorse-pgp-key-properties.c
+++ b/pgp/seahorse-pgp-key-properties.c
@@ -38,6 +38,7 @@
 #include "seahorse-pgp-dialogs.h"
 #include "seahorse-pgp-key.h"
 #include "seahorse-pgp-uid.h"
+#include "seahorse-pgp-uid-list-box.h"
 #include "seahorse-pgp-signature.h"
 #include "seahorse-pgp-subkey.h"
 #include "seahorse-pgp-subkey-list-box.h"
@@ -89,10 +90,10 @@ struct _SeahorsePgpKeyProperties {
     GtkLabel *details_strength_label;
     GtkLabel *details_expires_label;
     GtkComboBox *details_trust_combobox;
+    GtkWidget *uids_container;
     GtkWidget *subkeys_container;
 
     /* Private key widgets */
-    GtkTreeView *names_tree;
     GtkWidget *owner_photo_frame;
     GtkWidget *owner_photo_add_button;
     GtkWidget *owner_photo_delete_button;
@@ -100,10 +101,6 @@ struct _SeahorsePgpKeyProperties {
 
     /* Public key widgets */
     GtkBox *indicate_trust_box;
-    GtkTreeView *owner_userid_tree;
-    GtkTreeView *signatures_tree;
-    GtkWidget *signatures_area;
-    GtkWidget *uids_area;
     GtkWidget *trust_page;
     GtkLabel *trust_sign_label;
     GtkLabel *trust_revoke_label;
@@ -112,7 +109,6 @@ struct _SeahorsePgpKeyProperties {
     GtkWidget *revoke_area;
     GtkLabel *trust_marginal_label;
     GtkSwitch *trust_marginal_switch;
-    GtkToggleButton *signatures_toggle;
 };
 
 G_DEFINE_TYPE (SeahorsePgpKeyProperties, seahorse_pgp_key_properties, GTK_TYPE_DIALOG)
@@ -129,105 +125,10 @@ set_action_enabled (SeahorsePgpKeyProperties *self,
     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled);
 }
 
-static gpointer
-get_selected_object (GtkTreeView *widget, guint column)
-{
-    GtkTreeSelection *selection;
-    GtkTreeIter iter;
-    GtkTreeModel *model;
-    GList *rows;
-    gpointer object = NULL;
-
-    model = gtk_tree_view_get_model (widget);
-
-    selection = gtk_tree_view_get_selection (widget);
-    g_assert (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_SINGLE);
-
-    rows = gtk_tree_selection_get_selected_rows (selection, NULL);
-
-    if (g_list_length (rows) > 0) {
-        gtk_tree_model_get_iter (model, &iter, rows->data);
-        gtk_tree_model_get (model, &iter, column, &object, -1);
-        if (object)
-            g_object_unref (object);
-    }
-
-    g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
-    g_list_free (rows);
-
-    return object;
-}
-
-static void
-on_pgp_signature_row_activated (GtkTreeView *treeview,
-                                GtkTreePath *path,
-                                GtkTreeViewColumn *arg2,
-                                gpointer user_data)
-{
-    SeahorsePgpKeyProperties *self = SEAHORSE_PGP_KEY_PROPERTIES (user_data);
-    GObject *object = NULL;
-    GtkTreeModel *model;
-    GtkTreeIter iter;
-
-    model = gtk_tree_view_get_model (treeview);
-
-    if (GTK_IS_TREE_MODEL_FILTER (model))
-        model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
-
-    g_return_if_fail (gtk_tree_model_get_iter (model, &iter, path));
-
-    object = seahorse_object_model_get_row_key (SEAHORSE_OBJECT_MODEL (model), &iter);
-    if (object != NULL && SEAHORSE_PGP_IS_KEY (object)) {
-        GtkWindow *parent;
-        g_autoptr(GtkWindow) dialog = NULL;
-
-        parent = GTK_WINDOW (gtk_widget_get_parent (GTK_WIDGET (self)));
-        dialog = seahorse_pgp_key_properties_new (SEAHORSE_PGP_KEY (object),
-                                                  parent);
-        gtk_dialog_run (GTK_DIALOG (dialog));
-        gtk_widget_destroy (GTK_WIDGET (dialog));
-    }
-}
-
-static void
-unique_strings (GPtrArray *keyids)
-{
-    guint i;
-
-    g_ptr_array_sort (keyids, (GCompareFunc)g_ascii_strcasecmp);
-    for (i = 0; i + 1 < keyids->len; ) {
-        if (g_ascii_strcasecmp (keyids->pdata[i], keyids->pdata[i + 1]) == 0)
-            g_ptr_array_remove_index (keyids, i);
-        else
-            i++;
-    }
-}
-
 /* -----------------------------------------------------------------------------
  * NAMES PAGE (PRIVATE KEYS)
  */
 
-enum {
-    UIDSIG_OBJECT,
-    UIDSIG_ICON,
-    UIDSIG_NAME,
-    UIDSIG_KEYID,
-    UIDSIG_N_COLUMNS
-};
-
-static GType uidsig_columns[] = {
-    G_TYPE_OBJECT,  /* index */
-    0 /* later */,  /* icon */
-    G_TYPE_STRING,  /* name */
-    G_TYPE_STRING   /* keyid */
-};
-
-static SeahorsePgpUid*
-names_get_selected_uid (SeahorsePgpKeyProperties *self)
-{
-    return get_selected_object (self->names_tree, UIDSIG_OBJECT);
-}
-
 static void
 on_uids_add (GSimpleAction *action, GVariant *param, gpointer user_data)
 {
@@ -237,254 +138,6 @@ on_uids_add (GSimpleAction *action, GVariant *param, gpointer user_data)
                                        GTK_WINDOW (self));
 }
 
-static void
-on_uids_make_primary_cb (GObject *source, GAsyncResult *res, gpointer user_data)
-{
-    SeahorsePgpKeyProperties *self = SEAHORSE_PGP_KEY_PROPERTIES (user_data);
-    SeahorseGpgmeUid *uid = SEAHORSE_GPGME_UID (source);
-    g_autoptr(GError) error = NULL;
-
-    if (!seahorse_gpgme_key_op_make_primary_finish (uid, res, &error)) {
-        GtkWindow *window;
-        window = gtk_window_get_transient_for (GTK_WINDOW (self));
-        seahorse_util_show_error (GTK_WIDGET (window),
-                                  _("Couldn’t change primary user ID"),
-                                  error->message);
-    }
-}
-
-static void
-on_uids_make_primary (GSimpleAction *action, GVariant *param, gpointer user_data)
-{
-    SeahorsePgpKeyProperties *self = SEAHORSE_PGP_KEY_PROPERTIES (user_data);
-    SeahorsePgpUid *uid;
-
-    uid = names_get_selected_uid (self);
-    if (!uid)
-        return;
-
-    g_return_if_fail (SEAHORSE_GPGME_IS_UID (uid));
-    seahorse_gpgme_key_op_make_primary_async (SEAHORSE_GPGME_UID (uid),
-                                              NULL,
-                                              on_uids_make_primary_cb, self);
-}
-
-static void
-on_uids_delete (GSimpleAction *action, GVariant *param, gpointer user_data)
-{
-    SeahorsePgpKeyProperties *self = SEAHORSE_PGP_KEY_PROPERTIES (user_data);
-    SeahorsePgpUid *uid;
-    gboolean ret;
-    g_autofree gchar *message = NULL;
-    gpgme_error_t gerr;
-
-    uid = names_get_selected_uid (self);
-    if (uid == NULL)
-        return;
-
-    g_return_if_fail (SEAHORSE_GPGME_IS_UID (uid));
-    message = g_strdup_printf (_("Are you sure you want to permanently delete the “%s” user ID?"),
-                               seahorse_object_get_label (SEAHORSE_OBJECT (uid)));
-    ret = seahorse_delete_dialog_prompt (GTK_WINDOW (self), message);
-
-    if (ret == FALSE)
-        return;
-
-    gerr = seahorse_gpgme_key_op_del_uid (SEAHORSE_GPGME_UID (uid));
-    if (!GPG_IS_OK (gerr))
-        seahorse_gpgme_handle_error (gerr, _("Couldn’t delete user ID"));
-}
-
-static void
-on_uids_sign (GSimpleAction *action, GVariant *param, gpointer user_data)
-{
-    SeahorsePgpKeyProperties *self = SEAHORSE_PGP_KEY_PROPERTIES (user_data);
-    SeahorsePgpUid *uid;
-    SeahorseGpgmeSignDialog *dialog;
-
-    uid = names_get_selected_uid (self);
-    if (uid == NULL)
-        return;
-
-    g_return_if_fail (SEAHORSE_GPGME_IS_UID (uid));
-
-    dialog = seahorse_gpgme_sign_dialog_new (SEAHORSE_OBJECT (uid));
-    gtk_dialog_run (GTK_DIALOG (dialog));
-    gtk_widget_destroy (GTK_WIDGET (dialog));
-}
-
-static void
-on_uids_revoke (GSimpleAction *action, GVariant *param, gpointer user_data)
-{
-    /* TODO: */
-/*    SeahorseObject *skey;
-    int index;
-    Glist *keys = NULL;
-
-    skey = self->key;
-    index = names_get_selected_uid (swidget);
-
-    if (index >= 1) {
-        seahorse_revoke_show (SEAHORSE_PGP_KEY (skey), index - 1);
-
-#ifdef WITH_KEYSERVER
-        if (g_settings_get_boolean(AUTOSYNC_KEY) == TRUE) {
-            keys = g_list_append (keys, skey);
-            seahorse_keyserver_sync (keys);
-            g_list_free(keys);
-        }
-#endif
-    }*/
-}
-
-static void
-update_names (GtkTreeSelection *selection, SeahorsePgpKeyProperties *self)
-{
-    SeahorsePgpUid *uid = names_get_selected_uid (self);
-    int index = -1;
-
-    if (uid && SEAHORSE_GPGME_IS_UID (uid))
-        index = seahorse_gpgme_uid_get_gpgme_index (SEAHORSE_GPGME_UID (uid));
-
-    set_action_enabled (self, "uids.make-primary", index > 0);
-    set_action_enabled (self, "uids.sign", index >= 0);
-    set_action_enabled (self, "uids.delete", index >= 0);
-}
-
-/* Is called whenever a signature key changes, to update row */
-static void
-names_update_row (SeahorseObjectModel *skmodel, SeahorseObject *object,
-                  GtkTreeIter *iter, SeahorsePgpKeyProperties *self)
-{
-    g_autoptr(GIcon) icon = NULL;
-    const gchar *name, *id;
-
-    icon = g_themed_icon_new (SEAHORSE_PGP_IS_KEY (object) ?
-                              SEAHORSE_ICON_SIGN : "dialog-question");
-    name = seahorse_object_get_markup (object);
-    id = seahorse_object_get_identifier (object);
-
-    gtk_tree_store_set (GTK_TREE_STORE (skmodel), iter,
-                        UIDSIG_OBJECT, NULL,
-                        UIDSIG_ICON, icon,
-                        /* TRANSLATORS: [Unknown] signature name */
-                        UIDSIG_NAME, name ? name : _("[Unknown]"),
-                        UIDSIG_KEYID, id, -1);
-}
-
-static void
-names_populate (SeahorsePgpKeyProperties *self, GtkTreeStore *store, SeahorsePgpKey *pkey)
-{
-    GObject *object;
-    GtkTreeIter uiditer, sigiter;
-    GList *keys, *l;
-    GListModel *uids;
-
-    /* Insert all the fun-ness */
-    uids = seahorse_pgp_key_get_uids (pkey);
-
-    for (guint i = 0; i < g_list_model_get_n_items (uids); i++) {
-        g_autoptr(SeahorsePgpUid) uid = NULL;
-        g_autoptr(GIcon) icon = NULL;
-        g_autoptr(GPtrArray) keyids = NULL;
-        g_autoptr(GCancellable) cancellable = NULL;
-        GListModel *sigs;
-
-        uid = g_list_model_get_item (uids, i);
-        icon = g_themed_icon_new ("avatar-default-symbolic");
-        gtk_tree_store_append (store, &uiditer, NULL);
-        gtk_tree_store_set (store, &uiditer,
-                            UIDSIG_OBJECT, uid,
-                            UIDSIG_ICON, icon,
-                            UIDSIG_NAME, seahorse_object_get_markup (SEAHORSE_OBJECT (uid)),
-                            -1);
-
-        keyids = g_ptr_array_new ();
-
-        /* Build a list of all the keyids */
-        sigs = seahorse_pgp_uid_get_signatures (uid);
-        for (guint j = 0; j < g_list_model_get_n_items (sigs); j++) {
-            g_autoptr(SeahorsePgpSignature) sig = g_list_model_get_item (sigs, j);
-
-            /* Never show self signatures, they're implied */
-            if (seahorse_pgp_key_has_keyid (pkey, seahorse_pgp_signature_get_keyid (sig)))
-                continue;
-            g_ptr_array_add (keyids, (void *) seahorse_pgp_signature_get_keyid (sig));
-        }
-
-        g_ptr_array_add (keyids, NULL);
-
-        /*
-         * Pass it to 'DiscoverKeys' for resolution/download, cancellable
-         * ties search scope together
-         */
-        cancellable = g_cancellable_new ();
-        keys = seahorse_pgp_backend_discover_keys (NULL, (const gchar **)keyids->pdata, cancellable);
-
-        /* Add the keys to the store */
-        for (l = keys; l; l = g_list_next (l)) {
-            object = G_OBJECT (l->data);
-            gtk_tree_store_append (store, &sigiter, &uiditer);
-
-            /* This calls the 'update-row' callback, to set the values for the key */
-            seahorse_object_model_set_row_object (SEAHORSE_OBJECT_MODEL (store), &sigiter, object);
-        }
-    }
-}
-
-static void
-do_names (SeahorsePgpKeyProperties *self)
-{
-    GtkTreeStore *store;
-    GtkCellRenderer *renderer;
-
-    if (seahorse_object_get_usage (SEAHORSE_OBJECT (self->key)) != SEAHORSE_USAGE_PRIVATE_KEY)
-        return;
-
-    /* Clear/create table store */
-    g_return_if_fail (self->names_tree != NULL);
-
-    store = GTK_TREE_STORE (gtk_tree_view_get_model (self->names_tree));
-    if (store) {
-        gtk_tree_store_clear (store);
-
-    } else {
-        g_assert (UIDSIG_N_COLUMNS == G_N_ELEMENTS (uidsig_columns));
-        uidsig_columns[UIDSIG_ICON] = G_TYPE_ICON;
-
-        /* This is our first time so create a store */
-        store = GTK_TREE_STORE (seahorse_object_model_new (UIDSIG_N_COLUMNS, uidsig_columns));
-        g_signal_connect (store, "update-row", G_CALLBACK (names_update_row), self);
-
-        /* Icon column */
-        renderer = gtk_cell_renderer_pixbuf_new ();
-        gtk_tree_view_insert_column_with_attributes (self->names_tree,
-                                                     -1, "", renderer,
-                                                     "gicon", UIDSIG_ICON, NULL);
-
-        renderer = gtk_cell_renderer_text_new ();
-        g_object_set (renderer, "yalign", 0.0, "xalign", 0.0, NULL);
-        gtk_tree_view_insert_column_with_attributes (self->names_tree,
-                                                     /* TRANSLATORS: The name and email set on the PGP key */
-                                                     -1, _("Name/Email"), renderer,
-                                                     "markup", UIDSIG_NAME, NULL);
-
-        /* The signature ID column */
-        renderer = gtk_cell_renderer_text_new ();
-        g_object_set (renderer, "yalign", 0.0, "xalign", 0.0, NULL);
-        gtk_tree_view_insert_column_with_attributes (self->names_tree,
-                                                     -1, _("Signature ID"), renderer,
-                                                     "text", UIDSIG_KEYID, NULL);
-    }
-
-    names_populate (self, store, self->key);
-
-    gtk_tree_view_set_model (self->names_tree, GTK_TREE_MODEL(store));
-    gtk_tree_view_expand_all (self->names_tree);
-
-    update_names (NULL, self);
-}
-
 /* -----------------------------------------------------------------------------
  * PHOTO ID AREA
  */
@@ -737,22 +390,6 @@ on_pgp_owner_photoid_button (GtkWidget *widget,
  * OWNER PAGE
  */
 
-/* owner uid list */
-enum {
-    UID_OBJECT,
-    UID_ICON,
-    UID_MARKUP,
-    UID_N_COLUMNS
-};
-
-static GType uid_columns[] = {
-    G_TYPE_OBJECT,  /* object */
-    0 /* later */,  /* icon */
-    G_TYPE_STRING,  /* name */
-    G_TYPE_STRING,  /* email */
-    G_TYPE_STRING   /* comment */
-};
-
 static void
 on_gpgme_key_change_pass_done (GObject *source,
                                GAsyncResult *res,
@@ -790,9 +427,6 @@ on_change_password (GSimpleAction *action, GVariant *param, gpointer user_data)
 static void
 do_owner (SeahorsePgpKeyProperties *self)
 {
-    GtkCellRenderer *renderer;
-    GtkListStore *store;
-    GtkTreeIter iter;
     guint flags;
     const char *label;
     GListModel *uids;
@@ -829,11 +463,8 @@ do_owner (SeahorsePgpKeyProperties *self)
                                                     (flags & SEAHORSE_FLAG_DISABLED)));
     }
 
-    /* Hide or show the uids area */
     uids = seahorse_pgp_key_get_uids (self->key);
     primary_uid = g_list_model_get_item (uids, 0);
-    if (self->uids_area != NULL)
-        gtk_widget_set_visible (self->uids_area, primary_uid != NULL);
     if (primary_uid != NULL) {
         g_autofree gchar *title = NULL;
         g_autofree gchar *email_escaped = NULL;
@@ -865,48 +496,6 @@ do_owner (SeahorsePgpKeyProperties *self)
         gtk_label_set_text (self->owner_keyid_label, label);
     }
 
-    /* Clear/create table store */
-    if (self->owner_userid_tree) {
-        store = GTK_LIST_STORE (gtk_tree_view_get_model (self->owner_userid_tree));
-
-        if (store) {
-            gtk_list_store_clear (GTK_LIST_STORE (store));
-
-        } else {
-            g_assert (UID_N_COLUMNS != G_N_ELEMENTS (uid_columns));
-            uid_columns[1] = G_TYPE_ICON;
-
-            /* This is our first time so create a store */
-            store = gtk_list_store_newv (UID_N_COLUMNS, (GType*)uid_columns);
-
-            /* Make the columns for the view */
-            renderer = gtk_cell_renderer_pixbuf_new ();
-            gtk_tree_view_insert_column_with_attributes (self->owner_userid_tree,
-                                                         -1, "", renderer,
-                                                         "gicon", UID_ICON, NULL);
-
-            gtk_tree_view_insert_column_with_attributes (self->owner_userid_tree,
-                                                         -1, _("Name"), gtk_cell_renderer_text_new (),
-                                                         "markup", UID_MARKUP, NULL);
-        }
-
-        for (guint i = 0; i < g_list_model_get_n_items (uids); i++) {
-            g_autoptr(SeahorsePgpUid) uid = g_list_model_get_item (uids, i);
-            const char *markup;
-            g_autoptr(GIcon) icon = NULL;
-
-            markup = seahorse_object_get_markup (SEAHORSE_OBJECT (uid));
-            icon = g_themed_icon_new ("avatar-default-symbolic");
-            gtk_list_store_append (store, &iter);
-            gtk_list_store_set (store, &iter,
-                                UID_OBJECT, uid,
-                                UID_ICON, icon,
-                                UID_MARKUP, markup, -1);
-        }
-
-        gtk_tree_view_set_model (self->owner_userid_tree, GTK_TREE_MODEL (store));
-    }
-
     do_photo_id (self);
 }
 
@@ -1219,21 +808,6 @@ do_details (SeahorsePgpKeyProperties *self)
  * TRUST PAGE (PUBLIC KEYS)
  */
 
-enum {
-    SIGN_ICON,
-    SIGN_NAME,
-    SIGN_KEYID,
-    SIGN_TRUSTED,
-    SIGN_N_COLUMNS
-};
-
-static GType sign_columns[] = {
-    0 /* later */,
-    G_TYPE_STRING,
-    G_TYPE_STRING,
-    G_TYPE_BOOLEAN
-};
-
 static void
 on_toggle_action (GSimpleAction *action, GVariant *param, gpointer user_data) {
     GVariant *old_state, *new_state;
@@ -1263,104 +837,6 @@ on_trust_marginal_changed (GSimpleAction *action, GVariant *new_state, gpointer
     }
 }
 
-/* Is called whenever a signature key changes */
-static void
-trust_update_row (SeahorseObjectModel *skmodel, SeahorseObject *object,
-                  GtkTreeIter *iter, gpointer user_data)
-{
-    gboolean trusted = FALSE;
-    g_autoptr(GIcon) icon = NULL;
-    const gchar *name, *id;
-
-    if (seahorse_object_get_usage (object) == SEAHORSE_USAGE_PRIVATE_KEY)
-        trusted = TRUE;
-    else if (seahorse_object_get_flags (object) & SEAHORSE_FLAG_TRUSTED)
-        trusted = TRUE;
-
-    icon = g_themed_icon_new (SEAHORSE_PGP_IS_KEY (object) ?
-                              SEAHORSE_ICON_SIGN : "dialog-question");
-    name = seahorse_object_get_label (object);
-    id = seahorse_object_get_identifier (object);
-
-    gtk_tree_store_set (GTK_TREE_STORE (skmodel), iter,
-                        SIGN_ICON, icon,
-                        /* TRANSLATORS: [Unknown] signature name */
-                        SIGN_NAME, name ? name : _("[Unknown]"),
-                        SIGN_KEYID, id,
-                        SIGN_TRUSTED, trusted,
-                        -1);
-}
-
-static void
-signatures_populate_model (SeahorsePgpKeyProperties *self, SeahorseObjectModel *skmodel)
-{
-    GtkTreeIter iter;
-    gboolean have_sigs = FALSE;
-    g_autoptr(GPtrArray) rawids = NULL;
-    GListModel *uids;
-    GList *keys, *l;
-
-    if (self->signatures_tree == NULL)
-        return;
-
-    rawids = g_ptr_array_new ();
-    uids = seahorse_pgp_key_get_uids (self->key);
-
-    /* Build a list of all the keyids */
-    for (guint i = 0; i < g_list_model_get_n_items (uids); i++) {
-        g_autoptr(SeahorsePgpUid) uid = g_list_model_get_item (uids, i);
-        GListModel *sigs;
-
-        sigs = seahorse_pgp_uid_get_signatures (uid);
-        for (guint j = 0; j < g_list_model_get_n_items (sigs); j++) {
-            g_autoptr(SeahorsePgpSignature) sig = g_list_model_get_item (sigs, j);
-
-            /* Never show self signatures, they're implied */
-            if (seahorse_pgp_key_has_keyid (self->key,
-                                            seahorse_pgp_signature_get_keyid (sig)))
-                continue;
-            have_sigs = TRUE;
-            g_ptr_array_add (rawids, (char *) seahorse_pgp_signature_get_keyid (sig));
-        }
-    }
-
-    /* Strip out duplicates */
-    unique_strings (rawids);
-    g_ptr_array_add (rawids, NULL);
-
-    /* Only show signatures area when there are signatures */
-    gtk_widget_set_visible (self->signatures_area, have_sigs);
-
-    if (skmodel) {
-        g_autoptr(GCancellable) cancellable = NULL;
-
-        /* Pass it to 'DiscoverKeys' for resolution/download. cancellable ties
-         * search scope together */
-        cancellable = g_cancellable_new ();
-        keys = seahorse_pgp_backend_discover_keys (NULL, (const gchar **)rawids->pdata, cancellable);
-
-        /* Add the keys to the store */
-        for (l = keys; l; l = g_list_next (l)) {
-            GObject *object = G_OBJECT (l->data);
-
-            gtk_tree_store_append (GTK_TREE_STORE (skmodel), &iter, NULL);
-            /* This calls the 'update-row' callback, to set the values for the key */
-            seahorse_object_model_set_row_object (SEAHORSE_OBJECT_MODEL (skmodel), &iter, object);
-        }
-    }
-}
-
-/* Refilter when the user toggles the 'only show trusted' checkbox */
-static void
-on_pgp_trusted_toggled (GtkToggleButton *toggle, GtkTreeModelFilter *filter)
-{
-    /* Set flag on the store */
-    GtkTreeModel *model = gtk_tree_model_filter_get_model (filter);
-    g_object_set_data (G_OBJECT (model), "only-trusted",
-                       GINT_TO_POINTER (gtk_toggle_button_get_active (toggle)));
-    gtk_tree_model_filter_refilter (filter);
-}
-
 /* Add a signature */
 static void
 on_sign_key (GSimpleAction *action, GVariant *param, gpointer user_data)
@@ -1376,16 +852,6 @@ on_sign_key (GSimpleAction *action, GVariant *param, gpointer user_data)
     gtk_widget_destroy (GTK_WIDGET (dialog));
 }
 
-/* When the 'only display trusted' check is checked, hide untrusted rows */
-static gboolean
-trust_filter (GtkTreeModel *model, GtkTreeIter *iter, gpointer userdata)
-{
-    /* Read flag on the store */
-    gboolean trusted = FALSE;
-    gtk_tree_model_get (model, iter, SIGN_TRUSTED, &trusted, -1);
-    return !g_object_get_data (G_OBJECT (model), "only-trusted") || trusted;
-}
-
 static gboolean
 key_have_signatures (SeahorsePgpKey *pkey, guint types)
 {
@@ -1410,10 +876,7 @@ key_have_signatures (SeahorsePgpKey *pkey, guint types)
 static void
 do_trust (SeahorsePgpKeyProperties *self)
 {
-    GtkTreeStore *store;
-    GtkTreeModelFilter *filter;
     gboolean sigpersonal;
-    GtkCellRenderer *renderer;
     GAction *trust_action;
 
     if (seahorse_object_get_usage (SEAHORSE_OBJECT (self->key)) != SEAHORSE_USAGE_PUBLIC_KEY)
@@ -1474,50 +937,6 @@ do_trust (SeahorsePgpKeyProperties *self)
         gtk_widget_set_visible (self->sign_area, !sigpersonal);
         gtk_widget_set_visible (self->revoke_area, sigpersonal);
     }
-
-    /* The actual signatures listing */
-    if (self->signatures_tree != NULL) {
-        filter = GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (self->signatures_tree));
-
-        if (filter) {
-            /* First time create the store */
-            store = GTK_TREE_STORE (gtk_tree_model_filter_get_model (filter));
-            gtk_tree_store_clear (store);
-        } else {
-            /* Create a new SeahorseObjectModel store.... */
-            sign_columns[SIGN_ICON] = G_TYPE_ICON;
-            store = GTK_TREE_STORE (seahorse_object_model_new (SIGN_N_COLUMNS, (GType*)sign_columns));
-            g_signal_connect (store, "update-row",
-                              G_CALLBACK (trust_update_row), self);
-
-            /* .... and a filter to go ontop of it */
-            filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL));
-            gtk_tree_model_filter_set_visible_func (filter,
-                                                    (GtkTreeModelFilterVisibleFunc)trust_filter, NULL, NULL);
-
-            /* Make the colunms for the view */
-            renderer = gtk_cell_renderer_pixbuf_new ();
-            g_object_set (renderer, "stock-size", GTK_ICON_SIZE_LARGE_TOOLBAR, NULL);
-            gtk_tree_view_insert_column_with_attributes (self->signatures_tree,
-                                                         -1, "", renderer,
-                                                         "gicon", SIGN_ICON, NULL);
-            gtk_tree_view_insert_column_with_attributes (self->signatures_tree,
-                                                         /* TRANSLATORS: The name and email set on the PGP 
key */
-                                                         -1, _("Name/Email"), gtk_cell_renderer_text_new (),
-                                                         "text", SIGN_NAME, NULL);
-            gtk_tree_view_insert_column_with_attributes (self->signatures_tree,
-                                                         -1, _("Key ID"), gtk_cell_renderer_text_new (),
-                                                         "text", SIGN_KEYID, NULL);
-
-            gtk_tree_view_set_model (self->signatures_tree, GTK_TREE_MODEL (filter));
-
-            g_signal_connect (self->signatures_toggle, "toggled",
-                              G_CALLBACK (on_pgp_trusted_toggled), filter);
-            gtk_toggle_button_set_active (self->signatures_toggle, TRUE);
-        }
-
-        signatures_populate_model (self, SEAHORSE_OBJECT_MODEL (store));
-    }
 }
 
 /* -----------------------------------------------------------------------------
@@ -1529,17 +948,13 @@ static const GActionEntry PRIVATE_KEY_ACTIONS[] = {
     { "change-expires",   on_change_expires   },
     { "export-secret",    on_export_secret    },
     { "export-public",    on_export_public    },
-    { "uids.add",           on_uids_add           },
-    { "uids.delete",        on_uids_delete        },
-    { "uids.make-primary",  on_uids_make_primary  },
-    { "uids.revoke",        on_uids_revoke        },
-    { "uids.sign",          on_uids_sign          },
+    { "uids.add",         on_uids_add         },
+    { "subkeys.add",      on_subkeys_add      },
     { "photos.add",           on_photos_add           },
     { "photos.delete",        on_photos_delete        },
     { "photos.previous",      on_photos_previous      },
     { "photos.next",          on_photos_next          },
     { "photos.make-primary",  on_photos_make_primary  },
-    { "subkeys.add",             on_subkeys_add             },
 };
 
 static const GActionEntry PUBLIC_KEY_ACTIONS[] = {
@@ -1555,7 +970,6 @@ key_notify (GObject *object, GParamSpec *pspec, gpointer user_data)
     SeahorsePgpKeyProperties *self = SEAHORSE_PGP_KEY_PROPERTIES (user_data);
 
     do_owner (self);
-    do_names (self);
     do_trust (self);
     do_details (self);
 }
@@ -1563,7 +977,7 @@ key_notify (GObject *object, GParamSpec *pspec, gpointer user_data)
 static void
 get_common_widgets (SeahorsePgpKeyProperties *self, GtkBuilder *builder)
 {
-    GtkWidget *subkeys_listbox;
+    GtkWidget *uids_listbox, *subkeys_listbox;
 
     self->owner_name_label = GTK_LABEL (gtk_builder_get_object (builder, "owner-name-label"));
     self->owner_email_label = GTK_LABEL (gtk_builder_get_object (builder, "owner-email-label"));
@@ -1583,6 +997,7 @@ get_common_widgets (SeahorsePgpKeyProperties *self, GtkBuilder *builder)
     self->details_strength_label = GTK_LABEL (gtk_builder_get_object (builder, "details-strength-label"));
     self->details_expires_label = GTK_LABEL (gtk_builder_get_object (builder, "details-expires-label"));
     self->details_trust_combobox = GTK_COMBO_BOX (gtk_builder_get_object (builder, 
"details-trust-combobox"));
+    self->uids_container = GTK_WIDGET (gtk_builder_get_object (builder, "uids_container"));
     self->subkeys_container = GTK_WIDGET (gtk_builder_get_object (builder, "subkeys_container"));
 
     g_signal_connect_object (self->photo_event_box, "scroll-event",
@@ -1592,6 +1007,11 @@ get_common_widgets (SeahorsePgpKeyProperties *self, GtkBuilder *builder)
                              G_CALLBACK (on_pgp_details_trust_changed),
                              self, 0);
 
+    uids_listbox = seahorse_pgp_uid_list_box_new (self->key);
+    gtk_widget_show (uids_listbox);
+    gtk_container_add (GTK_CONTAINER (self->uids_container),
+                       uids_listbox);
+
     subkeys_listbox = seahorse_pgp_subkey_list_box_new (self->key);
     gtk_widget_show (subkeys_listbox);
     gtk_container_add (GTK_CONTAINER (self->subkeys_container),
@@ -1623,9 +1043,6 @@ create_public_key_dialog (SeahorsePgpKeyProperties *self)
 
     get_common_widgets (self, builder);
 
-    self->signatures_tree = GTK_TREE_VIEW (gtk_builder_get_object (builder, "signatures-tree"));
-    self->signatures_area = GTK_WIDGET (gtk_builder_get_object (builder, "signatures-area"));
-    self->uids_area = GTK_WIDGET (gtk_builder_get_object (builder, "uids-area"));
     self->trust_page = GTK_WIDGET (gtk_builder_get_object (builder, "trust-page"));
     self->indicate_trust_box = GTK_BOX (gtk_builder_get_object (builder, "indicate_trust_box"));
     self->trust_sign_label = GTK_LABEL (gtk_builder_get_object (builder, "trust-sign-label"));
@@ -1635,18 +1052,12 @@ create_public_key_dialog (SeahorsePgpKeyProperties *self)
     self->revoke_area = GTK_WIDGET (gtk_builder_get_object (builder, "revoke-area"));
     self->trust_marginal_switch = GTK_SWITCH (gtk_builder_get_object (builder, "trust-marginal-switch"));
     self->trust_marginal_label = GTK_LABEL (gtk_builder_get_object (builder, "trust-marginal-label"));
-    self->signatures_toggle = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "signatures-toggle"));
-    self->owner_userid_tree = GTK_TREE_VIEW (gtk_builder_get_object (builder, "owner-userid-tree"));
 
     setup_trust_combobox (self);
     do_owner (self);
     do_details (self);
     do_trust (self);
 
-    g_signal_connect_object (self->signatures_tree, "row-activated",
-                             G_CALLBACK (on_pgp_signature_row_activated),
-                             self, 0);
-
     /* Fill in trust labels with name. */
     user = seahorse_object_get_label (SEAHORSE_OBJECT (self->key));
     user_escaped = g_markup_escape_text (user, -1);
@@ -1678,7 +1089,6 @@ create_private_key_dialog (SeahorsePgpKeyProperties *self)
 {
     g_autoptr(GtkBuilder) builder = NULL;
     GtkWidget *content_area, *content;
-    GtkTreeSelection *selection;
 
     builder = gtk_builder_new_from_resource (PRIVATE_KEY_PROPERTIES_UI);
 
@@ -1696,7 +1106,6 @@ create_private_key_dialog (SeahorsePgpKeyProperties *self)
 
     get_common_widgets (self, builder);
 
-    self->names_tree = GTK_TREE_VIEW (gtk_builder_get_object (builder, "names-tree"));
     self->owner_photo_frame = GTK_WIDGET (gtk_builder_get_object (builder, "owner-photo-frame"));
     self->owner_photo_add_button = GTK_WIDGET (gtk_builder_get_object (builder, "owner-photo-add-button"));
     self->owner_photo_delete_button = GTK_WIDGET (gtk_builder_get_object (builder, 
"owner-photo-delete-button"));
@@ -1704,7 +1113,6 @@ create_private_key_dialog (SeahorsePgpKeyProperties *self)
 
     setup_trust_combobox (self);
     do_owner (self);
-    do_names (self);
     do_details (self);
 
     /* Allow DnD on the photo frame */
@@ -1715,10 +1123,6 @@ create_private_key_dialog (SeahorsePgpKeyProperties *self)
                        GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
                        target_list, G_N_ELEMENTS (target_list),
                        GDK_ACTION_COPY);
-
-    /* Enable and disable buttons as UIDs are selected */
-    selection = gtk_tree_view_get_selection (self->names_tree);
-    g_signal_connect (selection, "changed", G_CALLBACK (update_names), self);
 }
 
 static void
diff --git a/pgp/seahorse-pgp-key.c b/pgp/seahorse-pgp-key.c
index df7920ff..d7d528f8 100644
--- a/pgp/seahorse-pgp-key.c
+++ b/pgp/seahorse-pgp-key.c
@@ -91,16 +91,7 @@ seahorse_pgp_keyid_equal (gconstpointer v1,
         keyid_1 += len_1 - 8;
         keyid_2 += len_2 - 8;
     }
-    return g_str_equal (keyid_1, keyid_2);
-}
-
-static const char*
-calc_short_name (SeahorsePgpKey *self)
-{
-    SeahorsePgpKeyPrivate *priv = seahorse_pgp_key_get_instance_private (self);
-    g_autoptr(SeahorsePgpUid) uid = g_list_model_get_item (priv->uids, 0);
-
-    return uid ? seahorse_pgp_uid_get_name (uid) : NULL;
+    return g_ascii_strcasecmp (keyid_1, keyid_2) == 0;
 }
 
 static char*
@@ -247,7 +238,7 @@ seahorse_pgp_key_realize (SeahorsePgpKey *self)
 
     name = calc_name (self);
     markup = calc_markup (self);
-    nickname = calc_short_name (self);
+    nickname = seahorse_pgp_key_get_primary_name (self);
 
     g_object_get (self, "usage", &usage, NULL);
 
@@ -311,6 +302,18 @@ seahorse_pgp_key_get_uids (SeahorsePgpKey *self)
     return priv->uids;
 }
 
+const char *
+seahorse_pgp_key_get_primary_name (SeahorsePgpKey *self)
+{
+    SeahorsePgpKeyPrivate *priv = seahorse_pgp_key_get_instance_private (self);
+    g_autoptr(SeahorsePgpUid) uid = NULL;
+
+    g_return_val_if_fail (SEAHORSE_PGP_IS_KEY (self), NULL);
+
+    uid = g_list_model_get_item (priv->uids, 0);
+    return uid ? seahorse_pgp_uid_get_name (uid) : NULL;
+}
+
 void
 seahorse_pgp_key_add_uid (SeahorsePgpKey *self,
                           SeahorsePgpUid *uid)
diff --git a/pgp/seahorse-pgp-key.h b/pgp/seahorse-pgp-key.h
index d73e152b..8d01a0ce 100644
--- a/pgp/seahorse-pgp-key.h
+++ b/pgp/seahorse-pgp-key.h
@@ -85,6 +85,8 @@ gboolean          seahorse_pgp_key_has_keyid            (SeahorsePgpKey *self,
 
 const char*       seahorse_pgp_key_calc_identifier      (const char *keyid);
 
+const char *       seahorse_pgp_key_get_primary_name    (SeahorsePgpKey *self);
+
 guint             seahorse_pgp_keyid_hash               (gconstpointer v);
 
 gboolean          seahorse_pgp_keyid_equal              (gconstpointer v1,
diff --git a/pgp/seahorse-pgp-private-key-properties.ui b/pgp/seahorse-pgp-private-key-properties.ui
index 7c2dba76..525641ad 100644
--- a/pgp/seahorse-pgp-private-key-properties.ui
+++ b/pgp/seahorse-pgp-private-key-properties.ui
@@ -372,7 +372,7 @@
               </object>
             </child>
             <child>
-              <object class="GtkBox">
+              <object class="GtkBox" id="uids_container">
                 <property name="visible">True</property>
                 <property name="orientation">vertical</property>
                 <property name="border_width">12</property>
@@ -387,187 +387,6 @@
                     </attributes>
                   </object>
                 </child>
-                <child>
-                  <object class="GtkBox">
-                    <property name="visible">True</property>
-                    <property name="orientation">horizontal</property>
-                    <property name="border_width">6</property>
-                    <property name="margin">6</property>
-                    <child>
-                      <object class="GtkBox">
-                        <property name="visible">True</property>
-                        <property name="orientation">vertical</property>
-                        <child>
-                          <object class="GtkButton" id="names-primary-button">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="border_width">6</property>
-                            <property name="action-name">props.uids.make-primary</property>
-                            <child>
-                              <object class="GtkBox">
-                                <property name="visible">True</property>
-                                <property name="orientation">horizontal</property>
-                                <property name="halign">center</property>
-                                <property name="spacing">2</property>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="stock">gtk-go-up</property>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkLabel">
-                                    <property name="visible">True</property>
-                                    <property name="label" translatable="yes">Primary</property>
-                                    <property name="use_underline">True</property>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkButton" id="names-sign-button">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="border_width">6</property>
-                            <property name="action-name">props.uids.sign</property>
-                            <child>
-                              <object class="GtkBox">
-                                <property name="visible">True</property>
-                                <property name="orientation">horizontal</property>
-                                <property name="halign">center</property>
-                                <property name="spacing">2</property>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="stock">gtk-index</property>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkLabel">
-                                    <property name="visible">True</property>
-                                    <property name="label" translatable="yes">Sign</property>
-                                    <property name="use_underline">True</property>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkButton" id="names-delete-button">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="border_width">6</property>
-                            <property name="action-name">props.uids.delete</property>
-                            <child>
-                              <object class="GtkBox">
-                                <property name="visible">True</property>
-                                <property name="orientation">horizontal</property>
-                                <property name="halign">center</property>
-                                <property name="spacing">2</property>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="stock">gtk-delete</property>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkLabel">
-                                    <property name="visible">True</property>
-                                    <property name="label" translatable="yes">Delete</property>
-                                    <property name="use_underline">True</property>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkButton" id="names-add-button">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="border_width">6</property>
-                            <property name="action-name">props.uids.add</property>
-                            <child>
-                              <object class="GtkBox">
-                                <property name="visible">True</property>
-                                <property name="orientation">horizontal</property>
-                                <property name="halign">center</property>
-                                <property name="spacing">2</property>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="stock">gtk-add</property>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkLabel">
-                                    <property name="visible">True</property>
-                                    <property name="label" translatable="yes" comments="Add another name to 
the PGP key.">_Add Name</property>
-                                    <property name="use_underline">True</property>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkButton" id="names-revoke-button">
-                            <property name="visible">False</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="border_width">6</property>
-                            <property name="action-name">props.uids.revoke</property>
-                            <child>
-                              <object class="GtkBox">
-                                <property name="visible">True</property>
-                                <property name="halign">center</property>
-                                <property name="orientation">horizontal</property>
-                                <property name="spacing">2</property>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="stock">gtk-cancel</property>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkLabel">
-                                    <property name="visible">True</property>
-                                    <property name="label" translatable="yes">Revoke</property>
-                                    <property name="use_underline">True</property>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkScrolledWindow">
-                        <property name="visible">True</property>
-                        <property name="hexpand">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="hscrollbar_policy">automatic</property>
-                        <property name="vscrollbar_policy">automatic</property>
-                        <property name="shadow_type">in</property>
-                        <child>
-                          <object class="GtkTreeView" id="names-tree">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="margin">6</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
               </object>
             </child>
             <child type="tab">
diff --git a/pgp/seahorse-pgp-public-key-properties.ui b/pgp/seahorse-pgp-public-key-properties.ui
index 8c0bdbb5..68420723 100644
--- a/pgp/seahorse-pgp-public-key-properties.ui
+++ b/pgp/seahorse-pgp-public-key-properties.ui
@@ -34,6 +34,7 @@
                 <property name="orientation">vertical</property>
                 <property name="can_focus">False</property>
                 <property name="spacing">12</property>
+                <property name="margin">12</property>
                 <child>
                   <object class="GtkBox" id="revoked-area">
                     <property name="can_focus">False</property>
@@ -297,47 +298,15 @@
                             </child>
                           </object>
                         </child>
-                        <child>
-                          <object class="GtkExpander" id="uids-area">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <child>
-                              <object class="GtkScrolledWindow" id="scrolledwindow1">
-                                <property name="height_request">130</property>
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="hscrollbar_policy">never</property>
-                                <property name="shadow_type">in</property>
-                                <property name="margin-top">3</property>
-                                <child>
-                                  <object class="GtkTreeView" id="owner-userid-tree">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="headers_visible">False</property>
-                                    <child internal-child="selection">
-                                      <object class="GtkTreeSelection" id="treeview-selection1"/>
-                                    </child>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                            <child type="label">
-                              <object class="GtkLabel">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="label" translatable="yes" comments="Names set on the PGP 
key.">_Other Names:</property>
-                                <property name="use_underline">True</property>
-                                <attributes>
-                                  <attribute name="weight" value="bold"/>
-                                </attributes>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
                       </object>
                     </child>
                   </object>
                 </child>
+                <child>
+                  <object class="GtkBox" id="uids_container">
+                    <property name="visible">True</property>
+                  </object>
+                </child>
               </object>
             </child>
             <child type="tab">
@@ -515,52 +484,6 @@
                     </child>
                   </object>
                 </child>
-                <child>
-                  <object class="GtkBox" id="signatures-area">
-                    <property name="can_focus">False</property>
-                    <property name="orientation">vertical</property>
-                    <property name="spacing">6</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_People who have signed this 
key:</property>
-                        <property name="use_underline">True</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkScrolledWindow" id="scrolledwindow4">
-                        <property name="visible">True</property>
-                        <property name="shadow_type">in</property>
-                        <child>
-                          <object class="GtkTreeView" id="signatures-tree">
-                            <property name="visible">True</property>
-                            <property name="vexpand">True</property>
-                            <property name="can_focus">True</property>
-                            <child internal-child="selection">
-                              <object class="GtkTreeSelection" id="treeview-selection2"/>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkCheckButton" id="signatures-toggle">
-                        <property name="label" translatable="yes">_Only display the signatures of people I 
trust</property>
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">False</property>
-                        <property name="use_underline">True</property>
-                        <property name="xalign">0.5</property>
-                        <property name="draw_indicator">True</property>
-                      </object>
-                    </child>
-                  </object>
-                </child>
               </object>
             </child>
             <child type="tab">
diff --git a/pgp/seahorse-pgp-uid-list-box-row.ui b/pgp/seahorse-pgp-uid-list-box-row.ui
new file mode 100644
index 00000000..3b9c313e
--- /dev/null
+++ b/pgp/seahorse-pgp-uid-list-box-row.ui
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="3.24"/>
+  <menu id="actions_menu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Make _primary</attribute>
+        <attribute name="action">uid.make-primary</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Sign</attribute>
+        <attribute name="action">uid.sign</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Delete</attribute>
+        <attribute name="action">uid.delete</attribute>
+      </item>
+    </section>
+  </menu>
+  <template class="SeahorsePgpUidListBoxRow" parent="HdyExpanderRow">
+    <property name="visible">True</property>
+    <property name="focus-on-click">False</property>
+    <child type="action">
+      <object class="GtkMenuButton" id="actions_button">
+        <property name="visible">True</property>
+        <property name="valign">center</property>
+        <property name="halign">end</property>
+        <property name="margin-start">6</property>
+        <property name="menu-model">actions_menu</property>
+        <child>
+          <object class="GtkImage">
+            <property name="visible">True</property>
+            <property name="icon-name">open-menu-symbolic</property>
+          </object>
+        </child>
+        <style>
+          <class name="flat"/>
+        </style>
+      </object>
+    </child>
+    <child type="prefix">
+      <object class="GtkImage" id="avatar">
+        <property name="visible">True</property>
+        <property name="icon-name">avatar-default-symbolic</property>
+        <property name="icon-size">5</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="spacing">12</property>
+        <property name="margin">18</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <property name="orientation">horizontal</property>
+            <child>
+              <object class="GtkLabel" id="signed_by_label">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="valign">center</property>
+                <property name="label" translatable="yes">Signatures</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+            </child>
+            <child>
+              <object class="GtkSwitch" id="trusted_switch">
+                <property name="visible">True</property>
+                <property name="action-name">uid.only-trusted</property>
+                <property name="tooltip-text" translatable="yes">Only display the signatures of people I 
trust</property>
+              </object>
+              <packing>
+                <property name="pack_type">end</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="xalign">1</property>
+                <property name="label" translatable="yes">Only trusted</property>
+                <property name="tooltip-text" translatable="yes">Only display the signatures of people I 
trust</property>
+                <property name="mnemonic-widget">trusted_switch</property>
+              </object>
+              <packing>
+                <property name="pack_type">end</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkListBox" id="signatures_list">
+            <property name="visible">True</property>
+            <property name="selection-mode">none</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible" bind-source="signatures_list" bind-property="visible" 
bind-flags="bidirectional|sync-create|invert-boolean" />
+            <property name="label" translatable="yes">No signatures available</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/pgp/seahorse-pgp-uid-list-box.c b/pgp/seahorse-pgp-uid-list-box.c
new file mode 100644
index 00000000..b379078f
--- /dev/null
+++ b/pgp/seahorse-pgp-uid-list-box.c
@@ -0,0 +1,544 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2021 Niels De Graef
+ *
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "pgp/seahorse-pgp-uid-list-box.h"
+
+#include "seahorse-gpgme-key-op.h"
+#include "pgp/seahorse-pgp-backend.h"
+#include "pgp/seahorse-pgp-signature.h"
+#include "pgp/seahorse-gpgme-uid.h"
+#include "pgp/seahorse-gpgme-add-uid.h"
+#include "pgp/seahorse-gpgme-sign-dialog.h"
+#include "pgp/seahorse-unknown.h"
+#include "seahorse-gpgme-dialogs.h"
+
+#include <glib/gi18n.h>
+
+/* ListBox object */
+
+struct _SeahorsePgpUidListBox {
+    GtkListBox parent_instance;
+
+    SeahorsePgpKey *key;
+};
+
+enum {
+    PROP_0,
+    PROP_KEY,
+    N_PROPS
+};
+static GParamSpec *obj_props[N_PROPS] = { NULL, };
+
+G_DEFINE_TYPE (SeahorsePgpUidListBox, seahorse_pgp_uid_list_box, GTK_TYPE_LIST_BOX);
+
+static GtkWidget *
+create_row_for_uid (void *item, void *user_data)
+{
+    g_return_val_if_fail (SEAHORSE_PGP_IS_UID (item), NULL);
+
+    return g_object_new (SEAHORSE_PGP_TYPE_UID_LIST_BOX_ROW,
+                         "uid", SEAHORSE_PGP_UID (item),
+                         NULL);
+}
+
+static void
+seahorse_pgp_uid_list_box_constructed (GObject *object)
+{
+    SeahorsePgpUidListBox *self = SEAHORSE_PGP_UID_LIST_BOX (object);
+
+    G_OBJECT_CLASS (seahorse_pgp_uid_list_box_parent_class)->constructed (object);
+
+    gtk_list_box_bind_model (GTK_LIST_BOX (self),
+                             seahorse_pgp_key_get_uids (self->key),
+                             create_row_for_uid,
+                             self,
+                             NULL);
+}
+
+static void
+seahorse_pgp_uid_list_box_init (SeahorsePgpUidListBox *self)
+{
+    GtkStyleContext *style_context;
+
+    style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+    gtk_style_context_add_class (style_context, "content");
+}
+
+static void
+seahorse_pgp_uid_list_box_get_property (GObject      *object,
+                                        unsigned int  prop_id,
+                                        GValue       *value,
+                                        GParamSpec   *pspec)
+{
+    SeahorsePgpUidListBox *self = SEAHORSE_PGP_UID_LIST_BOX (object);
+
+    switch (prop_id) {
+    case PROP_KEY:
+        g_value_set_object (value, self->key);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+seahorse_pgp_uid_list_box_set_property (GObject      *object,
+                                        unsigned int  prop_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+    SeahorsePgpUidListBox *self = SEAHORSE_PGP_UID_LIST_BOX (object);
+
+    switch (prop_id) {
+    case PROP_KEY:
+        self->key = g_value_get_object (value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+seahorse_pgp_uid_list_box_class_init (SeahorsePgpUidListBoxClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->set_property = seahorse_pgp_uid_list_box_set_property;
+    gobject_class->get_property = seahorse_pgp_uid_list_box_get_property;
+    gobject_class->constructed = seahorse_pgp_uid_list_box_constructed;
+
+    obj_props[PROP_KEY] =
+        g_param_spec_object ("key", "PGP key", "The key to list the UIDs for",
+                             SEAHORSE_PGP_TYPE_KEY,
+                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
+
+    g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
+}
+
+GtkWidget *
+seahorse_pgp_uid_list_box_new (SeahorsePgpKey *key)
+{
+    g_return_val_if_fail (SEAHORSE_PGP_IS_KEY (key), NULL);
+
+    /* XXX We should store the key and connect to ::notify */
+    return g_object_new (SEAHORSE_PGP_TYPE_UID_LIST_BOX,
+                         "key", key,
+                         "selection-mode", GTK_SELECTION_NONE,
+                         NULL);
+}
+
+/* Row object */
+
+struct _SeahorsePgpUidListBoxRow {
+    HdyExpanderRow parent_instance;
+
+    SeahorsePgpUid *uid;
+
+    GSimpleActionGroup *actions;
+    GtkWidget *actions_button;
+
+    GtkWidget *signatures_list;
+    /* the key ids from the signature list that were discovered */
+    GList *discovered_keys;
+};
+
+enum {
+    ROW_PROP_0,
+    ROW_PROP_UID,
+    ROW_N_PROPS
+};
+
+G_DEFINE_TYPE (SeahorsePgpUidListBoxRow, seahorse_pgp_uid_list_box_row, HDY_TYPE_EXPANDER_ROW);
+
+static void
+update_actions (SeahorsePgpUidListBoxRow *row)
+{
+    SeahorsePgpKey *parent;
+    GListModel *uids;
+    g_autoptr(SeahorsePgpUid) primary_uid = NULL;
+    gboolean is_primary;
+    GAction *action;
+
+    /* Figure out if we're the primary UID and update the make-primary action */
+    parent = seahorse_pgp_uid_get_parent (row->uid);
+    uids = seahorse_pgp_key_get_uids (parent);
+    primary_uid = g_list_model_get_item (uids, 0);
+    is_primary = (primary_uid == row->uid);
+
+    action = g_action_map_lookup_action (G_ACTION_MAP (row->actions),
+                                         "make-primary");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !is_primary);
+
+    /* Don't allow deleting the last UID */
+    action = g_action_map_lookup_action (G_ACTION_MAP (row->actions), "delete");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                                 g_list_model_get_n_items (uids) > 1);
+}
+
+static void
+on_only_trusted_changed (GSimpleAction *action,
+                         GVariant      *new_state,
+                         void          *user_data)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (user_data);
+    GListModel *signatures;
+    unsigned int n_signatures = 0;
+    unsigned int n_shown = 0;
+    gboolean only_trusted;
+
+    only_trusted = g_variant_get_boolean (new_state);
+
+    signatures = seahorse_pgp_uid_get_signatures (row->uid);
+    n_signatures = g_list_model_get_n_items (signatures);
+
+    /* For each signature, check if we know about the key*/
+    for (unsigned int i = 0; i < n_signatures; i++) {
+        g_autoptr(SeahorsePgpSignature) sig = NULL;
+        GtkListBoxRow *sig_row;
+        SeahorseObject *signer;
+        gboolean trusted = FALSE;
+        gboolean should_show;
+
+        sig = g_list_model_get_item (signatures, i);
+        sig_row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (row->signatures_list), i);
+
+        signer = g_object_get_data (G_OBJECT (sig_row), "signer");
+        if (signer) {
+            if (seahorse_object_get_usage (signer) == SEAHORSE_USAGE_PRIVATE_KEY)
+                trusted = TRUE;
+            else if (seahorse_object_get_flags (signer) & SEAHORSE_FLAG_TRUSTED)
+                trusted = TRUE;
+        }
+
+        should_show = (trusted || !only_trusted);
+        gtk_widget_set_visible (sig_row, should_show);
+        if (should_show)
+            n_shown++;
+    }
+
+    g_simple_action_set_state (G_SIMPLE_ACTION (action), new_state);
+
+    /* Hide the signature list if there are no rows visible */
+    gtk_widget_set_visible (row->signatures_list, n_shown > 0);
+}
+
+static void
+on_uid_delete (GSimpleAction *action, GVariant *param, void *user_data)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (user_data);
+    GtkWidget *window;
+    g_autofree char *message = NULL;
+    gpgme_error_t gerr;
+
+    g_return_if_fail (SEAHORSE_GPGME_IS_UID (row->uid));
+
+    window = gtk_widget_get_toplevel (GTK_WIDGET (row));
+    message = g_strdup_printf (_("Are you sure you want to permanently delete the “%s” user ID?"),
+                               seahorse_object_get_label (SEAHORSE_OBJECT (row->uid)));
+
+    if (!seahorse_delete_dialog_prompt (GTK_WINDOW (window), message))
+        return;
+
+    gerr = seahorse_gpgme_key_op_del_uid (SEAHORSE_GPGME_UID (row->uid));
+    if (!GPG_IS_OK (gerr))
+        seahorse_gpgme_handle_error (gerr, _("Couldn’t delete user ID"));
+}
+
+static void
+on_uid_make_primary_cb (GObject *source, GAsyncResult *res, void *user_data)
+{
+    GtkWidget *toplevel = GTK_WIDGET (user_data);
+    SeahorseGpgmeUid *uid = SEAHORSE_GPGME_UID (source);
+    g_autoptr(GError) error = NULL;
+
+    if (!seahorse_gpgme_key_op_make_primary_finish (uid, res, &error)) {
+        seahorse_util_show_error (toplevel,
+                                  _("Couldn’t change primary user ID"),
+                                  error->message);
+    }
+}
+
+static void
+on_uid_make_primary (GSimpleAction *action, GVariant *param, void *user_data)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (user_data);
+    GtkWidget *toplevel;
+
+    g_return_if_fail (SEAHORSE_GPGME_IS_UID (row->uid));
+
+    /* Don't pass the row itself as user_data, as that might be destroyed as
+     * part of the GListModel shuffle */
+    toplevel = gtk_widget_get_toplevel (GTK_WIDGET (row));
+
+    seahorse_gpgme_key_op_make_primary_async (SEAHORSE_GPGME_UID (row->uid),
+                                              NULL,
+                                              on_uid_make_primary_cb,
+                                              toplevel);
+}
+
+static void
+on_uid_sign (GSimpleAction *action, GVariant *param, void *user_data)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (user_data);
+    SeahorseGpgmeSignDialog *dialog;
+
+    g_return_if_fail (SEAHORSE_GPGME_IS_UID (row->uid));
+
+    dialog = seahorse_gpgme_sign_dialog_new (SEAHORSE_OBJECT (row->uid));
+    gtk_dialog_run (GTK_DIALOG (dialog));
+    gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static const GActionEntry UID_ACTION_ENTRIES[] = {
+    { "only-trusted", seahorse_util_toggle_action, NULL, "false", on_only_trusted_changed },
+    { "delete", on_uid_delete },
+    { "make-primary", on_uid_make_primary },
+    { "sign", on_uid_sign },
+    /* { "revoke",        on_uid_revoke        }, TODO */
+};
+
+static GtkWidget *
+create_row_for_signature (void *item, void *user_data)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (user_data);
+    SeahorsePgpSignature *signature = SEAHORSE_PGP_SIGNATURE (item);
+    GtkWidget *sig_row;
+    GtkWidget *box;
+    const char *sig_keyid;
+    GtkWidget *keyid_label;
+    g_autofree char *signer_name = NULL;
+    GtkWidget *signer_label;
+
+    sig_row = gtk_list_box_row_new ();
+    box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+    gtk_widget_show (box);
+    gtk_container_add (GTK_CONTAINER (sig_row), box);
+
+    sig_keyid = seahorse_pgp_signature_get_keyid (signature);
+    keyid_label = gtk_label_new (sig_keyid);
+    gtk_widget_show (keyid_label);
+    gtk_box_pack_start (GTK_BOX (box), keyid_label, FALSE, FALSE, 0);
+
+    for (GList *l = row->discovered_keys; l; l = g_list_next (l)) {
+        if (SEAHORSE_PGP_IS_KEY (l->data)) {
+            SeahorsePgpKey *key = SEAHORSE_PGP_KEY (l->data);
+            const char *keyid = seahorse_pgp_key_get_keyid (key);
+
+            if (seahorse_pgp_keyid_equal (sig_keyid, keyid)) {
+                const char *name = seahorse_pgp_key_get_primary_name (key);
+                signer_name = g_strdup_printf ("(%s)", name);
+
+                g_object_set_data (G_OBJECT (sig_row), "signer", l->data);
+                break;
+            }
+        } else if (SEAHORSE_IS_UNKNOWN (l->data)) {
+            g_autofree char *keyid = NULL;
+            g_autofree char *label = NULL;
+
+            g_object_get (l->data, "identifier", &keyid, "label", &label, NULL);
+            if (seahorse_pgp_keyid_equal (sig_keyid, keyid)) {
+                signer_name = g_strdup_printf ("(%s)", label);
+                g_object_set_data (G_OBJECT (sig_row), "signer", l->data);
+                break;
+            }
+        }
+    }
+
+    if (!signer_name) {
+        /* Translators: (Unknown) signature name */
+        signer_name = g_strdup (_("(Unknown)"));
+    }
+
+    signer_label = gtk_label_new (signer_name);
+    gtk_widget_show (signer_label);
+    gtk_box_pack_start (GTK_BOX (box), signer_label, FALSE, FALSE, 0);
+
+    return sig_row;
+}
+
+static void
+on_row_expanded (GObject *object,
+                 GParamSpec *pspec,
+                 void *user_data)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (object);
+    gboolean expanded;
+    GListModel *signatures;
+    unsigned int n_signatures;
+    g_autofree char *signed_by_str = NULL;
+    g_autoptr(GCancellable) cancellable = NULL;
+    g_autoptr(GPtrArray) keyids = NULL;
+
+    /* Lazily discover keys by only loading when user actually expands the row
+     * (showing the signatures) and not reloading if already done earlier */
+    expanded = hdy_expander_row_get_expanded (HDY_EXPANDER_ROW (row));
+    if (!expanded || row->discovered_keys)
+        return;
+
+    signatures = seahorse_pgp_uid_get_signatures (row->uid);
+    n_signatures = g_list_model_get_n_items (signatures);
+
+    /* Discover the keys, so might be able to show their owner */
+    keyids = g_ptr_array_new ();
+    for (unsigned i = 0; i < n_signatures; i++) {
+        g_autoptr(SeahorsePgpSignature) sig = NULL;
+
+        sig = g_list_model_get_item (signatures, i);
+        g_ptr_array_add (keyids, (void *) seahorse_pgp_signature_get_keyid (sig));
+    }
+    g_ptr_array_add (keyids, NULL);
+
+    /* Pass it to the PGP backend for resolution/download, cancellable ties
+     * search scope together */
+    cancellable = g_cancellable_new ();
+    row->discovered_keys =
+        seahorse_pgp_backend_discover_keys (NULL,
+                                            (const char **) keyids->pdata,
+                                            cancellable);
+
+    /* Now build the list */
+    gtk_list_box_bind_model (GTK_LIST_BOX (row->signatures_list),
+                             signatures,
+                             create_row_for_signature,
+                             row,
+                             NULL);
+
+    gtk_widget_set_visible (row->signatures_list, n_signatures > 0);
+}
+
+static void
+seahorse_pgp_uid_list_box_row_constructed (GObject *object)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (object);
+    SeahorsePgpKey *parent_key;
+    gboolean is_editable;
+    g_autoptr(GString) title = NULL;
+    const char *comment, *email;
+
+    G_OBJECT_CLASS (seahorse_pgp_uid_list_box_row_parent_class)->constructed (object);
+
+    parent_key = seahorse_pgp_uid_get_parent (row->uid);
+    is_editable = (seahorse_object_get_usage (SEAHORSE_OBJECT (parent_key)) == SEAHORSE_USAGE_PRIVATE_KEY);
+
+    /* Put "name (comment)" as title */
+    title = g_string_new (seahorse_pgp_uid_get_name (row->uid));
+    comment = seahorse_pgp_uid_get_comment (row->uid);
+    if (comment && *comment)
+        g_string_append_printf (title, " (%s)", comment);
+    hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), title->str);
+
+    /* Make a linkified version the email as subtitle */
+    email = seahorse_pgp_uid_get_email (row->uid);
+    if (email && *email)
+        hdy_expander_row_set_subtitle (HDY_EXPANDER_ROW (row), email);
+
+    /* Actions */
+    gtk_widget_set_visible (row->actions_button, is_editable);
+
+    if (is_editable) {
+        update_actions (row);
+    }
+
+    /* Signatures we do later, since we try to lazy-load them */
+}
+
+static void
+seahorse_pgp_uid_list_box_row_init (SeahorsePgpUidListBoxRow *row)
+{
+    gtk_widget_init_template (GTK_WIDGET (row));
+
+    row->actions = g_simple_action_group_new ();
+    g_action_map_add_action_entries (G_ACTION_MAP (row->actions),
+                                     UID_ACTION_ENTRIES,
+                                     G_N_ELEMENTS (UID_ACTION_ENTRIES),
+                                     row);
+
+    gtk_widget_insert_action_group (GTK_WIDGET (row),
+                                    "uid",
+                                    G_ACTION_GROUP (row->actions));
+
+    g_signal_connect (row, "notify::expanded", G_CALLBACK (on_row_expanded), NULL);
+}
+
+static void
+seahorse_pgp_uid_list_box_row_finalize (GObject *object)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (object);
+
+    g_clear_object (&row->uid);
+    g_clear_pointer (&row->discovered_keys, g_list_free);
+
+    G_OBJECT_CLASS (seahorse_pgp_uid_list_box_row_parent_class)->finalize (object);
+}
+
+static void
+seahorse_pgp_uid_list_box_row_get_property (GObject      *object,
+                                            unsigned int  prop_id,
+                                            GValue       *value,
+                                            GParamSpec   *pspec)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (object);
+
+    switch (prop_id) {
+    case ROW_PROP_UID:
+        g_value_set_object (value, row->uid);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+seahorse_pgp_uid_list_box_row_set_property (GObject      *object,
+                                            unsigned int  prop_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec)
+{
+    SeahorsePgpUidListBoxRow *row = SEAHORSE_PGP_UID_LIST_BOX_ROW (object);
+
+    switch (prop_id) {
+    case ROW_PROP_UID:
+        row->uid = g_value_dup_object (value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+seahorse_pgp_uid_list_box_row_class_init (SeahorsePgpUidListBoxRowClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+    gobject_class->constructed  = seahorse_pgp_uid_list_box_row_constructed;
+    gobject_class->finalize     = seahorse_pgp_uid_list_box_row_finalize;
+    gobject_class->set_property = seahorse_pgp_uid_list_box_row_set_property;
+    gobject_class->get_property = seahorse_pgp_uid_list_box_row_get_property;
+
+    g_object_class_install_property (gobject_class, ROW_PROP_UID,
+        g_param_spec_object ("uid", "PGP uid", "The UID this row is showing",
+                             SEAHORSE_PGP_TYPE_UID,
+                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+
+    gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Seahorse/seahorse-pgp-uid-list-box-row.ui");
+
+    gtk_widget_class_bind_template_child (widget_class, SeahorsePgpUidListBoxRow, actions_button);
+    gtk_widget_class_bind_template_child (widget_class, SeahorsePgpUidListBoxRow, signatures_list);
+}
diff --git a/pgp/seahorse-pgp-uid-list-box.h b/pgp/seahorse-pgp-uid-list-box.h
new file mode 100644
index 00000000..9079ad4b
--- /dev/null
+++ b/pgp/seahorse-pgp-uid-list-box.h
@@ -0,0 +1,36 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2021 Niels De Graef
+ *
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "seahorse-pgp-uid.h"
+
+#define SEAHORSE_PGP_TYPE_UID_LIST_BOX (seahorse_pgp_uid_list_box_get_type ())
+G_DECLARE_FINAL_TYPE (SeahorsePgpUidListBox, seahorse_pgp_uid_list_box,
+                      SEAHORSE_PGP, UID_LIST_BOX,
+                      GtkListBox)
+
+#define SEAHORSE_PGP_TYPE_UID_LIST_BOX_ROW (seahorse_pgp_uid_list_box_row_get_type ())
+G_DECLARE_FINAL_TYPE (SeahorsePgpUidListBoxRow, seahorse_pgp_uid_list_box_row,
+                      SEAHORSE_PGP, UID_LIST_BOX_ROW,
+                      HdyExpanderRow)
+
+GtkWidget * seahorse_pgp_uid_list_box_new (SeahorsePgpKey *key);
diff --git a/pgp/seahorse-pgp-uid.c b/pgp/seahorse-pgp-uid.c
index ab4b8718..8f56087d 100644
--- a/pgp/seahorse-pgp-uid.c
+++ b/pgp/seahorse-pgp-uid.c
@@ -341,11 +341,13 @@ seahorse_pgp_uid_class_init (SeahorsePgpUidClass *klass)
 
 SeahorsePgpUid *
 seahorse_pgp_uid_new (SeahorsePgpKey *parent,
-                      const gchar *uid_string)
+                      const char     *uid_string)
 {
-    g_autofree gchar *name = NULL;
-    g_autofree gchar *email = NULL;
-    g_autofree gchar *comment = NULL;
+    g_autofree char *name = NULL;
+    g_autofree char *email = NULL;
+    g_autofree char *comment = NULL;
+
+       g_return_val_if_fail (SEAHORSE_PGP_IS_KEY (parent), NULL);
 
     if (uid_string)
         parse_user_id (uid_string, &name, &email, &comment);
@@ -381,10 +383,30 @@ seahorse_pgp_uid_add_signature (SeahorsePgpUid       *self,
                                 SeahorsePgpSignature *signature)
 {
     SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self);
+    const char *keyid;
+    const char *parent_keyid;
 
     g_return_if_fail (SEAHORSE_PGP_IS_UID (self));
     g_return_if_fail (SEAHORSE_PGP_IS_SIGNATURE (signature));
 
+    keyid = seahorse_pgp_signature_get_keyid (signature);
+
+    /* Don't add signature of the parent key */
+       parent_keyid = seahorse_pgp_key_get_keyid (priv->parent);
+       if (seahorse_pgp_keyid_equal (keyid, parent_keyid))
+               return;
+
+    /* Don't allow duplicates */
+    for (unsigned i = 0; i < g_list_model_get_n_items (priv->signatures); i++) {
+        g_autoptr(SeahorsePgpSignature) sig = g_list_model_get_item (priv->signatures, i);
+        const char *sig_keyid;
+
+        sig = g_list_model_get_item (priv->signatures, i);
+        sig_keyid = seahorse_pgp_signature_get_keyid (sig);
+        if (seahorse_pgp_keyid_equal (keyid, sig_keyid))
+            return;
+    }
+
     g_list_store_append (G_LIST_STORE (priv->signatures), signature);
 }
 


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