[Gpa-commits] r789 - trunk/src

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Thu Jan 17 23:32:04 CET 2008


Author: marcus
Date: 2008-01-17 23:32:03 +0100 (Thu, 17 Jan 2008)
New Revision: 789

Added:
   trunk/src/confdialog.h
Modified:
   trunk/src/ChangeLog
   trunk/src/Makefile.am
   trunk/src/confdialog.c
   trunk/src/fileman.c
   trunk/src/gpa.c
   trunk/src/gpa.h
   trunk/src/keyring.c
Log:
2008-01-17  Marcus Brinkmann  <marcus at g10code.de>

	* Makefile.am (noinst_PROGRAMS): Remove confdialog.
	(confdialog_SOURCES): Remove variable.
	(gpa_SOURCES): Add confdialog.h and confdialog.c.
	* confdialog.h: New file.
	* confdialog.c: Rewritten.
	* gpa.h (gpa_open_backend_config_dialog): New prototype.
	(gpa_show_backend_config): Prototype removed.
	* gpa.c: Include "confdialog.h".
	(backend_config_dialog): New variable.
	(gpa_open_backend_config_dialog): New function.
	* fileman.c (fileman_menu_new): Add entry for backend config.
	* keyring.c (keyring_editor_menubar_new): Likewise.


Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog	2007-11-12 19:31:22 UTC (rev 788)
+++ trunk/src/ChangeLog	2008-01-17 22:32:03 UTC (rev 789)
@@ -1,3 +1,18 @@
+2008-01-17  Marcus Brinkmann  <marcus at g10code.de>
+
+	* Makefile.am (noinst_PROGRAMS): Remove confdialog.
+	(confdialog_SOURCES): Remove variable.
+	(gpa_SOURCES): Add confdialog.h and confdialog.c.
+	* confdialog.h: New file.
+	* confdialog.c: Rewritten.
+	* gpa.h (gpa_open_backend_config_dialog): New prototype.
+	(gpa_show_backend_config): Prototype removed.
+	* gpa.c: Include "confdialog.h".
+	(backend_config_dialog): New variable.
+	(gpa_open_backend_config_dialog): New function.
+	* fileman.c (fileman_menu_new): Add entry for backend config.
+	* keyring.c (keyring_editor_menubar_new): Likewise.
+
 2007-11-12  Werner Koch  <wk at g10code.com>
 
 	* server.c (cmd_prep_encrypt, cont_prep_encrypt): New.

Modified: trunk/src/Makefile.am
===================================================================
--- trunk/src/Makefile.am	2007-11-12 19:31:22 UTC (rev 788)
+++ trunk/src/Makefile.am	2008-01-17 22:32:03 UTC (rev 789)
@@ -23,7 +23,7 @@
 
 
 bin_PROGRAMS = gpa
-noinst_PROGRAMS = dndtest confdialog
+noinst_PROGRAMS = dndtest
 
 AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/pixmaps
 AM_CFLAGS = $(GPGME_CFLAGS) $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) \
@@ -99,17 +99,8 @@
 	      gpakeyselector.h gpakeyselector.c \
 	      server.c \
 	      options.c \
+	      confdialog.h confdialog.c \
 	      utils.c $(gpa_w32_sources)
 
 
 dndtest_SOURCES = dndtest.c
-
-confdialog_SOURCES = confdialog.c
-
-
-
-
-
-
-
-

Modified: trunk/src/confdialog.c
===================================================================
--- trunk/src/confdialog.c	2007-11-12 19:31:22 UTC (rev 788)
+++ trunk/src/confdialog.c	2008-01-17 22:32:03 UTC (rev 789)
@@ -1,5 +1,5 @@
 /* confdialog.c - GPGME based configuration dialog for GPA.
- * Copyright (C) 2007 g10 Code GmbH
+ * Copyright (C) 2007, 2008 g10 Code GmbH
  *
  * This file is part of GPA.
  *
@@ -30,109 +30,1215 @@
 #include <gtk/gtk.h>
 
 #include "i18n.h"
+#include "gpgmetools.h"
 
+/* Violation of GNOME standards: Cancel does not revert previous
+   apply.  We do not auto-apply or syntax check after focus
+   change.  */
 
+
+/* Internal public interface.  */
+static void hide_backend_config (void);
 
-/* Return a new dialog widget.  */
-static GtkDialog *
-create_dialog (void)
+
+/* Some global variables.  */
+
+/* Custom response IDs.  */
+#define CUSTOM_RESPONSE_RESET 1
+
+
+/* The apply button of the configuration dialog.  */
+static GtkWidget *dialog;
+
+/* The notebook with one page for each component.  */
+static gpgme_ctx_t dialog_ctx;
+
+/* The notebook with one page for each component.  */
+static GtkWidget *dialog_notebook;
+
+/* The current configuration.  */
+static gpgme_conf_comp_t dialog_conf;
+
+/* If we modified something in the current tab.  */
+static int dialog_tab_modified;
+
+
+/* We define the following behaviour for options:
+
+   An option of alt-type NONE gets a check button, and if it has the
+   LIST flag set, also a spin button for a repeat count.  The spin
+   button is only active if the button is checked.
+
+   An option of a different alt-type does not get a check box button
+   but a combo box and a text entry field.  If the option does have a
+   default or default description, the combo box contains an entry for
+   "Use default value", which sets the text entry field to that
+   default (and makes it insensitive?).  Otherwise the combo box
+   contains an entry for "Option not active" which makes the entry
+   field insensitive.  If the option has the OPTIONAl flag set, the
+   combo box contains an entry for "Use default argument" which sets
+   the text entry field to the no arg value or description, if any
+   (and makes it insensitive?).  In any case, the combo box contains
+   an entry for "Use custom value" which makes the text entry field
+   sensitive and editable.
+
+   Options with the LIST flag set could get a more sophisticated
+   dialog where you have "Add" and "Remove" buttons, or a multi-line
+   entry field.  Currently, having the LIST flag and OPTIONAL flag at
+   the same time creates an ambiguity, and entering commas in values
+   is not supported.  */
+typedef enum
+  {
+    OPTION_SIMPLE,
+    OPTION_SPIN,
+    OPTION_ENTRY,
+    OPTION_OPT_ENTRY
+  } option_type_t;
+
+typedef enum
+  {
+    COMBO_UNDEF = -1,
+    COMBO_DEFAULT = 0,
+    COMBO_CUSTOM = 1,
+    COMBO_NO_ARG = 2
+  } option_combo_t;
+    
+
+/* This structure combines an option with its GUI elements.  */
+typedef struct option_widget_s
 {
-  FILE *fp;
-  char line[1024];
-  GtkWidget *dialog;
-  GtkWidget *vbox, *hbox, *label;
+  /* The option.  */
+  gpgme_conf_opt_t option;
 
-  dialog = gtk_dialog_new ();
-  gtk_window_set_title (GTK_WINDOW (dialog),
-                        _("Crypto Backend Configuration"));
+  /* We remember the type of the option to make life easier on us.  */
+  option_type_t type;
 
-  vbox = gtk_vbox_new (FALSE, 1);
-  gtk_container_add (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), vbox);
+  /* The check button (or, in the case of COMBO being not NULL, the
+     label) widget.  */
+  GtkWidget *check;
 
