In compose window, allow address completion to match any part of address, not just...
[claws.git] / src / addr_compl.c
index 575af4ed68d9f6bc42c5d46eb7663a68b403e918..ed73557880f382e2c0dad838754704a7c4074b0a 100644 (file)
@@ -1,11 +1,11 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
  *
- * Copyright (C) 2000-2006 by Alfons Hoogervorst & The Sylpheed Claws Team.
+ * Copyright (C) 2000-2012 by Alfons Hoogervorst & The Claws Mail Team.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
+#include "claws-features.h"
 #endif
 #include "defs.h"
 
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gdk/gdkkeysyms.h>
-#include <gtk/gtkmain.h>
-#include <gtk/gtkwindow.h>
-#include <gtk/gtkentry.h>
-#include <gtk/gtkeditable.h>
-#include <gtk/gtkscrolledwindow.h>
-#include <gtk/gtktreeview.h>
-#include <gtk/gtktreemodel.h>
-#include <gtk/gtkliststore.h>
+#include <gtk/gtk.h>
 
 #include <string.h>
 #include <ctype.h>
 #  include <wctype.h>
 #endif
 
-#include "addrindex.h"
 #include "addr_compl.h"
+#include "addritem.h"
 #include "utils.h"
 #include "prefs_common.h"
-#include "sylpheed.h"
+#include "claws.h"
+#include "hooks.h"
+#include "gtkutils.h"
+#include "stock_pixmap.h"
 #include <pthread.h>
 
+#ifndef USE_NEW_ADDRBOOK
+       #include "addrindex.h"
+#else
+       #include "addressbook-dbus.h"
+#endif
+
 /*!
  *\brief       For the GtkListStore
  */
@@ -76,18 +79,7 @@ enum {
  * 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;
-       GList *grp_emails;
-} address_entry;
 
 /**
  * completion_entry - structure used to complete addresses, with a reference
@@ -102,8 +94,8 @@ typedef struct
 /*******************************************************************************/
 
 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 GList      *g_completion_list = NULL;   /* list of strings to be checked */
+static GList      *g_address_list = NULL;      /* address storage */
 static GCompletion *g_completion;      /* completion object */
 
 static GHashTable *_groupAddresses_ = NULL;
@@ -118,7 +110,7 @@ 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 */
+                                                * is g_utf8_strdown()'ed */
 
 static gchar *completion_folder_path = NULL;
 
@@ -167,36 +159,35 @@ static gboolean addr_compl_defer_select_destruct(CompletionWindow *window);
  */
 static gchar *completion_func(gpointer data)
 {
-       g_return_val_if_fail(data != NULL, NULL);
+       cm_return_val_if_fail(data != NULL, NULL);
 
        return ((completion_entry *)data)->string;
 } 
 
+static gint addr_completion_func(const gchar *needle, const gchar *haystack,
+               gsize n)
+{
+       if (needle == NULL || haystack == NULL)
+               return 1;
+
+       return (strcasestr(haystack, needle) != NULL ? 0 : 1);
+}
+
 /**
  * Initialize all completion index data.
  */
 static void init_all(void)
 {
        g_completion = g_completion_new(completion_func);
-       g_return_if_fail(g_completion != NULL);
+       g_completion_set_compare(g_completion, addr_completion_func);
+       cm_return_if_fail(g_completion != NULL);
 }
 
-/**
- * Free up all completion index data.
- */
-static void free_all(void)
+static void free_all_addresses(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);
-       }
-       g_list_free(g_completion_list);
-       g_completion_list = NULL;
-       
+       if (!g_address_list)
+               return;
        walk = g_address_list;
        for (; walk != NULL; walk = g_list_next(walk)) {
                address_entry *ae = (address_entry *) walk->data;
@@ -207,20 +198,48 @@ static void free_all(void)
        }
        g_list_free(g_address_list);
        g_address_list = NULL;
-       
-       g_completion_free(g_completion);
-       g_completion = NULL;
        if (_groupAddresses_)
                g_hash_table_destroy(_groupAddresses_);
        _groupAddresses_ = NULL;
 }
 
+static void clear_completion_cache(void);
+static void free_completion_list(void)
+{
+       GList *walk;
+       if (!g_completion_list)
+               return;
+       
+       clear_completion_cache();
+       if (g_completion)
+               g_completion_clear_items(g_completion);
+
+       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);
+       }
+       g_list_free(g_completion_list);
+       g_completion_list = NULL;
+}
+/**
+ * Free up all completion index data.
+ */
+static void free_all(void)
+{
+       free_completion_list(); 
+       free_all_addresses();   
+       g_completion_free(g_completion);
+       g_completion = NULL;
+}
+
 /**
  * Append specified address entry to the index.
  * \param str Index string value.
  * \param ae  Entry containing address data.
  */
