/* gpa-key-details.c  -  A widget to show key details.
 * Copyright (C) 2000, 2001 G-N-U GmbH.
 * Copyright (C) 2005, 2008, 2009 g10 Code GmbH.
 *
 * This file is part of GPA.
 *
 * GPA 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 3 of the License, or
 * (at your option) any later version.
 *
 * GPA 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/>.
 */

/*
   This widget is used to display details of a key.

 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "gpa.h"
#include "convert.h"
#include "keytable.h"
#include "siglist.h"
#include "certchain.h"
#include "gpasubkeylist.h"
#include "gpa-uid-list.h"
#include "gpa-tofu-list.h"
#include "gpa-key-details.h"
#include "gtktools.h"


/* Object's class definition.  */
struct _GpaKeyDetailsClass
{
  GtkNotebookClass parent_class;
};


/* Object definition.  */
struct _GpaKeyDetails
{
  GtkNotebook parent_instance;

  /* Widgets in the details page.  */
  GtkWidget *details_num_label;
  GtkWidget *details_table;
  GtkWidget *detail_public_private;
  GtkWidget *detail_capabilities;
  GtkWidget *detail_name;
  GtkWidget *detail_fingerprint;
  GtkWidget *detail_expiry;
  GtkWidget *detail_key_id;
  GtkWidget *detail_owner_trust;
  GtkWidget *detail_key_trust;
  GtkWidget *detail_key_type;
  GtkWidget *detail_creation;
  GtkWidget *detail_last_update;

  /* The widgets in the user ID page.  */
  GtkWidget *uid_page;
  GtkWidget *uid_list;

  /* The widgets in the signatures page.  */
  GtkWidget *signatures_page;
  GtkWidget *signatures_list;
  GtkWidget *signatures_uids;
  GtkWidget *certchain_list;

  /* The widgets in the subkeys page.  */
  GtkWidget *subkeys_page;
  GtkWidget *subkeys_list;

  /* The widgets in the TOFU page.  */
  GtkWidget *tofu_page;
  GtkWidget *tofu_list;

  /* The key currently shown or NULL.  */
  gpgme_key_t current_key;

};


/* The parent class.  */
static GObjectClass *parent_class;



/* Local prototypes */
static void gpa_key_details_finalize (GObject *object);



/************************************************************
 *******************   Implementation   *********************
 ************************************************************/

/* Callback for the "changed" signal to update the popdown menu on the
   signatures page of the notebook.  */
static void
signatures_uid_changed (GtkComboBox *combo, gpointer user_data)
{
  GpaKeyDetails *kdt = user_data;

  if (kdt->signatures_list)
    {
      /* Note that we need to subtract one, as the first entry (with
         index 0 means) "all user names".  */
      gpa_siglist_set_signatures
        (kdt->signatures_list, kdt->current_key,
         gtk_combo_box_get_active (GTK_COMBO_BOX (combo)) - 1);
    }
}