-  hbox = gtk_hbox_new (FALSE, 1);
-  label = gtk_label_new ("Left Item");
-  gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
-  label = gtk_button_new_with_label ("Right Item");
-  gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
+  /* The entry or spin button widget.  */
+  GtkWidget *widget;
 
-  gtk_box_pack_start (GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
-  
-  fp = popen ("gpgconf --list-options gpgsm" , "r");
-  if (!fp)
+  /* The current user setting in string representation.  This defaults
+     to the last input by the user, followed by the current setting,
+     followed by the default.  */
+  char *saved_value;
+
+  /* This helps to know about state changes.  */
+  int old_combo_box_state;
+
+  /* The combo box, if any.  Only used for OPTION_ENTRY and
+     OPTION_OPT_ENTRY.  The combo has
+     COMBO_DEFAULT = 0: Use default value or Do not use option.
+     COMBO_CUSTOM = 1: Use custom value
+     COMBO_NO_ARG = 2: Use default argument (for OPTION_OPT_ENTRY).  */
+  GtkWidget *combo;
+} *option_widget_t;
+
+
+#if 0
+/* Not needed anymore, as all stock actions work on all tabs.
+   However, might come in handy at some point.  */
+
+static gpgme_conf_comp_t
+dialog_current_comp (void)
+{
+  gpgme_conf_comp_t comp = dialog_conf;
+  int page = gtk_notebook_get_current_page (GTK_NOTEBOOK (dialog_notebook));
+  const char *label;
+
+  /* Iterate over all options in the current tab, and reset them.  */
+
+  label = gtk_notebook_get_tab_label_text
+    (GTK_NOTEBOOK (dialog_notebook),
+     gtk_notebook_get_nth_page (GTK_NOTEBOOK (dialog_notebook), page));
+
+  while (comp)
     {
-      perror ("failed to start gpgconf");
-      exit (1);
+      if (! strcmp (label, comp->description))
+	break;
+      comp = comp->next;
     }
 
-  while (fgets (line, sizeof line, fp))
+  assert (comp);
+  return comp;
+}
+#endif
+
+
+/* Convert an argument value to a string representation.  Lists are
+   converted to comma-separated values, empty strings in lists are
+   surrounded by double-quotes.  */
+static char *
+arg_to_str (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
+{
+  static char *result;
+  char *new_result = NULL;
+
+  if (result)
     {
-      int c;
-      char *p, *endp;
-      int fieldno;
+      g_free (result);
+      result = NULL;
+    }
+
+  while (arg)
+    {
+      if (result)
+	{
+	  new_result = g_strdup_printf ("%s,", result);
+	  g_free (result);
+	  result = new_result;
+	}
+      else
+	result = g_strdup ("");
+
+      switch (type)
+	{
+	case GPGME_CONF_NONE:
+	case GPGME_CONF_UINT32:
+	  new_result = g_strdup_printf ("%s%u", result, arg->value.uint32);
+	  g_free (result);
+	  result = new_result;
+	  break;
+
+	case GPGME_CONF_INT32:
+	  new_result = g_strdup_printf ("%s%i", result, arg->value.int32);
+	  g_free (result);
+	  result = new_result;
+	  break;
+
+	case GPGME_CONF_STRING:
+	case GPGME_CONF_PATHNAME:
+	case GPGME_CONF_LDAP_SERVER:
+	  new_result = g_strdup_printf ("%s%s", result, arg->value.string);
+	  g_free (result);
+	  result = new_result;
+	  break;
+	  
+	default:
+	  assert (!"Not supported.");
+	  break;
+	}
+      arg = arg->next;
+    }
+  return result;
+}
+
+
+/* Extract the argument from a widget.  */
+static gpgme_conf_arg_t
+option_widget_to_arg (option_widget_t opt)
+{
+  gpgme_error_t err;
+  gpgme_conf_opt_t option = opt->option;
+  gpgme_conf_arg_t arg = NULL;
+
+  if (opt->type == OPTION_SIMPLE || opt->type == OPTION_SPIN)
+    {
+      int active;
+      unsigned int count = 1;
+ 
+      active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (opt->check));
+      if (!active)
+	return NULL;
+     
+      if (opt->type == OPTION_SPIN)
+	count = gtk_spin_button_get_value (GTK_SPIN_BUTTON (opt->widget));
+
+      err = gpgme_conf_arg_new (&arg, option->alt_type, &count);
+      if (err)
+	gpa_gpgme_error (err);
+
+      return arg;
+    }
+  else
+    {
+      int combo;
+      gpgme_conf_arg_t *argp;
+      char *val;
+      char *valp;
+
+      combo = gtk_combo_box_get_active (GTK_COMBO_BOX (opt->combo));
+      if (combo == COMBO_DEFAULT)
+	return NULL;
+
+      if (combo == COMBO_NO_ARG)
+	{
+	  /* A single no-arg option.  */
+	  err = gpgme_conf_arg_new (&arg, option->alt_type, NULL);
+	  if (err)
+	    gpa_gpgme_error (err);
+
+	  return arg;
+	}
+
+      argp = &arg;
+
+      val = g_strdup (gtk_entry_get_text (GTK_ENTRY (opt->widget)));
+      valp = val;
+      do
+	{
+	  while (*valp == ' ' || *valp == '\t')
+	    valp++;
+
+	  if (*valp == ',' || *valp == '\0')
+	    {
+	      if (option->flags & GPGME_CONF_OPTIONAL)
+		err = gpgme_conf_arg_new (argp, option->alt_type, NULL);
+	      else
+		/* This seems a useful default to make things simpler
+		   for the user, as the OPTIONAL flag is not much
+		   used.  */
+		err = gpgme_conf_arg_new (argp, option->alt_type, "");
+
+	      if (err)
+		gpa_gpgme_error (err);
+
+	      argp = &(*argp)->next;
+
+	      /* Skip comma.  */
+	      if (*valp)
+		valp++;
+	    }
+	  else
+	    {
+	      char *str = valp;
+	      char *strend = valp;
+	      int done = 0;
+	      int in_quote = 0;
+
+	      /* Remove quoting.  */
+	      do
+		{
+		  if (in_quote)
+		    {
+		      if (*valp == '\0')
+			done = 1;
+		      else if (*valp == '"')
+			{
+			  in_quote = 0;
+			  valp++;
+			}
+		      else
+			*(strend++) = *(valp++);
+		    }
+		  else
+		    {
+		      if (*valp == ',' || *valp == '\0')
+			done = 1;
+		      else if (*valp == '"')
+			{
+			  in_quote = 1;
+			  valp++;
+			}
+		      else
+			*(strend++) = *(valp++);
+		    }
+		}
+	      while (! done);
+
+	      if (*valp == ',')
+		valp++;
+
+	      /* Find end of the string.  */
+	      while (strend > str && (*strend == ' ' || *strend == '\t'))
+		strend--;
+
+	      *strend = '\0';
+
+	      switch (option->alt_type)
+		{
+		case GPGME_CONF_NONE:
+		case GPGME_CONF_UINT32:
+		  {
+		    unsigned int nr;
+		    nr = strtoul (str, NULL, 0);
+		    err = gpgme_conf_arg_new (argp, option->alt_type, &nr);
+		    if (err)
+		      gpa_gpgme_error (err);
+
+		    argp = &(*argp)->next;
+		  }
+		  break;
+		  
+		case GPGME_CONF_INT32:
+		  {
+		    int nr;
+		    nr = strtoul (str, NULL, 0);
+		    err = gpgme_conf_arg_new (argp, option->alt_type, &nr);
+		    if (err)
+		      gpa_gpgme_error (err);
+
+		    argp = &(*argp)->next;
+		  }
+		  break;
+
+		case GPGME_CONF_STRING:
+		case GPGME_CONF_LDAP_SERVER:
+		case GPGME_CONF_PATHNAME:
+		  {
+		    err = gpgme_conf_arg_new (argp, option->alt_type, str);
+		    if (err)
+		      gpa_gpgme_error (err);
+
+		    argp = &(*argp)->next;
+		  }
+		  break;
+
+		default:
+		  assert (!"Not supported.");
+		  break;
+		}
+	    }
+	}
+      while (*valp);
+
+      g_free (val);
+      return arg;
+    }
+}
+
+
+/* Compare two arguments and returns true if they are equal.  */
+static int
+args_are_equal (gpgme_conf_arg_t arg1, gpgme_conf_arg_t arg2,
+		gpgme_conf_type_t type)
+{
+  while (arg1 && arg2)
+    {
+      switch (type)
+	{
+	case GPGME_CONF_NONE:
+	case GPGME_CONF_UINT32:
+	  if (arg1->value.uint32 != arg2->value.uint32)
+	    return 0;
+	  break;
+
+	case GPGME_CONF_INT32:
+	  if (arg1->value.int32 != arg2->value.int32)
+	    return 0;
+	  break;
+
+	case GPGME_CONF_STRING:
+	case GPGME_CONF_LDAP_SERVER:
+	case GPGME_CONF_PATHNAME:
+	  if (strcmp (arg1->value.string, arg2->value.string))
+	    return 0;
+	  break;
+
+	default:
+	  assert (!"Not supported.");
+	  break;
+	}
+
+      arg1 = arg1->next;
+      arg2 = arg2->next;
+    }
+  if (arg1 || arg2)
+    return 0;
+  return 1;
+}
+
+
+/* Commit all the changes in component COMP.  */
+static void
+save_options (gpgme_conf_comp_t comp)
+{
+  gpgme_conf_opt_t option = comp->options;
+  gpgme_error_t err;
+  int changed;
+
+  changed = 0;
+  while (option)
+    {
+      option_widget_t opt = option->user_data;
+      gpgme_conf_arg_t arg;
       
-      if (!*line || line[strlen(line)-1] != '\n')
-        {
-          if (*line && feof (fp))
-            ; /* Last line not terminated - continue.  */
-          else
-            {
-              fprintf (stderr, _("line too long - skipped\n"));
-              while ( (c=fgetc (fp)) != EOF && c != '\n')
-                ; /* Skip until end of line. */
-              continue;
-            }
-        }
-      /* Skip empty and comment lines.  */
-      for (p=line; *p == ' '; p++)
-        ;
-      if (!*p || *p == '\n' || *p == '#')
-        continue;
+      /* Exclude group headers etc.  */
+      if (!opt)
+	{
+	  option = option->next;
+	  continue;
+	}
 
-      /* Parse the colon separated fields. */
-      for (fieldno=1, p = line; p; p = endp, fieldno++ )
-        {
-          endp = strchr (p, ':');
-          if (endp)
-            *endp++ = 0;
-          switch (fieldno)
-            {
-            case 1:
-              label = gtk_label_new (p);
-              gtk_box_pack_start (GTK_BOX(vbox), label, TRUE, TRUE, 0);
-              break;
+      arg = option_widget_to_arg (opt);
+      if (! args_are_equal (arg, option->value, option->alt_type))
+	{
+	  err = gpgme_conf_opt_change (option, 0, arg);
+	  if (err)
+	    gpa_gpgme_error (err);
+	  changed++;
 
-            default:
-              break;
-            }
-        }
-    } 
-  
-  if (ferror (fp))
-    fprintf (stderr, _("error reading gpgconf's output: %s\n"), 
-             strerror (errno));
+	  /* FIXME: Disable this.  */
+	  g_debug ("Changing component %s, option %s",
+		   comp->name, option->name);
+	}
 
-  pclose (fp);
+      option = option->next;
+    }
 
