a5c9f2dd9dd1e1cff6aaff2c2d0df0cde3b1c606
[claws.git] / src / addr_compl.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  *
4  * Copyright (c) 2000-2001 by Alfons Hoogervorst <alfons@proteus.demon.nl>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24 #include "intl.h"
25 #include "defs.h"
26
27 #include <glib.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <gtk/gtkmain.h>
30 #include <gtk/gtkwindow.h>
31 #include <gtk/gtkentry.h>
32 #include <gtk/gtkeditable.h>
33 #include <gtk/gtkclist.h>
34 #include <gtk/gtkscrolledwindow.h>
35
36 #include <string.h>
37 #include <ctype.h>
38 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
39 #  include <wchar.h>
40 #  include <wctype.h>
41 #endif
42
43 #include "xml.h"
44 #include "addr_compl.h"
45 #include "utils.h"
46 #include "addressbook.h"
47 #include "main.h"
48
49 #define LOG_MESSAGE \
50         debug_mode == 0 ? (debug_mode == debug_mode) : debug_print 
51
52 /* How it works:
53  *
54  * The address book is read into memory. We set up an address list
55  * containing all address book entries. Next we make the completion
56  * list, which contains all the completable strings, and store a
57  * reference to the address entry it belongs to.
58  * After calling the g_completion_complete(), we get a reference
59  * to a valid email address.  
60  *
61  * Completion is very simplified. We never complete on another prefix,
62  * i.e. we neglect the next smallest possible prefix for the current
63  * completion cache. This is simply done so we might break up the
64  * addresses a little more (e.g. break up alfons@proteus.demon.nl into
65  * something like alfons, proteus, demon, nl; and then completing on
66  * any of those words).
67  */ 
68         
69 /* address_entry - structure which refers to the original address entry in the
70  * address book 
71  */
72 typedef struct
73 {
74         gchar *name;
75         gchar *address;
76 } address_entry;
77
78 /* completion_entry - structure used to complete addresses, with a reference
79  * the the real address information.
80  */
81 typedef struct
82 {
83         gchar           *string; /* string to complete */
84         address_entry   *ref;    /* address the string belongs to  */
85 } completion_entry;
86
87 /*******************************************************************************/
88
89 static gint         g_ref_count;        /* list ref count */
90 static GList       *g_completion_list;  /* list of strings to be checked */
91 static GList       *g_address_list;     /* address storage */
92 static GCompletion *g_completion;       /* completion object */
93
94 /* To allow for continuing completion we have to keep track of the state
95  * using the following variables. No need to create a context object. */
96
97 static gint         g_completion_count;         /* nr of addresses incl. the prefix */
98 static gint         g_completion_next;          /* next prev address */
99 static GSList      *g_completion_addresses;     /* unique addresses found in the
100                                                    completion cache. */
101 static gchar       *g_completion_prefix;        /* last prefix. (this is cached here
102                                                  * because the prefix passed to g_completion
103                                                  * is g_strdown()'ed */
104
105 /*******************************************************************************/
106
107 /* completion_func() - used by GTK to find the string data to be used for 
108  * completion 
109  */
110 static gchar *completion_func(gpointer data)
111 {
112         g_return_val_if_fail(data != NULL, NULL);
113
114         return ((completion_entry *)data)->string;
115
116
117 static void init_all(void)
118 {
119         g_completion = g_completion_new(completion_func);
120         g_return_if_fail(g_completion != NULL);
121 }
122
123 static void free_all(void)
124 {
125         GList *walk;
126         
127         walk = g_list_first(g_completion_list);
128         for (; walk != NULL; walk = g_list_next(walk)) {
129                 completion_entry *ce = (completion_entry *) walk->data;
130                 g_free(ce->string);
131                 g_free(walk->data);
132         }
133         g_list_free(g_completion_list);
134         g_completion_list = NULL;
135         
136         walk = g_address_list;
137         for (; walk != NULL; walk = g_list_next(walk)) {
138                 address_entry *ae = (address_entry *) walk->data;
139                 g_free(ae->name);
140                 g_free(ae->address);
141                 g_free(walk->data);
142         }
143         g_list_free(g_address_list);
144         g_address_list = NULL;
145         
146         g_completion_free(g_completion);
147         g_completion = NULL;
148 }
149
150 /* add_address() - adds address to the completion list. this function looks
151  * complicated, but it's only allocation checks.
152  */
153 static gint add_address(const gchar *name, const gchar *address)
154 {
155         address_entry    *ae;
156         completion_entry *ce1;
157         completion_entry *ce2;
158
159         if (!name || !address) return -1;
160
161         debug_print( "completion: add_address: %s - %s\n", name, address );
162
163         ae = g_new0(address_entry, 1);
164         ce1 = g_new0(completion_entry, 1),
165         ce2 = g_new0(completion_entry, 1);
166
167         g_return_val_if_fail(ae != NULL, -1);
168         g_return_val_if_fail(ce1 != NULL && ce2 != NULL, -1);   
169
170         ae->name    = g_strdup(name);
171         ae->address = g_strdup(address);                
172         ce1->string = g_strdup(name);
173         ce2->string = g_strdup(address);
174
175         /* GCompletion list is case sensitive */
176         g_strdown(ce2->string);
177         g_strdown(ce1->string);
178         ce1->ref = ce2->ref = ae;
179
180         g_completion_list = g_list_append(g_completion_list, ce1);
181         g_completion_list = g_list_append(g_completion_list, ce2);
182         g_address_list    = g_list_append(g_address_list,    ae);
183
184         return 0;
185 }
186
187 /* read_address_book()
188  */ 
189 static void read_address_book(void) {   
190         addressbook_load_completion( add_address );
191 }
192
193 /* start_address_completion() - returns the number of addresses 
194  * that should be matched for completion.
195  */
196 gint start_address_completion(void)
197 {
198         clear_completion_cache();
199         if (!g_ref_count) {
200                 init_all();
201                 /* open the address book */
202                 read_address_book();
203                 /* merge the completion entry list into g_completion */
204                 if (g_completion_list)
205                         g_completion_add_items(g_completion, g_completion_list);
206         }
207         g_ref_count++;
208         LOG_MESSAGE("start_address_completion ref count %d\n", g_ref_count);
209
210         return g_list_length(g_completion_list);
211 }
212
213 /* get_address_from_edit() - returns a possible address (or a part)
214  * from an entry box. To make life easier, we only look at the last valid address 
215  * component; address completion only works at the last string component in
216  * the entry box. 
217  */ 
218 gchar *get_address_from_edit(GtkEntry *entry, gint *start_pos)
219 {
220         const gchar *edit_text;
221         gint cur_pos;
222         wchar_t *wtext;
223         wchar_t *wp;
224         wchar_t rfc_mail_sep;
225         gchar *str;
226
227         edit_text = gtk_entry_get_text(entry);
228         if (edit_text == NULL) return NULL;
229
230         wtext = strdup_mbstowcs(edit_text);
231         g_return_val_if_fail(wtext != NULL, NULL);
232
233         cur_pos = gtk_editable_get_position(GTK_EDITABLE(entry));
234
235         if (mbtowc(&rfc_mail_sep, ",", 1) < 0) {
236                 g_free(wtext);
237                 return NULL;
238         }
239
240         /* scan for a separator. doesn't matter if walk points at null byte. */
241         for (wp = wtext + cur_pos; wp > wtext && *wp != rfc_mail_sep; wp--)
242                 ;
243
244         /* have something valid */
245         if (wcslen(wp) == 0) {
246                 g_free(wtext);
247                 return NULL;
248         }
249
250 #define IS_VALID_CHAR(x)        (iswalnum(x) || ((x) > 0x7f))
251
252         /* now scan back until we hit a valid character */
253         for (; *wp && !IS_VALID_CHAR(*wp); wp++)
254                 ;
255
256 #undef IS_VALID_CHAR
257
258         if (wcslen(wp) == 0) {
259                 g_free(wtext);
260                 return NULL;
261         }
262
263         if (start_pos) *start_pos = wp - wtext;
264
265         str = strdup_wcstombs(wp);
266         g_free(wtext);
267
268         return str;
269
270
271 /* replace_address_in_edit() - replaces an incompleted address with a completed one.
272  */
273 void replace_address_in_edit(GtkEntry *entry, const gchar *newtext,
274                              gint start_pos)
275 {
276         gtk_editable_delete_text(GTK_EDITABLE(entry), start_pos, -1);
277         gtk_editable_insert_text(GTK_EDITABLE(entry), newtext, strlen(newtext),
278                                  &start_pos);
279         gtk_editable_set_position(GTK_EDITABLE(entry), -1);
280 }
281
282 /* complete_address() - tries to complete an addres, and returns the
283  * number of addresses found. use get_complete_address() to get one.
284  * returns zero if no match was found, otherwise the number of addresses,
285  * with the original prefix at index 0. 
286  */
287 guint complete_address(const gchar *str)
288 {
289         GList *result;
290         gchar *d;
291         guint  count, cpl;
292         completion_entry *ce;
293
294         g_return_val_if_fail(str != NULL, 0);
295
296         Xstrdup_a(d, str, return 0);
297
298         clear_completion_cache();
299         g_completion_prefix = g_strdup(str);
300
301         /* g_completion is case sensitive */
302         g_strdown(d);
303         result = g_completion_complete(g_completion, d, NULL);
304
305         count = g_list_length(result);
306         if (count) {
307                 /* create list with unique addresses  */
308                 for (cpl = 0, result = g_list_first(result);
309                      result != NULL;
310                      result = g_list_next(result)) {
311                         ce = (completion_entry *)(result->data);
312                         if (NULL == g_slist_find(g_completion_addresses,
313                                                  ce->ref)) {
314                                 cpl++;
315                                 g_completion_addresses =
316                                         g_slist_append(g_completion_addresses,
317                                                        ce->ref);
318                         }
319                 }
320                 count = cpl + 1;        /* index 0 is the original prefix */
321                 g_completion_next = 1;  /* we start at the first completed one */
322         } else {
323                 g_free(g_completion_prefix);
324                 g_completion_prefix = NULL;
325         }
326
327         g_completion_count = count;
328         return count;
329 }
330
331 /* get_complete_address() - returns a complete address. the returned
332  * string should be freed 
333  */
334 gchar *get_complete_address(gint index)
335 {
336         const address_entry *p;
337         
338         if (index < g_completion_count) {
339                 if (index == 0)
340                         return g_strdup(g_completion_prefix);
341                 else {
342                         /* get something from the unique addresses */
343                         p = (address_entry *)g_slist_nth_data
344                                 (g_completion_addresses, index - 1);
345                         if (p == NULL)
346                                 return NULL;
347                         else
348                                 return g_strdup_printf
349                                         ("%s <%s>", p->name, p->address);
350                 }
351         } else
352                 return NULL;
353 }
354
355 gchar *get_next_complete_address(void)
356 {
357         if (is_completion_pending()) {
358                 gchar *res;
359
360                 res = get_complete_address(g_completion_next);
361                 g_completion_next += 1;
362                 if (g_completion_next >= g_completion_count)
363                         g_completion_next = 0;
364
365                 return res;
366         } else
367                 return NULL;
368 }
369
370 gchar *get_prev_complete_address(void)
371 {
372         if (is_completion_pending()) {
373                 int n = g_completion_next - 2;
374
375                 /* real previous */
376                 n = (n + (g_completion_count * 5)) % g_completion_count;
377
378                 /* real next */
379                 g_completion_next = n + 1;
380                 if (g_completion_next >=  g_completion_count)
381                         g_completion_next = 0;
382                 return get_complete_address(n);
383         } else
384                 return NULL;
385 }
386
387 guint get_completion_count(void)
388 {
389         if (is_completion_pending())
390                 return g_completion_count;
391         else
392                 return 0;
393 }
394
395 /* should clear up anything after complete_address() */
396 void clear_completion_cache(void)
397 {
398         if (is_completion_pending()) {
399                 if (g_completion_prefix)
400                         g_free(g_completion_prefix);
401
402                 if (g_completion_addresses) {
403                         g_slist_free(g_completion_addresses);
404                         g_completion_addresses = NULL;
405                 }
406
407                 g_completion_count = g_completion_next = 0;
408         }
409 }
410
411 gboolean is_completion_pending(void)
412 {
413         /* check if completion pending, i.e. we might satisfy a request for the next
414          * or previous address */
415          return g_completion_count;
416 }
417
418 /* invalidate_address_completion() - should be called if address book
419  * changed; 
420  */
421 gint invalidate_address_completion(void)
422 {
423         if (g_ref_count) {
424                 /* simply the same as start_address_completion() */
425                 LOG_MESSAGE("Invalidation request for address completion\n");
426                 free_all();
427                 init_all();
428                 read_address_book();
429                 g_completion_add_items(g_completion, g_completion_list);
430                 clear_completion_cache();
431         }
432
433         return g_list_length(g_completion_list);
434 }
435
436 gint end_address_completion(void)
437 {
438         clear_completion_cache();
439
440         if (0 == --g_ref_count)
441                 free_all();
442
443         LOG_MESSAGE("end_address_completion ref count %d\n", g_ref_count);
444
445         return g_ref_count; 
446 }
447
448
449 /* address completion entry ui. the ui (completion list was inspired by galeon's
450  * auto completion list). remaining things powered by sylpheed's completion engine.
451  */
452
453 #define ENTRY_DATA_TAB_HOOK     "tab_hook"                      /* used to lookup entry */
454 #define WINDOW_DATA_COMPL_ENTRY "compl_entry"   /* used to store entry for compl. window */
455 #define WINDOW_DATA_COMPL_CLIST "compl_clist"   /* used to store clist for compl. window */
456
457 static void address_completion_mainwindow_set_focus     (GtkWindow   *window,
458                                                          GtkWidget   *widget,
459                                                          gpointer     data);
460 static gboolean address_completion_entry_key_pressed    (GtkEntry    *entry,
461                                                          GdkEventKey *ev,
462                                                          gpointer     data);
463 static gboolean address_completion_complete_address_in_entry
464                                                         (GtkEntry    *entry,
465                                                          gboolean     next);
466 static void address_completion_create_completion_window (GtkEntry    *entry);
467
468 static void completion_window_select_row(GtkCList        *clist,
469                                          gint             row,
470                                          gint             col,
471                                          GdkEvent        *event,
472                                          GtkWidget      **completion_window);
473 static gboolean completion_window_button_press
474                                         (GtkWidget       *widget,
475                                          GdkEventButton  *event,
476                                          GtkWidget      **completion_window);
477 static gboolean completion_window_key_press
478                                         (GtkWidget       *widget,
479                                          GdkEventKey     *event,
480                                          GtkWidget      **completion_window);
481
482
483 static void completion_window_advance_to_row(GtkCList *clist, gint row)
484 {
485         g_return_if_fail(row < g_completion_count);
486         gtk_clist_select_row(clist, row, 0);
487 }
488
489 static void completion_window_advance_selection(GtkCList *clist, gboolean forward)
490 {
491         int row;
492
493         g_return_if_fail(clist != NULL);
494         g_return_if_fail(clist->selection != NULL);
495
496         row = GPOINTER_TO_INT(clist->selection->data);
497
498         row = forward ? (row + 1) % g_completion_count :
499                         (row - 1) < 0 ? g_completion_count - 1 : row - 1;
500
501         gtk_clist_freeze(clist);
502         completion_window_advance_to_row(clist, row);                                   
503         gtk_clist_thaw(clist);
504 }
505
506 /* completion_window_accept_selection() - accepts the current selection in the
507  * clist, and destroys the window */
508 static void completion_window_accept_selection(GtkWidget **window,
509                                                GtkCList *clist,
510                                                GtkEntry *entry)
511 {
512         gchar *address = NULL, *text = NULL;
513         gint   cursor_pos, row, col;
514
515         g_return_if_fail(window != NULL);
516         g_return_if_fail(*window != NULL);
517         g_return_if_fail(clist != NULL);
518         g_return_if_fail(entry != NULL);
519         g_return_if_fail(clist->selection != NULL);
520
521         col = 0;
522
523         /* FIXME: I believe it's acceptable to access the selection member directly  */
524         row = GPOINTER_TO_INT(clist->selection->data);
525
526         /* we just need the cursor position */
527         address = get_address_from_edit(entry, &cursor_pos);
528         gtk_clist_get_text(clist, row, col, &text);
529         replace_address_in_edit(entry, text, cursor_pos);
530         g_free(address);                                
531
532         clear_completion_cache();
533         gtk_widget_destroy(*window);
534         *window = NULL;
535 }
536
537 /* should be called when creating the main window containing address
538  * completion entries */
539 void address_completion_start(GtkWidget *mainwindow)
540 {
541         start_address_completion();
542
543         /* register focus change hook */
544         gtk_signal_connect(GTK_OBJECT(mainwindow), "set_focus",
545                            GTK_SIGNAL_FUNC(address_completion_mainwindow_set_focus),
546                            mainwindow);
547 }
548
549 /* Need unique data to make unregistering signal handler possible for the auto
550  * completed entry */
551 #define COMPLETION_UNIQUE_DATA (GINT_TO_POINTER(0xfeefaa))
552
553 void address_completion_register_entry(GtkEntry *entry)
554 {
555         g_return_if_fail(entry != NULL);
556         g_return_if_fail(GTK_IS_ENTRY(entry));
557
558         /* add hooked property */
559         gtk_object_set_data(GTK_OBJECT(entry), ENTRY_DATA_TAB_HOOK, entry);
560
561         /* add keypress event */
562         gtk_signal_connect_full(GTK_OBJECT(entry), "key_press_event",
563                                 GTK_SIGNAL_FUNC(address_completion_entry_key_pressed),
564                                 NULL,
565                                 COMPLETION_UNIQUE_DATA,
566                                 NULL,
567                                 0,
568                                 0); /* magic */
569 }
570
571 void address_completion_unregister_entry(GtkEntry *entry)
572 {
573         GtkObject *entry_obj;
574
575         g_return_if_fail(entry != NULL);
576         g_return_if_fail(GTK_IS_ENTRY(entry));
577
578         entry_obj = gtk_object_get_data(GTK_OBJECT(entry), ENTRY_DATA_TAB_HOOK);
579         g_return_if_fail(entry_obj);
580         g_return_if_fail(entry_obj == GTK_OBJECT(entry));
581
582         /* has the hooked property? */
583         gtk_object_set_data(GTK_OBJECT(entry), ENTRY_DATA_TAB_HOOK, NULL);
584
585         /* remove the hook */
586         gtk_signal_disconnect_by_func(GTK_OBJECT(entry), 
587                 GTK_SIGNAL_FUNC(address_completion_entry_key_pressed),
588                 COMPLETION_UNIQUE_DATA);
589 }
590
591 /* should be called when main window with address completion entries
592  * terminates.
593  * NOTE: this function assumes that it is called upon destruction of
594  * the window */
595 void address_completion_end(GtkWidget *mainwindow)
596 {
597         /* if address_completion_end() is really called on closing the window,
598          * we don't need to unregister the set_focus_cb */
599         end_address_completion();
600 }
601
602 /* if focus changes to another entry, then clear completion cache */
603 static void address_completion_mainwindow_set_focus(GtkWindow *window,
604                                                     GtkWidget *widget,
605                                                     gpointer   data)
606 {
607         if (widget)
608                 clear_completion_cache();
609 }
610
611 /* watch for tabs in one of the address entries. if no tab then clear the
612  * completion cache */
613 static gboolean address_completion_entry_key_pressed(GtkEntry    *entry,
614                                                      GdkEventKey *ev,
615                                                      gpointer     data)
616 {
617         if (ev->keyval == GDK_Tab) {
618                 if (address_completion_complete_address_in_entry(entry, TRUE)) {
619                         address_completion_create_completion_window(entry);
620                         /* route a void character to the default handler */
621                         /* this is a dirty hack; we're actually changing a key
622                          * reported by the system. */
623                         ev->keyval = GDK_AudibleBell_Enable;
624                         ev->state &= ~GDK_SHIFT_MASK;
625                         gtk_signal_emit_stop_by_name(GTK_OBJECT(entry),
626                                                      "key_press_event");
627                 } else {
628                         /* old behaviour */
629                 }
630         } else if (ev->keyval == GDK_Shift_L
631                 || ev->keyval == GDK_Shift_R
632                 || ev->keyval == GDK_Control_L
633                 || ev->keyval == GDK_Control_R
634                 || ev->keyval == GDK_Caps_Lock
635                 || ev->keyval == GDK_Shift_Lock
636                 || ev->keyval == GDK_Meta_L
637                 || ev->keyval == GDK_Meta_R
638                 || ev->keyval == GDK_Alt_L
639                 || ev->keyval == GDK_Alt_R) {
640                 /* these buttons should not clear the cache... */
641         } else
642                 clear_completion_cache();
643
644         return TRUE;
645 }
646
647 /* initialize the completion cache and put first completed string
648  * in entry. this function used to do back cycling but this is not
649  * currently used. since the address completion behaviour has been
650  * changed regularly, we keep the feature in case someone changes
651  * his / her mind again. :) */
652 static gboolean address_completion_complete_address_in_entry(GtkEntry *entry,
653                                                              gboolean  next)
654 {
655         gint ncount, cursor_pos;
656         gchar *address, *new = NULL;
657         gboolean completed = FALSE;
658
659         g_return_val_if_fail(entry != NULL, FALSE);
660
661         if (!GTK_WIDGET_HAS_FOCUS(entry)) return FALSE;
662
663         /* get an address component from the cursor */
664         if (0 != (address = get_address_from_edit(entry, &cursor_pos))) {
665                 /* still something in the cache */
666                 if (is_completion_pending()) {
667                         new = next ? get_next_complete_address() :
668                                 get_prev_complete_address();
669                 } else {
670                         if (0 < (ncount = complete_address(address)))
671                                 new = get_next_complete_address();
672                 }
673
674                 if (new) {
675                         /* prevent "change" signal */
676                         replace_address_in_edit(entry, new, cursor_pos);
677                         g_free(new);
678                         completed = TRUE;
679                 }
680
681                 g_free(address);
682         }                                       
683
684         return completed;
685 }
686
687 static void address_completion_create_completion_window(GtkEntry *entry_)
688 {
689         static GtkWidget *completion_window;
690         gint x, y, height, width, depth;
691         GtkWidget *scroll, *clist;
692         GtkRequisition r;
693         guint count = 0;
694         GtkWidget *entry = GTK_WIDGET(entry_);
695
696         if (completion_window) {
697                 gtk_widget_destroy(completion_window);
698                 completion_window = NULL;
699         }
700
701         scroll = gtk_scrolled_window_new(NULL, NULL);
702         clist  = gtk_clist_new(1);
703         gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_SINGLE);
704         
705         completion_window = gtk_window_new(GTK_WINDOW_POPUP);
706
707         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
708                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
709         gtk_container_add(GTK_CONTAINER(completion_window), scroll);
710         gtk_container_add(GTK_CONTAINER(scroll), clist);
711
712         /* set the unique data so we can always get back the entry and
713          * clist window to which this completion window has been attached */
714         gtk_object_set_data(GTK_OBJECT(completion_window),
715                             WINDOW_DATA_COMPL_ENTRY, entry_);
716         gtk_object_set_data(GTK_OBJECT(completion_window),
717                             WINDOW_DATA_COMPL_CLIST, clist);
718
719         gtk_signal_connect(GTK_OBJECT(clist), "select_row",
720                            GTK_SIGNAL_FUNC(completion_window_select_row),
721                            &completion_window);
722
723         for (count = 0; count < get_completion_count(); count++) {
724                 gchar *text[] = {NULL, NULL};
725
726                 text[0] = get_complete_address(count);
727                 gtk_clist_append(GTK_CLIST(clist), text);
728                 g_free(text[0]);
729         }
730
731         gdk_window_get_geometry(entry->window, &x, &y, &width, &height, &depth);
732         gdk_window_get_deskrelative_origin (entry->window, &x, &y);
733         y += height;
734         gtk_widget_set_uposition(completion_window, x, y);
735
736         gtk_widget_size_request(clist, &r);
737         gtk_widget_set_usize(completion_window, width, r.height);
738         gtk_widget_show_all(completion_window);
739         gtk_widget_size_request(clist, &r);
740
741         if ((y + r.height) > gdk_screen_height()) {
742                 gtk_window_set_policy(GTK_WINDOW(completion_window),
743                                       TRUE, FALSE, FALSE);
744                 gtk_widget_set_usize(completion_window, width,
745                                      gdk_screen_height () - y);
746         }
747
748         gtk_signal_connect(GTK_OBJECT(completion_window),
749                            "button-press-event",
750                            GTK_SIGNAL_FUNC(completion_window_button_press),
751                            &completion_window);
752         gtk_signal_connect(GTK_OBJECT(completion_window),
753                            "key-press-event",
754                            GTK_SIGNAL_FUNC(completion_window_key_press),
755                            &completion_window);
756         gdk_pointer_grab(completion_window->window, TRUE,
757                          GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
758                          GDK_BUTTON_RELEASE_MASK,
759                          NULL, NULL, GDK_CURRENT_TIME);
760         gtk_grab_add(completion_window);
761
762         /* this gets rid of the irritating focus rectangle that doesn't
763          * follow the selection */
764         GTK_WIDGET_UNSET_FLAGS(clist, GTK_CAN_FOCUS);
765         gtk_clist_select_row(GTK_CLIST(clist), 1, 0);
766 }
767
768
769 /* row selection sends completed address to entry.
770  * note: event is NULL if selected by anything else than a mouse button. */
771 static void completion_window_select_row(GtkCList *clist, gint row, gint col,
772                                          GdkEvent *event,
773                                          GtkWidget **completion_window)
774 {
775         GtkEntry *entry;
776
777         /* first check if it's anything but a mouse event. Mouse events
778          * accept the completion. Anything else is accepted by the
779          * completion_window_key_press() */
780         if (!event) {
781                 /* event == NULL if key press did the selection or just
782                  * event emitted with signal_emit_XXX(). This seems to 
783                  * be the case for the gtk versions I have seen */
784                 return;
785         }
786
787         /* however, a future version of GTK might pass the event type
788          * that triggered the select_row. since this event handler
789          * only wants mouse clicks, we check for that. */
790         if (event->type != GDK_BUTTON_RELEASE) {
791                 return;
792         }
793
794         g_return_if_fail(completion_window != NULL);
795         g_return_if_fail(*completion_window != NULL);
796
797         entry = GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(*completion_window),
798                                               WINDOW_DATA_COMPL_ENTRY));
799         g_return_if_fail(entry != NULL);
800
801         completion_window_accept_selection(completion_window, clist, entry);    
802 }
803
804 /* completion_window_button_press() - check is mouse click is anywhere
805  * else (not in the completion window). in that case the completion
806  * window is destroyed, and the original prefix is restored */
807 static gboolean completion_window_button_press(GtkWidget *widget,
808                                                GdkEventButton *event,
809                                                GtkWidget **completion_window)
810 {
811         GtkWidget *event_widget, *entry;
812         gchar *prefix;
813         gint cursor_pos;
814         gboolean restore = TRUE;
815
816         g_return_val_if_fail(completion_window != NULL, FALSE);
817         g_return_val_if_fail(*completion_window != NULL, FALSE);
818
819         entry = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(*completion_window),
820                                                WINDOW_DATA_COMPL_ENTRY));
821         g_return_val_if_fail(entry != NULL, FALSE);
822
823         event_widget = gtk_get_event_widget((GdkEvent *)event);
824         if (event_widget != widget) {
825                 while (event_widget) {
826                         if (event_widget == widget)
827                                 return FALSE;
828                         else if (event_widget == entry) {
829                                 restore = FALSE;
830                                 break;
831                         }
832                     event_widget = event_widget->parent;
833                 }
834         }
835
836         if (restore) {
837                 prefix = get_complete_address(0);
838                 g_free(get_address_from_edit(GTK_ENTRY(entry), &cursor_pos));
839                 replace_address_in_edit(GTK_ENTRY(entry), prefix, cursor_pos);
840         }
841
842         gtk_widget_destroy(*completion_window);
843         *completion_window = NULL;
844
845         clear_completion_cache();
846         return TRUE;
847 }
848
849 static gboolean completion_window_key_press(GtkWidget *widget,
850                                             GdkEventKey *event,
851                                             GtkWidget **completion_window)
852 {
853         GdkEventKey tmp_event;
854         GtkWidget *entry;
855         gchar *prefix;
856         gint cursor_pos;
857         GtkWidget *clist;
858
859         g_return_val_if_fail(completion_window != NULL, FALSE);
860         g_return_val_if_fail(*completion_window != NULL, FALSE);
861
862         entry = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(*completion_window),
863                                                WINDOW_DATA_COMPL_ENTRY));
864         clist = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(*completion_window),
865                                                WINDOW_DATA_COMPL_CLIST));
866         g_return_val_if_fail(entry != NULL, FALSE);
867
868         /* allow keyboard navigation in the alternatives clist */
869         if (event->keyval == GDK_Up || event->keyval == GDK_Down ||
870             event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down) {
871                 completion_window_advance_selection
872                         (GTK_CLIST(clist),
873                          event->keyval == GDK_Down ||
874                          event->keyval == GDK_Page_Down ? TRUE : FALSE);
875                 return FALSE;
876         }               
877
878         /* also make tab / shift tab go to next previous completion entry. we're
879          * changing the key value */
880         if (event->keyval == GDK_Tab || event->keyval == GDK_ISO_Left_Tab) {
881                 event->keyval = (event->state & GDK_SHIFT_MASK)
882                         ? GDK_Up : GDK_Down;
883                 /* need to reset shift state if going up */
884                 if (event->state & GDK_SHIFT_MASK)
885                         event->state &= ~GDK_SHIFT_MASK;
886                 completion_window_advance_selection(GTK_CLIST(clist), 
887                         event->keyval == GDK_Down ? TRUE : FALSE);
888                 return FALSE;
889         }
890
891         /* look for presses that accept the selection */
892         if (event->keyval == GDK_Return || event->keyval == GDK_space) {
893                 completion_window_accept_selection(completion_window,
894                                                    GTK_CLIST(clist),
895                                                    GTK_ENTRY(entry));
896                 return FALSE;
897         }
898
899         /* key state keys should never be handled */
900         if (event->keyval == GDK_Shift_L
901                  || event->keyval == GDK_Shift_R
902                  || event->keyval == GDK_Control_L
903                  || event->keyval == GDK_Control_R
904                  || event->keyval == GDK_Caps_Lock
905                  || event->keyval == GDK_Shift_Lock
906                  || event->keyval == GDK_Meta_L
907                  || event->keyval == GDK_Meta_R
908                  || event->keyval == GDK_Alt_L
909                  || event->keyval == GDK_Alt_R) {
910                 return FALSE;
911         }
912
913         /* other key, let's restore the prefix (orignal text) */
914         prefix = get_complete_address(0);
915         g_free(get_address_from_edit(GTK_ENTRY(entry), &cursor_pos));
916         replace_address_in_edit(GTK_ENTRY(entry), prefix, cursor_pos);
917         g_free(prefix);
918         clear_completion_cache();
919
920         /* make sure anything we typed comes in the edit box */
921         tmp_event.type       = event->type;
922         tmp_event.window     = entry->window;
923         tmp_event.send_event = TRUE;
924         tmp_event.time       = event->time;
925         tmp_event.state      = event->state;
926         tmp_event.keyval     = event->keyval;
927         tmp_event.length     = event->length;
928         tmp_event.string     = event->string;
929         gtk_widget_event(entry, (GdkEvent *)&tmp_event);
930
931         /* and close the completion window */
932         gtk_widget_destroy(*completion_window);
933         *completion_window = NULL;
934
935         return TRUE;
936 }