/* Fill the details page with the properties of the public key.  */
static void
details_page_fill_key (GpaKeyDetails *kdt, gpgme_key_t key)
{
  gpgme_user_id_t uid;
  gpgme_key_t seckey;
  char *text;

  seckey = gpa_keytable_lookup_key (gpa_keytable_get_secret_instance(),
                                    key->subkeys->fpr);
  if (seckey)
    {
      if (seckey->subkeys && seckey->subkeys->is_cardkey)
        gtk_label_set_text (GTK_LABEL (kdt->detail_public_private),
                            _("The key has both a smartcard based private part"
                              " and a public part"));
      else
        gtk_label_set_text (GTK_LABEL (kdt->detail_public_private),
                            _("The key has both a private and a public part"));
    }
  else
    gtk_label_set_text (GTK_LABEL (kdt->detail_public_private),
			_("The key has only a public part"));

  gtk_label_set_text (GTK_LABEL (kdt->detail_capabilities),
		      gpa_get_key_capabilities_text (key));

  /* One user ID per line.  */
  text = gpa_gpgme_key_get_userid (key->uids);
  if (key->uids)
    {
      for (uid = key->uids->next; uid; uid = uid->next)
        {
          gchar *uid_string = gpa_gpgme_key_get_userid (uid);
          gchar *tmp = text;

          text = g_strconcat (text, "\n", uid_string, NULL);
          g_free (tmp);
          g_free (uid_string);
        }
    }
  gtk_label_set_text (GTK_LABEL (kdt->detail_name), text);
  g_free (text);

  text = gpa_gpgme_key_format_fingerprint (key->subkeys->fpr);
  gtk_label_set_text (GTK_LABEL (kdt->detail_fingerprint), text);
  g_free (text);

  text = (gchar*) gpa_gpgme_key_get_short_keyid (key);
  gtk_label_set_text (GTK_LABEL (kdt->detail_key_id), text);

  text = gpa_expiry_date_string (key->subkeys->expires);
  gtk_label_set_text (GTK_LABEL (kdt->detail_expiry), text);
  g_free (text);

  gtk_label_set_text (GTK_LABEL (kdt->detail_key_trust),
                      gpa_key_validity_string (key));

  text = gpgme_pubkey_algo_string (key->subkeys);
  gtk_label_set_text (GTK_LABEL (kdt->detail_key_type), text? text : "?");
  gpgme_free (text);

  text = g_strdup_printf (_("%s %u bits"),
                          gpgme_pubkey_algo_name (key->subkeys->pubkey_algo)?
                          gpgme_pubkey_algo_name (key->subkeys->pubkey_algo):
                          (key->subkeys->curve? "ECC" : "?"),
                          key->subkeys->length);
  if (key->subkeys->curve)
    {
      char *text2;
      text2 = g_strdup_printf ("%s, %s", text, key->subkeys->curve);
      g_free (text);
      text = text2;
    }
  gpa_add_tooltip (kdt->detail_key_type, text);
  g_free (text);

  gtk_label_set_text (GTK_LABEL (kdt->detail_owner_trust),
                      gpa_key_ownertrust_string (key));

  text = gpa_creation_date_string (key->subkeys->timestamp);
  gtk_label_set_text (GTK_LABEL (kdt->detail_creation), text);
  g_free (text);

#if GPGME_VERSION_NUMBER >= 0x010100  /* GPGME >= 1.10.0 */
  text = gpa_update_origin_string (key->last_update, key->origin);
  gtk_label_set_text (GTK_LABEL (kdt->detail_last_update), text);
  g_free (text);
#endif

  gtk_widget_hide (kdt->details_num_label);
  gtk_widget_show_all (kdt->details_table);
  gtk_widget_set_no_show_all (kdt->details_num_label, TRUE);
  gtk_widget_set_no_show_all (kdt->details_table, FALSE);
}


/* Fill the details page with the number of selected keys.  */
static void
details_page_fill_num_keys (GpaKeyDetails *kdt, gint num_key)
{
  if (!num_key)
    gtk_label_set_text (GTK_LABEL (kdt->details_num_label),
			_("No keys selected"));
  else
    {
      char *text = g_strdup_printf (ngettext("%d key selected",
                                             "%d keys selected",
                                             num_key), num_key);

      gtk_label_set_text (GTK_LABEL (kdt->details_num_label), text);
      g_free (text);
    }

  gtk_widget_show_all (kdt->details_num_label);
  gtk_widget_hide (kdt->details_table);
  gtk_widget_set_no_show_all (kdt->details_num_label, FALSE);
  gtk_widget_set_no_show_all (kdt->details_table, TRUE);

  gtk_notebook_set_current_page (GTK_NOTEBOOK (kdt), 0);
}