-static void add_address1(const char *str, address_entry *ae)
+void addr_compl_add_address1(const char *str, address_entry *ae)
 {
        completion_entry *ce1;
        ce1 = g_new0(completion_entry, 1),
@@ -245,35 +264,32 @@ static void add_address1(const char *str, address_entry *ae)
 static gint add_address(const gchar *name, const gchar *address, 
                        const gchar *nick, const gchar *alias, GList *grp_emails)
 {
-       address_entry    *ae;
-       gboolean is_group = FALSE;
+       address_entry *ae;
 
-       if (!name || !address) {
-               if (!address && !nick && !alias && grp_emails) {
-                       is_group = TRUE;
-               } else
-                       return -1;
-       }
+       if (!address && !grp_emails)
+               return -1;
 
-       ae = g_new0(address_entry, 1);
+       if (!name)
+               name = "";
 
-       g_return_val_if_fail(ae != NULL, -1);
+       ae = g_new0(address_entry, 1);
+       cm_return_val_if_fail(ae != NULL, -1);
 
-       ae->name    = g_strdup(name);
-       ae->address = g_strdup(address);                
+       ae->name = g_strdup(name);
+       ae->address = g_strdup(address);
        ae->grp_emails = grp_emails;
        g_address_list = g_list_prepend(g_address_list, ae);
 
-       add_address1(name, ae);
-       if (address != NULL)
-               add_address1(address, ae);
-       
-       if (nick != NULL)
-               add_address1(nick, ae);
-       
-       if ( alias != NULL ) {
-               add_address1(alias, ae);
-       }
+       addr_compl_add_address1(name, ae);
+
+       if (address != NULL && *address != '\0')
+               addr_compl_add_address1(address, ae);
+
+       if (nick != NULL && *nick != '\0')
+               addr_compl_add_address1(nick, ae);
+
+       if (alias != NULL && *alias != '\0')
+               addr_compl_add_address1(alias, ae);
 
        return 0;
 }
@@ -281,10 +297,35 @@ static gint add_address(const gchar *name, const gchar *address,
 /**
  * Read address book, creating all entries in the completion index.
  */ 
-static void read_address_book(gchar *folderpath) {     
+static void read_address_book(gchar *folderpath) {
+       free_all_addresses();
+       free_completion_list();
+
+#ifndef USE_NEW_ADDRBOOK
        addrindex_load_completion( add_address, folderpath );
+#else
+       GError* error = NULL;
+       
+       addrcompl_initialize();
+       if (! addrindex_dbus_load_completion(add_address, &error)) {
+               g_warning("Failed to populate address completion list");
+        g_error_free(error);
+               return;
+       }
+#endif
+       /* plugins may hook in here to modify/extend the completion list */
+       hooks_invoke(ADDDRESS_COMPLETION_BUILD_ADDRESS_LIST_HOOKLIST, &g_address_list);
+
        g_address_list = g_list_reverse(g_address_list);
        g_completion_list = g_list_reverse(g_completion_list);
+       /* merge the completion entry list into g_completion */
+       if (g_completion_list) {
+               g_completion_add_items(g_completion, g_completion_list);
+               if (debug_get_mode())
+                       debug_print("read %d items in %s\n",
+                               g_list_length(g_completion_list),
+                               folderpath?folderpath:"(null)");
+       }
 }
 
 /**
@@ -322,22 +363,11 @@ static void clear_completion_cache(void)
  */
 gint start_address_completion(gchar *folderpath)
 {
+       gboolean different_book = FALSE;
        clear_completion_cache();
 
-       if ((completion_folder_path == NULL && folderpath != NULL) ||
-               (completion_folder_path != NULL && folderpath == NULL) ||
-               (completion_folder_path != NULL && folderpath != NULL &&
-                strcmp(completion_folder_path, folderpath) != 0)) {
-
-               debug_print("start_address_completion: resetting\n");
-
-               /* TODO: wwp: optimize: only reset when the new folderpath is MORE restrictive than the old one
-                 (the most easy case is when folderpath is NULL and completion_folder_path is != NULL */
-               if (g_ref_count) {
-                       free_all();
-                       g_ref_count = 0;
-               }
-       }
+       if (strcmp2(completion_folder_path,folderpath))
+               different_book = TRUE;
 
        g_free(completion_folder_path);
        if (folderpath != NULL)
@@ -349,13 +379,12 @@ gint start_address_completion(gchar *folderpath)
                init_all();
                /* open the address book */
                read_address_book(folderpath);
-               /* merge the completion entry list into g_completion */
-               if (g_completion_list)
-                       g_completion_add_items(g_completion, g_completion_list);
-       }
+       } else if (different_book)
+               read_address_book(folderpath);
+
        g_ref_count++;
        debug_print("start_address_completion(%s) ref count %d\n",
-                               folderpath, g_ref_count);
+                               folderpath?folderpath:"(null)", g_ref_count);
 
        return g_list_length(g_completion_list);
 }
