[seahorse/pgp/uids-listbox] WIP
- From: Niels De Graef <nielsdg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [seahorse/pgp/uids-listbox] WIP
- Date: Sun, 15 Aug 2021 08:22:25 +0000 (UTC)
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]