-  gtk_widget_show_all (dialog);
+  if (changed)
+    {
+      err = gpgme_op_conf_save (dialog_ctx, comp);
+      if (err)
+	gpa_gpgme_warning (err);
+    }
+}
 
-  gtk_main ();
 
-  return GTK_DIALOG(dialog);
+static void
+save_all_options (void)
+{
+  gpgme_conf_comp_t comp;
+
+  /* Save all tabs.  */
+  comp = dialog_conf;
+  while (comp)
+    {
+      save_options (comp);
+      comp = comp->next;
+    }
 }
 
 
-void
-gpa_show_backend_config (void)
+/* Update user-changes to the option widget OPT.  */
+static void
+update_option (option_widget_t opt)
 {
+  gpgme_conf_opt_t option;
+
+  option = opt->option;
+
+  if (opt->type == OPTION_SPIN)
+    {
+      int active;
+
+      active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (opt->check));
+
+      gtk_widget_set_sensitive (opt->widget, active);
+    }
+  else if (opt->type == OPTION_ENTRY || opt->type == OPTION_OPT_ENTRY)
+    {
+      int combo;
+      GtkWidget *entry = opt->widget;
+
+      combo = gtk_combo_box_get_active (GTK_COMBO_BOX (opt->combo));
+
+      if (opt->old_combo_box_state == COMBO_UNDEF)
+	{
+	  /* Initialization.  */
+	  if (option->value)
+	    {
+	      /* A single no-arg argument can be represented directly
+		 in the combo box.  Otherwise, we use a custom
+		 argument and comma-separated lists.  */
+	      if (option->value->no_arg && !option->value->next)
+		combo = COMBO_NO_ARG;
+	      else
+		combo = COMBO_CUSTOM;
+	    }
+	  else
+	    combo = COMBO_DEFAULT;
+	  gtk_combo_box_set_active (GTK_COMBO_BOX (opt->combo), combo);
+	}
+      else if (combo == opt->old_combo_box_state)
+	return;
+      else if (opt->old_combo_box_state == COMBO_CUSTOM)
+	{
+	  if (opt->saved_value)
+	    g_free (opt->saved_value);
+	  opt->saved_value = g_strdup
+	    (gtk_entry_get_text (GTK_ENTRY (opt->widget)));
+	}
+
+      if (combo == COMBO_DEFAULT)
+	{
+	  if (option->default_value)
+	    gtk_entry_set_text (GTK_ENTRY (entry),
+				arg_to_str (option->default_value,
+					    option->alt_type));
+	  else if (option->default_description)
+	    gtk_entry_set_text (GTK_ENTRY (entry),
+				option->default_description);
+	  else
+	    gtk_entry_set_text (GTK_ENTRY (entry), "");
+
+	  gtk_entry_set_editable (GTK_ENTRY (entry), FALSE);
+	  gtk_widget_set_sensitive (entry, FALSE);
+	}
+      else if (combo == COMBO_NO_ARG)
+	{
+	  if (option->no_arg_value)
+	    gtk_entry_set_text (GTK_ENTRY (entry),
+				arg_to_str (option->no_arg_value,
+					    option->alt_type));
+	  else if (option->no_arg_description)
+	    gtk_entry_set_text (GTK_ENTRY (entry),
+				option->no_arg_description);
+	  else
+	    gtk_entry_set_text (GTK_ENTRY (entry), "");
+
+	  gtk_entry_set_editable (GTK_ENTRY (entry), FALSE);
+	  gtk_widget_set_sensitive (entry, FALSE);
+	  /* FIXME: Change focus.  */
+	}
+      else if (combo == COMBO_CUSTOM)
+	{
+	  if (opt->saved_value)
+	    gtk_entry_set_text (GTK_ENTRY (entry), opt->saved_value);
+	  else if (option->value)
+	    gtk_entry_set_text (GTK_ENTRY (entry),
+				arg_to_str (option->value,
+					    option->alt_type));
+	  else if (option->default_value)
+	    {
+	      gtk_entry_set_text (GTK_ENTRY (entry),
+				  arg_to_str (option->default_value,
+					      option->alt_type));
+	      /* A default (rather than current) value is selected
+		 initially.  */
+	      gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
+	    }
+	  else
+	    gtk_entry_set_text (GTK_ENTRY (entry), "");
+
+	  gtk_entry_set_editable (GTK_ENTRY (entry), TRUE);
+	  gtk_widget_set_sensitive (entry, TRUE);
+	  /* FIXME: Change focus.  */
+	}
+
+      opt->old_combo_box_state = combo;
+    }
+}	      
+
+
+/* Update the tab when it is modified (if modified is true) or reset
+   it (else).  */
+static void
+update_modified (int modified)
+{
+  if (modified)
+    {
+      /* Run with every button click, so keep it short.  */
+      if (!dialog_tab_modified)
+	{
+	  dialog_tab_modified = 1;
+	  gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+					     GTK_RESPONSE_APPLY, TRUE);
+	  gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+					     CUSTOM_RESPONSE_RESET, TRUE);
+	}
+    }
+  else
+    {
+      /* This is also called at initialization time.  */
+      
+      dialog_tab_modified = 0;
+      gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+					 GTK_RESPONSE_APPLY, FALSE);
+      gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+					     CUSTOM_RESPONSE_RESET, FALSE);
+    }
+}
+
+
+static void
+update_modified_cb (void *dummy)
+{
+  update_modified (1);
+}
+
+
+/* Update a toggled checkbox button.  */
+static void
+option_checkbutton_toggled (GtkToggleButton *button, gpointer data)
+{
+  option_widget_t opt = data;
+
+  update_option (opt);
+  update_modified (1);
+}
+
+
+/* Update a toggled combo box.  */
+static void
+option_combobox_changed (GtkComboBox *combo, gpointer data)
+{
+  option_widget_t opt = data;
+
+  update_option (opt);
+  update_modified (1);
+}
+
+
+static void create_dialog_tabs (void);
+
+
+/* Handle stock response "apply".  */
+static void
+dialog_response_apply (GtkDialog *dialog)
+{
+  save_all_options ();
+
+  /* Reload configuration.  */
+  create_dialog_tabs ();
+}
+
+
+/* Soft reset which reuses the configuration at last load.  */
+static void
+reset_options (gpgme_conf_comp_t comp)
+{
+  gpgme_conf_opt_t option;
+
+  option = comp->options;
+
+  while (option)
+    {
+      option_widget_t opt = option->user_data;
+      
+      if (!opt)
+	{
+	  /* Exclude group headers etc.  */
+	  option = option->next;
+	  continue;
+	}
+
+      if (opt->type == OPTION_SIMPLE || opt->type == OPTION_SPIN)
+	{
+	  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (opt->check),
+					!!option->value);
+	}
+      else if (opt->type == OPTION_ENTRY || opt->type == OPTION_OPT_ENTRY)
+	{
+	  /* Force re-initialization.  */
+	  opt->old_combo_box_state = COMBO_UNDEF;
+	  update_option (opt);
+	}
+
+    option = option->next;
+  }
+}
+
+
+static void
+reset_all_options (void)
+{
+  gpgme_conf_comp_t comp;
+
+  /* Save all tabs.  */
+  comp = dialog_conf;
+  while (comp)
+    {
+      reset_options (comp);
+      comp = comp->next;
+    }
+}
+
+
+/* Handle stock response "reset".  */
+static void
+dialog_response_reset (GtkDialog *dialog)
+{
+#if 0
+  /* Allow to use the reset button also as a refresh button.  Note:
+     This is kind of awkward, because the reset button is only
+     sensitive if local modifications exist.  Currently, we require a
+     dialog cancel/reopen cycle for a "hard" reset.  */
+  create_dialog_tabs ();
+#else
+  /* Soft reset, using last loaded configuration.  Much faster, and
+     consistent with sensitivity of Reset button.  */
+  reset_all_options ();
+  update_modified (0);
+#endif
+}
+
+
+/* Handle stock responses like OK, apply and cancel.  */
+static int
+dialog_response (GtkDialog *dialog, gint response, gpointer data)
+{
+  switch (response)
+    {
+    case GTK_RESPONSE_ACCEPT:
+    case GTK_RESPONSE_YES:
+      save_all_options ();
+      hide_backend_config ();
+      break;
+
+    case GTK_RESPONSE_CANCEL:
+    case GTK_RESPONSE_CLOSE:
+    case GTK_RESPONSE_REJECT:
+    case GTK_RESPONSE_NO:
+    case GTK_RESPONSE_DELETE_EVENT:
+      hide_backend_config ();
+      break;
+
+    case GTK_RESPONSE_APPLY:
+      dialog_response_apply (dialog);
+      break;
+
+    case CUSTOM_RESPONSE_RESET:
+      dialog_response_reset (dialog);
+      break;
+
+    default:
+      g_warning ("unhandled response: %i", response);
+      /* Do whatever is the default.  */
+      return FALSE;
+    }
+
+  /* Don't do anything.  We handled it all.  */
+  return TRUE;
+}
+
+
+/* Determine the width of a checkbox.  We use this to align labels
+   with and without checkboxes vertically.  This approach is not
+   proper, as Gtk+ can not automatically adjust the padding when the
+   theme switches, but it is simple.  */
+static gint
+get_checkbox_width (void)
+{
+  GtkWidget *checkbox;
+  GtkRequisition checkbox_size;
+
+  /* We do not save the result, as it may change with a theme change.
+     In this case we will at least refresh eventually.  */
+
+  checkbox = gtk_check_button_new ();
+  gtk_widget_size_request (checkbox, &checkbox_size);
+  gtk_widget_destroy (checkbox);
+
+  return checkbox_size.width;
+}
+
+
+/* A callback to be used with gtk_container_foreach, which removes each
+   widget unconditionally.  */
+static void
+remove_from_container (GtkWidget *widget, gpointer data)
+{
+  GtkWidget *container = data;
+
+  gtk_container_remove (GTK_CONTAINER (container), widget);
+}
+
+
+static void
+create_dialog_tabs_2 (gpgme_conf_comp_t old_conf, gpgme_conf_comp_t new_conf)
+{
+  gpgme_conf_comp_t comp;
+  gpgme_conf_comp_t comp_alt;
+  int reset;
+  int page_nr;
+  gint checkbox_width = get_checkbox_width ();
+  /* We keep size groups for the field elements across all tabs for
+     visual consistency.  */
+  GtkSizeGroup *size_group[2];
+
+  size_group[0] = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+  size_group[1] = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+  /* In most cases, the new components are the same as the old
+     components.  We catch this special case here, and if it is true,
+     we reuse the existing tabs in the notebook below.  Otherwise, we
+     reset everything.  */
+  reset = 0;
+  comp = old_conf;
+  comp_alt = new_conf;
+  while (comp && comp_alt)
+    {
+      /* A mismatch is found if either the description changes, or if
+	 a component changes from no options to some options or vice
+	 verse (as we suppress generating tabs for components without
+	 options below).  */
+
+      if (strcmp (comp->description, comp_alt->description)
+	  || (comp->options == NULL && comp_alt != NULL)
+	  || (comp->options != NULL && comp_alt == NULL))
+	break;
+
+      comp = comp->next;
+      comp_alt = comp_alt->next;
+    }
+  if (comp || comp_alt)
+    reset = 1;
+
+  if (reset)
+    {
+      gtk_widget_hide (dialog_notebook);
+
+      /* Remove the current tabs.  */
+      gtk_container_foreach (GTK_CONTAINER (dialog_notebook),
+			     &remove_from_container, dialog_notebook);
+    }
+
+  comp = new_conf;
+  page_nr = 0;
+
+  while (comp)
+    {
+      GtkWidget *label;
+      GtkWidget *page;
+      gpgme_conf_opt_t option;
+      /* For each group in the component, we keep track of a frame,
+	 and the table inside the frame.  */
+      GtkWidget *frame = NULL;
+      GtkWidget *frame_vbox = NULL;
+
+      /* Skip over all components that do not have any options.  This
+	 can happen for example with old installed versions of
+	 components.  */
+      if (comp->options == NULL)
+	{
+	  comp = comp->next;
+	  continue;
+	}
+
+      if (reset)
+	{
+	  page = gtk_vbox_new (FALSE, 0);
+	  gtk_container_add (GTK_CONTAINER (dialog_notebook), page);
+
+	  label = gtk_label_new (comp->description);
+	  gtk_notebook_set_tab_label
+	    (GTK_NOTEBOOK (dialog_notebook),
+	     gtk_notebook_get_nth_page (GTK_NOTEBOOK (dialog_notebook),
+					page_nr),
+	     label);
+	}
+      else
+	{
+	  page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (dialog_notebook),
+					    page_nr);
+
+	  gtk_container_foreach (GTK_CONTAINER (page),
+				 &remove_from_container, page);
+	}
+
+      option = comp->options;
+	  /* All ungrouped options come first.  We put them in a frame
+	     titled "Main", just so we have a consistent layout.  */
+      if (option && ! (option->flags & GPGME_CONF_GROUP))
+	{
+	  frame = gtk_frame_new (NULL);
+	  gtk_box_pack_start (GTK_BOX (page), frame, TRUE, TRUE, 0);
+	  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+
+	  label = gtk_label_new (_("<b>Main</b>"));
+	  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+	  gtk_frame_set_label_widget (GTK_FRAME (frame), label);
+
+	  frame_vbox = gtk_vbox_new (FALSE, 0);
+	  gtk_container_add (GTK_CONTAINER (frame), frame_vbox);
+	}
+
+      while (option)
+	{
+	  if (option->flags & GPGME_CONF_GROUP)
+	    {
+	      char name[80];
+
+	      frame = gtk_frame_new (NULL);
+	      gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+	      gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+
+	      /* FIXME: l10n? */
+	      snprintf (name, sizeof (name), "<b>%s</b>", option->name);
+	      name[sizeof (name) - 1] = '\0';
+	      label = gtk_label_new (name);
+
+	      gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+	      gtk_frame_set_label_widget (GTK_FRAME (frame), label);
+	      
+	      frame_vbox = gtk_vbox_new (FALSE, 0);
+	      gtk_container_add (GTK_CONTAINER (frame), frame_vbox);
+	    }
+	  else
+	    {
+	      GtkWidget *vbox;
+	      GtkWidget *hbox;
+	      option_widget_t opt;
+	      GtkWidget *widget;
+	      GtkWidget *entry;
+
+	      opt = g_new0 (struct option_widget_s, 1);
+	      opt->option = option;
+	      option->user_data = opt;
+
+	      /* Add the entry fields for the option, depending on its
+		 type and flags.  */
+	      if (option->alt_type == GPGME_CONF_NONE
+		  && !(option->flags & GPGME_CONF_LIST))
+		opt->type = OPTION_SIMPLE;
+	      else if (option->alt_type == GPGME_CONF_NONE
+		       && (option->flags & GPGME_CONF_LIST))
+		opt->type = OPTION_SPIN;
+	      else if (option->flags & GPGME_CONF_OPTIONAL
+		       && !(option->flags & GPGME_CONF_LIST))
+		opt->type = OPTION_OPT_ENTRY;
+	      else
+		opt->type = OPTION_ENTRY;
+
+	      /* We create a vbox for the option, in case we want to
+		 support multi-hbox setups for options.  Currently we
+		 only add one hbox to that vbox though.  */
+
+	      vbox = gtk_vbox_new (FALSE, 0);
+	      gtk_box_pack_start (GTK_BOX (frame_vbox), vbox, TRUE, TRUE, 0);
+
+	      hbox = gtk_hbox_new (FALSE, 0);
+	      gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+	      /* Add the entry fields for the option, depending on its
+		 type and flags.  */
+	      if (opt->type == OPTION_SIMPLE)
+		{
+		  widget = gtk_check_button_new_with_label (option->name);
+		  opt->check = widget; 
+		  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+		  
+		  /* Add the checkbox label to the size group.  */
+		  gtk_size_group_add_widget (GTK_SIZE_GROUP (size_group[0]),
+					     widget);
+		  if (option->value)
+		    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
+						  TRUE);
+		}
+	      else if (opt->type == OPTION_SPIN)
+		{
+		  widget = gtk_check_button_new_with_label (option->name);
+		  opt->check = widget; 
+		  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+		  
+		  /* Add the checkbox label to the size group.  */
+		  gtk_size_group_add_widget (GTK_SIZE_GROUP (size_group[0]),
+					     widget);
+		  if (option->value)
+		    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
+						  TRUE);
+
+		  /* For list options without arguments, we use a
+		     simple spin button to capture the repeat
+		     count.  */
+		  widget = gtk_spin_button_new_with_range (1, 100, 1);
+		  opt->widget = widget;
+
+		  if (option->value)
+		    gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget),
+					       option->value->value.count);
+
+		  g_signal_connect ((gpointer) widget, "value-changed",
+				    G_CALLBACK (update_modified_cb), NULL);
+		  
+		  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+		}
+	      else
+		{
+		  GtkWidget *align;
+		  guint pt, pb, pl, pr;
+
+		  align = gtk_alignment_new (0, 0.5, 0, 0);
+		  gtk_alignment_get_padding (GTK_ALIGNMENT (align),
+					     &pt, &pb, &pl, &pr);
+		  gtk_alignment_set_padding (GTK_ALIGNMENT (align), pt, pb,
+					     pl + checkbox_width, pr);
+		  gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, FALSE, 0);
+
+		  widget = gtk_label_new (option->name);
+		  opt->check = widget; 
+		  gtk_container_add (GTK_CONTAINER (align), widget);
+		  
+		  /* Add the checkbox label to the size group.  */
+		  gtk_size_group_add_widget (GTK_SIZE_GROUP (size_group[0]),
+					     align);
+
+		  widget = gtk_combo_box_new_text ();
+		  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+		  gtk_size_group_add_widget (GTK_SIZE_GROUP (size_group[1]),
+					     widget);
+		  opt->combo = widget;
+
+		  entry = gtk_entry_new ();
+		  gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+		  
+		  opt->widget = entry;
+
+		  g_signal_connect ((gpointer) entry, "changed",
+				    G_CALLBACK (update_modified_cb), NULL);
+
+		  if (option->flags & GPGME_CONF_DEFAULT
+		      || option->flags & GPGME_CONF_DEFAULT_DESC)
+		    gtk_combo_box_append_text
+		      (GTK_COMBO_BOX (widget),
+		       (option->flags & GPGME_CONF_LIST) ?
+		       _("Use default values") : _("Use default value"));
+		  else
+		    gtk_combo_box_append_text
+		      (GTK_COMBO_BOX (widget), _("Do not use option"));
+
+		  gtk_combo_box_append_text
+		    (GTK_COMBO_BOX (widget),
+		     (option->flags & GPGME_CONF_LIST) ?
+		     _("Use custom values") : _("Use custom value"));
+
+		  if (opt->type == OPTION_OPT_ENTRY)
+		    gtk_combo_box_append_text
+		      (GTK_COMBO_BOX (widget), _("Use default argument"));
+		}
+
+	      /* Force update.  */
+	      opt->old_combo_box_state = COMBO_UNDEF;
+	      update_option (opt);
+
+	      if (opt->combo)
+		g_signal_connect ((gpointer) opt->combo, "changed",
+				  G_CALLBACK (option_combobox_changed),
+				  opt);
+	      else
+		g_signal_connect ((gpointer) opt->check, "toggled",
+				  G_CALLBACK (option_checkbutton_toggled),
+				  opt);
+      
+	      /* Grey out options which can not be changed at all.  */
+	      if (option->flags & GPGME_CONF_NO_CHANGE)
+		gtk_widget_set_sensitive (vbox, FALSE);
+
+	      /* Add a tooltip description.  */
+	      if (option->description)
+		gtk_widget_set_tooltip_text (vbox, option->description);
+	    }
+	  option = option->next;
+	}
+      page_nr++;
+      comp = comp->next;
+    }
+
+  /* Release our references to the size groups.  */
+  g_object_unref (size_group[0]);
+  g_object_unref (size_group[1]);
+
+  gtk_widget_show_all (dialog_notebook);
+
+  update_modified (0);
+}
+
+
+static void
+create_dialog_tabs (void)
+{
+  gpgme_error_t err;
+  gpgme_conf_comp_t new_conf;
+  int page;
+  int nr_pages;
+  char *current_tab = NULL;
+
+  if (dialog_notebook)
+    {
+      /* Remember the current tab by its label.  */
+      page = gtk_notebook_get_current_page (GTK_NOTEBOOK (dialog_notebook));
+      if (page >= 0)
+	current_tab = strdup (gtk_notebook_get_tab_label_text
+			      (GTK_NOTEBOOK (dialog_notebook),
+			       gtk_notebook_get_nth_page
+			       (GTK_NOTEBOOK (dialog_notebook), page)));
+    }
+
+  err = gpgme_op_conf_load (dialog_ctx, &new_conf);
+  if (err)
+    gpa_gpgme_error (err);
+
+  create_dialog_tabs_2 (dialog_conf, new_conf);
+  gpgme_conf_release (dialog_conf);
+  dialog_conf = new_conf;
+
+  if (current_tab)
+    {
+      /* Rediscover the current tab.  */
+      page = 0;
+      nr_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (dialog_notebook));
+      while (page < nr_pages)
+	{
+	  const char *label = gtk_notebook_get_tab_label_text
+	    (GTK_NOTEBOOK (dialog_notebook),
+	     gtk_notebook_get_nth_page (GTK_NOTEBOOK (dialog_notebook), page));
+	  if (! strcmp (label, current_tab))
+	    break;
+	  page++;
+	}
+      if (page < nr_pages)
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (dialog_notebook), page);
+
+      free (current_tab);
+    }
+}
+
+
+/* Return a new dialog widget.  */
+static GtkDialog *
+create_dialog (void)
+{
+  gpgme_error_t err;
+  GtkWidget *dialog_vbox;
+
+  /* Check error.  */
+  err = gpgme_new (&dialog_ctx);
+  if (err)
+    gpa_gpgme_error (err);
+
+  dialog = gtk_dialog_new_with_buttons (_("Crypto Backend Configuration"),
+					NULL /* transient parent */,
+					0,
+					NULL);
+  gtk_dialog_add_button (GTK_DIALOG (dialog),
+			 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT); 
+  gtk_dialog_add_button (GTK_DIALOG (dialog),
+ 			 _("Reset"), CUSTOM_RESPONSE_RESET);
+  gtk_dialog_add_button (GTK_DIALOG (dialog),
+			 GTK_STOCK_APPLY, GTK_RESPONSE_APPLY);
+  gtk_dialog_add_button (GTK_DIALOG (dialog),
+			 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+  g_signal_connect ((gpointer) dialog, "response",
+		    G_CALLBACK (dialog_response), NULL);
+  
+  dialog_vbox = GTK_DIALOG (dialog)->vbox;
+
+  dialog_notebook = gtk_notebook_new ();
+  gtk_box_pack_start (GTK_BOX (dialog_vbox), dialog_notebook, TRUE, TRUE, 0);
+
+  /* This should also be run on show after hide.  */
+  dialog_tab_modified = 0;
+  create_dialog_tabs ();
+
+  return GTK_DIALOG (dialog);
+}
+
+
+GtkWidget *
+gpa_backend_config_dialog_new (void)
+{
+  assert (! dialog);
+
   create_dialog ();
+
+  return dialog;
 }
 
 
-int
-main (int argc, char **argv)
+static void
+hide_backend_config (void)
 {
-  gtk_init (&argc, &argv);
-  gpa_show_backend_config ();
-  return 0;
-}
-        
+  gtk_widget_destroy (dialog);
+  dialog = NULL;
+  dialog_notebook = NULL;
+
+  gpgme_conf_release (dialog_conf);
+  dialog_conf = NULL;
+
+  gpgme_release (dialog_ctx);
+  dialog_ctx = NULL;
+}  