/* Add a single row to the details table.  */
static GtkWidget *
add_details_row (GtkWidget *grid, gint row, gchar *text,
                 gboolean selectable)
{
  GtkWidget *widget;

  widget = gtk_label_new (text);
  gtk_grid_attach (GTK_GRID (grid), widget, 0, row, 1, 1);
  gtk_widget_set_halign (GTK_WIDGET (widget), GTK_ALIGN_END);
  gtk_widget_set_valign (GTK_WIDGET (widget), GTK_ALIGN_CENTER);

  widget = gtk_label_new ("");
  gtk_label_set_selectable (GTK_LABEL (widget), selectable);
  gtk_widget_set_halign (GTK_WIDGET (widget), GTK_ALIGN_START);
  gtk_widget_set_valign (GTK_WIDGET (widget), GTK_ALIGN_CENTER);

  gtk_widget_set_margin_start (GTK_WIDGET (widget), 5);

  gtk_grid_attach (GTK_GRID (grid), widget, 1, row, 1, 1);

  return widget;
}


static void
construct_details_page (GpaKeyDetails *kdt)
{
  GtkWidget *grid;
  GtkWidget *label;
  GtkWidget *vbox;
  GtkWidget *scrolled;
  GtkWidget *viewport;
  gint table_row;

  /* Details Page */
  scrolled = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
				       GTK_SHADOW_NONE);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);
  viewport = gtk_viewport_new (NULL, NULL);
  gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
  gtk_container_add (GTK_CONTAINER (viewport), vbox);
  gtk_container_add (GTK_CONTAINER (scrolled), viewport);

  label = gtk_label_new ("");
  kdt->details_num_label = label;
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);

  grid = gtk_grid_new ();
  kdt->details_table = grid;
  gtk_box_pack_start (GTK_BOX (vbox), grid, TRUE, TRUE, 0);
  // gtk_table_set_row_spacing (GTK_TABLE (grid), 0, 2);
  // gtk_table_set_col_spacing (GTK_TABLE (grid), 0, 4);

  table_row = 0;
  kdt->detail_public_private = add_details_row
    (grid, table_row++, "", TRUE);
  kdt->detail_capabilities = add_details_row
    (grid, table_row++, "", TRUE);
  kdt->detail_name = add_details_row
    (grid, table_row++, _("User name:"), TRUE);
  kdt->detail_fingerprint = add_details_row
    (grid, table_row++, _("Fingerprint:"), TRUE);
  kdt->detail_key_id = add_details_row
    (grid, table_row++, _("Key ID:"), TRUE);
  kdt->detail_expiry = add_details_row
    (grid, table_row++, _("Expires at:"), FALSE);
  kdt->detail_owner_trust = add_details_row
    (grid, table_row++, _("Owner Trust:"), FALSE);
  kdt->detail_key_trust = add_details_row
    (grid, table_row++, _("Key validity:"), FALSE);
  kdt->detail_key_type = add_details_row
    (grid, table_row++, _("Key type:"), FALSE);
  kdt->detail_creation = add_details_row
    (grid, table_row++, _("Created at:"), FALSE);
  kdt->detail_last_update = add_details_row
    (grid, table_row++, _("Last update:"), FALSE);

  gtk_notebook_append_page (GTK_NOTEBOOK (kdt), scrolled,
                            gtk_label_new (_("Details")));
}


/* Create and append new page with USER ID info for KEY.  If KEY is NULL
   remove an existing USER ID page. */
static void
build_uid_page (GpaKeyDetails *kdt, gpgme_key_t key)
{
  GtkWidget *vbox;
  GtkWidget *scrolled;
  GtkWidget *uidlist;
  int pnum;

  /* First remove an existing page.  */
  if (kdt->uid_page)
    {
      pnum = gtk_notebook_page_num (GTK_NOTEBOOK (kdt), kdt->uid_page);
      if (pnum >= 0)
        gtk_notebook_remove_page (GTK_NOTEBOOK (kdt), pnum);
      kdt->uid_page = NULL;
      if (kdt->uid_list)
        {
          g_object_unref (kdt->uid_list);
          kdt->uid_list = NULL;
        }
    }
  if (!key)
    return;

  /* Create a new page.  */
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
  scrolled = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
                                       GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
  uidlist = gpa_uid_list_new ();
  gtk_container_add (GTK_CONTAINER (scrolled), uidlist);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);
  kdt->uid_list = uidlist;
  g_object_ref (kdt->uid_list);
  kdt->uid_page = vbox;
  gtk_notebook_append_page (GTK_NOTEBOOK (kdt), kdt->uid_page,
                            gtk_label_new (_("User IDs")));

  /* Fill this page.  */
  gpa_uid_list_set_key (kdt->uid_list, key);
}


