Statistics
| Revision:

root / src / select-keys.c @ 31

History | View | Annotate | Download (14.8 kB)

1 1 hiro
/* select-keys.c - GTK+ based key selection
2 1 hiro
 *      Copyright (C) 2001 Werner Koch (dd9jn)
3 1 hiro
 *
4 1 hiro
 * This program is free software; you can redistribute it and/or modify
5 1 hiro
 * it under the terms of the GNU General Public License as published by
6 1 hiro
 * the Free Software Foundation; either version 2 of the License, or
7 1 hiro
 * (at your option) any later version.
8 1 hiro
 *
9 1 hiro
 * This program is distributed in the hope that it will be useful,
10 1 hiro
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 1 hiro
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 1 hiro
 * GNU General Public License for more details.
13 1 hiro
 *
14 1 hiro
 * You should have received a copy of the GNU General Public License
15 1 hiro
 * along with this program; if not, write to the Free Software
16 1 hiro
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 1 hiro
 */
18 1 hiro
19 1 hiro
#ifdef HAVE_CONFIG_H
20 1 hiro
#  include <config.h>
21 1 hiro
#endif
22 1 hiro
23 1 hiro
#ifdef USE_GPGME
24 1 hiro
#include <stdio.h>
25 1 hiro
#include <stdlib.h>
26 1 hiro
27 1 hiro
#include <glib.h>
28 1 hiro
#include <gdk/gdkkeysyms.h>
29 1 hiro
#include <gtk/gtkmain.h>
30 1 hiro
#include <gtk/gtkwidget.h>
31 1 hiro
#include <gtk/gtkwindow.h>
32 1 hiro
#include <gtk/gtkscrolledwindow.h>
33 1 hiro
#include <gtk/gtkvbox.h>
34 1 hiro
#include <gtk/gtkhbox.h>
35 1 hiro
#include <gtk/gtkclist.h>
36 1 hiro
#include <gtk/gtklabel.h>
37 1 hiro
#include <gtk/gtkentry.h>
38 1 hiro
#include <gtk/gtkhbbox.h>
39 1 hiro
#include <gtk/gtkbutton.h>
40 1 hiro
#include <gtk/gtksignal.h>
41 1 hiro
42 1 hiro
#include "intl.h"
43 1 hiro
#include "select-keys.h"
44 1 hiro
#include "utils.h"
45 1 hiro
#include "gtkutils.h"
46 1 hiro
#include "inputdialog.h"
47 1 hiro
#include "manage_window.h"
48 1 hiro
49 1 hiro
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
50 1 hiro
#define DIMof(type,member)   DIM(((type *)0)->member)
51 1 hiro
52 1 hiro
53 1 hiro
enum col_titles {
54 1 hiro
    COL_ALGO,
55 1 hiro
    COL_KEYID,
56 1 hiro
    COL_NAME,
57 1 hiro
    COL_EMAIL,
58 1 hiro
    COL_VALIDITY,
59 1 hiro
60 1 hiro
    N_COL_TITLES
61 1 hiro
};
62 1 hiro
63 1 hiro
struct select_keys_s {
64 1 hiro
    int okay;
65 1 hiro
    GtkWidget *window;
66 1 hiro
    GtkLabel *toplabel;
67 1 hiro
    GtkCList *clist;
68 1 hiro
    const char *pattern;
69 1 hiro
    GpgmeRecipients rset;
70 1 hiro
    GpgmeCtx select_ctx;
71 1 hiro
72 1 hiro
    GtkSortType sort_type;
73 1 hiro
    enum col_titles sort_column;
74 1 hiro
75 1 hiro
};
76 1 hiro
77 1 hiro
78 1 hiro
static void set_row (GtkCList *clist, GpgmeKey key);
79 1 hiro
static void fill_clist (struct select_keys_s *sk, const char *pattern);
80 1 hiro
static void create_dialog (struct select_keys_s *sk);
81 1 hiro
static void open_dialog (struct select_keys_s *sk);
82 1 hiro
static void close_dialog (struct select_keys_s *sk);
83 1 hiro
static gint delete_event_cb (GtkWidget *widget,
84 1 hiro
                             GdkEventAny *event, gpointer data);
85 1 hiro
static gboolean key_pressed_cb (GtkWidget *widget,
86 1 hiro
                                GdkEventKey *event, gpointer data);
