2008-06-13 [wwp] 3.4.0cvs92
[claws.git] / src / addressbook.c
index 8a1267a5b5d1ddfbc0c6931da336d4dc8b9ac941..76e2fe51d826430c3eaaca9235a58c559476fe64 100644 (file)
@@ -4,7 +4,7 @@
  *
  * 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,
@@ -13,8 +13,8 @@
  * 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
@@ -44,6 +44,8 @@
 #include <gtk/gtkitemfactory.h>
 #include <string.h>
 #include <setjmp.h>
+#include <sys/types.h>
+#include <dirent.h>
 
 #include "main.h"
 #include "addressbook.h"
@@ -69,6 +71,7 @@
 #include "addrbook.h"
 #include "addrindex.h"
 #include "addressadd.h"
+#include "addrduplicates.h"
 #include "addressbook_foldersel.h"
 #include "vcard.h"
 #include "editvcard.h"
 #include "exphtmldlg.h"
 #include "expldifdlg.h"
 #include "browseldap.h"
+#include "addrcustomattr.h"
 
 typedef enum
 {
@@ -366,6 +370,8 @@ static void addressbook_list_select_add             ( AddrItemObject    *aio,
 static void addressbook_list_select_remove     ( AddrItemObject    *aio );
 
 static void addressbook_import_ldif_cb         ( void );
+static void addressbook_find_duplicates_cb     ( void );
+static void addressbook_edit_custom_attr_cb    ( void );
 static void addressbook_import_mutt_cb         ( void );
 static void addressbook_import_pine_cb         ( void );
 static void addressbook_export_html_cb         ( void );
@@ -424,79 +430,82 @@ static GtkTargetList *addressbook_target_list = NULL;
 
 static GtkItemFactoryEntry addressbook_entries[] =
 {
-       {N_("/_Book"),                  NULL,           NULL, 0, "<Branch>"},
-       {N_("/_Book/New _Book"),        "<control>B",   addressbook_new_book_cb,        0, NULL},
-       {N_("/_Book/New _Folder"),      "<control>R",   addressbook_new_folder_cb,      0, NULL},
-       {N_("/_Book/New _vCard"),       "<control><shift>D",    addressbook_new_vcard_cb,       0, NULL},
+       {N_("/_Book"),                  NULL,           NULL, 0, "<Branch>", NULL},
+       {N_("/_Book/New _Book"),        "<control>B",   addressbook_new_book_cb,        0, NULL, NULL},
+       {N_("/_Book/New _Folder"),      "<control>R",   addressbook_new_folder_cb,      0, NULL, NULL},
+       {N_("/_Book/New _vCard"),       "<control><shift>D",    addressbook_new_vcard_cb,       0, NULL, NULL},
 #ifdef USE_JPILOT
-       {N_("/_Book/New _JPilot"),      "<control>J",   addressbook_new_jpilot_cb,      0, NULL},
+       {N_("/_Book/New _JPilot"),      "<control>J",   addressbook_new_jpilot_cb,      0, NULL, NULL},
 #endif
 #ifdef USE_LDAP
-       {N_("/_Book/New LDAP _Server"), "<control><shift>S",    addressbook_new_ldap_cb,        0, NULL},
+       {N_("/_Book/New LDAP _Server"), "<control><shift>S",    addressbook_new_ldap_cb,        0, NULL, NULL},
 #endif
-       {N_("/_Book/---"),              NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Book/_Edit book"),       NULL,           addressbook_treenode_edit_cb,   0, NULL},
-       {N_("/_Book/_Delete book"),     NULL,           addressbook_treenode_delete_cb, 0, NULL},
-       {N_("/_Book/---"),              NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Book/_Save"),            "<control>S",   addressbook_file_save_cb,       0, NULL},
-       {N_("/_Book/_Close"),           "<control>W",   close_cb,                       0, NULL},
-       {N_("/_Address"),                       NULL,           NULL, 0, "<Branch>"},
-       {N_("/_Address/_Select all"),   "<control>A",   addressbook_select_all_cb,      0, NULL},
-       {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Address/C_ut"),          "<control>X",   addressbook_clip_cut_cb,        0, NULL},
-       {N_("/_Address/_Copy"),         "<control>C",   addressbook_clip_copy_cb,       0, NULL},
-       {N_("/_Address/_Paste"),        "<control>V",   addressbook_clip_paste_cb,      0, NULL},
-       {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Address/_Edit"),         "<control>Return",addressbook_edit_address_cb,    0, NULL},
-       {N_("/_Address/_Delete"),       "<control>D",   addressbook_delete_address_cb,  0, NULL},
-       {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Address/New _Address"),  "<control>N",   addressbook_new_address_cb,     0, NULL},
-       {N_("/_Address/New _Group"),    "<control>G",   addressbook_new_group_cb,       0, NULL},
-       {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Address/_Mail To"),      NULL,           addressbook_mail_to_cb,         0, NULL},
-       {N_("/_Tools"),                 NULL,           NULL, 0, "<Branch>"},
-       {N_("/_Tools/Import _LDIF file..."), NULL,      addressbook_import_ldif_cb,     0, NULL},
-       {N_("/_Tools/Import M_utt file..."), NULL,      addressbook_import_mutt_cb,     0, NULL},
-       {N_("/_Tools/Import _Pine file..."), NULL,      addressbook_import_pine_cb,     0, NULL},
-       {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Tools/Export _HTML..."), NULL,           addressbook_export_html_cb,     0, NULL},
-       {N_("/_Tools/Export LDI_F..."), NULL,           addressbook_export_ldif_cb,     0, NULL},
-       {N_("/_Help"),                  NULL,           NULL, 0, "<Branch>"},
-       {N_("/_Help/_About"),           NULL,           about_show, 0, NULL}
+       {N_("/_Book/---"),              NULL,           NULL, 0, "<Separator>", NULL},
+       {N_("/_Book/_Edit book"),       NULL,           addressbook_treenode_edit_cb,   0, NULL, NULL},
+       {N_("/_Book/_Delete book"),     NULL,           addressbook_treenode_delete_cb, 0, NULL, NULL},
+       {N_("/_Book/---"),              NULL,           NULL, 0, "<Separator>", NULL},
+       {N_("/_Book/_Save"),            "<control>S",   addressbook_file_save_cb,       0, NULL, NULL},
+       {N_("/_Book/_Close"),           "<control>W",   close_cb,                       0, NULL, NULL},
+       {N_("/_Address"),                       NULL,           NULL, 0, "<Branch>", NULL},
+       {N_("/_Address/_Select all"),   "<control>A",   addressbook_select_all_cb,      0, NULL, NULL},
+       {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>", NULL},
+       {N_("/_Address/C_ut"),          "<control>X",   addressbook_clip_cut_cb,        0, NULL, NULL},
+       {N_("/_Address/_Copy"),         "<control>C",   addressbook_clip_copy_cb,       0, NULL, NULL},
+       {N_("/_Address/_Paste"),        "<control>V",   addressbook_clip_paste_cb,      0, NULL, NULL},
+       {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>", NULL},
+       {N_("/_Address/_Edit"),         "<control>Return",addressbook_edit_address_cb,    0, NULL, NULL},
+       {N_("/_Address/_Delete"),       "<control>D",   addressbook_delete_address_cb,  0, NULL, NULL},
+       {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>", NULL},
+       {N_("/_Address/New _Address"),  "<control>N",   addressbook_new_address_cb,     0, NULL, NULL},
+       {N_("/_Address/New _Group"),    "<control>G",   addressbook_new_group_cb,       0, NULL, NULL},
+       {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>", NULL},
+       {N_("/_Address/_Mail To"),      NULL,           addressbook_mail_to_cb,         0, NULL, NULL},
+       {N_("/_Tools"),                 NULL,           NULL, 0, "<Branch>", NULL},
+       {N_("/_Tools/Import _LDIF file..."), NULL,      addressbook_import_ldif_cb,     0, NULL, NULL},
+       {N_("/_Tools/Import M_utt file..."), NULL,      addressbook_import_mutt_cb,     0, NULL, NULL},
+       {N_("/_Tools/Import _Pine file..."), NULL,      addressbook_import_pine_cb,     0, NULL, NULL},
+       {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>", NULL},
+       {N_("/_Tools/Export _HTML..."), NULL,           addressbook_export_html_cb,     0, NULL, NULL},
+       {N_("/_Tools/Export LDI_F..."), NULL,           addressbook_export_ldif_cb,     0, NULL, NULL},
+       {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>", NULL},
+       {N_("/_Tools/Find duplicates..."), NULL, addressbook_find_duplicates_cb,        0, NULL, NULL},
+       {N_("/_Tools/Edit custom attributes..."), NULL, addressbook_edit_custom_attr_cb,        0, NULL, NULL},
+       {N_("/_Help"),                  NULL,           NULL, 0, "<Branch>", NULL},
+       {N_("/_Help/_About"),           NULL,           about_show, 0, NULL, NULL}
 };
 
 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
 {
-       {N_("/_Edit"),          NULL, addressbook_treenode_edit_cb,   0, NULL},
-       {N_("/_Delete"),        NULL, addressbook_treenode_delete_cb, 0, NULL},
-       {"/---",                NULL, NULL, 0, "<Separator>"},
-       {N_("/New _Book"),      NULL, addressbook_new_book_cb,      0, NULL},
-       {N_("/New _Folder"),    NULL, addressbook_new_folder_cb,      0, NULL},
-       {N_("/New _Group"),     NULL, addressbook_new_group_cb,      0, NULL},
-       {"/---",                NULL, NULL, 0, "<Separator>"},
-       {N_("/C_ut"),           NULL, addressbook_treenode_cut_cb,    0, NULL},
-       {N_("/_Copy"),          NULL, addressbook_treenode_copy_cb,   0, NULL},
-       {N_("/_Paste"),         NULL, addressbook_treenode_paste_cb,  0, NULL}
+       {N_("/_Edit"),          NULL, addressbook_treenode_edit_cb,   0, NULL, NULL},
+       {N_("/_Delete"),        NULL, addressbook_treenode_delete_cb, 0, NULL, NULL},
+       {"/---",                NULL, NULL, 0, "<Separator>", NULL},
+       {N_("/New _Book"),      NULL, addressbook_new_book_cb,      0, NULL, NULL},
+       {N_("/New _Folder"),    NULL, addressbook_new_folder_cb,      0, NULL, NULL},
+       {N_("/New _Group"),     NULL, addressbook_new_group_cb,      0, NULL, NULL},
+       {"/---",                NULL, NULL, 0, "<Separator>", NULL},
+       {N_("/C_ut"),           NULL, addressbook_treenode_cut_cb,    0, NULL, NULL},
+       {N_("/_Copy"),          NULL, addressbook_treenode_copy_cb,   0, NULL, NULL},
+       {N_("/_Paste"),         NULL, addressbook_treenode_paste_cb,  0, NULL, NULL}
 };
 
 static GtkItemFactoryEntry addressbook_list_popup_entries[] =
 {
-       {N_("/_Select all"),    NULL, addressbook_select_all_cb, 0, NULL},
-       {"/---",                NULL, NULL, 0, "<Separator>"},
-       {N_("/_Edit"),          NULL, addressbook_edit_address_cb,   0, NULL},
-       {N_("/_Delete"),        NULL, addressbook_delete_address_cb, 0, NULL},
-       {"/---",                NULL, NULL, 0, "<Separator>"},
-       {N_("/New _Address"),   NULL, addressbook_new_address_cb,    0, NULL},
-       {N_("/New _Group"),     NULL, addressbook_new_group_cb,      0, NULL},
-       {"/---",                NULL, NULL, 0, "<Separator>"},
-       {N_("/C_ut"),           NULL, addressbook_clip_cut_cb,       0, NULL},
-       {N_("/_Copy"),          NULL, addressbook_clip_copy_cb,      0, NULL},
-       {N_("/_Paste"),         NULL, addressbook_clip_paste_cb,     0, NULL},
-       {"/---",                NULL, NULL, 0, "<Separator>"},
-/*     {N_("/Pa_ste Address"), NULL, addressbook_clip_paste_address_cb, 0, NULL},*/
-       {N_("/_Mail To"),       NULL, addressbook_mail_to_cb,            0, NULL},
+       {N_("/_Select all"),    NULL, addressbook_select_all_cb, 0, NULL, NULL},
+       {"/---",                NULL, NULL, 0, "<Separator>", NULL},
+       {N_("/_Edit"),          NULL, addressbook_edit_address_cb,   0, NULL, NULL},
+       {N_("/_Delete"),        NULL, addressbook_delete_address_cb, 0, NULL, NULL},
+       {"/---",                NULL, NULL, 0, "<Separator>", NULL},
+       {N_("/New _Address"),   NULL, addressbook_new_address_cb,    0, NULL, NULL},
+       {N_("/New _Group"),     NULL, addressbook_new_group_cb,      0, NULL, NULL},
+       {"/---",                NULL, NULL, 0, "<Separator>", NULL},
+       {N_("/C_ut"),           NULL, addressbook_clip_cut_cb,       0, NULL, NULL},
+       {N_("/_Copy"),          NULL, addressbook_clip_copy_cb,      0, NULL, NULL},
+       {N_("/_Paste"),         NULL, addressbook_clip_paste_cb,     0, NULL, NULL},
+       {"/---",                NULL, NULL, 0, "<Separator>", NULL},
+/*     {N_("/Pa_ste Address"), NULL, addressbook_clip_paste_address_cb, 0, NULL, NULL},*/
+       {N_("/_Mail To"),       NULL, addressbook_mail_to_cb,            0, NULL, NULL},
 #ifdef USE_LDAP
-       {N_("/_Browse Entry"),  NULL, addressbook_browse_entry_cb,       0, NULL},
+       {N_("/_Browse Entry"),  NULL, addressbook_browse_entry_cb,       0, NULL, NULL},
 #endif 
 };
 
@@ -545,7 +554,7 @@ static ErrMsgTableEntry _lutErrorsLDAP_[] = {
        { LDAPRC_NOENTRIES,                     N_("No LDAP entries found for search criteria") },
        { LDAPRC_STOP_FLAG,                     N_("LDAP search terminated on request") },
        { LDAPRC_TLS,                           N_("Error starting TLS connection") },
-       { LDAPRC_NODN,                          N_("Distinguised Name (dn) is missing") },
+       { LDAPRC_NODN,                          N_("Distinguished Name (dn) is missing") },
        { LDAPRC_NAMING_VIOLATION,              N_("Missing required information") },
        { LDAPRC_ALREADY_EXIST,                 N_("Another contact exists with that key") },
        { LDAPRC_STRONG_AUTH,                   N_("Strong(er) authentication required") },
@@ -686,7 +695,7 @@ Compose *addressbook_get_target_compose(void)
 /**
  * Refresh addressbook and save to file(s).
  */
-static void addressbook_refresh( void )
+void addressbook_refresh( void )
 {
        if (addrbook.window) {
                if (addrbook.treeSelected) {
@@ -745,7 +754,7 @@ static gint list_case_sort(
                        name2 = GTK_CELL_TEXT (row2->cell[sort_column_number])->text;
                if( ! name1 ) return ( name2 != NULL );
                if( ! name2 ) return -1;
-               return strcasecmp( name1, name2 );
+               return g_utf8_collate( name1, name2 );
        } else {
                /* Order groups before person */
                if( aio1->type == ITEMTYPE_GROUP ) {
@@ -964,9 +973,16 @@ static void addressbook_create(void)
        gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
        gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
        gtk_clist_set_column_width(GTK_CLIST(ctree), 0, COL_FOLDER_WIDTH);
-       gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
-       gtk_ctree_set_expander_style(GTK_CTREE(ctree),
+       if (prefs_common.enable_dotted_lines) {
+               gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
+               gtk_ctree_set_expander_style(GTK_CTREE(ctree),
                                     GTK_CTREE_EXPANDER_SQUARE);
+       } else {
+               gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
+               gtk_ctree_set_expander_style(GTK_CTREE(ctree),
+                                    GTK_CTREE_EXPANDER_TRIANGLE);
+       }
+       gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
        gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
        gtk_clist_set_compare_func(GTK_CLIST(ctree),
                                   addressbook_treenode_compare_func);
@@ -1012,8 +1028,16 @@ static void addressbook_create(void)
        clist = gtk_sctree_new_with_titles(N_LIST_COLS, 0, list_titles);
        gtk_container_add(GTK_CONTAINER(clist_swin), clist);
        gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_EXTENDED);
-       gtk_ctree_set_line_style(GTK_CTREE(clist), GTK_CTREE_LINES_NONE);
-       gtk_ctree_set_expander_style(GTK_CTREE(clist), GTK_CTREE_EXPANDER_SQUARE);
+       if (prefs_common.enable_dotted_lines) {
+               gtk_ctree_set_line_style(GTK_CTREE(clist), GTK_CTREE_LINES_DOTTED);
+               gtk_ctree_set_expander_style(GTK_CTREE(clist),
+                                    GTK_CTREE_EXPANDER_SQUARE);
+       } else {
+               gtk_ctree_set_line_style(GTK_CTREE(clist), GTK_CTREE_LINES_NONE);
+               gtk_ctree_set_expander_style(GTK_CTREE(clist),
+                                    GTK_CTREE_EXPANDER_TRIANGLE);
+       }
+       gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
        gtk_ctree_set_indent(GTK_CTREE(clist), CTREE_INDENT);
        gtk_clist_set_column_width(GTK_CLIST(clist), COL_NAME,
                                   COL_NAME_WIDTH);
@@ -1294,22 +1318,6 @@ static void addressbook_status_show( gchar *msg ) {
        }
 }
 
-static void addressbook_ds_status_message( AddressDataSource *ds, gchar *msg ) {
-       *addressbook_msgbuf = '\0';
-       if( ds ) {
-               gchar *name;
-
-               name = addrindex_ds_get_name( ds );
-               g_snprintf( addressbook_msgbuf, sizeof(addressbook_msgbuf),
-                           "%s: %s", name, msg );
-       }
-       else {
-               g_snprintf( addressbook_msgbuf, sizeof(addressbook_msgbuf),
-                           "%s", msg );
-       }
-       addressbook_status_show( addressbook_msgbuf );
-}
-
 static void addressbook_ds_show_message( AddressDataSource *ds ) {
        gint retVal;
        gchar *name;
@@ -1355,6 +1363,11 @@ static void addressbook_edit_clicked(GtkButton *button, gpointer data)
        addressbook_edit_address_cb(NULL, 0, NULL);
 }
 
+static gboolean find_person(AddrSelectItem *item_a, ItemPerson *person)
+{
+       return ((ItemPerson *)item_a->addressItem == person)?0:-1;
+}
+
 /*
 * Delete one or more objects from address list.
 */
@@ -1411,9 +1424,14 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
        abf = ds->rawDataSource;
        if( abf == NULL ) return;
 
+       gtk_clist_freeze(GTK_CLIST(addrbook.clist));
+       g_signal_handlers_block_by_func
+               (G_OBJECT(addrbook.clist),
+                G_CALLBACK(addressbook_list_row_unselected), NULL);
 
        /* Process deletions */
        if( pobj->type == ADDR_DATASOURCE || pobj->type == ADDR_ITEM_FOLDER ) {
+               GList *groups = NULL, *persons = NULL, *emails = NULL;
                gboolean group_delete = TRUE;
                /* Items inside folders */
                list = addrselect_get_list( _addressSelect_ );
@@ -1433,23 +1451,62 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
                                        _("Really delete the group(s)?\n"
                                          "The addresses it contains will not be lost."),
                                        GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL );
-                       if( aval != G_ALERTALTERNATE ) return;
+                       if( aval != G_ALERTALTERNATE ) {
+                               goto thaw_ret;
+                       }
                } else {
                        aval = alertpanel( _("Delete address(es)"),
                                        _("Really delete the address(es)?"),
                                        GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL );
-                       if( aval != G_ALERTALTERNATE ) return;
+                       if( aval != G_ALERTALTERNATE ) {
+                               goto thaw_ret;
+                       }
                }
-
+       
+       /* first, set lists of groups and persons to remove */
+               node = list;
+               while( node ) {
+                       item = node->data;
+                       node = g_list_next( node );
+                       aio = ( AddrItemObject * ) item->addressItem;
+                       if (!aio)
+                               continue;
+                       if( aio->type == ADDR_ITEM_GROUP ) {
+                               groups = g_list_prepend(groups, item);
+                       }
+                       else if( aio->type == ADDR_ITEM_PERSON ) {
+                               persons = g_list_prepend(persons, item);
+                       }
+               }
+       /* then set list of emails to remove *if* they're not children of
+        * persons to remove */
                node = list;
                while( node ) {
                        item = node->data;
                        node = g_list_next( node );
                        aio = ( AddrItemObject * ) item->addressItem;
+                       if (!aio)
+                               continue;
+                       if( aio->type == ADDR_ITEM_EMAIL ) {
+                               ItemEMail *sitem = ( ItemEMail * ) aio;
+                               ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
+                               if (!g_list_find_custom(persons, person, (GCompareFunc)(find_person))) {
+                                       emails = g_list_prepend(emails, item);
+                               }
+                               /* else, the email will be removed via the parent person */
+                       }
+               }
+       /* then delete groups */
+               node = groups;
+               while( node ) {
+                       item = node->data;
+                       node = g_list_next( node );
+                       aio = ( AddrItemObject * ) item->addressItem;
+                       if (!aio)
+                               continue;
                        if( aio->type == ADDR_ITEM_GROUP ) {
                                ItemGroup *item = ( ItemGroup * ) aio;
                                GtkCTreeNode *nd = NULL;
-
                                nd = addressbook_find_group_node( addrbook.opened, item );
                                item = addrbook_remove_group( abf, item );
                                if( item ) {
@@ -1459,7 +1516,16 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
                                gtk_ctree_remove_node( ctree, nd );
                                refreshList = TRUE;
                        }
-                       else if( aio->type == ADDR_ITEM_PERSON ) {
+               }
+       /* then delete persons */
+               node = persons;
+               while( node ) {
+                       item = node->data;
+                       node = g_list_next( node );
+                       aio = ( AddrItemObject * ) item->addressItem;
+                       if (!aio)
+                               continue;
+                       if( aio->type == ADDR_ITEM_PERSON ) {
                                ItemPerson *item = ( ItemPerson * ) aio;
                                item->status = DELETE_ENTRY; 
                                addressbook_folder_remove_one_person( clist, item );
@@ -1467,27 +1533,44 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
                                        addritem_folder_remove_person(ADAPTER_FOLDER(pobj)->itemFolder, item);
                                item = addrbook_remove_person( abf, item );
 #ifdef USE_LDAP
-                               if (ds->type == ADDR_IF_LDAP) {
+                               if (ds && ds->type == ADDR_IF_LDAP) {
                                        LdapServer *server = ds->rawDataSource;
                                        ldapsvr_set_modified(server, TRUE);
                                        ldapsvr_update_book(server, item);
                                }
 #endif
                                if( item ) {
+                                       gchar *filename = addritem_person_get_picture(item);
+                                       if (filename && is_file_exist(filename))
+                                               claws_unlink(filename);
+                                       g_free(filename);
                                        addritem_free_item_person( item );
                                }
                        }
-                       else if( aio->type == ADDR_ITEM_EMAIL ) {
-                               ItemEMail *item = ( ItemEMail * ) aio;
-                               ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(item);
-                               item = addrbook_person_remove_email( abf, person, item );
-                               if( item ) {
-                                       addrcache_remove_email(abf->addressCache, item);
-                                       addritem_free_item_email( item );
+               }
+       /* then delete emails */
+               node = emails;
+               while( node ) {
+                       item = node->data;
+                       node = g_list_next( node );
+                       aio = ( AddrItemObject * ) item->addressItem;
+                       if (!aio)
+                               continue;
+
+                       if( aio->type == ADDR_ITEM_EMAIL ) {
+                               ItemEMail *sitem = ( ItemEMail * ) aio;
+                               ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
+                               sitem = addrbook_person_remove_email( abf, person, sitem );
+                               if( sitem ) {
+                                       addrcache_remove_email(abf->addressCache, sitem);
+                                       addritem_free_item_email( sitem );
                                }
                                addressbook_folder_refresh_one_person( clist, person );
                        }
                }
+               g_list_free( groups );
+               g_list_free( persons );
+               g_list_free( emails );
                g_list_free( list );
                addressbook_list_select_clear();
                if( refreshList ) {
@@ -1500,7 +1583,7 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
                addrbook_set_dirty(abf, TRUE);
                addressbook_export_to_file();
                addressbook_list_menu_setup();
-               return;
+               goto thaw_ret;
        }
        else if( pobj->type == ADDR_ITEM_GROUP ) {
                /* Items inside groups */
@@ -1530,12 +1613,16 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
                addrbook_set_dirty(abf, TRUE);
                addressbook_export_to_file();
                addressbook_list_menu_setup();
-               return;
+               goto thaw_ret;
        }
 
        gtk_ctree_node_set_row_data( clist, nodeList, NULL );
        gtk_ctree_remove_node( clist, nodeList );
-
+thaw_ret:
+       gtk_clist_thaw(GTK_CLIST(addrbook.clist));
+       g_signal_handlers_unblock_by_func
+               (G_OBJECT(addrbook.clist),
+                G_CALLBACK(addressbook_list_row_unselected), NULL);
 }
 
 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
@@ -1678,7 +1765,6 @@ static void addressbook_menubar_set_sensitive( gboolean sensitive ) {
        menu_set_sensitive( addrbook.menu_factory, "/Address/New Address", sensitive );
        menu_set_sensitive( addrbook.menu_factory, "/Address/New Group",   sensitive );
        menu_set_sensitive( addrbook.menu_factory, "/Address/Mail To",     sensitive );
-       gtk_widget_set_sensitive( addrbook.reg_btn, sensitive );
        gtk_widget_set_sensitive( addrbook.edit_btn, sensitive );
        gtk_widget_set_sensitive( addrbook.del_btn, sensitive );
 }
@@ -1741,7 +1827,8 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
                }
        }
 
-       if( addrbook.listSelected == NULL ) canEdit = FALSE;
+       if( addrbook.listSelected == NULL )
+               canEdit = FALSE;
 
        /* Enable add */
        menu_set_sensitive( addrbook.menu_factory, "/Address/New Address", editAddress );
@@ -1763,8 +1850,6 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
        menu_set_sensitive( addrbook.menu_factory, "/Tools/Export LDIF...", canExport );
 }
 
-static void addressbook_list_menu_setup( void );
-
 /**
  * Address book tree callback function that responds to selection of tree
  * items.
@@ -1821,10 +1906,10 @@ static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
 
                        /* Load folders into the tree */
                        rootFolder = addrindex_ds_get_root_folder( ds );
-                       if( ds->type == ADDR_IF_JPILOT ) {
+                       if( ds && ds->type == ADDR_IF_JPILOT ) {
                                aot = ADDR_CATEGORY;
                        }
-                       else if( ds->type == ADDR_IF_LDAP ) {
+                       else if( ds && ds->type == ADDR_IF_LDAP ) {
                                aot = ADDR_LDAP_QUERY;
                        }
                        else {
@@ -1851,9 +1936,7 @@ static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
 
        /* Setup main menu selections */
        addressbook_menubar_set_sensitive( FALSE );
-       addressbook_list_menu_setup();
        addressbook_menuitem_set_sensitive( obj, node );
-
        addressbook_list_select_clear();
        addressbook_list_menu_setup();
        return;
@@ -1891,17 +1974,20 @@ static void addressbook_list_menu_setup( void ) {
                /* Parent object is a data source */
                ads = ADAPTER_DSOURCE(pobj);
                ds = ads->dataSource;
+               if (!ds)
+                       return;
                iface = ds->interface;
+               if (!iface)
+                       return;
                if( ! iface->readOnly ) {
                        menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
                        if (iface->type != ADDR_IF_LDAP)
                                menu_set_sensitive( addrbook.list_factory, "/New Group", TRUE );
                        gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
-                       if( ! addrclip_is_empty( _clipBoard_ ) ) canPaste = TRUE;
-                       if( ! addrselect_test_empty( _addressSelect_ ) ) canCut = TRUE;
-                       if( obj ) canEdit = TRUE;
+                       if( obj )
+                               canEdit = TRUE;
+                       canDelete = canEdit;
                }
-               canDelete = canEdit;
        }
        else if( pobj->type != ADDR_INTERFACE ) {
                /* Parent object is not an interface */
@@ -1909,6 +1995,8 @@ static void addressbook_list_menu_setup( void ) {
                if (!ds)
                        return;
                iface = ds->interface;
+               if (!iface)
+                       return;
                if( ! iface->readOnly ) {
                        /* Folder or group */
                        if( pobj->type == ADDR_ITEM_FOLDER || pobj->type == ADDR_ITEM_GROUP ) {
@@ -1922,8 +2010,6 @@ static void addressbook_list_menu_setup( void ) {
                                        menu_set_sensitive( addrbook.list_factory, "/New Group", TRUE );
                                if( obj ) canEdit = TRUE;
                        }
-                       if( ! addrclip_is_empty( _clipBoard_ ) ) canPaste = TRUE;
-                       if( ! addrselect_test_empty( _addressSelect_ ) ) canCut = TRUE;
                        canDelete = canEdit;
                }
                if( iface->type == ADDR_IF_LDAP ) {
@@ -1932,7 +2018,17 @@ static void addressbook_list_menu_setup( void ) {
                        canDelete = TRUE;
                }
        }
-       if( ! addrselect_test_empty( _addressSelect_ ) ) canCopy = TRUE;
+
+       if( iface ) {
+               /* Enable cut and paste */
+               if( ! addrclip_is_empty( _clipBoard_ ) )
+                       canPaste = TRUE;
+               if( ! addrselect_test_empty( _addressSelect_ ) )
+                       canCut = TRUE;
+               /* Enable copy if something is selected */
+               if( ! addrselect_test_empty( _addressSelect_ ) )
+                       canCopy = TRUE;
+       }
 
        /* Disable edit or browse if more than one row selected */
        if( GTK_CLIST(clist)->selection && GTK_CLIST(clist)->selection->next ) {
@@ -1940,6 +2036,13 @@ static void addressbook_list_menu_setup( void ) {
                canBrowse = FALSE;
        }
 
+       /* Forbid write changes when read-only */
+       if( iface && iface->readOnly ) {
+               canCut = FALSE;
+               canDelete = FALSE;
+               canPaste = FALSE;
+       }
+
        /* Now go finalize menu items */
        menu_set_sensitive( addrbook.list_factory, "/Edit",   canEdit );
        menu_set_sensitive( addrbook.list_factory, "/Delete", canDelete );
@@ -1947,18 +2050,12 @@ static void addressbook_list_menu_setup( void ) {
        menu_set_sensitive( addrbook.list_factory, "/Cut",           canCut );
        menu_set_sensitive( addrbook.list_factory, "/Copy",          canCopy );
        menu_set_sensitive( addrbook.list_factory, "/Paste",         canPaste );
-/*     menu_set_sensitive( addrbook.list_factory, "/Paste Address", canPaste );*/
 
        menu_set_sensitive( addrbook.list_factory, "/Mail To",       canCopy );
 
        menu_set_sensitive( addrbook.menu_factory, "/Address/Cut",           canCut );
        menu_set_sensitive( addrbook.menu_factory, "/Address/Copy",          canCopy );
        menu_set_sensitive( addrbook.menu_factory, "/Address/Paste",         canPaste );
-/*     menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste Address", canPaste );*/
-
-       menu_set_sensitive( addrbook.tree_factory, "/Cut",             canCut );
-       menu_set_sensitive( addrbook.tree_factory, "/Copy",            canCopy );
-       menu_set_sensitive( addrbook.tree_factory, "/Paste",           canPaste );
 
        menu_set_sensitive( addrbook.menu_factory, "/Address/Edit",    canEdit );
        menu_set_sensitive( addrbook.menu_factory, "/Address/Delete",  canDelete );
@@ -2051,8 +2148,7 @@ static void addressbook_clip_paste_cb( void ) {
        ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
        if( ds == NULL ) return;
        if( addrindex_ds_get_readonly( ds ) ) {
-               addressbook_ds_status_message(
-                       ds, _( "Cannot paste. Target address book is readonly." ) );
+               alertpanel_error( _("Cannot paste. Target address book is readonly.") );
                return;
        }
 
@@ -2062,8 +2158,7 @@ static void addressbook_clip_paste_cb( void ) {
                        folder = ADAPTER_FOLDER(pobj)->itemFolder;
                }
                else if( pobj->type == ADDR_ITEM_GROUP ) {
-                       addressbook_ds_status_message(
-                               ds, _( "Cannot paste into an address group." ) );
+                       alertpanel_error( _("Cannot paste into an address group.") );
                        return;
                }
        }
@@ -2260,14 +2355,18 @@ static void addressbook_list_row_selected( GtkCTree *clist,
 
        aio = gtk_ctree_node_get_row_data( clist, node );
        if( aio ) {
-               /* printf( "list select: %d : '%s'\n", aio->type, aio->name ); */
+               /* g_print( "list select: %d : '%s'\n", aio->type, aio->name ); */
                addressbook_list_select_add( aio, ds );
        }
 
        addressbook_list_menu_setup();
 
-       if (!addrbook.target_compose && !prefs_common.addressbook_use_editaddress_dialog)
-               addressbook_edit_address(NULL, 0, NULL, FALSE);
+       if (!addrbook.target_compose && !prefs_common.addressbook_use_editaddress_dialog) {
+               AddressObject *obj = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
+
+               if (obj && obj->type != ADDR_ITEM_GROUP)
+                       addressbook_edit_address(NULL, 0, NULL, FALSE);
+       }
 }
 
 static void addressbook_list_row_unselected( GtkCTree *ctree,
@@ -2279,7 +2378,7 @@ static void addressbook_list_row_unselected( GtkCTree *ctree,
 
        aio = gtk_ctree_node_get_row_data( ctree, node );
        if( aio != NULL ) {
-               /* printf( "list unselect: %d : '%s'\n", aio->type, aio->name ); */
+               /* g_print( "list unselect: %d : '%s'\n", aio->type, aio->name ); */
                addressbook_list_select_remove( aio );
        }
 
@@ -2287,14 +2386,10 @@ static void addressbook_list_row_unselected( GtkCTree *ctree,
                addressbook_edit_person_invalidate(NULL, NULL, NULL);
 }
 
-/* from gdkevents.c */
-#define DOUBLE_CLICK_TIME 250
-
 static gboolean addressbook_list_button_pressed(GtkWidget *widget,
                                                GdkEventButton *event,
                                                gpointer data)
 {
-       static guint32 lasttime = 0;
        if( ! event ) return FALSE;
 
        addressbook_list_menu_setup();
@@ -2303,17 +2398,20 @@ static gboolean addressbook_list_button_pressed(GtkWidget *widget,
                gtk_menu_popup( GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
                       event->button, event->time );
        } else if (event->button == 1) {
-               if (event->time - lasttime < DOUBLE_CLICK_TIME) {
+               if (event->type == GDK_2BUTTON_PRESS) {
                        if (prefs_common.add_address_by_click &&
                            addrbook.target_compose)
                                addressbook_to_clicked(NULL, GINT_TO_POINTER(COMPOSE_TO));
                        else
                                if (prefs_common.addressbook_use_editaddress_dialog)
-                               addressbook_edit_address_cb(NULL, 0, NULL);
-
-                       lasttime = 0;
-               } else
-                       lasttime = event->time;
+                                       addressbook_edit_address_cb(NULL, 0, NULL);
+                               else {
+                                       GtkCTree *clist = GTK_CTREE(addrbook.clist);
+                                       AddressObject *obj = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
+                                       if( obj && obj->type == ADDR_ITEM_GROUP )
+                                               addressbook_edit_address_cb(NULL, 0, NULL);
+                               }
+               }
        }
 
        return FALSE;
@@ -2346,8 +2444,31 @@ static gboolean addressbook_tree_button_pressed(GtkWidget *ctree,
        gboolean canTreePaste = FALSE;
        gboolean canLookup = FALSE;
        GtkCTreeNode *node = NULL;
-
+       
        if( ! event ) return FALSE;
+/*     if( ! event || event->type != GDK_BUTTON_PRESS) return FALSE;*/
+
+       if (event->button == 1) {
+               if (event->type == GDK_2BUTTON_PRESS) {
+                       if( gtk_clist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
+                               gtkut_clist_set_focus_row(clist, row);
+                               obj = gtk_clist_get_row_data( clist, row );
+                       }
+                       if( obj == NULL )
+                               return FALSE;
+
+                       if (obj->type == ADDR_ITEM_GROUP) {
+                               /* edit group */
+                               addressbook_treenode_edit_cb(NULL, 0, NULL);
+                       } else {
+                               /* expand pr collapse */
+                               node = gtk_ctree_node_nth(GTK_CTREE(ctree), row);
+                               gtk_ctree_toggle_expansion(GTK_CTREE(ctree), node);
+                       }
+                       return FALSE;
+               }
+       }
+
        addressbook_menubar_set_sensitive( FALSE );
 
        if( gtk_clist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
@@ -2357,61 +2478,54 @@ static gboolean addressbook_tree_button_pressed(GtkWidget *ctree,
 
        menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
 
-       if( obj == NULL ) return FALSE;
+       if( obj == NULL )
+               return FALSE;
        node = gtk_ctree_node_nth(GTK_CTREE(clist), row);
 
-       if( ! addrclip_is_empty( _clipBoard_ ) ) {
+       if( ! addrclip_is_empty( _clipBoard_ ) )
                canTreePaste = TRUE;
-       }
 
        if (obj->type == ADDR_INTERFACE) {
                AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
+               if( !adapter )
+                       goto just_set_sens;
                iface = adapter->interface;
-               canEdit = FALSE;
-               canDelete = FALSE;
-               canTreeCopy = FALSE;
-               if( iface->readOnly ) {
-                       canTreePaste = FALSE;
-               }
-               else {
+               if( !iface )
+                       goto just_set_sens;
+               if( !iface->readOnly ) {
                        menu_set_sensitive( addrbook.tree_factory, "/New Book", TRUE );
                        gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
                }
-               if( iface->externalQuery ) canLookup = TRUE;
+               if( iface->externalQuery )
+                       canLookup = TRUE;
        }
        if (obj->type == ADDR_DATASOURCE) {
                ads = ADAPTER_DSOURCE(obj);
                ds = ads->dataSource;
-               if (!ds)
+               if( !ds )
                        goto just_set_sens;
                iface = ds->interface;
-               if (!iface)
+               if( !iface )
                        goto just_set_sens;
-               canEdit = TRUE;
-               canDelete = TRUE;
-               if( iface->readOnly ) {
-                       canTreePaste = FALSE;
-               }
-               else {
+               if( !iface->readOnly ) {
+                       canDelete = TRUE;
                        menu_set_sensitive( addrbook.tree_factory, "/New Folder", TRUE );
                        menu_set_sensitive( addrbook.tree_factory, "/New Group", TRUE );
                        gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
                }
+               canEdit = TRUE;
                canTreeCopy = TRUE;
-               if( iface->externalQuery ) canLookup = TRUE;
+               if( iface->externalQuery )
+                       canLookup = TRUE;
        }
        else if (obj->type == ADDR_ITEM_FOLDER) {
                ds = addressbook_find_datasource( node );
-               if (!ds) {
+               if( !ds )
                        goto just_set_sens;
-               }
                iface = ds->interface;
-               if (!iface)
+               if( !iface )
                        goto just_set_sens;
-               if( iface->readOnly ) {
-                       canTreePaste = FALSE;
-               }
-               else {
+               if( !iface->readOnly ) {
                        canEdit = TRUE;
                        canDelete = TRUE;
                        canTreeCut = TRUE;
@@ -2429,10 +2543,10 @@ static gboolean addressbook_tree_button_pressed(GtkWidget *ctree,
        }
        else if (obj->type == ADDR_ITEM_GROUP) {
                ds = addressbook_find_datasource( node );
-               if (!ds)
+               if( !ds )
                        goto just_set_sens;
                iface = ds->interface;
-               if (!iface)
+               if( !iface )
                        goto just_set_sens;
                if( ! iface->readOnly ) {
                        canEdit = TRUE;
@@ -2441,15 +2555,22 @@ static gboolean addressbook_tree_button_pressed(GtkWidget *ctree,
                        gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
                }
        }
-       else if (obj->type == ADDR_INTERFACE) {
-               canTreePaste = FALSE;
-       }
 
-       if( canEdit ) {
-               if( ! addrselect_test_empty( _addressSelect_ ) ) canCut = TRUE;
+       if( canEdit && !addrselect_test_empty( _addressSelect_ ) )
+               canCut = TRUE;
+       if( ! addrselect_test_empty( _addressSelect_ ) )
+               canCopy = TRUE;
+       if( ! addrclip_is_empty( _clipBoard_ ) )
+               canPaste = TRUE;
+
+       /* Forbid write changes when read-only */
+       if( iface && iface->readOnly ) {
+               canTreeCut = FALSE;
+               canTreePaste = FALSE;
+               canCut = FALSE;
+               canDelete = FALSE;
+               canPaste = FALSE;
        }
-       if( ! addrselect_test_empty( _addressSelect_ ) ) canCopy = TRUE;
-       if( ! addrclip_is_empty( _clipBoard_ ) ) canPaste = TRUE;
 
 just_set_sens:
        /* Enable edit */
@@ -2464,13 +2585,13 @@ just_set_sens:
        menu_set_sensitive( addrbook.menu_factory, "/Address/Cut",           canCut );
        menu_set_sensitive( addrbook.menu_factory, "/Address/Copy",          canCopy );
        menu_set_sensitive( addrbook.menu_factory, "/Address/Paste",         canPaste );
-/*     menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste Address", canPaste );*/
 
-       addressbook_show_buttons(addrbook.target_compose == NULL, canLookup, addrbook.target_compose != NULL);
-       if( event->button == 3 ) {
+       addressbook_show_buttons(addrbook.target_compose == NULL, canLookup,
+                       addrbook.target_compose != NULL);
+
+       if( event->button == 3 )
                gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
                               event->button, event->time);
-       }
 
        return FALSE;
 }
@@ -2520,7 +2641,6 @@ static void addressbook_new_folder_cb(gpointer data, guint action,
                if( addrbook.treeSelected == addrbook.opened )
                        addressbook_set_clist(obj, TRUE);
        }
-
 }
 
 static void addressbook_new_group_cb(gpointer data, guint action,
@@ -2559,7 +2679,6 @@ static void addressbook_new_group_cb(gpointer data, guint action,
                if( addrbook.treeSelected == addrbook.opened )
                        addressbook_set_clist(obj, TRUE);
        }
-
 }
 
 static void addressbook_change_node_name(GtkCTreeNode *node, const gchar *name)
@@ -2758,7 +2877,7 @@ static void addressbook_treenode_delete_cb(
                }
                else {
                        message = g_strdup_printf
-                               ( _( "Do you want to delete '%s' ?"
+                               ( _( "Do you want to delete '%s'"
                                     "If you delete the folder only, the addresses it contains will be moved into the parent folder." ),
                                 obj->name );
                        aval = alertpanel( _("Delete folder"), message,
@@ -2813,12 +2932,12 @@ static void addressbook_treenode_delete_cb(
 
                adapter->itemFolder = NULL;
                /*
-               printf( "remove folder for ::%s::\n", obj->name );
-               printf( "      folder name ::%s::\n", ADDRITEM_NAME(folder) );
-               printf( "-------------- remove results\n" );
+               g_print( "remove folder for ::%s::\n", obj->name );
+               g_print( "      folder name ::%s::\n", ADDRITEM_NAME(folder) );
+               g_print( "-------------- remove results\n" );
                */
                addrindex_remove_results( ds, folder );
-               /* printf( "-------------- remove node\n" ); */
+               /* g_print( "-------------- remove node\n" ); */
                gtk_ctree_remove_node( ctree, node );
                return;
        }
@@ -2927,7 +3046,7 @@ static ItemFolder * addressbook_setup_subf(
        /* Setup a query */
        if( *title == '\0' || strlen( title ) < 1 ) return NULL;
 
-       if( ds->type == ADDR_IF_LDAP ) {
+       if( ds && ds->type == ADDR_IF_LDAP ) {
 #if USE_LDAP
                aoType = ADDR_LDAP_QUERY;
 #endif
@@ -3003,7 +3122,7 @@ static void addressbook_new_address_cb( gpointer data, guint action, GtkWidget *
 
        abf = ds->rawDataSource;
        if( abf == NULL ) {
-               printf("no addressbook file\n");
+               g_print("no addressbook file\n");
                return;
        }
 
@@ -3013,7 +3132,7 @@ static void addressbook_new_address_cb( gpointer data, guint action, GtkWidget *
                        ItemPerson *person;
                        ItemFolder *folder = NULL;
 #ifdef USE_LDAP
-                       if (abf->type == ADDR_IF_LDAP) {
+                       if (abf && abf->type == ADDR_IF_LDAP) {
                                GtkCTreeNode *parentNode;
                                ds = addressbook_find_datasource( GTK_CTREE_NODE( addrbook.treeSelected ) );
                                if( ds == NULL ) return;
@@ -3040,7 +3159,7 @@ static void addressbook_new_address_cb( gpointer data, guint action, GtkWidget *
                                                                  addressbook_new_address_from_book_post_cb,
                                                                  TRUE );
 #ifdef USE_LDAP
-                       if (abf->type == ADDR_IF_LDAP) {
+                       if (abf && abf->type == ADDR_IF_LDAP) {
                                LdapServer *server = ds->rawDataSource;
                                ldapsvr_set_modified(server, TRUE);
                                ldapsvr_update_book(server, NULL);
@@ -3062,7 +3181,7 @@ static void addressbook_new_address_cb( gpointer data, guint action, GtkWidget *
                ItemFolder *folder = ADAPTER_FOLDER(pobj)->itemFolder;
                ItemPerson *person;
 #ifdef USE_LDAP
-               if (abf->type == ADDR_IF_LDAP) {
+               if (abf && abf->type == ADDR_IF_LDAP) {
                        GtkCTreeNode *parentNode;
                        ds = addressbook_find_datasource( GTK_CTREE_NODE( addrbook.treeSelected ) );
                        if( ds == NULL ) return;
@@ -3090,7 +3209,7 @@ static void addressbook_new_address_cb( gpointer data, guint action, GtkWidget *
                                                          addressbook_new_address_from_folder_post_cb,
                                                          TRUE );
 #ifdef USE_LDAP
-               if (abf->type == ADDR_IF_LDAP) {
+               if (abf && abf->type == ADDR_IF_LDAP) {
                        LdapServer *server = ds->rawDataSource;
                        ldapsvr_set_modified(server, TRUE);
                        ldapsvr_update_book(server, NULL);
@@ -3187,7 +3306,10 @@ static void addressbook_move_nodes_up( GtkCTree *ctree, GtkCTreeNode *node ) {
 static void addressbook_edit_address_post_cb( ItemPerson *person )
 {
        if( person ) {
-
+#ifdef USE_LDAP
+               if (strcmp2(person->nickName, ADDRITEM_NAME(person)))
+                       addritem_person_set_nick_name( person, ADDRITEM_NAME(person));
+#endif
                addressbook_folder_refresh_one_person( GTK_CTREE(addrbook.clist), person );
                invalidate_address_completion();
        }
@@ -3208,10 +3330,6 @@ void addressbook_address_list_disable_some_actions(void)
        menu_set_sensitive( addrbook.menu_factory, "/Address/Cut",   FALSE );
        menu_set_sensitive( addrbook.menu_factory, "/Address/Copy",  FALSE );
        menu_set_sensitive( addrbook.menu_factory, "/Address/Paste", FALSE );
-
-       /* we're already editing contact's detail here */
-       menu_set_sensitive( addrbook.menu_factory, "/Address/Edit",  FALSE );
-       gtk_widget_set_sensitive( addrbook.edit_btn, FALSE );
 }
 
 static void addressbook_edit_address_cb( gpointer data, guint action, GtkWidget *widget ) {
@@ -3263,7 +3381,7 @@ static void addressbook_edit_address( gpointer data, guint action, GtkWidget *wi
                                                                                   (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
                                  != NULL ) { 
 #ifdef USE_LDAP
-                               if (abf->type == ADDR_IF_LDAP) {
+                               if (abf && abf->type == ADDR_IF_LDAP) {
                                        ldapsvr_set_modified( (LdapServer *) abf, TRUE );
                                        person->status = UPDATE_ENTRY;
                                }
@@ -3282,7 +3400,7 @@ static void addressbook_edit_address( gpointer data, guint action, GtkWidget *wi
                                                                          (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
                        != NULL ) {
 #ifdef USE_LDAP
-                               if (abf->type == ADDR_IF_LDAP) {
+                               if (abf && abf->type == ADDR_IF_LDAP) {
                                        ldapsvr_set_modified( (LdapServer *) abf, TRUE );
                                        person->status = UPDATE_ENTRY;
                                }
@@ -3318,9 +3436,7 @@ static void addressbook_edit_address( gpointer data, guint action, GtkWidget *wi
 static void addressbook_delete_address_cb(gpointer data, guint action,
                                          GtkWidget *widget)
 {
-       gtk_clist_freeze(GTK_CLIST(addrbook.clist));
        addressbook_del_clicked(NULL, NULL);
-       gtk_clist_thaw(GTK_CLIST(addrbook.clist));
 }
 
 static void close_cb(gpointer data, guint action, GtkWidget *widget)
@@ -3375,10 +3491,10 @@ static void addressbook_load_group( GtkCTree *clist, ItemGroup *itemGroup ) {
                person = ( ItemPerson * ) ADDRITEM_PARENT(email);
                str = addressbook_format_item_clist( person, email );
                if( str ) {
-                       text[COL_NAME] = str;
+                       text[COL_NAME] = addressbook_set_col_name_guard(str);
                }
                else {
-                       text[COL_NAME] = ADDRITEM_NAME(person);
+                       text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
                }
                text[COL_ADDRESS] = email->address;
                text[COL_REMARKS] = email->remarks;
@@ -3394,6 +3510,17 @@ static void addressbook_load_group( GtkCTree *clist, ItemGroup *itemGroup ) {
        }
 }
 
+gchar *addressbook_set_col_name_guard(gchar *value)
+{
+       gchar *ret = "<not set>";
+       gchar *tmp = g_strdup(value);
+       g_strstrip(tmp);
+       if (tmp !=NULL && *tmp != '\0')
+               ret = value;
+       g_free(tmp);
+       return ret;
+}
+
 static void addressbook_folder_load_one_person(
                GtkCTree *clist, ItemPerson *person,
                AddressTypeControlItem *atci,
@@ -3404,7 +3531,9 @@ static void addressbook_folder_load_one_person(
        gchar *text[N_LIST_COLS];
        gboolean flgFirst = TRUE, haveAddr = FALSE;
        GList *node;
+#ifdef USE_LDAP
        AddressBookFile *abf = addressbook_get_book_file();
+#endif
 
        if( person == NULL ) return;
 
@@ -3423,23 +3552,23 @@ static void addressbook_folder_load_one_person(
                        /* First email belongs with person */
                        gchar *str = addressbook_format_item_clist( person, email );
                        if( str ) {
-                               text[COL_NAME] = str;
+                               text[COL_NAME] = addressbook_set_col_name_guard(str);
                        }
 #ifdef USE_LDAP
                        else if( abf && abf->type == ADDR_IF_LDAP && 
                                 person && person->nickName ) {
                                if (person->nickName) {
                                        if (strcmp(person->nickName, "") != 0) {
-                                               text[COL_NAME] = person->nickName;
+                                               text[COL_NAME] = addressbook_set_col_name_guard(person->nickName);
                                        }
                                        else {
-                                               text[COL_NAME] = ADDRITEM_NAME(person);
+                                               text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
                                        }
                                }
                        }
 #endif
                        else {
-                               text[COL_NAME] = ADDRITEM_NAME(person);
+                               text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
                        }
                        nodePerson = gtk_sctree_insert_node(
                                        clist, NULL, NULL,
@@ -3467,7 +3596,7 @@ static void addressbook_folder_load_one_person(
        }
        if( ! haveAddr ) {
                /* Have name without EMail */
-               text[COL_NAME] = ADDRITEM_NAME(person);
+               text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
                text[COL_ADDRESS] = "";
                text[COL_REMARKS] = "";
                nodePerson = gtk_sctree_insert_node(
@@ -3653,10 +3782,10 @@ static AddressDataSource *addressbook_find_datasource( GtkCTreeNode *node ) {
                if( GTK_CTREE_ROW(node)->level < 2 ) return NULL;
                ao = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), node );
                if( ao ) {
-                       /* printf( "ao->type = %d\n", ao->type ); */
+                       /* g_print( "ao->type = %d\n", ao->type ); */
                        if( ao->type == ADDR_DATASOURCE ) {
                                AdapterDSource *ads = ADAPTER_DSOURCE(ao);
-                               /* printf( "found it\n" ); */
+                               /* g_print( "found it\n" ); */
                                ds = ads->dataSource;
                                break;
                        }
@@ -3690,7 +3819,7 @@ static void addressbook_set_clist( AddressObject *obj, gboolean refresh ) {
        }
 
        if( obj->type == ADDR_INTERFACE ) {
-               /* printf( "set_clist: loading datasource...\n" ); */
+               /* g_print( "set_clist: loading datasource...\n" ); */
                /* addressbook_node_load_datasource( GTK_CTREE(clist), obj ); */
                return;
        }
@@ -3909,9 +4038,61 @@ static gboolean addressbook_convert( AddressIndex *addrIndex ) {
        return retVal;
 }
 
+static gboolean migrate_addrbook(const gchar *origdir, const gchar *destdir)
+{
+       DIR *dp;
+       struct dirent *d;
+       gboolean failed = FALSE;
+
+       if( ( dp = opendir( origdir ) ) == NULL ) {
+               return FALSE;
+       }
+       
+       while( ( d = readdir( dp ) ) != NULL ) {
+               if (strncmp(d->d_name, "addrbook-", strlen("addrbook-")))
+                       continue;
+               else {
+                       gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S, 
+                                       d->d_name, NULL);
+                       gchar *dest_file = g_strconcat(destdir, G_DIR_SEPARATOR_S, 
+                                       d->d_name, NULL);
+                       if (copy_file(orig_file, dest_file, FALSE) < 0) {
+                               failed = TRUE;
+                       }
+                       g_free(orig_file);
+                       g_free(dest_file);
+                       if (failed) {
+                               break;
+                       }
+               }
+       }
+
+       closedir( dp );
+       if (!failed) {
+               /* all copies succeeded, we can remove source files */
+               if( ( dp = opendir( origdir ) ) == NULL ) {
+                       return FALSE;
+               }
+               while( ( d = readdir( dp ) ) != NULL ) {
+                       if (strncmp(d->d_name, "addrbook-", strlen("addrbook-")))
+                               continue;
+                       else {
+                               gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S, 
+                                               d->d_name, NULL);
+                               claws_unlink(orig_file);
+                               g_free(orig_file);
+                       }
+               }
+               closedir( dp );
+       }
+       
+       return !failed;
+}
+
 void addressbook_read_file( void ) {
        AddressIndex *addrIndex = NULL;
-
+       gchar *indexdir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRBOOK_DIR, NULL);
+       
        debug_print( "Reading address index...\n" );
        if( _addressIndex_ ) {
                debug_print( "address book already read!!!\n" );
@@ -3922,7 +4103,24 @@ void addressbook_read_file( void ) {
        addrindex_initialize();
 
        /* Use new address book index. */
-       addrindex_set_file_path( addrIndex, get_rc_dir() );
+       
+       if ( !is_dir_exist(indexdir) ) {
+               if ( make_dir(indexdir) < 0 ) {
+                       addrindex_set_file_path( addrIndex, get_rc_dir() );
+                       g_warning( "couldn't create dir %s\n", indexdir);
+               } else {
+                       if (!migrate_addrbook(get_rc_dir(), indexdir)) {
+                               remove_dir_recursive(indexdir);
+                               addrindex_set_file_path( addrIndex, get_rc_dir() );
+                               g_error("couldn't migrate dir %s", indexdir);
+                       } else {
+                               addrindex_set_file_path( addrIndex, indexdir);
+                       }
+               }
+       } else {
+               addrindex_set_file_path( addrIndex, indexdir);
+       }
+       g_free(indexdir);
        addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
        addrindex_read_data( addrIndex );
        if( addrIndex->retVal == MGU_NO_FILE ) {
@@ -4123,7 +4321,7 @@ void addressbook_export_to_file( void ) {
 
 static gboolean addressbook_entry_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
 {
-       if (event && event->keyval == GDK_Return)
+       if (event && (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter))
                addressbook_lup_clicked(NULL, NULL);
        return FALSE;
 }
@@ -4280,7 +4478,7 @@ static void addressbook_search_idle( gpointer data ) {
        gint queryID;
 
        queryID = GPOINTER_TO_INT( data );
-       printf( "addressbook_ldap_idle... queryID=%d\n", queryID );
+       g_print( "addressbook_ldap_idle... queryID=%d\n", queryID );
        */
 }
 
@@ -4342,7 +4540,7 @@ static void addressbook_perform_search(
        /* Setup a query */
        if( *searchTerm == '\0' || strlen( searchTerm ) < 1 ) return;
 
-       if( ds->type == ADDR_IF_LDAP ) {
+       if( ds && ds->type == ADDR_IF_LDAP ) {
 #if USE_LDAP
                aoType = ADDR_LDAP_QUERY;
 #endif
@@ -4471,7 +4669,7 @@ static void addressbook_browse_entry_cb(void)
                return;
        }
 
-       if( iface->type == ADDR_IF_LDAP ) {
+       if( iface && iface->type == ADDR_IF_LDAP ) {
                browseldap_entry(ds, person->externalID);
        }
 }
@@ -4577,7 +4775,7 @@ static void addrbookctl_build_map( GtkWidget *window ) {
        atci->showInTree = FALSE;
        atci->treeExpand = FALSE;
        atci->treeLeaf = TRUE;
-       atci->displayName = _( "EMail Address" );
+       atci->displayName = _( "Email Address" );
        atci->iconXpm = addressxpm;
        atci->maskXpm = addressxpmmask;
        atci->iconXpmOpen = addressxpm;
@@ -4828,7 +5026,7 @@ static void addrbookctl_build_ifselect( void ) {
        splitStr = g_strsplit( selectStr, ",", -1 );
        for( i = 0; i < ADDRESSBOOK_MAX_IFACE; i++ ) {
                if( splitStr[i] ) {
-                       /* printf( "%d : %s\n", i, splitStr[i] ); */
+                       /* g_print( "%d : %s\n", i, splitStr[i] ); */
                        ifType = strtol( splitStr[i], &endptr, 10 );
                        enabled = TRUE;
                        if( *endptr ) {
@@ -4836,7 +5034,7 @@ static void addrbookctl_build_ifselect( void ) {
                                        enabled = FALSE;
                                }
                        }
-                       /* printf( "\t%d : %s\n", ifType, enabled ? "yes" : "no" ); */
+                       /* g_print( "\t%d : %s\n", ifType, enabled ? "yes" : "no" ); */
                        adapter = addrbookctl_find_interface( ifType );
                        if( adapter ) {
                                newList = g_list_append( newList, adapter );
@@ -4846,7 +5044,7 @@ static void addrbookctl_build_ifselect( void ) {
                        break;
                }
        }
-       /* printf( "i=%d\n", i ); */
+       /* g_print( "i=%d\n", i ); */
        g_strfreev( splitStr );
        g_free( selectStr );
 
@@ -4866,10 +5064,11 @@ static void addrbookctl_build_ifselect( void ) {
  * This function is used by the Add sender to address book function.
  */
 gboolean addressbook_add_contact(
-               const gchar *name, const gchar *address, const gchar *remarks )
+               const gchar *name, const gchar *address, const gchar *remarks,
+               GdkPixbuf *picture )
 {
        debug_print( "addressbook_add_contact: name/address: %s - %s\n", name, address );
-       if( addressadd_selection( _addressIndex_, name, address, remarks ) ) {
+       if( addressadd_selection( _addressIndex_, name, address, remarks, picture ) ) {
                debug_print( "addressbook_add_contact - added\n" );
                addressbook_refresh();
        }
@@ -4884,17 +5083,15 @@ gboolean addressbook_add_contact(
 /*
  * This function is used by the matcher dialog to select a book/folder.
  */
-gboolean addressbook_folder_selection( gchar **folderpath )
+gchar *addressbook_folder_selection( const gchar *folderpath)
 {
        AddressBookFile *book = NULL;
        ItemFolder *folder = NULL;
-       gchar *path;
+       gchar *path = NULL;
 
-       g_return_val_if_fail( folderpath != NULL, FALSE);
+       g_return_val_if_fail( folderpath != NULL, NULL);
 
-       path = *folderpath;
-       *folderpath = NULL;
-       if ( addressbook_foldersel_selection( _addressIndex_, &book, &folder, path )
+       if ( addressbook_foldersel_selection( _addressIndex_, &book, &folder, folderpath )
                && book != NULL ) {
                if ( folder != NULL) {
                        gchar *tmp = NULL;
@@ -4914,15 +5111,15 @@ gboolean addressbook_folder_selection( gchar **folderpath )
                                        g_free(oldtmp);
                                }
                        }
-                       *folderpath = g_strdup_printf("%s/%s", book->fileName, tmp);
+                       path = g_strdup_printf("%s/%s", book->fileName, tmp);
                        g_free(tmp);
                } else {
-                       *folderpath = g_strdup_printf("%s", book->fileName);
+                       path = g_strdup_printf("%s", book->fileName);
                }
-               debug_print( "addressbook_foldersel: %s\n", *folderpath);
-               return (*folderpath != NULL);
+               debug_print( "addressbook_foldersel: %s\n", path?path:"(null)");
+               return path;
        }
-       return FALSE;
+       return NULL;
 }
 
 /* ***********************************************************************
@@ -5012,7 +5209,7 @@ gboolean addressbook_peek_folder_exists( gchar *folderpath,
        if ( folderpath == NULL )
                return FALSE;
 
-       if ( strcasecmp(folderpath, _("Any")) == 0 || *folderpath == '\0' )
+       if ( strcasecmp(folderpath, "Any") == 0 || *folderpath == '\0' )
                return TRUE;
 
        /* split the folder path we've received, we'll try to match this path, subpath by
@@ -5282,6 +5479,16 @@ static void addressbook_export_ldif_cb( void ) {
        addressbook_exp_ldif( cache );
 }
 
+static void addressbook_find_duplicates_cb(void)
+{
+       addrduplicates_find(GTK_WINDOW(addrbook.window));       
+}
+
+static void addressbook_edit_custom_attr_cb(void)
+{
+       addressbook_custom_attr_edit();
+}
+               
 static void addressbook_start_drag(GtkWidget *widget, gint button, 
                                   GdkEvent *event,
                                   void *data)
@@ -5339,11 +5546,11 @@ static void addressbook_drag_data_get(GtkWidget        *widget,
                if( ds && ds->interface && ds->interface->readOnly)
                        gtk_selection_data_set(selection_data,
                                       selection_data->target, 8,
-                                      "Dummy_addr_copy", 15);
+                                      (const guchar *)"Dummy_addr_copy", 15);
                else
                        gtk_selection_data_set(selection_data,
                                       selection_data->target, 8,
-                                      "Dummy_addr_move", 15);
+                                      (const guchar *)"Dummy_addr_move", 15);
        } 
 }
 
@@ -5366,12 +5573,14 @@ static gboolean addressbook_drag_motion_cb(GtkWidget      *widget,
        if (gtk_clist_get_selection_info
                (GTK_CLIST(widget), x - 24, y - 24, &row, &column)) {
 
-               if (y > height - 24 && height + vpos < total_height)
+               if (y > height - 24 && height + vpos < total_height) {
                        gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
-
-               if (y < 24 && y > 0)
+                       gtk_adjustment_changed(pos);
+               }
+               if (y < 24 && y > 0) {
                        gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
-
+                       gtk_adjustment_changed(pos);
+               }
                node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
 
                if (node != NULL) {
@@ -5406,7 +5615,6 @@ static gboolean addressbook_drag_motion_cb(GtkWidget      *widget,
        } else {
                gdk_drag_status(context, 0, time);
        }
-
        return acceptable;
 }
 
@@ -5467,4 +5675,3 @@ static void addressbook_drag_received_cb(GtkWidget        *widget,
 /*
 * End of Source.
 */
-