/* Add the signatures page to the notebook.  */
static void
build_signatures_page (GpaKeyDetails *kdt, gpgme_key_t key)
{
  GtkWidget *label;
  GtkWidget *vbox, *hbox;
  GtkWidget *scrolled;
  int pnum;

  if (kdt->signatures_page)
    {
      if (kdt->signatures_uids)
        g_signal_handlers_disconnect_by_func
          (G_OBJECT (kdt->signatures_uids),
           G_CALLBACK (signatures_uid_changed), kdt);

      pnum = gtk_notebook_page_num (GTK_NOTEBOOK (kdt), kdt->signatures_page);
      if (pnum >= 0)
        gtk_notebook_remove_page (GTK_NOTEBOOK (kdt), pnum);
      kdt->signatures_page = NULL;
      kdt->signatures_uids = NULL;
    }
  if (kdt->signatures_list)
    {
      g_object_unref (kdt->signatures_list);
      kdt->signatures_list = NULL;
    }
  if (kdt->certchain_list)
    {
      g_object_unref (kdt->certchain_list);
      kdt->certchain_list = NULL;
    }
  if (!key)
    return;

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);

  /* If there is more than one OpenPGP UID, we need a select button.  */
  if (key->uids && key->uids->next && key->protocol == GPGME_PROTOCOL_OpenPGP)
    {
      hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
      label = gtk_label_new (_("Show signatures on user name:"));
      gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
      kdt->signatures_uids = gtk_combo_box_text_new ();
      gtk_box_pack_start (GTK_BOX (hbox), kdt->signatures_uids, TRUE, TRUE, 0);
      gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

      /* Connect the signal to update the list of user IDs in the
         signatures page of the notebook.  */
      g_signal_connect (G_OBJECT (kdt->signatures_uids), "changed",
                        G_CALLBACK (signatures_uid_changed), kdt);
    }

  /* Signature list.  */
  scrolled = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
				       GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
  if (key->protocol == GPGME_PROTOCOL_OpenPGP)
    {
      kdt->signatures_list = gpa_siglist_new ();
      g_object_ref (kdt->signatures_list);
      gtk_container_add (GTK_CONTAINER (scrolled), kdt->signatures_list);
    }
  else
    {
      kdt->certchain_list = gpa_certchain_new ();
      g_object_ref (kdt->certchain_list);
      gtk_container_add (GTK_CONTAINER (scrolled), kdt->certchain_list);
    }
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);

  kdt->signatures_page = vbox;
  gtk_notebook_append_page (GTK_NOTEBOOK (kdt), vbox,
                            gtk_label_new
                            (kdt->certchain_list? _("Chain"):_("Signatures")));


  /* Fill this page.  */
  if (kdt->certchain_list)
    {
      gpa_certchain_update (kdt->certchain_list, key);
    }
  else if (kdt->signatures_uids)
    {
      gpgme_user_id_t uid;
      GtkComboBox *combo;
      int i;

      /* Make the combo widget's width shrink as much as
	 possible. This (hopefully) fixes the previous behaviour
	 correctly: displaying a key with slightly longer signed UIDs
	 caused the top-level window to pseudo-randomly increase it's
	 size (which couldn't even be undone by the user anymore).  */
      combo = GTK_COMBO_BOX (kdt->signatures_uids);
      gtk_widget_set_size_request (GTK_WIDGET (combo), 0, -1);

      gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), NULL, _("All signatures"));
      gtk_combo_box_set_active (combo, 0);
      for (i=1, uid = key->uids; uid; i++, uid = uid->next)
	{
	  gchar *uid_string = gpa_gpgme_key_get_userid (uid);
	  gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), NULL, uid_string);
	  g_free (uid_string);
	}

      gpa_siglist_set_signatures (kdt->signatures_list, key, -1);
    }
  else
    {
      gpa_siglist_set_signatures (kdt->signatures_list, key, 0);
    }
}