87 1 hiro
static void select_btn_cb (GtkWidget *widget, gpointer data);
88 1 hiro
static void cancel_btn_cb (GtkWidget *widget, gpointer data);
89 1 hiro
static void other_btn_cb (GtkWidget *widget, gpointer data);
90 1 hiro
static void sort_keys (struct select_keys_s *sk, enum col_titles column);
91 1 hiro
static void sort_keys_name (GtkWidget *widget, gpointer data);
92 1 hiro
static void sort_keys_email (GtkWidget *widget, gpointer data);
93 1 hiro
94 1 hiro
95 1 hiro
static void
96 1 hiro
update_progress (struct select_keys_s *sk, int running, const char *pattern)
97 1 hiro
{
98 1 hiro
    static int windmill[] = { '-', '\\', '|', '/' };
99 1 hiro
    char *buf;
100 1 hiro
101 1 hiro
    if (!running)
102 1 hiro
        buf = g_strdup_printf (_("Please select key for `%s'"),
103 1 hiro
                               pattern);
104 1 hiro
    else
105 1 hiro
        buf = g_strdup_printf (_("Collecting info for `%s' ... %c"),
106 1 hiro
                               pattern,
107 1 hiro
                               windmill[running%DIM(windmill)]);
108 1 hiro
    gtk_label_set_text (sk->toplabel, buf);
109 1 hiro
    g_free (buf);
110 1 hiro
}
111 1 hiro
112 1 hiro
113 1 hiro
/**
114 1 hiro
 * select_keys_get_recipients:
115 1 hiro
 * @recp_names: A list of email addresses
116 1 hiro
 *
117 1 hiro
 * Select a list of recipients from a given list of email addresses.
118 1 hiro
 * This may pop up a window to present the user a choice, it will also
119 1 hiro
 * check that the recipients key are all valid.
120 1 hiro
 *
121 1 hiro
 * Return value: NULL on error or a list of list of recipients.
122 1 hiro
 **/
123 1 hiro
GpgmeRecipients
124 1 hiro
gpgmegtk_recipient_selection (GSList *recp_names)
125 1 hiro
{
126 1 hiro
    struct select_keys_s sk;
127 1 hiro
    GpgmeError err;
128 1 hiro
129 1 hiro
    memset (&sk, 0, sizeof sk);
130 1 hiro
131 1 hiro
    err = gpgme_recipients_new (&sk.rset);
132 1 hiro
    if (err) {
133 1 hiro
        g_warning ("failed to allocate recipients set: %s",
134 1 hiro
                   gpgme_strerror (err));
135 1 hiro
        return NULL;
136 1 hiro
    }
137 1 hiro
138 1 hiro
    open_dialog (&sk);
139 1 hiro
140 1 hiro
    do {
141 1 hiro
        sk.pattern = recp_names? recp_names->data:NULL;
142 1 hiro
        gtk_clist_clear (sk.clist);
143 1 hiro
        fill_clist (&sk, sk.pattern);
144 1 hiro
        update_progress (&sk, 0, sk.pattern);
145 1 hiro
        gtk_main ();
146 1 hiro
        if (recp_names)
147 1 hiro
            recp_names = recp_names->next;
148 1 hiro
    } while (sk.okay && recp_names);
149 1 hiro
150 1 hiro
    close_dialog (&sk);
151 1 hiro
152 1 hiro
    if (!sk.okay) {
153 1 hiro
        gpgme_recipients_release (sk.rset);
154 1 hiro
        sk.rset = NULL;
155 1 hiro
    }
156 1 hiro
    return sk.rset;
157 1 hiro
}
158 1 hiro
159 1 hiro
static void
160 1 hiro
destroy_key (gpointer data)
161 1 hiro
{
162 1 hiro
    GpgmeKey key = data;
163 1 hiro
    gpgme_key_release (key);
164 1 hiro
}
165 1 hiro
166 1 hiro
static void
167 1 hiro
set_row (GtkCList *clist, GpgmeKey key)
168 1 hiro
{
169 1 hiro
    const char *s;
170 1 hiro
    const char *text[N_COL_TITLES];
171 1 hiro
    char *algo_buf;
172 1 hiro
    int row;
173 1 hiro
174 1 hiro
    /* first check whether the key is capable of encryption which is not
175 1 hiro
     * the case for revoked, expired or sign-only keys */
176 1 hiro
    if ( !gpgme_key_get_ulong_attr (key, GPGME_ATTR_CAN_ENCRYPT, NULL, 0 ) )
177 1 hiro
        return;
178 1 hiro
179 1 hiro
    algo_buf = g_strdup_printf ("%lu/%s",
180 1 hiro
         gpgme_key_get_ulong_attr (key, GPGME_ATTR_LEN, NULL, 0 ),
181 1 hiro
         gpgme_key_get_string_attr (key, GPGME_ATTR_ALGO, NULL, 0 ) );
182 1 hiro
    text[COL_ALGO] = algo_buf;
183 1 hiro
184 1 hiro
    s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID, NULL, 0);
