fix bug 4239, 'Preferences: Text Options Header Display modal is not modal' (sic)
[claws.git] / src / plugins / pgpcore / select-keys.c
index ec1ec4a8132ead94e1c6e0c3c3629c28c8302ccb..cb9cb7847ce3e8a3fb7e5348e54d8237910a0b2e 100644 (file)
@@ -42,17 +42,24 @@ enum col_titles {
     COL_ALGO,
     COL_KEYID,
     COL_NAME,
-    COL_EMAIL,
-    COL_VALIDITY,
+    COL_ADDRESS,
+    COL_TRUST,
+    COL_PTR,
 
     N_COL_TITLES
 };
 
+#define COL_ALGO_WIDTH 70
+#define COL_KEYID_WIDTH 120
+#define COL_NAME_WIDTH 115
+#define COL_ADDRESS_WIDTH 140
+#define COL_TRUST_WIDTH 20
+
 struct select_keys_s {
     int okay;
     GtkWidget *window;
     GtkLabel *toplabel;
-    GtkCMCList *clist;
+    GtkWidget *view;
     const char *pattern;
     unsigned int num_keys;
     gpgme_key_t *kset;
@@ -64,8 +71,8 @@ struct select_keys_s {
 };
 
 
-static void set_row (GtkCMCList *clist, gpgme_key_t key, gpgme_protocol_t proto);
-static gpgme_key_t fill_clist (struct select_keys_s *sk, const char *pattern,
+static void set_row (GtkListStore *store, gpgme_key_t key, gpgme_protocol_t proto);
+static gpgme_key_t fill_view (struct select_keys_s *sk, const char *pattern,
                        gpgme_protocol_t proto);
 static void create_dialog (struct select_keys_s *sk);
 static void open_dialog (struct select_keys_s *sk);
@@ -78,9 +85,6 @@ static void select_btn_cb (GtkWidget *widget, gpointer data);
 static void cancel_btn_cb (GtkWidget *widget, gpointer data);
 static void dont_encrypt_btn_cb (GtkWidget *widget, gpointer data);
 static void other_btn_cb (GtkWidget *widget, gpointer data);
-static void sort_keys (struct select_keys_s *sk, enum col_titles column);
-static void sort_keys_name (GtkWidget *widget, gpointer data);
-static void sort_keys_email (GtkWidget *widget, gpointer data);
 
 static gboolean use_untrusted (gpgme_key_t, gpgme_user_id_t uid, gpgme_protocol_t proto);
 
@@ -124,9 +128,13 @@ gpgmegtk_recipient_selection (GSList *recp_names, SelectionResult *result,
 
     do {
         sk.pattern = recp_names? recp_names->data:NULL;
-       sk.proto = proto;
-        gtk_cmclist_clear (sk.clist);
-        key = fill_clist (&sk, sk.pattern, proto);
+        sk.proto = proto;
+        if (sk.view != NULL) {
+            GtkTreeModel *model =
+                gtk_tree_view_get_model(GTK_TREE_VIEW(sk.view));
+            gtk_list_store_clear(GTK_LIST_STORE(model));
+        }
+        key = fill_view (&sk, sk.pattern, proto);
         update_progress (&sk, 0, sk.pattern ? sk.pattern : "NULL");
        if (!key) {
                gtk_widget_show_all (sk.window);
@@ -164,22 +172,11 @@ gpgmegtk_recipient_selection (GSList *recp_names, SelectionResult *result,
 } 
 
 static void
-destroy_key (gpointer data)
+set_row (GtkListStore *store, gpgme_key_t key, gpgme_protocol_t proto)
 {
-    gpgme_key_t key = data;
-
-    debug_print("unref key %p\n", key);
-
-    gpgme_key_unref (key);
-}
-
-static void
-set_row (GtkCMCList *clist, gpgme_key_t key, gpgme_protocol_t proto)
-{
-    const char *s;
-    const char *text[N_COL_TITLES];
-    char *algo_buf;
-    int row;
+    const gchar *s;
+    gchar *algo_buf, *name, *address;
+    GtkTreeIter iter;
     gsize by_read = 0, by_written = 0;
     gchar *ret_str = NULL;
 
@@ -191,9 +188,6 @@ set_row (GtkCMCList *clist, gpgme_key_t key, gpgme_protocol_t proto)
     algo_buf = g_strdup_printf ("%du/%s", 
          key->subkeys->length,
          gpgme_pubkey_algo_name(key->subkeys->pubkey_algo) );
-    text[COL_ALGO] = algo_buf;
-
-    text[COL_KEYID] = key->subkeys->keyid;
 
     s = key->uids->name;
     if (!s || !*s)
@@ -211,7 +205,7 @@ set_row (GtkCMCList *clist, gpgme_key_t key, gpgme_protocol_t proto)
     if (ret_str && by_written) {
         s = ret_str;
     }
-    text[COL_NAME] = s;
+    name = g_strdup(s);
 
     if (proto == GPGME_PROTOCOL_CMS && (!key->uids->email || !*key->uids->email)) {
        gpgme_user_id_t uid = key->uids->next;
@@ -229,7 +223,7 @@ set_row (GtkCMCList *clist, gpgme_key_t key, gpgme_protocol_t proto)
     if (ret_str && by_written) {
         s = ret_str;
     }
-    text[COL_EMAIL] = s;
+    address = g_strdup(s);
 
     switch (key->uids->validity)
       {
@@ -253,19 +247,30 @@ set_row (GtkCMCList *clist, gpgme_key_t key, gpgme_protocol_t proto)
         s = _("Unknown");
         break;
       }
-    text[COL_VALIDITY] = s;
-
-    row = gtk_cmclist_append (clist, (gchar**)text);
-    g_free (algo_buf);
 
+    gtk_list_store_append(store, &iter);
+    gtk_list_store_set(store, &iter,
+        COL_ALGO, algo_buf,
+        COL_KEYID, key->subkeys->keyid,
+        COL_NAME, name,
+        COL_ADDRESS, address,
+        COL_TRUST, s,
+        COL_PTR, key,
+        -1);
     gpgme_key_ref(key);
-    gtk_cmclist_set_row_data_full (clist, row, key, destroy_key);
+
+    g_free(name);
+    g_free(address);
+    g_free (algo_buf);
 }
 
 static gpgme_key_t 
-fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t proto)
+fill_view (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t proto)
 {
-    GtkCMCList *clist;
+    GtkWidget *view;
+    GtkTreeModel *model;
+    GtkTreeSelection *sel;
+    GtkTreeIter iter;
     gpgme_ctx_t ctx;
     gpgme_error_t err;
     gpgme_key_t key;
@@ -274,13 +279,15 @@ fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t prot
     gboolean exact_match = FALSE;
     gpgme_key_t last_key = NULL;
     gpgme_user_id_t last_uid = NULL;
+
     cm_return_val_if_fail (sk, NULL);
-    clist = sk->clist;
-    cm_return_val_if_fail (clist, NULL);
 
-    debug_print ("select_keys:fill_clist:  pattern '%s' proto %d\n", pattern != NULL ? pattern : "NULL", proto);
+    view = sk->view;
+    cm_return_val_if_fail (view, NULL);
+    model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
+
+    debug_print ("select_keys:fill_view:  pattern '%s' proto %d\n", pattern != NULL ? pattern : "NULL", proto);
 
-    /*gtk_cmclist_freeze (select_keys.clist);*/
     err = gpgme_new (&ctx);
     g_assert (!err);
 
@@ -307,7 +314,7 @@ fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t prot
                continue;
        }
         debug_print ("%% %s:%d:  insert\n", __FILE__ ,__LINE__ );
-        set_row (clist, key, proto ); 
+        set_row (GTK_LIST_STORE(model), key, proto );
        for (; uid; uid = uid->next) {
                gchar *raw_mail = NULL;
 
@@ -325,6 +332,12 @@ fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t prot
                }
                g_free(raw_mail);
        }
+
+       /* Select the first row */
+       sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+       if (gtk_tree_model_get_iter_first(model, &iter))
+               gtk_tree_selection_select_iter(sel, &iter);
+
        num_results++;
        if (last_key != NULL)
                gpgme_key_unref(last_key);
@@ -351,7 +364,7 @@ fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t prot
            sk->select_ctx = NULL;
            gpgme_release (ctx);
     }
-    /*gtk_cmclist_thaw (select_keys.clist);*/
+
     if (exact_match && num_results == 1)
            return last_key;
 
@@ -362,6 +375,16 @@ fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t prot
 }
 
 
+static void
+view_row_activated_cb(GtkTreeView *view,
+               GtkTreePath *path,
+               GtkTreeViewColumn *column,
+               gpointer user_data)
+{
+       select_btn_cb(NULL, user_data);
+}
+
+
 static void 
 create_dialog (struct select_keys_s *sk)
 {
@@ -369,10 +392,14 @@ create_dialog (struct select_keys_s *sk)
     GtkWidget *vbox, *vbox2, *hbox;
     GtkWidget *bbox;
     GtkWidget *scrolledwin;
-    GtkWidget *clist;
+    GtkWidget *view;
     GtkWidget *label;
     GtkWidget *select_btn, *cancel_btn, *dont_encrypt_btn, *other_btn;
-    const char *titles[N_COL_TITLES];
+    GtkListStore *store;
+    GtkCellRenderer *rdr;
+    GtkTreeViewColumn *col;
+    GtkTreeSelection *sel;
+    gint i = 0;
 
     g_assert (!sk->window);
     window = gtkut_window_new (GTK_WINDOW_TOPLEVEL, "select-keys");
@@ -380,6 +407,7 @@ create_dialog (struct select_keys_s *sk)
     gtk_container_set_border_width (GTK_CONTAINER (window), 8);
     gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
     gtk_window_set_modal (GTK_WINDOW (window), TRUE);
+    gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
     g_signal_connect (G_OBJECT (window), "delete_event",
                       G_CALLBACK (delete_event_cb), sk);
     g_signal_connect (G_OBJECT (window), "key_press_event",
@@ -404,26 +432,57 @@ create_dialog (struct select_keys_s *sk)
                                     GTK_POLICY_AUTOMATIC,
                                     GTK_POLICY_AUTOMATIC);
 
-    titles[COL_ALGO]     = _("Size");
-    titles[COL_KEYID]    = _("Key ID");
-    titles[COL_NAME]     = _("Name");
-    titles[COL_EMAIL]    = _("Address");
-    titles[COL_VALIDITY] = _("Trust");
-
-    clist = gtk_cmclist_new_with_titles (N_COL_TITLES, (char**)titles);
-    gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
-    gtk_cmclist_set_column_width (GTK_CMCLIST(clist), COL_ALGO,      70);
-    gtk_cmclist_set_column_width (GTK_CMCLIST(clist), COL_KEYID,    120);
-    gtk_cmclist_set_column_width (GTK_CMCLIST(clist), COL_NAME,     115);
-    gtk_cmclist_set_column_width (GTK_CMCLIST(clist), COL_EMAIL,    140);
-    gtk_cmclist_set_column_width (GTK_CMCLIST(clist), COL_VALIDITY,  20);
-    gtk_cmclist_set_selection_mode (GTK_CMCLIST(clist), GTK_SELECTION_BROWSE);
-    g_signal_connect (G_OBJECT(GTK_CMCLIST(clist)->column[COL_NAME].button),
-                     "clicked",
-                      G_CALLBACK(sort_keys_name), sk);
-    g_signal_connect (G_OBJECT(GTK_CMCLIST(clist)->column[COL_EMAIL].button),
-                     "clicked",
-                      G_CALLBACK(sort_keys_email), sk);
+               store = gtk_list_store_new(N_COL_TITLES,
+                               G_TYPE_STRING,
+                               G_TYPE_STRING,
+                               G_TYPE_STRING,
+                               G_TYPE_STRING,
+                               G_TYPE_STRING,
+                               G_TYPE_POINTER,
+                               -1);
+
+               view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+               g_object_unref(store);
+               gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
+               gtk_tree_view_set_reorderable(GTK_TREE_VIEW(view), FALSE);
+               sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+               gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
+
+               rdr = gtk_cell_renderer_text_new();
+               col = gtk_tree_view_column_new_with_attributes(_("Size"), rdr,
+                               "markup", COL_ALGO, NULL);
+               gtk_tree_view_column_set_min_width(col, COL_ALGO_WIDTH);
+               gtk_tree_view_column_set_sort_column_id(col, i++);
+               gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
+
+               col = gtk_tree_view_column_new_with_attributes(_("Key ID"), rdr,
+                               "markup", COL_KEYID, NULL);
+               gtk_tree_view_column_set_min_width(col, COL_KEYID_WIDTH);
+               gtk_tree_view_column_set_sort_column_id(col, i++);
+               gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
+
+               col = gtk_tree_view_column_new_with_attributes(_("Name"), rdr,
+                               "markup", COL_NAME, NULL);
+               gtk_tree_view_column_set_min_width(col, COL_NAME_WIDTH);
+               gtk_tree_view_column_set_sort_column_id(col, i++);
+               gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
+
+               col = gtk_tree_view_column_new_with_attributes(_("Address"), rdr,
+                               "markup", COL_ADDRESS, NULL);
+               gtk_tree_view_column_set_min_width(col, COL_ADDRESS_WIDTH);
+               gtk_tree_view_column_set_sort_column_id(col, i++);
+               gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
+
+               col = gtk_tree_view_column_new_with_attributes(_("Trust"), rdr,
+                               "markup", COL_TRUST, NULL);
+               gtk_tree_view_column_set_min_width(col, COL_TRUST_WIDTH);
+               gtk_tree_view_column_set_sort_column_id(col, i++);
+               gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
+
+               g_signal_connect(G_OBJECT(view), "row-activated",
+                               G_CALLBACK(view_row_activated_cb), sk);
+
+    gtk_container_add (GTK_CONTAINER (scrolledwin), view);
 
     hbox = gtk_hbox_new (FALSE, 8);
     gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
@@ -457,7 +516,23 @@ create_dialog (struct select_keys_s *sk)
 
     sk->window = window;
     sk->toplabel = GTK_LABEL (label);
-    sk->clist  = GTK_CMCLIST (clist);
+    sk->view  = view;
+}
+
+
+/* Function called by gtk_tree_model_foreach() upon dialog close,
+ * which unrefs the gpgme_key_t pointer from each model line */
+static gboolean
+close_dialog_foreach_func(GtkTreeModel *model,
+               GtkTreePath *path,
+               GtkTreeIter *iter,
+               gpointer user_data)
+{
+       gpgme_key_t key;
+
+       gtk_tree_model_get(model, iter, COL_PTR, &key, -1);
+       gpgme_key_unref(key);
+       return FALSE;
 }
 
 
@@ -476,7 +551,16 @@ open_dialog (struct select_keys_s *sk)
 static void
 close_dialog (struct select_keys_s *sk)
 {
+    GtkTreeModel *model;
     cm_return_if_fail (sk);
+
+    debug_print("pgpcore select-keys dialog closing\n");
+    if (sk->view != NULL) {
+        model = gtk_tree_view_get_model(GTK_TREE_VIEW(sk->view));
+        gtk_tree_model_foreach(model, close_dialog_foreach_func, NULL);
+        gtk_list_store_clear(GTK_LIST_STORE(model));
+    }
+
     gtk_widget_destroy (sk->window);
     sk->window = NULL;
 }
@@ -512,17 +596,14 @@ static void
 select_btn_cb (GtkWidget *widget, gpointer data)
 {
     struct select_keys_s *sk = data;
-    int row;
     gboolean use_key;
     gpgme_key_t key;
 
     cm_return_if_fail (sk);
-    if (!sk->clist->selection) {
-        debug_print ("** nothing selected\n");
-        return;
-    }
-    row = GPOINTER_TO_INT(sk->clist->selection->data);
-    key = gtk_cmclist_get_row_data(sk->clist, row);
+
+    key = gtkut_tree_view_get_selected_pointer(
+        GTK_TREE_VIEW(sk->view), COL_PTR,
+                               NULL, NULL, NULL);
     if (key) {
         gpgme_user_id_t uid;
        for (uid = key->uids; uid; uid = uid->next) {
@@ -598,7 +679,7 @@ other_btn_cb (GtkWidget *widget, gpointer data)
                          NULL );
     if (!uid)
         return;
-    if (fill_clist (sk, uid, sk->proto) != NULL) {
+    if (fill_view (sk, uid, sk->proto) != NULL) {
            gpgme_release(sk->select_ctx);
            sk->select_ctx = NULL;
     }
@@ -620,7 +701,7 @@ use_untrusted (gpgme_key_t key, gpgme_user_id_t uid, gpgme_protocol_t proto)
     buf = g_strdup_printf(_("This encryption key is not fully trusted.\n"
               "If you choose to encrypt the message with this key, you don't\n"
               "know for sure that it will go to the person you mean it to.\n\n"
-              "Key details: ID %s, primary identity %s <%s>\n\n"
+              "Key details: ID %s, primary identity %s &lt;%s&gt;\n\n"
               "Do you trust this key enough to use it anyway?"), 
               key->subkeys->keyid, key->uids->name, key->uids->email);
     aval = alertpanel(title, buf,
@@ -633,78 +714,4 @@ use_untrusted (gpgme_key_t key, gpgme_user_id_t uid, gpgme_protocol_t proto)
        return FALSE;
 }
 
-
-static gint 
-cmp_name (GtkCMCList *clist, gconstpointer pa, gconstpointer pb)
-{
-    gpgme_key_t a = ((GtkCMCListRow *)pa)->data;
-    gpgme_key_t b = ((GtkCMCListRow *)pb)->data;
-    const char *sa, *sb;
-    
-    sa = a? a->uids->name : NULL;
-    sb = b? b->uids->name : NULL;
-    if (!sa)
-        return !!sb;
-    if (!sb)
-        return -1;
-    return g_ascii_strcasecmp(sa, sb);
-}
-
-static gint 
-cmp_email (GtkCMCList *clist, gconstpointer pa, gconstpointer pb)
-{
-    gpgme_key_t a = ((GtkCMCListRow *)pa)->data;
-    gpgme_key_t b = ((GtkCMCListRow *)pb)->data;
-    const char *sa, *sb;
-    
-    sa = a? a->uids->email : NULL;
-    sb = b? b->uids->email : NULL;
-    if (!sa)
-        return !!sb;
-    if (!sb)
-        return -1;
-    return g_ascii_strcasecmp(sa, sb);
-}
-
-static void
-sort_keys ( struct select_keys_s *sk, enum col_titles column)
-{
-    GtkCMCList *clist = sk->clist;
-
-    switch (column) {
-      case COL_NAME:
-        gtk_cmclist_set_compare_func (clist, cmp_name);
-        break;
-      case COL_EMAIL:
-        gtk_cmclist_set_compare_func (clist, cmp_email);
-        break;
-      default:
-        return;
-    }
-
-    /* column clicked again: toggle as-/decending */
-    if ( sk->sort_column == column) {
-        sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
-                        GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
-    }
-    else
-        sk->sort_type = GTK_SORT_ASCENDING;
-
-    sk->sort_column = column;
-    gtk_cmclist_set_sort_type (clist, sk->sort_type);
-    gtk_cmclist_sort (clist);
-}
-
-static void
-sort_keys_name (GtkWidget *widget, gpointer data)
-{
-    sort_keys ((struct select_keys_s*)data, COL_NAME);
-}
-
-static void
-sort_keys_email (GtkWidget *widget, gpointer data)
-{
-    sort_keys ((struct select_keys_s*)data, COL_EMAIL);
-}
-
 #endif /*USE_GPGME*/