From: Paul Mangan Date: Fri, 2 May 2003 13:20:50 +0000 (+0000) Subject: revert match's changes to (finally) remove the bug where opening a folder causes... X-Git-Tag: rel_0_9_0~59 X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=commitdiff_plain;h=21aa5157ab975707afdd32cad73bf757895010e3 revert match's changes to (finally) remove the bug where opening a folder causes massive slowdown when using the 'Display sender using address book' option --- diff --git a/ChangeLog.claws b/ChangeLog.claws index 880888981..8f59cf2fd 100644 --- a/ChangeLog.claws +++ b/ChangeLog.claws @@ -1,3 +1,13 @@ +2003-05-02 [paul] 0.8.11claws139 + + * src/addr_compl.[ch] + src/addressbook.[ch] + src/addrindex.c + src/summaryview.c + revert match's changes to (finally) remove the bug where + opening a folder causes massive slowdown when using the + 'Display sender using address book' option + 2003-05-01 [melvin] 0.8.11claws138 * src/plugins/dillo_viewer/dillo_prefs.[ch] diff --git a/configure.ac b/configure.ac index c6fbac1e2..dc51d269d 100644 --- a/configure.ac +++ b/configure.ac @@ -11,7 +11,7 @@ MINOR_VERSION=8 MICRO_VERSION=11 INTERFACE_AGE=0 BINARY_AGE=0 -EXTRA_VERSION=claws138 +EXTRA_VERSION=claws139 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION dnl set $target diff --git a/src/addr_compl.c b/src/addr_compl.c index 8a1ee9502..959f66ed9 100644 --- a/src/addr_compl.c +++ b/src/addr_compl.c @@ -43,9 +43,6 @@ #include "addressbook.h" #include "addr_compl.h" #include "utils.h" -#include "addritem.h" -#include "addrquery.h" -#include /* How it works: * @@ -56,122 +53,166 @@ * After calling the g_completion_complete(), we get a reference * to a valid email address. * - * Completion is very simplified. We never complete on another searchTerm, - * i.e. we neglect the next smallest possible searchTerm for the current + * Completion is very simplified. We never complete on another prefix, + * i.e. we neglect the next smallest possible prefix for the current * completion cache. This is simply done so we might break up the * addresses a little more (e.g. break up alfons@proteus.demon.nl into * something like alfons, proteus, demon, nl; and then completing on * any of those words). + */ + +/* address_entry - structure which refers to the original address entry in the + * address book */ +typedef struct +{ + gchar *name; + gchar *address; +} address_entry; -typedef struct _CompletionWindow CompletionWindow; -struct _CompletionWindow { - gint listCount; - gchar *searchTerm; - GtkWidget *window; - GtkWidget *entry; - GtkWidget *clist; -}; - -/** - * Current query ID. +/* completion_entry - structure used to complete addresses, with a reference + * the the real address information. */ -static gint _queryID_ = 0; +typedef struct +{ + gchar *string; /* string to complete */ + address_entry *ref; /* address the string belongs to */ +} completion_entry; -/** - * Completion idle ID. - */ -static gint _completionIdleID_ = 0; +/*******************************************************************************/ -/** - * Completion window. - */ -static CompletionWindow *_compWindow_ = NULL; +static gint g_ref_count; /* list ref count */ +static GList *g_completion_list; /* list of strings to be checked */ +static GList *g_address_list; /* address storage */ +static GCompletion *g_completion; /* completion object */ -/** - * Mutex to protect callback from multiple threads. - */ -static pthread_mutex_t _completionMutex_ = PTHREAD_MUTEX_INITIALIZER; +/* To allow for continuing completion we have to keep track of the state + * using the following variables. No need to create a context object. */ -/** - * Completion queue list. - */ -static GList *_displayQueue_ = NULL; +static gint g_completion_count; /* nr of addresses incl. the prefix */ +static gint g_completion_next; /* next prev address */ +static GSList *g_completion_addresses; /* unique addresses found in the + completion cache. */ +static gchar *g_completion_prefix; /* last prefix. (this is cached here + * because the prefix passed to g_completion + * is g_strdown()'ed */ + +/*******************************************************************************/ -/** - * Create a completion window object. - * \return Initialized completion window. +/* completion_func() - used by GTK to find the string data to be used for + * completion */ -static CompletionWindow *addrcompl_create_window( void ) { - CompletionWindow *cw; +static gchar *completion_func(gpointer data) +{ + g_return_val_if_fail(data != NULL, NULL); - cw = g_new0( CompletionWindow, 1 ); - cw->listCount = 0; - cw->searchTerm = NULL; - cw->window = NULL; - cw->entry = NULL; - cw->clist = NULL; + return ((completion_entry *)data)->string; +} - return cw; +static void init_all(void) +{ + g_completion = g_completion_new(completion_func); + g_return_if_fail(g_completion != NULL); } -/** - * Destroy completion window. - * \param cw Window to destroy. - */ -static void addrcompl_destroy_window( CompletionWindow *cw ) { - /* Remove idler function... or application may not terminate */ - if( _completionIdleID_ != 0 ) { - gtk_idle_remove( _completionIdleID_ ); - _completionIdleID_ = 0; +static void free_all(void) +{ + GList *walk; + + walk = g_list_first(g_completion_list); + for (; walk != NULL; walk = g_list_next(walk)) { + completion_entry *ce = (completion_entry *) walk->data; + g_free(ce->string); + g_free(walk->data); } - - /* Now destroy window */ - if( cw ) { - /* Clear references to widgets */ - cw->entry = NULL; - cw->clist = NULL; - - /* Free objects */ - if( cw->window ) { - gtk_widget_hide( cw->window ); - gtk_widget_destroy( cw->window ); - } - cw->window = NULL; + g_list_free(g_completion_list); + g_completion_list = NULL; + + walk = g_address_list; + for (; walk != NULL; walk = g_list_next(walk)) { + address_entry *ae = (address_entry *) walk->data; + g_free(ae->name); + g_free(ae->address); + g_free(walk->data); } + g_list_free(g_address_list); + g_address_list = NULL; + + g_completion_free(g_completion); + g_completion = NULL; } -/** - * Free up completion window. - * \param cw Window to free. +static void add_address1(const char *str, address_entry *ae) +{ + completion_entry *ce1; + ce1 = g_new0(completion_entry, 1), + ce1->string = g_strdup(str); + /* GCompletion list is case sensitive */ + g_strdown(ce1->string); + ce1->ref = ae; + + g_completion_list = g_list_prepend(g_completion_list, ce1); +} + +/* add_address() - adds address to the completion list. this function looks + * complicated, but it's only allocation checks. */ -static void addrcompl_free_window( CompletionWindow *cw ) { - /* printf( "addrcompl_free_window...\n" ); */ - if( cw ) { - addrcompl_destroy_window( cw ); +static gint add_address(const gchar *name, const gchar *address, const gchar *alias) +{ + address_entry *ae; + + if (!name || !address) return -1; + + ae = g_new0(address_entry, 1); + + g_return_val_if_fail(ae != NULL, -1); + + ae->name = g_strdup(name); + ae->address = g_strdup(address); + + g_address_list = g_list_prepend(g_address_list, ae); - g_free( cw->searchTerm ); - cw->searchTerm = NULL; + add_address1(name, ae); + add_address1(address, ae); + add_address1(alias, ae); - /* Clear references */ - cw->listCount = 0; + return 0; +} + +/* read_address_book() + */ +static void read_address_book(void) { + addressbook_load_completion( add_address ); + g_address_list = g_list_reverse(g_address_list); + g_completion_list = g_list_reverse(g_completion_list); +} - /* Free object */ - g_free( cw ); +/* start_address_completion() - returns the number of addresses + * that should be matched for completion. + */ +gint start_address_completion(void) +{ + clear_completion_cache(); + if (!g_ref_count) { + init_all(); + /* open the address book */ + read_address_book(); + /* merge the completion entry list into g_completion */ + if (g_completion_list) + g_completion_add_items(g_completion, g_completion_list); } - /* printf( "addrcompl_free_window...done\n" ); */ + g_ref_count++; + debug_print("start_address_completion ref count %d\n", g_ref_count); + + return g_list_length(g_completion_list); } -/** - * Retrieve a possible address (or a part) from an entry box. To make life - * easier, we only look at the last valid address component; address - * completion only works at the last string component in the entry box. - * - * \param entry Address entry field. - * \param start_pos Address of start position of address. - * \return Possible address. +/* get_address_from_edit() - returns a possible address (or a part) + * from an entry box. To make life easier, we only look at the last valid address + * component; address completion only works at the last string component in + * the entry box. */ -static gchar *get_address_from_edit(GtkEntry *entry, gint *start_pos) +gchar *get_address_from_edit(GtkEntry *entry, gint *start_pos) { const gchar *edit_text; gint cur_pos; @@ -238,15 +279,11 @@ static gchar *get_address_from_edit(GtkEntry *entry, gint *start_pos) g_free(wtext); return str; -} +} -/** - * Replace text in entry field with specified address. - * \param entry Address entry field. - * \param newtext New text. - * \param start_pos Insertion point in entry field. +/* replace_address_in_edit() - replaces an incompleted address with a completed one. */ -static void replace_address_in_edit(GtkEntry *entry, const gchar *newtext, +void replace_address_in_edit(GtkEntry *entry, const gchar *newtext, gint start_pos) { if (!newtext) return; @@ -256,250 +293,370 @@ static void replace_address_in_edit(GtkEntry *entry, const gchar *newtext, gtk_editable_set_position(GTK_EDITABLE(entry), -1); } -/** - * Resize window to accommodate maximum number of address entries. - * \param cw Completion window. +/* complete_address() - tries to complete an addres, and returns the + * number of addresses found. use get_complete_address() to get one. + * returns zero if no match was found, otherwise the number of addresses, + * with the original prefix at index 0. */ -static void addrcompl_resize_window( CompletionWindow *cw ) { - GtkRequisition r; - gint x, y, width, height, depth; - - /* Get current geometry of window */ - gdk_window_get_geometry( cw->window->window, &x, &y, &width, &height, &depth ); - - gtk_widget_size_request( cw->clist, &r ); - gtk_widget_set_usize( cw->window, width, r.height ); - gtk_widget_show_all( cw->window ); - gtk_widget_size_request( cw->clist, &r ); - - /* Adjust window height to available screen space */ - if( ( y + r.height ) > gdk_screen_height() ) { - gtk_window_set_policy( GTK_WINDOW( cw->window ), TRUE, FALSE, FALSE ); - gtk_widget_set_usize( cw->window, width, gdk_screen_height() - y ); +guint complete_address(const gchar *str) +{ + GList *result; + gchar *d; + guint count, cpl; + completion_entry *ce; + + g_return_val_if_fail(str != NULL, 0); + + Xstrdup_a(d, str, return 0); + + clear_completion_cache(); + g_completion_prefix = g_strdup(str); + + /* g_completion is case sensitive */ + g_strdown(d); + result = g_completion_complete(g_completion, d, NULL); + + count = g_list_length(result); + if (count) { + /* create list with unique addresses */ + for (cpl = 0, result = g_list_first(result); + result != NULL; + result = g_list_next(result)) { + ce = (completion_entry *)(result->data); + if (NULL == g_slist_find(g_completion_addresses, + ce->ref)) { + cpl++; + g_completion_addresses = + g_slist_append(g_completion_addresses, + ce->ref); + } + } + count = cpl + 1; /* index 0 is the original prefix */ + g_completion_next = 1; /* we start at the first completed one */ + } else { + g_free(g_completion_prefix); + g_completion_prefix = NULL; } + + g_completion_count = count; + return count; } -/** - * Add an address the completion window address list. - * \param cw Completion window. - * \param address Address to add. +/* get_complete_address() - returns a complete address. the returned + * string should be freed */ -static void addrcompl_add_entry( CompletionWindow *cw, gchar *address ) { - gchar *text[] = { NULL, NULL }; +gchar *get_complete_address(gint index) +{ + const address_entry *p; + gchar *address = NULL; + + if (index < g_completion_count) { + if (index == 0) + address = g_strdup(g_completion_prefix); + else { + /* get something from the unique addresses */ + p = (address_entry *)g_slist_nth_data + (g_completion_addresses, index - 1); + if (p != NULL) { + if (!p->name || p->name[0] == '\0') + address = g_strdup_printf(p->address); + else if (strchr_with_skip_quote(p->name, '"', ',')) + address = g_strdup_printf + ("\"%s\" <%s>", p->name, p->address); + else + address = g_strdup_printf + ("%s <%s>", p->name, p->address); + } + } + } - /* printf( "\t\tAdding :%s\n", address ); */ - text[0] = address; - gtk_clist_append( GTK_CLIST(cw->clist), text ); - cw->listCount++; + return address; +} - /* Resize window */ - addrcompl_resize_window( cw ); - gtk_grab_add( cw->window ); +gchar *get_next_complete_address(void) +{ + if (is_completion_pending()) { + gchar *res; - if( cw->listCount == 2 ) { - /* Move off first row */ - gtk_clist_select_row( GTK_CLIST(cw->clist), 1, 0); - } + res = get_complete_address(g_completion_next); + g_completion_next += 1; + if (g_completion_next >= g_completion_count) + g_completion_next = 0; + + return res; + } else + return NULL; } -/** - * Completion idle function. This function is called by the main (UI) thread - * during UI idle time while an address search is in progress. Items from the - * display queue are processed and appended to the address list. - * - * \param data Target completion window to receive email addresses. - * \return TRUE to ensure that idle event do not get ignored. - */ -static gboolean addrcompl_idle( gpointer data ) { - GList *node; - gchar *address; - CompletionWindow *cw; - - /* Process all entries in display queue */ - pthread_mutex_lock( & _completionMutex_ ); - if( _displayQueue_ ) { - cw = data; - node = _displayQueue_; - while( node ) { - ItemEMail *email = node->data; - /* printf( "email/address ::%s::\n", email->address ); */ - address = addritem_format_email( email ); - /* printf( "address ::: %s :::\n", address ); */ - addrcompl_add_entry( cw, address ); - g_free( address ); - node = g_list_next( node ); - } - g_list_free( _displayQueue_ ); - _displayQueue_ = NULL; - } - pthread_mutex_unlock( & _completionMutex_ ); +gchar *get_prev_complete_address(void) +{ + if (is_completion_pending()) { + int n = g_completion_next - 2; + + /* real previous */ + n = (n + (g_completion_count * 5)) % g_completion_count; + + /* real next */ + g_completion_next = n + 1; + if (g_completion_next >= g_completion_count) + g_completion_next = 0; + return get_complete_address(n); + } else + return NULL; +} - return TRUE; +guint get_completion_count(void) +{ + if (is_completion_pending()) + return g_completion_count; + else + return 0; } -/** - * Callback entry point. The background thread (if any) appends the address - * list to the display queue. - * \param queryID Query ID of search request. - * \param listEMail List of zero of more email objects that met search - * criteria. - * \param target Target object to received data. - */ -static gint addrcompl_callback( - gint queryID, GList *listEMail, gpointer target ) +/* should clear up anything after complete_address() */ +void clear_completion_cache(void) { - GList *node; - gchar *address; + if (is_completion_pending()) { + if (g_completion_prefix) + g_free(g_completion_prefix); - pthread_mutex_lock( & _completionMutex_ ); - if( queryID == _queryID_ ) { - /* Append contents to end of display queue */ - node = listEMail; - while( node ) { - ItemEMail *email = node->data; - if( target ) { - /* - printf( "\temail/address ::%s::\n", email->address ); - */ - _displayQueue_ = g_list_append( _displayQueue_, email ); - } - node = g_list_next( node ); + if (g_completion_addresses) { + g_slist_free(g_completion_addresses); + g_completion_addresses = NULL; } + + g_completion_count = g_completion_next = 0; } - pthread_mutex_unlock( & _completionMutex_ ); } -/** - * Start the search. - */ -static void addrcompl_start_search( void ) { - gchar *searchTerm; +gboolean is_completion_pending(void) +{ + /* check if completion pending, i.e. we might satisfy a request for the next + * or previous address */ + return g_completion_count; +} - searchTerm = g_strdup( _compWindow_->searchTerm ); +/* invalidate_address_completion() - should be called if address book + * changed; + */ +gint invalidate_address_completion(void) +{ + if (g_ref_count) { + /* simply the same as start_address_completion() */ + debug_print("Invalidation request for address completion\n"); + free_all(); + init_all(); + read_address_book(); + g_completion_add_items(g_completion, g_completion_list); + clear_completion_cache(); + } - /* Clear out display queue */ - pthread_mutex_lock( & _completionMutex_ ); + return g_list_length(g_completion_list); +} - g_list_free( _displayQueue_ ); - _displayQueue_ = NULL; +gint end_address_completion(void) +{ + clear_completion_cache(); - pthread_mutex_unlock( & _completionMutex_ ); + if (0 == --g_ref_count) + free_all(); - /* Setup the search */ - _queryID_ = addressbook_setup_search( - searchTerm, _compWindow_, addrcompl_callback ); - g_free( searchTerm ); + debug_print("end_address_completion ref count %d\n", g_ref_count); - /* Sit back and wait until something happens */ - _completionIdleID_ = - gtk_idle_add( ( GtkFunction ) addrcompl_idle, _compWindow_ ); - addressbook_start_search( _queryID_ ); + return g_ref_count; } -/* - * address completion entry ui. the ui (completion list was inspired by galeon's + +/* address completion entry ui. the ui (completion list was inspired by galeon's * auto completion list). remaining things powered by sylpheed's completion engine. */ -#define ENTRY_DATA_TAB_HOOK "tab_hook" /* used to lookup entry */ +#define ENTRY_DATA_TAB_HOOK "tab_hook" /* used to lookup entry */ +#define WINDOW_DATA_COMPL_ENTRY "compl_entry" /* used to store entry for compl. window */ +#define WINDOW_DATA_COMPL_CLIST "compl_clist" /* used to store clist for compl. window */ +static void address_completion_mainwindow_set_focus (GtkWindow *window, + GtkWidget *widget, + gpointer data); static gboolean address_completion_entry_key_pressed (GtkEntry *entry, GdkEventKey *ev, gpointer data); static gboolean address_completion_complete_address_in_entry - (GtkEntry *entry); - + (GtkEntry *entry, + gboolean next); static void address_completion_create_completion_window (GtkEntry *entry); static void completion_window_select_row(GtkCList *clist, gint row, gint col, GdkEvent *event, - CompletionWindow *compWin ); - + GtkWidget **completion_window); static gboolean completion_window_button_press (GtkWidget *widget, GdkEventButton *event, - CompletionWindow *compWin ); - + GtkWidget **completion_window); static gboolean completion_window_key_press (GtkWidget *widget, GdkEventKey *event, - CompletionWindow *compWin ); + GtkWidget **completion_window); -/** - * Advance selection to previous/next item in list. - * \param clist List to process. - * \param forward Set to TRUE to select next or FALSE for - * previous entry. - */ +static void completion_window_advance_to_row(GtkCList *clist, gint row) +{ + g_return_if_fail(row < g_completion_count); + gtk_clist_select_row(clist, row, 0); +} + static void completion_window_advance_selection(GtkCList *clist, gboolean forward) { int row; g_return_if_fail(clist != NULL); + g_return_if_fail(clist->selection != NULL); - if( clist->selection ) { - row = GPOINTER_TO_INT(clist->selection->data); - row = forward ? ( row + 1 ) : ( row - 1 ); - gtk_clist_freeze(clist); - gtk_clist_select_row(clist, row, 0); - gtk_clist_thaw(clist); - } + row = GPOINTER_TO_INT(clist->selection->data); + + row = forward ? (row + 1) % g_completion_count : + (row - 1) < 0 ? g_completion_count - 1 : row - 1; + + gtk_clist_freeze(clist); + completion_window_advance_to_row(clist, row); + gtk_clist_thaw(clist); } -/** - * Apply the current selection in the list to the entry field. Focus is also - * moved to the next widget so that Tab key works correctly. - * \param clist List to process. - * \param entry Address entry field. - */ -static void completion_window_apply_selection(GtkCList *clist, GtkEntry *entry) +#if 0 +/* completion_window_accept_selection() - accepts the current selection in the + * clist, and destroys the window */ +static void completion_window_accept_selection(GtkWidget **window, + GtkCList *clist, + GtkEntry *entry) { gchar *address = NULL, *text = NULL; gint cursor_pos, row; - GtkWidget *parent; + g_return_if_fail(window != NULL); + g_return_if_fail(*window != NULL); g_return_if_fail(clist != NULL); g_return_if_fail(entry != NULL); g_return_if_fail(clist->selection != NULL); - /* First remove the idler */ - if( _completionIdleID_ != 0 ) { - gtk_idle_remove( _completionIdleID_ ); - _completionIdleID_ = 0; - } + /* FIXME: I believe it's acceptable to access the selection member directly */ + row = GPOINTER_TO_INT(clist->selection->data); + + /* we just need the cursor position */ + address = get_address_from_edit(entry, &cursor_pos); + g_free(address); + gtk_clist_get_text(clist, row, 0, &text); + replace_address_in_edit(entry, text, cursor_pos); + + clear_completion_cache(); + gtk_widget_destroy(*window); + *window = NULL; +} +#endif + +/* completion_window_apply_selection() - apply the current selection in the + * clist */ +static void completion_window_apply_selection(GtkCList *clist, GtkEntry *entry) +{ + gchar *address = NULL, *text = NULL; + gint cursor_pos, row; + + g_return_if_fail(clist != NULL); + g_return_if_fail(entry != NULL); + g_return_if_fail(clist->selection != NULL); - /* Process selected item */ row = GPOINTER_TO_INT(clist->selection->data); address = get_address_from_edit(entry, &cursor_pos); g_free(address); gtk_clist_get_text(clist, row, 0, &text); replace_address_in_edit(entry, text, cursor_pos); +} - /* Move focus to next widget */ - parent = GTK_WIDGET(entry)->parent; - if( parent ) { - gtk_container_focus( GTK_CONTAINER(parent), GTK_DIR_TAB_FORWARD ); - } +/* should be called when creating the main window containing address + * completion entries */ +void address_completion_start(GtkWidget *mainwindow) +{ + start_address_completion(); + /* register focus change hook */ + gtk_signal_connect(GTK_OBJECT(mainwindow), "set_focus", + GTK_SIGNAL_FUNC(address_completion_mainwindow_set_focus), + mainwindow); } -/** - * Listener that watches for tab or other keystroke in address entry field. - * \param entry Address entry field. - * \param ev Event object. - * \param data User data. - * \return TRUE. - */ +/* Need unique data to make unregistering signal handler possible for the auto + * completed entry */ +#define COMPLETION_UNIQUE_DATA (GINT_TO_POINTER(0xfeefaa)) + +void address_completion_register_entry(GtkEntry *entry) +{ + g_return_if_fail(entry != NULL); + g_return_if_fail(GTK_IS_ENTRY(entry)); + + /* add hooked property */ + gtk_object_set_data(GTK_OBJECT(entry), ENTRY_DATA_TAB_HOOK, entry); + + /* add keypress event */ + gtk_signal_connect_full(GTK_OBJECT(entry), "key_press_event", + GTK_SIGNAL_FUNC(address_completion_entry_key_pressed), + NULL, + COMPLETION_UNIQUE_DATA, + NULL, + 0, + 0); /* magic */ +} + +void address_completion_unregister_entry(GtkEntry *entry) +{ + GtkObject *entry_obj; + + g_return_if_fail(entry != NULL); + g_return_if_fail(GTK_IS_ENTRY(entry)); + + entry_obj = gtk_object_get_data(GTK_OBJECT(entry), ENTRY_DATA_TAB_HOOK); + g_return_if_fail(entry_obj); + g_return_if_fail(entry_obj == GTK_OBJECT(entry)); + + /* has the hooked property? */ + gtk_object_set_data(GTK_OBJECT(entry), ENTRY_DATA_TAB_HOOK, NULL); + + /* remove the hook */ + gtk_signal_disconnect_by_func(GTK_OBJECT(entry), + GTK_SIGNAL_FUNC(address_completion_entry_key_pressed), + COMPLETION_UNIQUE_DATA); +} + +/* should be called when main window with address completion entries + * terminates. + * NOTE: this function assumes that it is called upon destruction of + * the window */ +void address_completion_end(GtkWidget *mainwindow) +{ + /* if address_completion_end() is really called on closing the window, + * we don't need to unregister the set_focus_cb */ + end_address_completion(); +} + +/* if focus changes to another entry, then clear completion cache */ +static void address_completion_mainwindow_set_focus(GtkWindow *window, + GtkWidget *widget, + gpointer data) +{ + if (widget) + clear_completion_cache(); +} + +/* watch for tabs in one of the address entries. if no tab then clear the + * completion cache */ static gboolean address_completion_entry_key_pressed(GtkEntry *entry, GdkEventKey *ev, gpointer data) { if (ev->keyval == GDK_Tab) { - if (address_completion_complete_address_in_entry(entry)) { + if (address_completion_complete_address_in_entry(entry, TRUE)) { + address_completion_create_completion_window(entry); /* route a void character to the default handler */ /* this is a dirty hack; we're actually changing a key * reported by the system. */ @@ -507,173 +664,193 @@ static gboolean address_completion_entry_key_pressed(GtkEntry *entry, ev->state &= ~GDK_SHIFT_MASK; gtk_signal_emit_stop_by_name(GTK_OBJECT(entry), "key_press_event"); - - /* printf( "entry_key_pressed::create window\n" ); */ - address_completion_create_completion_window( entry ); - /* printf( "entry_key_pressed::create window...done\n" ); */ - addrcompl_start_search(); + } else { + /* old behaviour */ } - } + } else if (ev->keyval == GDK_Shift_L + || ev->keyval == GDK_Shift_R + || ev->keyval == GDK_Control_L + || ev->keyval == GDK_Control_R + || ev->keyval == GDK_Caps_Lock + || ev->keyval == GDK_Shift_Lock + || ev->keyval == GDK_Meta_L + || ev->keyval == GDK_Meta_R + || ev->keyval == GDK_Alt_L + || ev->keyval == GDK_Alt_R) { + /* these buttons should not clear the cache... */ + } else + clear_completion_cache(); + return TRUE; } -/** - * Initialize search term for address completion. - * \param entry Address entry field. - */ -static gboolean address_completion_complete_address_in_entry( GtkEntry *entry ) +/* initialize the completion cache and put first completed string + * in entry. this function used to do back cycling but this is not + * currently used. since the address completion behaviour has been + * changed regularly, we keep the feature in case someone changes + * his / her mind again. :) */ +static gboolean address_completion_complete_address_in_entry(GtkEntry *entry, + gboolean next) { - gint cursor_pos; - gchar *searchTerm; + gint ncount, cursor_pos; + gchar *address, *new = NULL; + gboolean completed = FALSE; g_return_val_if_fail(entry != NULL, FALSE); if (!GTK_WIDGET_HAS_FOCUS(entry)) return FALSE; /* get an address component from the cursor */ - searchTerm = get_address_from_edit( entry, &cursor_pos ); - if (! searchTerm ) return FALSE; + address = get_address_from_edit(entry, &cursor_pos); + if (!address) return FALSE; + + /* still something in the cache */ + if (is_completion_pending()) { + new = next ? get_next_complete_address() : + get_prev_complete_address(); + } else { + if (0 < (ncount = complete_address(address))) + new = get_next_complete_address(); + } - /* Clear any existing search */ - if( _compWindow_->searchTerm ) { - g_free( _compWindow_->searchTerm ); + if (new) { + /* prevent "change" signal */ + /* replace_address_in_edit(entry, new, cursor_pos); */ + g_free(new); + completed = TRUE; } - /* Replace with new search term */ - _compWindow_->searchTerm = searchTerm; + g_free(address); - return TRUE; + return completed; } -/** - * Create new address completion window for specified entry. - * \param entry_ Entry widget to associate with window. - */ -static void address_completion_create_completion_window( GtkEntry *entry_ ) +static void address_completion_create_completion_window(GtkEntry *entry_) { + static GtkWidget *completion_window; gint x, y, height, width, depth; GtkWidget *scroll, *clist; GtkRequisition r; - GtkWidget *window; + guint count = 0; GtkWidget *entry = GTK_WIDGET(entry_); - gchar *searchTerm; - /* Create new window and list */ - window = gtk_window_new(GTK_WINDOW_POPUP); - clist = gtk_clist_new(1); - - /* Destroy any existing window */ - addrcompl_destroy_window( _compWindow_ ); - - /* Create new object */ - _compWindow_->window = window; - _compWindow_->entry = entry; - _compWindow_->clist = clist; - _compWindow_->listCount = 0; + if (completion_window) { + gtk_widget_destroy(completion_window); + completion_window = NULL; + } scroll = gtk_scrolled_window_new(NULL, NULL); + clist = gtk_clist_new(1); + gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_SINGLE); + + completion_window = gtk_window_new(GTK_WINDOW_POPUP); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(window), scroll); + gtk_container_add(GTK_CONTAINER(completion_window), scroll); gtk_container_add(GTK_CONTAINER(scroll), clist); - gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_SINGLE); - /* Use entry widget to create initial window */ + /* set the unique data so we can always get back the entry and + * clist window to which this completion window has been attached */ + gtk_object_set_data(GTK_OBJECT(completion_window), + WINDOW_DATA_COMPL_ENTRY, entry_); + gtk_object_set_data(GTK_OBJECT(completion_window), + WINDOW_DATA_COMPL_CLIST, clist); + + gtk_signal_connect(GTK_OBJECT(clist), "select_row", + GTK_SIGNAL_FUNC(completion_window_select_row), + &completion_window); + + for (count = 0; count < get_completion_count(); count++) { + gchar *text[] = {NULL, NULL}; + + text[0] = get_complete_address(count); + gtk_clist_append(GTK_CLIST(clist), text); + g_free(text[0]); + } + gdk_window_get_geometry(entry->window, &x, &y, &width, &height, &depth); gdk_window_get_deskrelative_origin (entry->window, &x, &y); y += height; - gtk_widget_set_uposition(window, x, y); - - /* Resize window to fit initial (empty) address list */ - gtk_widget_size_request( clist, &r ); - gtk_widget_set_usize( window, width, r.height ); - gtk_widget_show_all( window ); - gtk_widget_size_request( clist, &r ); + gtk_widget_set_uposition(completion_window, x, y); + + gtk_widget_size_request(clist, &r); + gtk_widget_set_usize(completion_window, width, r.height); + gtk_widget_show_all(completion_window); + gtk_widget_size_request(clist, &r); + + if ((y + r.height) > gdk_screen_height()) { + gtk_window_set_policy(GTK_WINDOW(completion_window), + TRUE, FALSE, FALSE); + gtk_widget_set_usize(completion_window, width, + gdk_screen_height () - y); + } - /* Setup handlers */ - gtk_signal_connect(GTK_OBJECT(clist), "select_row", - GTK_SIGNAL_FUNC(completion_window_select_row), - _compWindow_ ); - gtk_signal_connect(GTK_OBJECT(window), + gtk_signal_connect(GTK_OBJECT(completion_window), "button-press-event", GTK_SIGNAL_FUNC(completion_window_button_press), - _compWindow_ ); - gtk_signal_connect(GTK_OBJECT(window), + &completion_window); + gtk_signal_connect(GTK_OBJECT(completion_window), "key-press-event", GTK_SIGNAL_FUNC(completion_window_key_press), - _compWindow_ ); - gdk_pointer_grab(window->window, TRUE, + &completion_window); + gdk_pointer_grab(completion_window->window, TRUE, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK, NULL, NULL, GDK_CURRENT_TIME); - gtk_grab_add( window ); + gtk_grab_add(completion_window); /* this gets rid of the irritating focus rectangle that doesn't * follow the selection */ GTK_WIDGET_UNSET_FLAGS(clist, GTK_CAN_FOCUS); - - /* Add first entry into address list */ - searchTerm = g_strdup( _compWindow_->searchTerm ); - addrcompl_add_entry( _compWindow_, searchTerm ); - gtk_clist_select_row(GTK_CLIST(clist), 0, 0); - - g_free( searchTerm ); + gtk_clist_select_row(GTK_CLIST(clist), 1, 0); } -/** - * Respond to select row event in clist object. selection sends completed - * address to entry. Note: event is NULL if selected by anything else than a - * mouse button. - * \param widget Window object. - * \param event Event. - * \param compWind Reference to completion window. - */ + +/* row selection sends completed address to entry. + * note: event is NULL if selected by anything else than a mouse button. */ static void completion_window_select_row(GtkCList *clist, gint row, gint col, GdkEvent *event, - CompletionWindow *compWin ) + GtkWidget **completion_window) { GtkEntry *entry; - g_return_if_fail(compWin != NULL); + g_return_if_fail(completion_window != NULL); + g_return_if_fail(*completion_window != NULL); - entry = GTK_ENTRY(compWin->entry); + entry = GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(*completion_window), + WINDOW_DATA_COMPL_ENTRY)); g_return_if_fail(entry != NULL); - /* Don't update unless user actually selects ! */ + completion_window_apply_selection(clist, entry); + if (!event || event->type != GDK_BUTTON_RELEASE) return; - /* User selected address by releasing the mouse in drop-down list*/ - completion_window_apply_selection( clist, entry ); - - addrcompl_destroy_window( _compWindow_ ); + clear_completion_cache(); + gtk_widget_destroy(*completion_window); + *completion_window = NULL; } -/** - * Respond to button press in completion window. Check if mouse click is - * anywhere outside the completion window. In that case the completion - * window is destroyed, and the original searchTerm is restored. - * - * \param widget Window object. - * \param event Event. - * \param compWin Reference to completion window. - */ +/* completion_window_button_press() - check is mouse click is anywhere + * else (not in the completion window). in that case the completion + * window is destroyed, and the original prefix is restored */ static gboolean completion_window_button_press(GtkWidget *widget, GdkEventButton *event, - CompletionWindow *compWin ) - + GtkWidget **completion_window) { GtkWidget *event_widget, *entry; - gchar *searchTerm; + gchar *prefix; gint cursor_pos; gboolean restore = TRUE; - g_return_val_if_fail(compWin != NULL, FALSE); + g_return_val_if_fail(completion_window != NULL, FALSE); + g_return_val_if_fail(*completion_window != NULL, FALSE); - entry = compWin->entry; + entry = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(*completion_window), + WINDOW_DATA_COMPL_ENTRY)); g_return_val_if_fail(entry != NULL, FALSE); - /* Test where mouse was clicked */ event_widget = gtk_get_event_widget((GdkEvent *)event); if (event_widget != widget) { while (event_widget) { @@ -683,42 +860,41 @@ static gboolean completion_window_button_press(GtkWidget *widget, restore = FALSE; break; } - event_widget = event_widget->parent; + event_widget = event_widget->parent; } } if (restore) { - /* Clicked outside of completion window - restore */ - searchTerm = _compWindow_->searchTerm; + prefix = get_complete_address(0); g_free(get_address_from_edit(GTK_ENTRY(entry), &cursor_pos)); - replace_address_in_edit(GTK_ENTRY(entry), searchTerm, cursor_pos); + replace_address_in_edit(GTK_ENTRY(entry), prefix, cursor_pos); + g_free(prefix); } - addrcompl_destroy_window( _compWindow_ ); + clear_completion_cache(); + gtk_widget_destroy(*completion_window); + *completion_window = NULL; return TRUE; } -/** - * Respond to key press in completion window. - * \param widget Window object. - * \param event Event. - * \param compWind Reference to completion window. - */ static gboolean completion_window_key_press(GtkWidget *widget, GdkEventKey *event, - CompletionWindow *compWin ) + GtkWidget **completion_window) { GdkEventKey tmp_event; GtkWidget *entry; - gchar *searchTerm; + gchar *prefix; gint cursor_pos; GtkWidget *clist; - g_return_val_if_fail(compWin != NULL, FALSE); + g_return_val_if_fail(completion_window != NULL, FALSE); + g_return_val_if_fail(*completion_window != NULL, FALSE); - entry = compWin->entry; - clist = compWin->clist; + entry = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(*completion_window), + WINDOW_DATA_COMPL_ENTRY)); + clist = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(*completion_window), + WINDOW_DATA_COMPL_CLIST)); g_return_val_if_fail(entry != NULL, FALSE); /* allow keyboard navigation in the alternatives clist */ @@ -746,14 +922,9 @@ static gboolean completion_window_key_press(GtkWidget *widget, /* look for presses that accept the selection */ if (event->keyval == GDK_Return || event->keyval == GDK_space) { - /* User selected address with a key press */ - - /* Display selected address in entry field */ - completion_window_apply_selection( - GTK_CLIST(clist), GTK_ENTRY(entry) ); - - /* Discard the window */ - addrcompl_destroy_window( _compWindow_ ); + clear_completion_cache(); + gtk_widget_destroy(*completion_window); + *completion_window = NULL; return FALSE; } @@ -771,10 +942,12 @@ static gboolean completion_window_key_press(GtkWidget *widget, return FALSE; } - /* some other key, let's restore the searchTerm (orignal text) */ - searchTerm = _compWindow_->searchTerm; + /* other key, let's restore the prefix (orignal text) */ + prefix = get_complete_address(0); g_free(get_address_from_edit(GTK_ENTRY(entry), &cursor_pos)); - replace_address_in_edit(GTK_ENTRY(entry), searchTerm, cursor_pos); + replace_address_in_edit(GTK_ENTRY(entry), prefix, cursor_pos); + g_free(prefix); + clear_completion_cache(); /* make sure anything we typed comes in the edit box */ tmp_event.type = event->type; @@ -788,139 +961,8 @@ static gboolean completion_window_key_press(GtkWidget *widget, gtk_widget_event(entry, (GdkEvent *)&tmp_event); /* and close the completion window */ - addrcompl_destroy_window( _compWindow_ ); + gtk_widget_destroy(*completion_window); + *completion_window = NULL; return TRUE; } - -/* - * ============================================================================ - * Publically accessible functions. - * ============================================================================ - */ -/** - * Setup completion object. - */ -void addrcompl_initialize( void ) { - /* printf( "addrcompl_initialize...\n" ); */ - if( ! _compWindow_ ) { - _compWindow_ = addrcompl_create_window(); - } - _queryID_ = 0; - _completionIdleID_ = 0; - /* printf( "addrcompl_initialize...done\n" ); */ -} - -/** - * Teardown completion object. - */ -void addrcompl_teardown( void ) { - /* printf( "addrcompl_teardown...\n" ); */ - addrcompl_free_window( _compWindow_ ); - _compWindow_ = NULL; - if( _displayQueue_ ) { - g_list_free( _displayQueue_ ); - } - _displayQueue_ = NULL; - _completionIdleID_ = 0; - /* printf( "addrcompl_teardown...done\n" ); */ -} - -/** - * Start address completion operation. - */ -gint start_address_completion(void) -{ - if( ! _compWindow_ ) { - addrcompl_initialize(); - } - addressbook_read_all(); - return 0; -} - -/** - * Terminate addess completion. - */ -gint end_address_completion(void) -{ - return 0; -} - -/** - * Start address completion. Should be called when creating the main window - * containing address completion entries. This originally cleared the cache. - * Function no longer required? - */ -void address_completion_start(GtkWidget *mainwindow) -{ - start_address_completion(); -} - -/** - * Need unique data to make unregistering signal handler possible for the auto - * completed entry. - */ -#define COMPLETION_UNIQUE_DATA (GINT_TO_POINTER(0xfeefaa)) - -/** - * Register specified entry widget for address completion. - * \param entry Address entry field. - */ -void address_completion_register_entry(GtkEntry *entry) -{ - g_return_if_fail(entry != NULL); - g_return_if_fail(GTK_IS_ENTRY(entry)); - - /* add hooked property */ - gtk_object_set_data(GTK_OBJECT(entry), ENTRY_DATA_TAB_HOOK, entry); - - /* add keypress event */ - gtk_signal_connect_full(GTK_OBJECT(entry), "key_press_event", - GTK_SIGNAL_FUNC(address_completion_entry_key_pressed), - NULL, - COMPLETION_UNIQUE_DATA, - NULL, - 0, - 0); /* magic */ -} - -/** - * Unregister specified entry widget from address completion operations. - * \param entry Address entry field. - */ -void address_completion_unregister_entry(GtkEntry *entry) -{ - GtkObject *entry_obj; - - g_return_if_fail(entry != NULL); - g_return_if_fail(GTK_IS_ENTRY(entry)); - - entry_obj = gtk_object_get_data(GTK_OBJECT(entry), ENTRY_DATA_TAB_HOOK); - g_return_if_fail(entry_obj); - g_return_if_fail(entry_obj == GTK_OBJECT(entry)); - - /* has the hooked property? */ - gtk_object_set_data(GTK_OBJECT(entry), ENTRY_DATA_TAB_HOOK, NULL); - - /* remove the hook */ - gtk_signal_disconnect_by_func(GTK_OBJECT(entry), - GTK_SIGNAL_FUNC(address_completion_entry_key_pressed), - COMPLETION_UNIQUE_DATA); -} - -/** - * End address completion. Should be called when main window with address - * completion entries terminates. NOTE: this function assumes that it is - * called upon destruction of the window. - */ -void address_completion_end(GtkWidget *mainwindow) -{ - /* if address_completion_end() is really called on closing the window, - * we don't need to unregister the set_focus_cb */ - end_address_completion(); -} - -/* - * End of Source. - */ - diff --git a/src/addr_compl.h b/src/addr_compl.h index 17b216be7..fcb65a47b 100644 --- a/src/addr_compl.h +++ b/src/addr_compl.h @@ -22,6 +22,8 @@ #define __ADDR_COMPL_H__ gint start_address_completion (void); +guint complete_address (const gchar *str); +gchar *get_complete_address (gint index); gint end_address_completion (void); /* ui functions */ diff --git a/src/addressbook.c b/src/addressbook.c index 7d7b910e9..f528d578f 100644 --- a/src/addressbook.c +++ b/src/addressbook.c @@ -527,8 +527,7 @@ void addressbook_destroy() { addrclip_free( _clipBoard_ ); } if( _addressIndex_ != NULL ) { - addrindex_teardown( _addressIndex_ ); - addrindex_free_index( _addressIndex_ ); + addrindex_free_index( _addressIndex_ ); } _addressSelect_ = NULL; _clipBoard_ = NULL; @@ -2378,6 +2377,7 @@ static void addressbook_edit_address_cb( gpointer data, guint action, GtkWidget person = ( ItemPerson * ) ADDRITEM_PARENT(email); if( addressbook_edit_person( abf, NULL, person, TRUE ) == NULL ) return; addressbook_folder_refresh_one_person( clist, person ); + invalidate_address_completion(); return; } } @@ -2385,6 +2385,7 @@ static void addressbook_edit_address_cb( gpointer data, guint action, GtkWidget /* Edit person - basic page */ ItemPerson *person = ( ItemPerson * ) obj; if( addressbook_edit_person( abf, NULL, person, FALSE ) == NULL ) return; + invalidate_address_completion(); addressbook_folder_refresh_one_person( clist, person ); return; } @@ -3000,7 +3001,6 @@ void addressbook_read_file( void ) { } addrIndex = addrindex_create_index(); - addrindex_initialize( addrIndex ); /* Use new address book index. */ addrindex_set_file_path( addrIndex, get_rc_dir() ); @@ -3154,9 +3154,8 @@ static GtkCTreeNode *addressbook_node_add_folder( newNode = gtk_ctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING, atci->iconXpm, atci->maskXpm, atci->iconXpm, atci->maskXpm, atci->treeLeaf, atci->treeExpand ); - if( newNode ) - gtk_ctree_node_set_row_data_full( ctree, newNode, adapter, - addressbook_free_treenode ); + gtk_ctree_node_set_row_data_full( ctree, newNode, adapter, + addressbook_free_treenode ); } listItems = itemFolder->listFolder; @@ -3186,6 +3185,9 @@ void addressbook_export_to_file( void ) { if( _addressIndex_->retVal != MGU_SUCCESS ) { addrindex_print_index( _addressIndex_, stdout ); } + + /* Notify address completion of new data */ + invalidate_address_completion(); } } @@ -3438,11 +3440,9 @@ static void addressbook_ldap_lookup( AdapterDSource *ads, gchar *sLookup ) { */ static void addressbook_lup_clicked( GtkButton *button, gpointer data ) { GtkCTree *ctree = GTK_CTREE(addrbook.ctree); - AddressObject *pobj; AddressObject *obj; AdapterDSource *ads = NULL; gchar *sLookup; - GtkCTreeNode *node = NULL, *parentNode = NULL; if( ! addrbook.treeSelected ) return; if( GTK_CTREE_ROW( addrbook.treeSelected )->level == 1 ) return; @@ -3452,25 +3452,16 @@ static void addressbook_lup_clicked( GtkButton *button, gpointer data ) { sLookup = gtk_editable_get_chars( GTK_EDITABLE(addrbook.entry), 0, -1 ); g_strchomp( sLookup ); - /* printf( "addressbook_lup_clicked/Lookup: '%s'\n", sLookup ); */ if( obj->type == ADDR_DATASOURCE ) { - /* printf( "I am a datasource\n" ); */ ads = ADAPTER_DSOURCE(obj); - } - else { - /* printf( "Test my parent\n" ); */ - parentNode = GTK_CTREE_ROW(addrbook.treeSelected)->parent; - obj = gtk_ctree_node_get_row_data( ctree, parentNode ); - if( obj->type == ADDR_DATASOURCE ) { - ads = ADAPTER_DSOURCE(obj); - } - } #ifdef USE_LDAP - if( ads && ads->subType == ADDR_LDAP ) { - addressbook_ldap_lookup( ads, sLookup ); + if( ads->subType == ADDR_LDAP ) { + addressbook_ldap_lookup( ads, sLookup ); + } +#endif /* USE_LDAP */ } -#endif + g_free( sLookup ); } @@ -3864,6 +3855,88 @@ gboolean addressbook_add_contact( const gchar *name, const gchar *address, const return TRUE; } +/* ********************************************************************** +* Address completion support. +* *********************************************************************** +*/ + +/* +* This function is used by the address completion function to load +* addresses. +* Enter: callBackFunc Function to be called when an address is +* to be loaded. +* Return: TRUE if data loaded, FALSE if address index not loaded. +*/ +gboolean addressbook_load_completion( gint (*callBackFunc) ( const gchar *, const gchar *, const gchar * ) ) { + AddressDataSource *ds; + GList *nodeIf, *nodeDS; + GList *listP, *nodeP; + GList *nodeM; + gchar *sName, *sAddress, *sAlias, *sFriendly; + + debug_print( "addressbook_load_completion\n" ); + + if( _addressIndex_ == NULL ) return FALSE; + + nodeIf = addrindex_get_interface_list( _addressIndex_ ); + while( nodeIf ) { + AddressInterface *interface = nodeIf->data; + nodeDS = interface->listSource; + while( nodeDS ) { + ds = nodeDS->data; + + /* Read address book */ + if( addrindex_ds_get_modify_flag( ds ) ) { + addrindex_ds_read_data( ds ); + } + + if( ! addrindex_ds_get_read_flag( ds ) ) { + addrindex_ds_read_data( ds ); + } + + /* Get all persons */ + listP = addrindex_ds_get_all_persons( ds ); + nodeP = listP; + while( nodeP ) { + ItemPerson *person = nodeP->data; + nodeM = person->listEMail; + + /* Figure out name to use */ + sName = person->nickName; + if( sName == NULL || *sName == '\0' ) { + sName = ADDRITEM_NAME(person); + } + + /* Process each E-Mail address */ + while( nodeM ) { + ItemEMail *email = nodeM->data; + /* Have mail */ + sFriendly = sName; + sAddress = email->address; + if( sAddress || *sAddress != '\0' ) { + sAlias = ADDRITEM_NAME(email); + if( sAlias && *sAlias != '\0' ) { + sFriendly = sAlias; + } + ( callBackFunc ) ( sFriendly, sAddress, sName ); + } + + nodeM = g_list_next( nodeM ); + } + nodeP = g_list_next( nodeP ); + } + /* Free up the list */ + g_list_free( listP ); + + nodeDS = g_list_next( nodeDS ); + } + nodeIf = g_list_next( nodeIf ); + } + debug_print( "addressbook_load_completion... done\n" ); + + return TRUE; +} + /* ********************************************************************** * Address Import. * *********************************************************************** @@ -3899,6 +3972,9 @@ static void addressbook_import_ldif_cb() { newNode ); addrbook.treeSelected = newNode; } + + /* Notify address completion */ + invalidate_address_completion(); } } } @@ -3934,6 +4010,9 @@ static void addressbook_import_mutt_cb() { newNode ); addrbook.treeSelected = newNode; } + + /* Notify address completion */ + invalidate_address_completion(); } } } @@ -3969,6 +4048,9 @@ static void addressbook_import_pine_cb() { newNode ); addrbook.treeSelected = newNode; } + + /* Notify address completion */ + invalidate_address_completion(); } } } @@ -4005,6 +4087,9 @@ void addressbook_harvest( ADDRESS_OBJECT(ads) ); } } + + /* Notify address completion */ + invalidate_address_completion(); } } @@ -4035,133 +4120,4 @@ static void addressbook_export_html_cb( void ) { /* * End of Source. */ -/* ********************************************************************** -* Address completion and search support. -* *********************************************************************** -*/ - -/** - * Setup search for specified search string. - * \param searchTerm Search string. - * \param target Target data. - * \param callBack Call back function. - * \return ID allocated to this query, or 0 if none. - */ -gint addressbook_setup_search( - const gchar *searchTerm, const gpointer target, - AddrSearchCallbackFunc callback ) -{ - if( _addressIndex_ == NULL ) { - printf( "address index not loaded\n" ); - return 0; - } - return addrindex_setup_search( _addressIndex_, searchTerm, target, callback ); -} - -/** - * Perform the previously registered search. - * \param queryID ID of search query to be executed. - * \return TRUE if search started successfully, or FALSE if - * failed. - */ -gboolean addressbook_start_search( const gint queryID ) { - if( _addressIndex_ == NULL ) { - printf( "address index not loaded\n" ); - return FALSE; - } - return addrindex_start_search( _addressIndex_, queryID ); -} - -/** - * Stop the previously registered search. - * \param queryID ID of search query to stop. - */ -void addressbook_stop_search( const gint queryID ){ - if( _addressIndex_ == NULL ) { - /* printf( "address index not loaded\n" ); */ - return; - } - addrindex_stop_search( _addressIndex_, queryID ); -} - -/** - * Read all address interfaces and data sources. - */ -void addressbook_read_all( void ) { - if( _addressIndex_ == NULL ) { - /* Load index file */ - /* printf( "address index not loaded\n" ); */ - /* addressbook_read_file(); */ - } - - if( ! addrindex_get_loaded( _addressIndex_ ) ) { - /* Read all address books */ - addrindex_read_all( _addressIndex_ ); - } -} - -/** - * Perform a simple search of all non-query type data sources for specified - * search term. If several entries are found, only the first item is - * returned. Interfaces that require a time-consuming "query" are ignored for - * this search. - * - * \param searchTerm Search term to find. Typically an email address. - * \return Reference to a single E-Mail object that was found in the address - * book, or NULL if nothing found. This should *NOT* be freed - * when done. - */ -ItemEMail *addressbook_quick_search_single( const gchar *searchTerm ) { - if( _addressIndex_ == NULL ) { - return NULL; - } - return addrindex_quick_search_single( _addressIndex_, searchTerm ); -} - -/** - * Perform a simple search of all non-query type data sources for specified - * search term. If several entries are found, only the first item is - * returned. Interfaces that require a time-consuming "query" are ignored for - * this search. - * - * \param addrIndex Address index object. - * \param searchTerm Search term to find. Typically an email address. - * \return List of references to zero or mail E-Mail object that was found in - * the address books, or NULL if nothing found. This list - * *SHOULD* be freed when done. - */ -GList *addressbook_quick_search_list( const gchar *searchTerm ) -{ - if( _addressIndex_ == NULL ) { - return NULL; - } - return addrindex_quick_search_list( _addressIndex_, searchTerm ); -} - -/** - * Lookup person's name for specified E-Mail address. This function performs a - * simple search of all non-query type data sources for specified address - * (see addrindex_quick_search_single()). If several entries are - * found, only the first item is returned. - * - * \param address E-Mail address to find. - * \return Formatted name, or NULL if nothing found. This name should - * be freed when done. - */ -gchar *addressbook_lookup_name( const gchar *address ) { - gchar *name = NULL; - ItemEMail *email = NULL; - - if( address ) { - email = addrindex_quick_search_single( _addressIndex_, address ); - if( email ) { - name = addritem_format_email( email ); - } - } - return name; -} - -/* -* End of Source. -*/ diff --git a/src/addressbook.h b/src/addressbook.h index 85208eec0..ddd0a29e7 100644 --- a/src/addressbook.h +++ b/src/addressbook.h @@ -39,6 +39,11 @@ void addressbook_destroy ( void ); gboolean addressbook_add_contact ( const gchar *name, const gchar *address, const gchar *remarks ); + +gboolean addressbook_load_completion (gint (*callBackFunc) + (const gchar *, + const gchar *, + const gchar *)); void addressbook_gather ( FolderItem *folderItem, gboolean sourceInd, diff --git a/src/addrindex.c b/src/addrindex.c index 46693d95b..b2c3e2abc 100644 --- a/src/addrindex.c +++ b/src/addrindex.c @@ -675,17 +675,17 @@ GList *addrindex_get_interface_list( AddressIndex *addrIndex ) { * Perform any other initialization of address index. * \param addrIndex Address index. */ -void addrindex_initialize( AddressIndex *addrIndex ) { +/* void addrindex_initialize( AddressIndex *addrIndex ) { addrcompl_initialize(); -} +} */ /** * Perform any other teardown of address index. * \param addrIndex Address index. */ -void addrindex_teardown( AddressIndex *addrIndex ) { +/* void addrindex_teardown( AddressIndex *addrIndex ) { addrcompl_teardown(); -} +} */ /** * Free up address index. diff --git a/src/summaryview.c b/src/summaryview.c index e31fb23d2..d72dfaab2 100644 --- a/src/summaryview.c +++ b/src/summaryview.c @@ -76,6 +76,7 @@ #include "inc.h" #include "imap.h" #include "addressbook.h" +#include "addr_compl.h" #include "scoring.h" #include "prefs_folder_item.h" #include "filtering.h" @@ -2252,6 +2253,9 @@ static void summary_set_ctree_from_list(SummaryView *summaryview, subject_table = g_hash_table_new(g_str_hash, g_str_equal); summaryview->subject_table = subject_table; + if (prefs_common.use_addr_book) + start_address_completion(); + for (cur = mlist ; cur != NULL; cur = cur->next) { msginfo = (MsgInfo *)cur->data; msginfo->threadscore = msginfo->score; @@ -2308,6 +2312,9 @@ static void summary_set_ctree_from_list(SummaryView *summaryview, optimal_width); } + if (prefs_common.use_addr_book) + end_address_completion(); + debug_print("done.\n"); STATUSBAR_POP(summaryview->mainwin); if (debug_get_mode()) { @@ -2320,6 +2327,7 @@ static void summary_set_ctree_from_list(SummaryView *summaryview, static gchar *summary_complete_address(const gchar *addr) { + gint count; gchar *res, *tmp, *email_addr; Xstrdup_a(email_addr, addr, return NULL); @@ -2330,8 +2338,10 @@ static gchar *summary_complete_address(const gchar *addr) * completion stuff must be already initialized */ res = NULL; - tmp = addressbook_lookup_name( email_addr ); - if( tmp ) { + if (1 < (count = complete_address(email_addr))) { + tmp = get_complete_address(1); +/* tmp = addressbook_lookup_name( email_addr ); + if( tmp ) { */ res = procheader_get_fromname(tmp); g_free(tmp); }