185 1 hiro
    if (strlen (s) == 16)
186 1 hiro
        s += 8; /* show only the short keyID */
187 1 hiro
    text[COL_KEYID] = s;
188 1 hiro
189 1 hiro
    s = gpgme_key_get_string_attr (key, GPGME_ATTR_NAME, NULL, 0);
190 1 hiro
    text[COL_NAME] = s;
191 1 hiro
192 1 hiro
    s = gpgme_key_get_string_attr (key, GPGME_ATTR_EMAIL, NULL, 0);
193 1 hiro
    text[COL_EMAIL] = s;
194 1 hiro
195 1 hiro
    s = gpgme_key_get_string_attr (key, GPGME_ATTR_VALIDITY, NULL, 0);
196 1 hiro
    text[COL_VALIDITY] = s;
197 1 hiro
198 1 hiro
    row = gtk_clist_append (clist, (gchar**)text);
199 1 hiro
    g_free (algo_buf);
200 1 hiro
201 1 hiro
    gtk_clist_set_row_data_full (clist, row, key, destroy_key);
202 1 hiro
}
203 1 hiro
204 1 hiro
205 1 hiro
static void
206 1 hiro
fill_clist (struct select_keys_s *sk, const char *pattern)
207 1 hiro
{
208 1 hiro
    GtkCList *clist;
209 1 hiro
    GpgmeCtx ctx;
210 1 hiro
    GpgmeError err;
211 1 hiro
    GpgmeKey key;
212 1 hiro
    int running=0;
213 1 hiro
214 1 hiro
    g_return_if_fail (sk);
215 1 hiro
    clist = sk->clist;
216 1 hiro
    g_return_if_fail (clist);
217 1 hiro
218 1 hiro
    debug_print ("select_keys:fill_clist:  pattern `%s'\n", pattern);
219 1 hiro
220 1 hiro
    /*gtk_clist_freeze (select_keys.clist);*/
221 1 hiro
    err = gpgme_new (&ctx);
222 1 hiro
    g_assert (!err);
223 1 hiro
224 1 hiro
    sk->select_ctx = ctx;
225 1 hiro
226 1 hiro
    update_progress (sk, ++running, pattern);
227 1 hiro
    while (gtk_events_pending ())
228 1 hiro
        gtk_main_iteration ();
229 1 hiro
230 1 hiro
    err = gpgme_op_keylist_start (ctx, pattern, 0);
231 1 hiro
    if (err) {
232 1 hiro
        debug_print ("** gpgme_op_keylist_start(%s) failed: %s",
233 1 hiro
                     pattern, gpgme_strerror (err));
234 1 hiro
        sk->select_ctx = NULL;
235 1 hiro
        return;
236 1 hiro
    }
237 1 hiro
    update_progress (sk, ++running, pattern);
238 1 hiro
    while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
239 1 hiro
        debug_print ("%% %s:%d:  insert\n", __FILE__ ,__LINE__ );
240 1 hiro
        set_row (clist, key ); key = NULL;
241 1 hiro
        update_progress (sk, ++running, pattern);
242 1 hiro
        while (gtk_events_pending ())
243 1 hiro
            gtk_main_iteration ();
244 1 hiro
    }
245 1 hiro
    debug_print ("%% %s:%d:  ready\n", __FILE__ ,__LINE__ );
246 1 hiro
    if (err != GPGME_EOF)
247 1 hiro
        debug_print ("** gpgme_op_keylist_next failed: %s",
248 1 hiro
                     gpgme_strerror (err));
249 1 hiro
    sk->select_ctx = NULL;
250 1 hiro
    gpgme_release (ctx);
251 1 hiro
    /*gtk_clist_thaw (select_keys.clist);*/