@@ -490,7 +519,7 @@ guint complete_address(const gchar *str)
        guint  cpl = 0;
        completion_entry *ce = NULL;
 
-       g_return_val_if_fail(str != NULL, 0);
+       cm_return_val_if_fail(str != NULL, 0);
 
        /* g_completion is case sensitive */
        d = g_utf8_strdown(str, -1);
@@ -529,6 +558,33 @@ guint complete_address(const gchar *str)
        return count;
 }
 
+/**
+ * complete_matches_found() returns the number of matched addresses according
+ * to the completion mechanism. Unlike complete_address(), the returned value
+ * doesn't count str itself. If there's no match, it returns 0.
+ * To get a list of completion matches, see complete_address() instead.
+ */
+guint complete_matches_found(const gchar *str)
+{
+       GList *result = NULL;
+       gchar *d = NULL;
+
+       cm_return_val_if_fail(str != NULL, 0);
+
+       /* g_completion is case sensitive */
+       d = g_utf8_strdown(str, -1);
+
+       clear_completion_cache();
+       g_completion_prefix = g_strdup(str);
+
+       result = g_completion_complete(g_completion, d, NULL);
+
+       g_free(g_completion_prefix);
+       g_free(d);
+
+       return g_list_length(result);
+}
+
 /**
  * Return a complete address from the index.
  * \param index Index of entry that was found (by the previous call to
@@ -551,12 +607,13 @@ gchar *get_complete_address(gint index)
                                address = get_complete_address_from_name_email(p->name, p->address);
                        } else if (p != NULL && p->address == NULL && p->name != NULL) {
                                /* that's a group */
-                               address = g_strdup_printf("%s%s <!--___group___-->", p->name, _(" (Group)"));
+                               address = g_strdup_printf("%s (%s) <!--___group___-->", p->name, _("Group"));
                                if (!_groupAddresses_) {
                                        _groupAddresses_ = g_hash_table_new(NULL, g_direct_equal);
                                }
                                if (!g_hash_table_lookup(_groupAddresses_, GINT_TO_POINTER(g_str_hash(address)))) {
                                        g_hash_table_insert(_groupAddresses_, GINT_TO_POINTER(g_str_hash(address)), p->grp_emails);
+
                                }
                        }
                }
@@ -607,11 +664,7 @@ 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(completion_folder_path);
-               if (g_completion_list)
-               g_completion_add_items(g_completion, g_completion_list);
                clear_completion_cache();
        }
 
@@ -625,12 +678,23 @@ gint invalidate_address_completion(void)
  */
 gint end_address_completion(void)
 {
+       gboolean different_folder = FALSE;
        clear_completion_cache();
 
+       /* reset the folderpath to NULL */
+       if (completion_folder_path) {
+               g_free(completion_folder_path);
+               completion_folder_path = NULL;
+               different_folder = TRUE;
+       }
        if (0 == --g_ref_count)
                free_all();
 
        debug_print("end_address_completion ref count %d\n", g_ref_count);
+       if (g_ref_count && different_folder) {
+               debug_print("still ref'd, different folder\n");
+               invalidate_address_completion();
+       }
 
        return g_ref_count; 
 }
@@ -661,7 +725,7 @@ static guint _completionIdleID_ = 0;
 
 /*
  * address completion entry ui. the ui (completion list was inspired by galeon's
- * auto completion list). remaining things powered by sylpheed's completion engine.
+ * auto completion list). remaining things powered by claws's completion engine.
  */
 
 #define ENTRY_DATA_TAB_HOOK    "tab_hook"      /* used to lookup entry */