Added: trunk/src/confdialog.h
===================================================================
--- trunk/src/confdialog.h	2007-11-12 19:31:22 UTC (rev 788)
+++ trunk/src/confdialog.h	2008-01-17 22:32:03 UTC (rev 789)
@@ -0,0 +1,26 @@
+/* confdialog.c - The GNU Privacy Assistant
+ * Copyright (C) 2008 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef CONFDIALOG_H
+#define CONFDIALOG_H
+
+GtkWidget *gpa_backend_config_dialog_new (void);
+
+#endif

Modified: trunk/src/fileman.c
===================================================================
--- trunk/src/fileman.c	2007-11-12 19:31:22 UTC (rev 788)
+++ trunk/src/fileman.c	2008-01-17 22:32:03 UTC (rev 789)
@@ -482,6 +482,8 @@
     {_("/Edit/sep2"), NULL, NULL, 0, "<Separator>"},
     {_("/Edit/Pr_eferences..."), NULL, gpa_open_settings_dialog, 0,
      "<StockItem>", GTK_STOCK_PREFERENCES},
+    {_("/Edit/_Backend Preferences..."), NULL,
+     gpa_open_backend_config_dialog, 0, "<StockItem>", GTK_STOCK_PREFERENCES},
   };
   GtkItemFactoryEntry windows_menu[] = {
     {_("/_Windows"), NULL, NULL, 0, "<Branch>"},

Modified: trunk/src/gpa.c
===================================================================
--- trunk/src/gpa.c	2007-11-12 19:31:22 UTC (rev 788)
+++ trunk/src/gpa.c	2008-01-17 22:32:03 UTC (rev 789)
@@ -41,6 +41,7 @@
 #include "keyserver.h"
 #include "keytable.h"
 #include "settingsdlg.h"
+#include "confdialog.h"
 
 #ifdef __MINGW32__
 #include "w32reg.h"
@@ -267,6 +268,7 @@
 
 static GtkWidget *keyringeditor = NULL;
 static GtkWidget *settings_dialog = NULL;
+static GtkWidget *backend_config_dialog = NULL;
 
 static void
 quit_if_no_window (void)
@@ -311,6 +313,7 @@
   gdk_window_raise (gpa_file_manager_get_instance ()->window);
 }
 
+
 void
 gpa_open_settings_dialog (void)
 {
@@ -325,6 +328,22 @@
   gdk_window_raise (settings_dialog->window);
 }
 
+
+void
+gpa_open_backend_config_dialog (void)
+{
+  if (!backend_config_dialog)
+    {
+      backend_config_dialog = gpa_backend_config_dialog_new ();
+      gtk_signal_connect (GTK_OBJECT (backend_config_dialog), "destroy",
+			  GTK_SIGNAL_FUNC (close_main_window),
+                          &backend_config_dialog);
+      gtk_widget_show_all (backend_config_dialog);
+    }
+  gdk_window_raise (backend_config_dialog->window);
+}
+
+
 GtkWidget *
 gpa_get_keyring_editor (void)
 {

Modified: trunk/src/gpa.h
===================================================================
--- trunk/src/gpa.h	2007-11-12 19:31:22 UTC (rev 788)
+++ trunk/src/gpa.h	2008-01-17 22:32:03 UTC (rev 789)
@@ -1,5 +1,6 @@
 /* gpa.h  -  main header
- *	Copyright (C) 2000, 2001 G-N-U GmbH.
+ * Copyright (C) 2000, 2001 G-N-U GmbH.
+ * Copyright (C) 2008 g10 Code GmbH.
  *
  * This file is part of GPA
  *
@@ -56,6 +57,7 @@
 void gpa_open_keyring_editor (void);
 void gpa_open_filemanager (void);
 void gpa_open_settings_dialog (void);
+void gpa_open_backend_config_dialog (void);
 GtkWidget * gpa_get_keyring_editor (void);
 GtkWidget * gpa_get_settings_dialog (void);
 
@@ -64,9 +66,6 @@
 void gpa_run_server_continuation (assuan_context_t ctx, gpg_error_t err);
 void gpa_start_server (void);
 
-
-void gpa_show_backend_config (void);
-
 /*-- utils.c --*/
 /* We are so used to these function thus provide them.  */
 void *xmalloc (size_t n);

Modified: trunk/src/keyring.c
===================================================================
--- trunk/src/keyring.c	2007-11-12 19:31:22 UTC (rev 788)
+++ trunk/src/keyring.c	2008-01-17 22:32:03 UTC (rev 789)
@@ -855,6 +855,8 @@
     {_("/Edit/sep2"), NULL, NULL, 0, "<Separator>"},
     {_("/Edit/Pr_eferences..."), NULL, gpa_open_settings_dialog, 0,
      "<StockItem>", GTK_STOCK_PREFERENCES},
+    {_("/Edit/_Backend Preferences..."), NULL,
+     gpa_open_backend_config_dialog, 0, "<StockItem>", GTK_STOCK_PREFERENCES},
   };
   GtkItemFactoryEntry keys_menu[] = {
     {_("/_Keys"), NULL, NULL, 0, "<Branch>"},



More information about the Gpa-commits mailing list