252 1 hiro
}
253 1 hiro
254 1 hiro
255 1 hiro
static void
256 1 hiro
create_dialog (struct select_keys_s *sk)
257 1 hiro
{
258 1 hiro
    GtkWidget *window;
259 1 hiro
    GtkWidget *vbox, *vbox2, *hbox;
260 1 hiro
    GtkWidget *bbox;
261 1 hiro
    GtkWidget *scrolledwin;
262 1 hiro
    GtkWidget *clist;
263 1 hiro
    GtkWidget *label;
264 1 hiro
    GtkWidget *select_btn, *cancel_btn, *other_btn;
265 1 hiro
    const char *titles[N_COL_TITLES];
266 1 hiro
267 1 hiro
    g_assert (!sk->window);
268 1 hiro
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
269 1 hiro
    gtk_widget_set_size_request (window, 520, 280);
270 1 hiro
    gtk_container_set_border_width (GTK_CONTAINER (window), 8);
271 1 hiro
    gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
272 1 hiro
    gtk_window_set_modal (GTK_WINDOW (window), TRUE);
273 1 hiro
    g_signal_connect (G_OBJECT (window), "delete_event",
274 1 hiro
                      G_CALLBACK (delete_event_cb), sk);
275 1 hiro
    g_signal_connect (G_OBJECT (window), "key_press_event",
276 1 hiro
                      G_CALLBACK (key_pressed_cb), sk);
277 1 hiro
    MANAGE_WINDOW_SIGNALS_CONNECT (window);
278 1 hiro
279 1 hiro
    vbox = gtk_vbox_new (FALSE, 8);
280 1 hiro
    gtk_container_add (GTK_CONTAINER (window), vbox);
281 1 hiro
282 1 hiro
    hbox  = gtk_hbox_new(FALSE, 4);
283 1 hiro
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
284 1 hiro
    label = gtk_label_new ( "" );
285 1 hiro
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
286 1 hiro
287 1 hiro
    hbox = gtk_hbox_new (FALSE, 8);
288 1 hiro
    gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
289 1 hiro
    gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
290 1 hiro
291 1 hiro
    scrolledwin = gtk_scrolled_window_new (NULL, NULL);
292 1 hiro
    gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
293 1 hiro
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
294 1 hiro
                                    GTK_POLICY_AUTOMATIC,
295 1 hiro
                                    GTK_POLICY_AUTOMATIC);
296 1 hiro
297 1 hiro
    titles[COL_ALGO]     = _("Size");
298 1 hiro
    titles[COL_KEYID]    = _("Key ID");
299 1 hiro
    titles[COL_NAME]     = _("Name");
300 1 hiro
    titles[COL_EMAIL]    = _("Address");
301 1 hiro
    titles[COL_VALIDITY] = _("Val");
302 1 hiro
303 1 hiro
    clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
304 1 hiro
    gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
305 1 hiro
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO,      72);
306 1 hiro
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID,     76);
307 1 hiro
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME,     130);
308 1 hiro
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL,    130);
309 1 hiro
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY,  20);
310 1 hiro
    gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
311 1 hiro
    g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
312 1 hiro
                      "clicked",
313 1 hiro
                      G_CALLBACK(sort_keys_name), sk);
314 1 hiro
    g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
315 1 hiro
                      "clicked",
316 1 hiro
                      G_CALLBACK(sort_keys_email), sk);
317 1 hiro
318 1 hiro
    hbox = gtk_hbox_new (FALSE, 8);
319 1 hiro
    gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
320 1 hiro
321 31 hiro
    gtkut_stock_button_set_create (&bbox,
322 31 hiro
                                   &select_btn, _("Select"),
323 31 hiro
                                   &cancel_btn, GTK_STOCK_CANCEL,
324 31 hiro
                                   &other_btn,  _("Other"));
325 1 hiro
    gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
326 1 hiro
    gtk_widget_grab_default (select_btn);
327 1 hiro
328 1 hiro
    g_signal_connect (G_OBJECT (select_btn), "clicked",
329 1 hiro
                      G_CALLBACK (select_btn_cb), sk);
330 1 hiro
    g_signal_connect (G_OBJECT(cancel_btn), "clicked",
331 1 hiro
                      G_CALLBACK (cancel_btn_cb), sk);
332 1 hiro
    g_signal_connect (G_OBJECT (other_btn), "clicked",
333 1 hiro
                      G_CALLBACK (other_btn_cb), sk);