@@ -714,11 +778,12 @@ static CompletionWindow *addrcompl_create_window( void ) {
  */
 static void addrcompl_destroy_window( CompletionWindow *cw ) {
        /* Stop all searches currently in progress */
+#ifndef USE_NEW_ADDRBOOK
        addrindex_stop_search( _queryID_ );
-
+#endif
        /* Remove idler function... or application may not terminate */
        if( _completionIdleID_ != 0 ) {
-               gtk_idle_remove( _completionIdleID_ );
+               g_source_remove( _completionIdleID_ );
                _completionIdleID_ = 0;
        }
 
@@ -771,7 +836,7 @@ static void completion_window_advance_selection(GtkTreeView *list_view, gboolean
        GtkTreeIter iter;
        GtkTreeModel *model;
 
-       g_return_if_fail(list_view != NULL);
+       cm_return_if_fail(list_view != NULL);
 
        selection = gtk_tree_view_get_selection(list_view);
        if (!gtk_tree_selection_get_selected(selection, &model, &iter))
@@ -804,18 +869,35 @@ static void addrcompl_resize_window( CompletionWindow *cw ) {
        gint x, y, width, height, depth;
 
        /* Get current geometry of window */
-       gdk_window_get_geometry( cw->window->window, &x, &y, &width, &height, &depth );
+#if !GTK_CHECK_VERSION(3, 0, 0)
+       gdk_window_get_geometry( gtk_widget_get_window( cw->window ), &x, &y, &width, &height, &depth );
+#else
+       gdk_window_get_geometry( gtk_widget_get_window( cw->window ), &x, &y, &width, &height );
+#endif
 
+       /* simple _hide breaks size requisition !? */
+#if !GTK_CHECK_VERSION(3, 0, 0)
        gtk_widget_hide_all( cw->window );
        gtk_widget_show_all( cw->window );
+#else
+       gtk_widget_hide( cw->window );
+       gtk_widget_show( cw->window );
+#endif
        gtk_widget_size_request( cw->list_view, &r );
 
        /* Adjust window height to available screen space */
-       if( ( y + r.height ) > gdk_screen_height() ) {
-               gtk_window_set_resizable(GTK_WINDOW(cw->window), FALSE);
-               gtk_widget_set_size_request( cw->window, width, gdk_screen_height() - y );
-       } else
-               gtk_widget_set_size_request(cw->window, width, r.height);
+       if( y + r.height > gdk_screen_height())
+               r.height = gdk_screen_height() - y;
+
+       gtk_widget_set_size_request(cw->window, width, r.height);
+
+       gdk_pointer_grab(gtk_widget_get_window(cw->window), TRUE,
+                        GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
+                        GDK_BUTTON_RELEASE_MASK,
+                        NULL, NULL, GDK_CURRENT_TIME);
+       gdk_keyboard_grab(gtk_widget_get_window(cw->window), FALSE, GDK_CURRENT_TIME);
+       gtk_grab_add(cw->window);
+
 }
 
 static GdkPixbuf *group_pixbuf = NULL;
@@ -835,12 +917,15 @@ static void addrcompl_add_entry( CompletionWindow *cw, gchar *address ) {
        store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cw->list_view)));
        GdkPixbuf *pixbuf;
        
-       if (!group_pixbuf)
+       if (!group_pixbuf) {
                stock_pixbuf_gdk(cw->list_view, STOCK_PIXMAP_ADDR_TWO, &group_pixbuf);
-       if (!email_pixbuf)
+               g_object_ref(G_OBJECT(group_pixbuf));
+       }
+       if (!email_pixbuf) {
                stock_pixbuf_gdk(cw->list_view, STOCK_PIXMAP_ADDR_ONE, &email_pixbuf);
-
-       /* printf( "\t\tAdding :%s\n", address ); */
+               g_object_ref(G_OBJECT(email_pixbuf));
+       }
+       /* g_print( "\t\tAdding :%s\n", address ); */
        if (strstr(address, " <!--___group___-->")) {
                is_group = TRUE;
                if (_groupAddresses_)
@@ -875,11 +960,13 @@ static void addrcompl_add_entry( CompletionWindow *cw, gchar *address ) {
                /* Select first row for now */
                gtk_tree_selection_select_iter(selection, &iter);
        }
+#ifndef GENERIC_UMPC
        else if( cw->listCount == 2 ) {
                gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
                /* Move off first row */
                gtk_tree_selection_select_iter(selection, &iter);
        }
+#endif
 }
 
 /**
@@ -900,7 +987,7 @@ static gboolean addrcompl_idle( gpointer data ) {
                node = _displayQueue_;
                while( node ) {
                        address = node->data;
-                       /* printf( "address ::: %s :::\n", address ); */
+                       /* g_print( "address ::: %s :::\n", address ); */
                        addrcompl_add_entry( _compWindow_, address );
                        g_free( address );
                        node = g_list_next( node );
@@ -909,7 +996,7 @@ static gboolean addrcompl_idle( gpointer data ) {
                _displayQueue_ = NULL;
        }
        pthread_mutex_unlock( & _completionMutex_ );
-       sylpheed_do_idle();
+       claws_do_idle();
 
        return TRUE;
 }
