speed improvements
[claws.git] / src / addr_compl.c
index c475e5b0bcff3f0470ab6aa56cc0fd78a086124a..32aa5f4ba58b13b10eac68eb2a91f00845994abe 100644 (file)
 #  include <wctype.h>
 #endif
 
-#include "xml.h"
+#include "addressbook.h"
 #include "addr_compl.h"
 #include "utils.h"
-#include "addressbook.h"
-#include "main.h"
 
 /* How it works:
  *
@@ -144,39 +142,39 @@ static void free_all(void)
        g_completion = NULL;
 }
 
+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 gint add_address(const gchar *name, const gchar *address)
+static gint add_address(const gchar *name, const gchar *address, const gchar *alias)
 {
        address_entry    *ae;
-       completion_entry *ce1;
-       completion_entry *ce2;
 
        if (!name || !address) return -1;
 
-       debug_print( "completion: add_address: %s - %s\n", name, address );
-
        ae = g_new0(address_entry, 1);
-       ce1 = g_new0(completion_entry, 1),
-       ce2 = g_new0(completion_entry, 1);
 
        g_return_val_if_fail(ae != NULL, -1);
-       g_return_val_if_fail(ce1 != NULL && ce2 != NULL, -1);   
 
        ae->name    = g_strdup(name);
        ae->address = g_strdup(address);                
-       ce1->string = g_strdup(name);
-       ce2->string = g_strdup(address);
 
-       /* GCompletion list is case sensitive */
-       g_strdown(ce2->string);
-       g_strdown(ce1->string);
-       ce1->ref = ce2->ref = ae;
+       g_address_list    = g_list_prepend(g_address_list,    ae);
 
-       g_completion_list = g_list_append(g_completion_list, ce1);
-       g_completion_list = g_list_append(g_completion_list, ce2);
-       g_address_list    = g_list_append(g_address_list,    ae);
+       add_address1(name, ae);
+       add_address1(address, ae);
+       add_address1(alias, ae);
 
        return 0;
 }
@@ -185,6 +183,8 @@ static gint add_address(const gchar *name, const gchar *address)
  */ 
 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);
 }
 
 /* start_address_completion() - returns the number of addresses 
@@ -219,8 +219,18 @@ gchar *get_address_from_edit(GtkEntry *entry, gint *start_pos)
        wchar_t *wtext;
        wchar_t *wp;
        wchar_t rfc_mail_sep;
+       wchar_t quote;
+       wchar_t lt;
+       wchar_t gt;
+       gboolean in_quote = FALSE;
+       gboolean in_bracket = FALSE;
        gchar *str;
 
+       if (mbtowc(&rfc_mail_sep, ",", 1) < 0) return NULL;
+       if (mbtowc(&quote, "\"", 1) < 0) return NULL;
+       if (mbtowc(&lt, "<", 1) < 0) return NULL;
+       if (mbtowc(&gt, ">", 1) < 0) return NULL;
+
        edit_text = gtk_entry_get_text(entry);
        if (edit_text == NULL) return NULL;
 
@@ -229,14 +239,19 @@ gchar *get_address_from_edit(GtkEntry *entry, gint *start_pos)
 
        cur_pos = gtk_editable_get_position(GTK_EDITABLE(entry));
 
-       if (mbtowc(&rfc_mail_sep, ",", 1) < 0) {
-               g_free(wtext);
-               return NULL;
-       }
-
        /* scan for a separator. doesn't matter if walk points at null byte. */
-       for (wp = wtext + cur_pos; wp > wtext && *wp != rfc_mail_sep; wp--)
-               ;
+       for (wp = wtext + cur_pos; wp > wtext; wp--) {
+               if (*wp == quote)
+                       in_quote ^= TRUE;
+               else if (!in_quote) {
+                       if (!in_bracket && *wp == rfc_mail_sep)
+                               break;
+                       else if (*wp == gt)
+                               in_bracket = TRUE;
+                       else if (*wp == lt)
+                               in_bracket = FALSE;
+               }
+       }
 
        /* have something valid */
        if (wcslen(wp) == 0) {
@@ -244,7 +259,8 @@ gchar *get_address_from_edit(GtkEntry *entry, gint *start_pos)
                return NULL;
        }
 
-#define IS_VALID_CHAR(x)       (iswalnum(x) || ((x) > 0x7f))
+#define IS_VALID_CHAR(x) \
+       (iswalnum(x) || (x) == quote || (x) == lt || ((x) > 0x7f))
 
        /* now scan back until we hit a valid character */
        for (; *wp && !IS_VALID_CHAR(*wp); wp++)