334 1 hiro
335 1 hiro
    vbox2 = gtk_vbox_new (FALSE, 4);
336 1 hiro
    gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
337 1 hiro
338 1 hiro
    gtk_widget_show_all (window);
339 1 hiro
340 1 hiro
    sk->window = window;
341 1 hiro
    sk->toplabel = GTK_LABEL (label);
342 1 hiro
    sk->clist  = GTK_CLIST (clist);
343 1 hiro
}
344 1 hiro
345 1 hiro
346 1 hiro
static void
347 1 hiro
open_dialog (struct select_keys_s *sk)
348 1 hiro
{
349 1 hiro
    if (!sk->window)
350 1 hiro
        create_dialog (sk);
351 1 hiro
    manage_window_set_transient (GTK_WINDOW (sk->window));
352 1 hiro
    sk->okay = 0;
353 1 hiro
    sk->sort_column = N_COL_TITLES; /* use an invalid value */
354 1 hiro
    sk->sort_type = GTK_SORT_ASCENDING;
355 1 hiro
    gtk_widget_show (sk->window);
356 1 hiro
}
357 1 hiro
358 1 hiro
359 1 hiro
static void
360 1 hiro
close_dialog (struct select_keys_s *sk)
361 1 hiro
{
362 1 hiro
    g_return_if_fail (sk);
363 1 hiro
    gtk_widget_destroy (sk->window);
364 1 hiro
    sk->window = NULL;
365 1 hiro
}
366 1 hiro
367 1 hiro
368 1 hiro
static gint
369 1 hiro
delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
370 1 hiro
{
371 1 hiro
    struct select_keys_s *sk = data;
372 1 hiro
373 1 hiro
    sk->okay = 0;
374 1 hiro
    gtk_main_quit ();
375 1 hiro
376 1 hiro
    return TRUE;
377 1 hiro
}
378 1 hiro
379 1 hiro
380 1 hiro
static gboolean
381 1 hiro
key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
382 1 hiro
{
383 1 hiro
    struct select_keys_s *sk = data;
384 1 hiro
385 1 hiro
    g_return_val_if_fail (sk, FALSE);
386 1 hiro
    if (event && event->keyval == GDK_Escape) {
387 1 hiro
        sk->okay = 0;
388 1 hiro
        gtk_main_quit ();
389 1 hiro
    }
390 1 hiro
    return FALSE;
391 1 hiro
}
392 1 hiro
393 1 hiro
394 1 hiro
static void
395 1 hiro
select_btn_cb (GtkWidget *widget, gpointer data)
396 1 hiro
{
397 1 hiro
    struct select_keys_s *sk = data;
398 1 hiro
    int row;
399 1 hiro
    GpgmeKey key;
400 1 hiro
401 1 hiro
    g_return_if_fail (sk);
402 1 hiro
    if (!sk->clist->selection) {
403 1 hiro
        debug_print ("** nothing selected");
404 1 hiro
        return;
405 1 hiro
    }
406 1 hiro
    row = GPOINTER_TO_INT(sk->clist->selection->data);
407 1 hiro
    key = gtk_clist_get_row_data(sk->clist, row);
408 1 hiro
    if (key) {
409 1 hiro
        const char *s = gpgme_key_get_string_attr (key,
410 1 hiro
                                                   GPGME_ATTR_FPR,
411 1 hiro
                                                   NULL, 0 );
412 1 hiro
        if ( gpgme_key_get_ulong_attr (key, GPGME_ATTR_VALIDITY, NULL, 0 )
413 1 hiro
             < GPGME_VALIDITY_FULL ) {
414 1 hiro
            debug_print ("** FIXME: we are faking the trust calculation");
415 1 hiro
        }
416 1 hiro
        if (!gpgme_recipients_add_name_with_validity (sk->rset, s,
417 1 hiro
                                                      GPGME_VALIDITY_FULL) ) {
418 1 hiro
            sk->okay = 1;
419 1 hiro
            gtk_main_quit ();
420 1 hiro
        }
421 1 hiro
    }
422 1 hiro
}
423 1 hiro
424 1 hiro
425 1 hiro
static void
426 1 hiro
cancel_btn_cb (GtkWidget *widget, gpointer data)
427 1 hiro
{
428 1 hiro
    struct select_keys_s *sk = data;
429 1 hiro
430 1 hiro
    g_return_if_fail (sk);
431 1 hiro
    sk->okay = 0;
432 1 hiro
    if (sk->select_ctx)
433 1 hiro
        gpgme_cancel (sk->select_ctx);
434 1 hiro
    gtk_main_quit ();
435 1 hiro
}
436 1 hiro
437 1 hiro
438 1 hiro
static void
439 1 hiro
other_btn_cb (GtkWidget *widget, gpointer data)
440 1 hiro
{
441 1 hiro
    struct select_keys_s *sk = data;
442 1 hiro
    char *uid;
443 1 hiro
444 1 hiro
    g_return_if_fail (sk);
445 1 hiro
    uid = input_dialog ( _("Add key"),
446 1 hiro
                         _("Enter another user or key ID:"),
447 1 hiro
                         NULL );
448 1 hiro
    if (!uid)
449 1 hiro
        return;
450 1 hiro
    fill_clist (sk, uid);
451 1 hiro
    update_progress (sk, 0, sk->pattern);
452 1 hiro
    g_free (uid);
453 1 hiro
}
454 1 hiro
455 1 hiro
456 1 hiro
static gint
457 1 hiro
cmp_attr (gconstpointer pa, gconstpointer pb, GpgmeAttr attr)
458 1 hiro
{
459 1 hiro
    GpgmeKey a = ((GtkCListRow *)pa)->data;
460 1 hiro
    GpgmeKey b = ((GtkCListRow *)pb)->data;
461 1 hiro
    const char *sa, *sb;
462 1 hiro
463 1 hiro
    sa = a? gpgme_key_get_string_attr (a, attr, NULL, 0 ) : NULL;
464 1 hiro
    sb = b? gpgme_key_get_string_attr (b, attr, NULL, 0 ) : NULL;
465 1 hiro
    if (!sa)
466 1 hiro
        return !!sb;
467 1 hiro
    if (!sb)
468 1 hiro
        return -1;
469 1 hiro
    return strcasecmp(sa, sb);
470 1 hiro
}
471 1 hiro
472 1 hiro
static gint
473 1 hiro
cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
474 1 hiro
{
475 1 hiro
    return cmp_attr (pa, pb, GPGME_ATTR_NAME);
476 1 hiro
}
477 1 hiro
478 1 hiro
static gint
479 1 hiro
cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
480 1 hiro
{
481 1 hiro
    return cmp_attr (pa, pb, GPGME_ATTR_EMAIL);
482 1 hiro
}
483 1 hiro
484 1 hiro
static void
485 1 hiro
sort_keys ( struct select_keys_s *sk, enum col_titles column)
486 1 hiro
{
487 1 hiro
    GtkCList *clist = sk->clist;
488 1 hiro
489 1 hiro
    switch (column) {
490 1 hiro
      case COL_NAME:
491 1 hiro
        gtk_clist_set_compare_func (clist, cmp_name);
492 1 hiro
        break;
493 1 hiro
      case COL_EMAIL:
494 1 hiro
        gtk_clist_set_compare_func (clist, cmp_email);
495 1 hiro
        break;
496 1 hiro
      default:
497 1 hiro
        return;
498 1 hiro
    }
499 1 hiro
500 1 hiro
    /* column clicked again: toggle as-/decending */
501 1 hiro
    if ( sk->sort_column == column) {
502 1 hiro
        sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
503 1 hiro
                        GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
504 1 hiro
    }
505 1 hiro
    else
506 1 hiro
        sk->sort_type = GTK_SORT_ASCENDING;
507 1 hiro
508 1 hiro
    sk->sort_column = column;
509 1 hiro
    gtk_clist_set_sort_type (clist, sk->sort_type);
510 1 hiro
    gtk_clist_sort (clist);
511 1 hiro
}
512 1 hiro
513 1 hiro
static void
514 1 hiro
sort_keys_name (GtkWidget *widget, gpointer data)
515 1 hiro
{
516 1 hiro
    sort_keys ((struct select_keys_s*)data, COL_NAME);
517 1 hiro
}
518 1 hiro
519 1 hiro
static void
520 1 hiro
sort_keys_email (GtkWidget *widget, gpointer data)
521 1 hiro
{
522 1 hiro
    sort_keys ((struct select_keys_s*)data, COL_EMAIL);
523 1 hiro
}
524 1 hiro
525 1 hiro
#endif /*USE_GPGME*/