@@ -923,13 +1010,14 @@ static gboolean addrcompl_idle( gpointer data ) {
  *                   criteria.
  * \param data       Query data.
  */
+#ifndef USE_NEW_ADDRBOOK
 static gint addrcompl_callback_entry(
        gpointer sender, gint queryID, GList *listEMail, gpointer data )
 {
        GList *node;
        gchar *address;
 
-       /* printf( "addrcompl_callback_entry::queryID=%d\n", queryID ); */
+       /* g_print( "addrcompl_callback_entry::queryID=%d\n", queryID ); */
        pthread_mutex_lock( & _completionMutex_ );
        if( queryID == _queryID_ ) {
                /* Append contents to end of display queue */
@@ -938,7 +1026,7 @@ static gint addrcompl_callback_entry(
                        ItemEMail *email = node->data;
 
                        address = addritem_format_email( email );
-                       /* printf( "\temail/address ::%s::\n", address ); */
+                       /* g_print( "\temail/address ::%s::\n", address ); */
                        _displayQueue_ = g_list_append( _displayQueue_, address );
                        node = g_list_next( node );
                }
@@ -948,6 +1036,7 @@ static gint addrcompl_callback_entry(
 
        return 0;
 }
+#endif
 
 /**
  * Clear the display queue.
@@ -982,7 +1071,7 @@ static void addrcompl_load_local( void ) {
                gchar *address;
 
                address = get_complete_address( count );
-               /* printf( "\taddress ::%s::\n", address ); */
+               /* g_print( "\taddress ::%s::\n", address ); */
 
                /* Append contents to end of display queue */
                addrcompl_add_queue( address );
@@ -993,6 +1082,7 @@ static void addrcompl_load_local( void ) {
  * Start the search.
  */
 static void addrcompl_start_search( void ) {
+#ifndef USE_NEW_ADDRBOOK
        gchar *searchTerm;
 
        searchTerm = g_strdup( _compWindow_->searchTerm );
@@ -1001,17 +1091,22 @@ static void addrcompl_start_search( void ) {
        _queryID_ = addrindex_setup_search(
                searchTerm, NULL, addrcompl_callback_entry );
        g_free( searchTerm );
-       /* printf( "addrcompl_start_search::queryID=%d\n", _queryID_ ); */
+#endif
+       /* g_print( "addrcompl_start_search::queryID=%d\n", _queryID_ ); */
 
        /* Load local stuff */
        addrcompl_load_local();
 
        /* Sit back and wait until something happens */
        _completionIdleID_ =
-               gtk_idle_add( ( GtkFunction ) addrcompl_idle, NULL );
-       /* printf( "addrindex_start_search::queryID=%d\n", _queryID_ ); */
+               g_idle_add( (GSourceFunc) addrcompl_idle, NULL );
+       /* g_print( "addrindex_start_search::queryID=%d\n", _queryID_ ); */
 
+#ifndef USE_NEW_ADDRBOOK
        addrindex_start_search( _queryID_ );
+#else
+       
+#endif
 }
 
 /**
@@ -1032,8 +1127,8 @@ static void completion_window_apply_selection(GtkTreeView *list_view,
        GtkTreeModel *model;
        GtkTreeIter iter;
        gboolean is_group = FALSE;
-       g_return_if_fail(list_view != NULL);
-       g_return_if_fail(entry != NULL);
+       cm_return_if_fail(list_view != NULL);
+       cm_return_if_fail(entry != NULL);
        GList *grp_emails = NULL;
 
        selection = gtk_tree_view_get_selection(list_view);
@@ -1042,7 +1137,7 @@ static void completion_window_apply_selection(GtkTreeView *list_view,
 
        /* First remove the idler */
        if( _completionIdleID_ != 0 ) {
-               gtk_idle_remove( _completionIdleID_ );
+               g_source_remove( _completionIdleID_ );
                _completionIdleID_ = 0;
        }
 
@@ -1058,7 +1153,7 @@ static void completion_window_apply_selection(GtkTreeView *list_view,
        g_free(text);
 
        /* Move focus to next widget */
-       parent = GTK_WIDGET(entry)->parent;
+       parent = gtk_widget_get_parent(GTK_WIDGET(entry));
        if( parent && move_focus) {
                gtk_widget_child_focus( parent, GTK_DIR_TAB_FORWARD );
        }
@@ -1091,8 +1186,8 @@ void address_completion_start(GtkWidget *mainwindow)
  */
 void address_completion_register_entry(GtkEntry *entry, gboolean allow_commas)
 {
-       g_return_if_fail(entry != NULL);
-       g_return_if_fail(GTK_IS_ENTRY(entry));
+       cm_return_if_fail(entry != NULL);
+       cm_return_if_fail(GTK_IS_ENTRY(entry));
 
        /* add hooked property */
        g_object_set_data(G_OBJECT(entry), ENTRY_DATA_TAB_HOOK, entry);
@@ -1113,14 +1208,14 @@ void address_completion_register_entry(GtkEntry *entry, gboolean allow_commas)
  */
 void address_completion_unregister_entry(GtkEntry *entry)
 {
-       GtkObject *entry_obj;
+       GObject *entry_obj;
 
-       g_return_if_fail(entry != NULL);
-       g_return_if_fail(GTK_IS_ENTRY(entry));
+       cm_return_if_fail(entry != NULL);
+       cm_return_if_fail(GTK_IS_ENTRY(entry));
 
        entry_obj = g_object_get_data(G_OBJECT(entry), ENTRY_DATA_TAB_HOOK);
-       g_return_if_fail(entry_obj);
-       g_return_if_fail(G_OBJECT(entry_obj) == G_OBJECT(entry));
+       cm_return_if_fail(entry_obj);
+       cm_return_if_fail(G_OBJECT(entry_obj) == G_OBJECT(entry));
 
        /* has the hooked property? */
        g_object_set_data(G_OBJECT(entry), ENTRY_DATA_TAB_HOOK, NULL);
@@ -1168,14 +1263,14 @@ static gboolean address_completion_entry_key_pressed(GtkEntry    *entry,
                                                     GdkEventKey *ev,
                                                     gpointer     data)
 {
-       if (ev->keyval == GDK_Tab) {
+       if (ev->keyval == GDK_KEY_Tab) {
                addrcompl_clear_queue();
                _allowCommas_ = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(entry), ENTRY_DATA_ALLOW_COMMAS));
                if( address_completion_complete_address_in_entry( entry, TRUE ) ) {
                        /* route a void character to the default handler */
                        /* this is a dirty hack; we're actually changing a key
                         * reported by the system. */
-                       ev->keyval = GDK_AudibleBell_Enable;
+                       ev->keyval = GDK_KEY_AudibleBell_Enable;
                        ev->state &= ~GDK_SHIFT_MASK;
 
                        /* Create window */                     
@@ -1189,16 +1284,16 @@ static gboolean address_completion_entry_key_pressed(GtkEntry    *entry,
                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) {
+       } else if (ev->keyval == GDK_KEY_Shift_L
+               || ev->keyval == GDK_KEY_Shift_R
+               || ev->keyval == GDK_KEY_Control_L
+               || ev->keyval == GDK_KEY_Control_R
+               || ev->keyval == GDK_KEY_Caps_Lock
+               || ev->keyval == GDK_KEY_Shift_Lock
+               || ev->keyval == GDK_KEY_Meta_L
+               || ev->keyval == GDK_KEY_Meta_R
+               || ev->keyval == GDK_KEY_Alt_L
+               || ev->keyval == GDK_KEY_Alt_R) {
                /* these buttons should not clear the cache... */
        } else
                clear_completion_cache();
@@ -1215,14 +1310,14 @@ static gboolean address_completion_complete_address_in_entry(GtkEntry *entry,
        gint ncount, cursor_pos;
        gchar *searchTerm, *new = NULL;
 
-       g_return_val_if_fail(entry != NULL, FALSE);
+       cm_return_val_if_fail(entry != NULL, FALSE);
 
-       if (!GTK_WIDGET_HAS_FOCUS(entry)) return FALSE;
+       if (!gtk_widget_has_focus(GTK_WIDGET(entry))) return FALSE;
 
        /* get an address component from the cursor */
        searchTerm = get_address_from_edit( entry, &cursor_pos );
        if( ! searchTerm ) return FALSE;
-       /* printf( "search for :::%s:::\n", searchTerm ); */
+       /* g_print( "search for :::%s:::\n", searchTerm ); */
 
        /* Clear any existing search */
        g_free( _compWindow_->searchTerm );
@@ -1234,7 +1329,7 @@ static gboolean address_completion_complete_address_in_entry(GtkEntry *entry,
                new = get_next_complete_address();
                g_free( new );
        }
-#ifndef USE_LDAP
+#if (!defined(USE_LDAP) && !defined(GENERIC_UMPC))
        /* Select the address if there is only one match */
        if (ncount == 2) {
                /* Display selected address in entry field */           
@@ -1268,6 +1363,7 @@ static void address_completion_create_completion_window( GtkEntry *entry_ )
        GtkRequisition r;
        GtkWidget *window;
        GtkWidget *entry = GTK_WIDGET(entry_);
+       GdkWindow *gdkwin;
 
        /* Create new window and list */
        window = gtk_window_new(GTK_WINDOW_POPUP);
@@ -1291,8 +1387,13 @@ static void address_completion_create_completion_window( GtkEntry *entry_ )
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
                GTK_SHADOW_OUT);
        /* Use entry widget to create initial window */
-       gdk_window_get_geometry(entry->window, &x, &y, &width, &height, &depth);
-       gdk_window_get_deskrelative_origin (entry->window, &x, &y);
+       gdkwin = gtk_widget_get_window(entry),
+#if !GTK_CHECK_VERSION(3, 0, 0)
+       gdk_window_get_geometry(gdkwin, &x, &y, &width, &height, &depth);
+#else
+       gdk_window_get_geometry(gdkwin, &x, &y, &width, &height);
+#endif
+       gdk_window_get_origin (gdkwin, &x, &y);
        y += height;
        gtk_window_move(GTK_WINDOW(window), x, y);
 
@@ -1319,17 +1420,12 @@ static void address_completion_create_completion_window( GtkEntry *entry_ )
                         "key-press-event",
                         G_CALLBACK(completion_window_key_press),
                         _compWindow_ );
-       gdk_pointer_grab(window->window, TRUE,
+       gdk_pointer_grab(gtk_widget_get_window(window), TRUE,
                         GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
                         GDK_BUTTON_RELEASE_MASK,
                         NULL, NULL, GDK_CURRENT_TIME);
+       gdk_keyboard_grab(gtk_widget_get_window(window), FALSE, GDK_CURRENT_TIME);
        gtk_grab_add( window );
-
-       /* XXX: GTK2 too??? 
-        *
-        * GTK1: this gets rid of the irritating focus rectangle that doesn't
-        * follow the selection */
-       GTK_WIDGET_UNSET_FLAGS(list_view, GTK_CAN_FOCUS);
 }
 
 /**
@@ -1350,10 +1446,10 @@ static gboolean completion_window_button_press(GtkWidget *widget,
        gint cursor_pos;
        gboolean restore = TRUE;
 
-       g_return_val_if_fail(compWin != NULL, FALSE);
+       cm_return_val_if_fail(compWin != NULL, FALSE);
 
        entry = compWin->entry;
-       g_return_val_if_fail(entry != NULL, FALSE);
+       cm_return_val_if_fail(entry != NULL, FALSE);
 
        /* Test where mouse was clicked */
        event_widget = gtk_get_event_widget((GdkEvent *)event);
@@ -1365,7 +1461,7 @@ static gboolean completion_window_button_press(GtkWidget *widget,
                                restore = FALSE;
                                break;
                        }
-                       event_widget = event_widget->parent;
+                       event_widget = gtk_widget_get_parent(event_widget);
                }
        }
 
@@ -1398,26 +1494,26 @@ static gboolean completion_window_key_press(GtkWidget *widget,
        gint cursor_pos;
        GtkWidget *list_view;
        GtkWidget *parent;
-       g_return_val_if_fail(compWin != NULL, FALSE);
+       cm_return_val_if_fail(compWin != NULL, FALSE);
 
        entry = compWin->entry;
        list_view = compWin->list_view;
-       g_return_val_if_fail(entry != NULL, FALSE);
+       cm_return_val_if_fail(entry != NULL, FALSE);
 
        /* allow keyboard navigation in the alternatives tree view */
-       if (event->keyval == GDK_Up || event->keyval == GDK_Down ||
-           event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down) {
+       if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down ||
+           event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_Page_Down) {
                completion_window_advance_selection
                        (GTK_TREE_VIEW(list_view),
-                        event->keyval == GDK_Down ||
-                        event->keyval == GDK_Page_Down ? TRUE : FALSE);
+                        event->keyval == GDK_KEY_Down ||
+                        event->keyval == GDK_KEY_Page_Down ? TRUE : FALSE);
                return FALSE;
        }               
 
        /* make tab move to next field */
-       if( event->keyval == GDK_Tab ) {
+       if( event->keyval == GDK_KEY_Tab ) {
                /* Reference to parent */
-               parent = GTK_WIDGET(entry)->parent;
+               parent = gtk_widget_get_parent(GTK_WIDGET(entry));
 
                /* Discard the window */
                clear_completion_cache();
@@ -1431,9 +1527,9 @@ static gboolean completion_window_key_press(GtkWidget *widget,
        }
 
        /* make backtab move to previous field */
-       if( event->keyval == GDK_ISO_Left_Tab ) {
+       if( event->keyval == GDK_KEY_ISO_Left_Tab ) {
                /* Reference to parent */
-               parent = GTK_WIDGET(entry)->parent;
+               parent = gtk_widget_get_parent(GTK_WIDGET(entry));
 
                /* Discard the window */
                clear_completion_cache();
@@ -1448,16 +1544,17 @@ static gboolean completion_window_key_press(GtkWidget *widget,
        _allowCommas_ = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(entry), ENTRY_DATA_ALLOW_COMMAS));
 
        /* look for presses that accept the selection */
-       if (event->keyval == GDK_Return || event->keyval == GDK_space
-                       || (_allowCommas_ && event->keyval == GDK_comma)) {
+       if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_space ||
+                       event->keyval == GDK_KEY_KP_Enter ||
+                       (_allowCommas_ && event->keyval == GDK_KEY_comma)) {
                /* User selected address with a key press */
 
                /* Display selected address in entry field */           
                completion_window_apply_selection(
                        GTK_TREE_VIEW(list_view), GTK_ENTRY(entry),
-                       event->keyval != GDK_comma);
+                       event->keyval != GDK_KEY_comma);
 
-               if (event->keyval == GDK_comma) {
+               if (event->keyval == GDK_KEY_comma) {
                        gint pos = gtk_editable_get_position(GTK_EDITABLE(entry));
                        gtk_editable_insert_text(GTK_EDITABLE(entry), ", ", 2, &pos);
                        gtk_editable_set_position(GTK_EDITABLE(entry), pos + 1);
@@ -1470,16 +1567,16 @@ static gboolean completion_window_key_press(GtkWidget *widget,
        }
 
        /* key state keys should never be handled */
-       if (event->keyval == GDK_Shift_L
-                || event->keyval == GDK_Shift_R
-                || event->keyval == GDK_Control_L
-                || event->keyval == GDK_Control_R
-                || event->keyval == GDK_Caps_Lock
-                || event->keyval == GDK_Shift_Lock
-                || event->keyval == GDK_Meta_L
-                || event->keyval == GDK_Meta_R
-                || event->keyval == GDK_Alt_L
-                || event->keyval == GDK_Alt_R) {
+       if (event->keyval == GDK_KEY_Shift_L
+                || event->keyval == GDK_KEY_Shift_R
+                || event->keyval == GDK_KEY_Control_L
+                || event->keyval == GDK_KEY_Control_R
+                || event->keyval == GDK_KEY_Caps_Lock
+                || event->keyval == GDK_KEY_Shift_Lock
+                || event->keyval == GDK_KEY_Meta_L
+                || event->keyval == GDK_KEY_Meta_R
+                || event->keyval == GDK_KEY_Alt_L
+                || event->keyval == GDK_KEY_Alt_R) {
                return FALSE;
        }
 
@@ -1490,7 +1587,7 @@ static gboolean completion_window_key_press(GtkWidget *widget,
 
        /* make sure anything we typed comes in the edit box */
        tmp_event.type       = event->type;
-       tmp_event.window     = entry->window;
+       tmp_event.window     = gtk_widget_get_window(GTK_WIDGET(entry));
        tmp_event.send_event = TRUE;
        tmp_event.time       = event->time;
        tmp_event.state      = event->state;
@@ -1516,20 +1613,20 @@ static gboolean completion_window_key_press(GtkWidget *widget,
  * Setup completion object.
  */
 void addrcompl_initialize( void ) {
-       /* printf( "addrcompl_initialize...\n" ); */
+       /* g_print( "addrcompl_initialize...\n" ); */
        if( ! _compWindow_ ) {
                _compWindow_ = addrcompl_create_window();
        }
        _queryID_ = 0;
        _completionIdleID_ = 0;
-       /* printf( "addrcompl_initialize...done\n" ); */
+       /* g_print( "addrcompl_initialize...done\n" ); */
 }
 
 /**
  * Teardown completion object.
  */
 void addrcompl_teardown( void ) {
-       /* printf( "addrcompl_teardown...\n" ); */
+       /* g_print( "addrcompl_teardown...\n" ); */
        addrcompl_free_window( _compWindow_ );
        _compWindow_ = NULL;
        if( _displayQueue_ ) {
@@ -1537,7 +1634,7 @@ void addrcompl_teardown( void ) {
        }
        _displayQueue_ = NULL;
        _completionIdleID_ = 0;
-       /* printf( "addrcompl_teardown...done\n" ); */
+       /* g_print( "addrcompl_teardown...done\n" ); */
 }
 
 /*
@@ -1631,11 +1728,11 @@ static gboolean addr_compl_selected(GtkTreeSelection *selector,
         * not using a time out would result in a crash. if this doesn't work
         * safely, maybe we should set variables when receiving button presses
         * in the tree view. */
-       if (!window->destroying) {       
-               window->destroying = TRUE;       
+       if (!window->destroying) {
+               window->destroying = TRUE;
                g_idle_add((GSourceFunc) addr_compl_defer_select_destruct, data);
-       }               
-       
+       }
+
        return TRUE;
 }