@@ -270,6 +286,7 @@ gchar *get_address_from_edit(GtkEntry *entry, gint *start_pos)
 void replace_address_in_edit(GtkEntry *entry, const gchar *newtext,
                             gint start_pos)
 {
+       if (!newtext) return;
        gtk_editable_delete_text(GTK_EDITABLE(entry), start_pos, -1);
        gtk_editable_insert_text(GTK_EDITABLE(entry), newtext, strlen(newtext),
                                 &start_pos);
@@ -331,22 +348,29 @@ guint complete_address(const gchar *str)
 gchar *get_complete_address(gint index)
 {
        const address_entry *p;
-       
+       gchar *address = NULL;
+
        if (index < g_completion_count) {
                if (index == 0)
-                       return g_strdup(g_completion_prefix);
+                       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)
-                               return NULL;
-                       else
-                               return g_strdup_printf
-                                       ("%s <%s>", p->name, p->address);
+                       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);
+                       }
                }
-       } else
-               return NULL;
+       }
+
+       return address;
 }
 
 gchar *get_next_complete_address(void)
@@ -500,6 +524,7 @@ static void completion_window_advance_selection(GtkCList *clist, gboolean forwar
        gtk_clist_thaw(clist);
 }
 
+#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,
@@ -507,7 +532,7 @@ static void completion_window_accept_selection(GtkWidget **window,
                                               GtkEntry *entry)
 {
        gchar *address = NULL, *text = NULL;
-       gint   cursor_pos, row, col;
+       gint   cursor_pos, row;
 
        g_return_if_fail(window != NULL);
        g_return_if_fail(*window != NULL);
@@ -515,21 +540,39 @@ static void completion_window_accept_selection(GtkWidget **window,
        g_return_if_fail(entry != NULL);
        g_return_if_fail(clist->selection != NULL);
 
-       col = 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);
-       gtk_clist_get_text(clist, row, col, &text);
+       g_free(address);
+       gtk_clist_get_text(clist, row, 0, &text);
        replace_address_in_edit(entry, text, cursor_pos);
-       g_free(address);                                
 
        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);
+
+       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);
+}
 
 /* should be called when creating the main window containing address
  * completion entries */
@@ -658,25 +701,26 @@ static gboolean address_completion_complete_address_in_entry(GtkEntry *entry,
        if (!GTK_WIDGET_HAS_FOCUS(entry)) return FALSE;
 
        /* get an address component from the cursor */
-       if (0 != (address = get_address_from_edit(entry, &cursor_pos))) {
-               /* 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();
-               }
+       address = get_address_from_edit(entry, &cursor_pos);
+       if (!address) return FALSE;
 
-               if (new) {
-                       /* prevent "change" signal */
-                       replace_address_in_edit(entry, new, cursor_pos);
-                       g_free(new);
-                       completed = TRUE;
-               }
+       /* 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();
+       }
 
-               g_free(address);
-       }                                       
+       if (new) {
+               /* prevent "change" signal */
+               /* replace_address_in_edit(entry, new, cursor_pos); */
+               g_free(new);
+               completed = TRUE;
+       }
+
+       g_free(address);
 
        return completed;
 }
@@ -771,23 +815,6 @@ static void completion_window_select_row(GtkCList *clist, gint row, gint col,
 {
        GtkEntry *entry;
 
-       /* first check if it's anything but a mouse event. Mouse events
-        * accept the completion. Anything else is accepted by the
-        * completion_window_key_press() */
-       if (!event) {
-               /* event == NULL if key press did the selection or just
-                * event emitted with signal_emit_XXX(). This seems to 
-                * be the case for the gtk versions I have seen */
-               return;
-       }
-
-       /* however, a future version of GTK might pass the event type
-        * that triggered the select_row. since this event handler
-        * only wants mouse clicks, we check for that. */
-       if (event->type != GDK_BUTTON_RELEASE) {
-               return;
-       }
-
        g_return_if_fail(completion_window != NULL);
        g_return_if_fail(*completion_window != NULL);
 
@@ -795,7 +822,14 @@ static void completion_window_select_row(GtkCList *clist, gint row, gint col,
                                              WINDOW_DATA_COMPL_ENTRY));
        g_return_if_fail(entry != NULL);
 
-       completion_window_accept_selection(completion_window, clist, entry);    
+       completion_window_apply_selection(clist, entry);
+
+       if (!event || event->type != GDK_BUTTON_RELEASE)
+               return;
+
+       clear_completion_cache();
+       gtk_widget_destroy(*completion_window);
+       *completion_window = NULL;
 }
 
 /* completion_window_button_press() - check is mouse click is anywhere
@@ -834,12 +868,13 @@ static gboolean completion_window_button_press(GtkWidget *widget,
                prefix = get_complete_address(0);
                g_free(get_address_from_edit(GTK_ENTRY(entry), &cursor_pos));
                replace_address_in_edit(GTK_ENTRY(entry), prefix, cursor_pos);
+               g_free(prefix);
        }
 
+       clear_completion_cache();
        gtk_widget_destroy(*completion_window);
        *completion_window = NULL;
 
-       clear_completion_cache();
        return TRUE;
 }
 
@@ -887,9 +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) {
-               completion_window_accept_selection(completion_window,
-                                                  GTK_CLIST(clist),
-                                                  GTK_ENTRY(entry));
+               clear_completion_cache();
+               gtk_widget_destroy(*completion_window);
+               *completion_window = NULL;
                return FALSE;
        }