/* Create and append new page with all subkeys for KEY.  If KEY is
   NULL remove an existing subkeys page. */
static void
build_subkeys_page (GpaKeyDetails *kdt, gpgme_key_t key)
{
  GtkWidget *vbox;
  GtkWidget *scrolled;
  GtkWidget *subkeylist;
  int pnum;

  /* First remove an existing page.  */
  if (kdt->subkeys_page)
    {
      pnum = gtk_notebook_page_num (GTK_NOTEBOOK (kdt), kdt->subkeys_page);
      if (pnum >= 0)
        gtk_notebook_remove_page (GTK_NOTEBOOK (kdt), pnum);
      kdt->subkeys_page = NULL;
      if (kdt->subkeys_list)
        {
          g_object_unref (kdt->subkeys_list);
          kdt->subkeys_list = NULL;
        }
    }
  if (!key)
    return;

  /* Create a new page.  */
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
  scrolled = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
                                       GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
  subkeylist = gpa_subkey_list_new ();
  gtk_container_add (GTK_CONTAINER (scrolled), subkeylist);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);
  kdt->subkeys_list = subkeylist;
  g_object_ref (kdt->subkeys_list);
  kdt->subkeys_page = vbox;
  gtk_notebook_append_page (GTK_NOTEBOOK (kdt), kdt->subkeys_page,
                            gtk_label_new
                            (key->protocol == GPGME_PROTOCOL_OpenPGP
                             ? _("Subkeys") : _("Key")));

  /* Fill this page.  */
  gpa_subkey_list_set_key (kdt->subkeys_list, key);


}


/* Create and append new page with TOFU info for KEY.  If KEY is NULL
   remove an existing TOFU page. */
static void
build_tofu_page (GpaKeyDetails *kdt, gpgme_key_t key)
{
#ifdef ENABLE_TOFU_INFO
  GtkWidget *vbox;
  GtkWidget *scrolled;
  GtkWidget *tofulist;
  int pnum;

  /* First remove an existing page.  */
  if (kdt->tofu_page)
    {
      pnum = gtk_notebook_page_num (GTK_NOTEBOOK (kdt), kdt->tofu_page);
      if (pnum >= 0)
        gtk_notebook_remove_page (GTK_NOTEBOOK (kdt), pnum);
      kdt->tofu_page = NULL;
      if (kdt->tofu_list)
        {
          g_object_unref (kdt->tofu_list);
          kdt->tofu_list = NULL;
        }
    }
  if (!key)
    return;

  /* Create a new page.  */
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
  scrolled = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
                                       GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
  tofulist = gpa_tofu_list_new ();
  gtk_container_add (GTK_CONTAINER (scrolled), tofulist);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);
  kdt->tofu_list = tofulist;
  g_object_ref (kdt->tofu_list);
  kdt->tofu_page = vbox;
  gtk_notebook_append_page (GTK_NOTEBOOK (kdt), kdt->tofu_page,
                            gtk_label_new (_("Tofu")));

  /* Fill this page.  */
  gpa_tofu_list_set_key (kdt->tofu_list, key);
#endif /*ENABLE_TOFU_INFO*/
}


/* Signal handler for the "changed_ui_mode" signal.  */
static void
ui_mode_changed (GpaOptions *options, gpointer param)
{
  GpaKeyDetails *kdt = param;

  if (gpa_options_get_simplified_ui (gpa_options_get_instance ()))
    {
      build_uid_page (kdt, NULL);
      build_signatures_page (kdt, NULL);
      build_subkeys_page (kdt, NULL);
    }
  else
    {
      build_uid_page (kdt, kdt->current_key);
      build_signatures_page (kdt, kdt->current_key);
      build_subkeys_page (kdt, kdt->current_key);
    }
  build_tofu_page (kdt, kdt->current_key);
  gtk_notebook_set_show_tabs
    (GTK_NOTEBOOK (kdt), gtk_notebook_get_n_pages (GTK_NOTEBOOK (kdt)) > 1);
  gtk_widget_show_all (GTK_WIDGET (kdt));
}


/* This function constructs the container holding all widgets making
   up this data widget.  It is called during instance creation.  */
static void
construct_main_widget (GpaKeyDetails *kdt)
{
  /* Details Page */
  construct_details_page (kdt);

  /* Connect the signal to act on the simplified UI change signal.  */
  g_signal_connect (G_OBJECT (gpa_options_get_instance ()),
		    "changed_ui_mode",
                    G_CALLBACK (ui_mode_changed), kdt);
}



/************************************************************
 ******************   Object Management  ********************
 ************************************************************/

static void
gpa_key_details_class_init (void *class_ptr, void *class_data)
{
  GpaKeyDetailsClass *klass = class_ptr;

  parent_class = g_type_class_peek_parent (klass);

  G_OBJECT_CLASS (klass)->finalize = gpa_key_details_finalize;
}


static void
gpa_key_details_init (GTypeInstance *instance, void *class_ptr)
{
  GpaKeyDetails *kdt = GPA_KEY_DETAILS (instance);

  construct_main_widget (kdt);
}


static void
gpa_key_details_finalize (GObject *object)
{
  GpaKeyDetails *kdt = GPA_KEY_DETAILS (object);

  if (kdt->current_key)
    {
      gpgme_key_unref (kdt->current_key);
      kdt->current_key = NULL;
    }
  if (kdt->uid_list)
    {
      g_object_unref (kdt->uid_list);
      kdt->uid_list = NULL;
    }
  if (kdt->signatures_list)
    {
      g_object_unref (kdt->signatures_list);
      kdt->signatures_list = NULL;
    }
  if (kdt->certchain_list)
    {
      g_object_unref (kdt->certchain_list);
      kdt->certchain_list = NULL;
    }
  if (kdt->subkeys_list)
    {
      g_object_unref (kdt->subkeys_list);
      kdt->subkeys_list = NULL;
    }
  if (kdt->tofu_list)
    {
      g_object_unref (kdt->tofu_list);
      kdt->tofu_list = NULL;
    }

  parent_class->finalize (object);
}


/* Construct the class.  */
GType
gpa_key_details_get_type (void)
{
  static GType this_type = 0;

  if (!this_type)
    {
      static const GTypeInfo this_info =
	{
	  sizeof (GpaKeyDetailsClass),
	  (GBaseInitFunc) NULL,
	  (GBaseFinalizeFunc) NULL,
	  gpa_key_details_class_init,
	  (GClassFinalizeFunc) NULL,
	  NULL, /* class_data */
	  sizeof (GpaKeyDetails),
	  0,    /* n_preallocs */
	  gpa_key_details_init
	};

      this_type = g_type_register_static (GTK_TYPE_NOTEBOOK,
                                          "GpaKeyDetails",
                                          &this_info, 0);
    }

  return this_type;
}


/************************************************************
 **********************  Public API  ************************
 ************************************************************/
GtkWidget *
gpa_key_details_new ()
{
  return GTK_WIDGET (g_object_new (GPA_KEY_DETAILS_TYPE, NULL));
}


/* Update the key details widget KEYDETAILS with KEY.  The caller also
   needs to provide the number of keys, so that the widget may show a
   key count instead of a key.  The actual key details are only shown
   if KEY is not NULL and KEYCOUNT is 1.  */
void
gpa_key_details_update (GtkWidget *keydetails, gpgme_key_t key, int keycount)
{
  GpaKeyDetails *kdt;
  GtkWidget *widget;
  int pnum;

  g_return_if_fail (GPA_IS_KEY_DETAILS (keydetails));
  kdt = GPA_KEY_DETAILS (keydetails);

  /* Save the currently selected page.  */
  pnum = gtk_notebook_get_current_page (GTK_NOTEBOOK (kdt));
  if (pnum >= 0
      && (widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (kdt), pnum)))
    {
      if (widget == kdt->uid_page)
        pnum = 1;
      else if (widget == kdt->signatures_page)
        pnum = 2;
      else if (widget == kdt->subkeys_page)
        pnum = 3;
#ifdef ENABLE_TOFU_INFO
      else if (widget == kdt->tofu_page)
        pnum = 4;
#endif /*ENABLE_TOFU_INFO*/
      else
        pnum = 0;
    }
  else
    pnum = 0;


  if (kdt->current_key)
    {
      gpgme_key_unref (kdt->current_key);
      kdt->current_key = NULL;
    }

  if (key && keycount == 1)
    {
      gpgme_key_ref (key);
      kdt->current_key = key;
      details_page_fill_key (kdt, key);

      /* Depend the generation of pages on the mode of the UI.  */
      if (gpa_options_get_simplified_ui (gpa_options_get_instance ()))
        {
          build_uid_page (kdt, NULL);
          build_signatures_page (kdt, NULL);
          build_subkeys_page (kdt, NULL);
        }
      else
        {
          build_uid_page (kdt, key);
          build_signatures_page (kdt, key);
          build_subkeys_page (kdt, key);
        }
    }
  else
    {
      details_page_fill_num_keys (kdt, keycount);
      build_signatures_page (kdt, NULL);
    }
  build_tofu_page (kdt, key);

  gtk_notebook_set_show_tabs
    (GTK_NOTEBOOK (kdt), gtk_notebook_get_n_pages (GTK_NOTEBOOK (kdt)) > 1);

  gtk_widget_show_all (keydetails);

  /* Try to select the last selected page.  */
  if (pnum == 1 && kdt->uid_page)
    pnum = gtk_notebook_page_num (GTK_NOTEBOOK (kdt), kdt->uid_page);
  else if (pnum == 2 && kdt->signatures_page)
    pnum = gtk_notebook_page_num (GTK_NOTEBOOK (kdt), kdt->signatures_page);
  else if (pnum == 3 && kdt->subkeys_page)
    pnum = gtk_notebook_page_num (GTK_NOTEBOOK (kdt), kdt->subkeys_page);
#ifdef ENABLE_TOFU_INFO
  else if (pnum == 4 && kdt->tofu_page)
    pnum = gtk_notebook_page_num (GTK_NOTEBOOK (kdt), kdt->tofu_page);
#endif /*ENABLE_TOFU_INFO*/
  else
    pnum = 0;
  gtk_notebook_set_current_page (GTK_NOTEBOOK (kdt), pnum);
}


/* Find the key identified by pattern and show the details of the
   first key found.  Pattern should be the fingerprint of a key so
   that only one key will be shown.  */
void
gpa_key_details_find (GtkWidget *keydetails, const char *pattern)
{
  gpg_error_t err;
  gpgme_ctx_t ctx;
  gpgme_key_t key = NULL;
  int any = 0;

  err = gpgme_new (&ctx);
  if (err)
    gpa_gpgme_error (err);
  gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP);

  if (!gpgme_op_keylist_start (ctx, pattern, 0))
    {
      while (!gpgme_op_keylist_next (ctx, &key))
        {
          gpa_key_details_update (keydetails, key, 1);
          gpgme_key_unref (key);
          any = 1;
          break;
        }
    }
  gpgme_op_keylist_end (ctx);

  if (!any)
    {
      gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);

      if (!gpgme_op_keylist_start (ctx, pattern, 0))
        {
          while (!gpgme_op_keylist_next (ctx, &key))
            {
              gpa_key_details_update (keydetails, key, 1);
              gpgme_key_unref (key);
              any = 1;
              break;
            }
        }
      gpgme_op_keylist_end (ctx);
    }

  gpgme_release (ctx);
  if (!any)
    gpa_key_details_update (keydetails, NULL, 0);
}

