add new ctree sorting code
[claws.git] / src / addressbook.c
index 25481764b64cdd47a775c65e787c835ffe273eae..bb1073088be597c87e3ff804809dab755f64a4b6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2001 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2002 Hiroyuki Yamamoto
  *
  * 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
@@ -52,6 +52,7 @@
 #include "alertpanel.h"
 #include "inputdialog.h"
 #include "menu.h"
+#include "stock_pixmap.h"
 #include "xml.h"
 #include "prefs.h"
 #include "procmime.h"
@@ -73,6 +74,9 @@
 #include "editgroup.h"
 #include "editaddress.h"
 #include "editbook.h"
+#include "importldif.h"
+#include "importmutt.h"
+#include "importpine.h"
 
 #ifdef USE_JPILOT
 #include "jpilot.h"
 #define ADDRESSBOOK_LDAP_BUSYMSG "Busy"
 #endif
 
-#include "pixmaps/dir-close.xpm"
-#include "pixmaps/dir-open.xpm"
-#include "pixmaps/group.xpm"
-#include "pixmaps/interface.xpm"
-#include "pixmaps/book.xpm"
-#include "pixmaps/address.xpm"
-#include "pixmaps/vcard.xpm"
-#include "pixmaps/jpilot.xpm"
-#include "pixmaps/category.xpm"
-#include "pixmaps/ldap.xpm"
+#include "addrselect.h"
+#include "addrclip.h"
+#include "addrgather.h"
+#include "adbookbase.h"
+#include "exphtmldlg.h"
 
 typedef enum
 {
@@ -136,11 +135,12 @@ static GdkBitmap *categoryxpmmask;
 static GdkPixmap *ldapxpm;
 static GdkBitmap *ldapxpmmask;
 
-/* Message buffer  */
+/* Message buffer */
 static gchar addressbook_msgbuf[ ADDRESSBOOK_MSGBUF_SIZE ];
 
-/* Address list selection  */
-static GList *_addressListSelection_ = NULL;
+/* Address list selection */
+static AddrSelectList *_addressSelect_ = NULL;
+static AddressClipboard *_clipBoard_ = NULL;
 
 /* Address index file and interfaces */
 static AddressIndex *_addressIndex_ = NULL;
@@ -153,7 +153,7 @@ static AddressBook_win addrbook;
 static GHashTable *_addressBookTypeHash_ = NULL;
 static GList *_addressBookTypeList_ = NULL;
 
-static void addressbook_create                 ( void );
+static void addressbook_create                 (void);
 static gint addressbook_close                  (void);
 static void addressbook_button_set_sensitive   (void);
 
@@ -244,24 +244,24 @@ static void addressbook_file_save_cb              (gpointer        data,
                                                 guint           action,
                                                 GtkWidget      *widget);
 
-/* Data source edit stuff  */
-static void addressbook_new_book_cb            ( gpointer      data,
-                                                 guint         action,
-                                                 GtkWidget     *widget );
-static void addressbook_new_vcard_cb           ( gpointer      data,
-                                                 guint         action,
-                                                 GtkWidget     *widget );
+/* Data source edit stuff */
+static void addressbook_new_book_cb            (gpointer        data,
+                                                guint           action,
+                                                GtkWidget      *widget);
+static void addressbook_new_vcard_cb           (gpointer        data,
+                                                guint           action,
+                                                GtkWidget      *widget);
 
 #ifdef USE_JPILOT
-static void addressbook_new_jpilot_cb          ( gpointer      data,
-                                                 guint         action,
-                                                 GtkWidget     *widget );
+static void addressbook_new_jpilot_cb          (gpointer        data,
+                                                guint           action,
+                                                GtkWidget      *widget);
 #endif
 
 #ifdef USE_LDAP
-static void addressbook_new_ldap_cb            ( gpointer      data,
-                                                 guint         action,
-                                                 GtkWidget     *widget );
+static void addressbook_new_ldap_cb            (gpointer        data,
+                                                guint           action,
+                                                GtkWidget      *widget);
 #endif
 
 static void addressbook_set_clist              (AddressObject  *obj);
@@ -271,77 +271,80 @@ void addressbook_read_file                        (void);
 
 static GtkCTreeNode *addressbook_add_object    (GtkCTreeNode   *node,
                                                 AddressObject  *obj);
+static void addressbook_treenode_remove_item   ( void );
+
 static AddressDataSource *addressbook_find_datasource
                                                (GtkCTreeNode   *node );
 
-static AddressBookFile *addressbook_get_book_file();
+static AddressBookFile *addressbook_get_book_file(void);
 
 static GtkCTreeNode *addressbook_node_add_folder
                                                (GtkCTreeNode   *node,
                                                AddressDataSource *ds,
                                                ItemFolder      *itemFolder,
-                                               AddressObjectType otype );
+                                               AddressObjectType otype);
 static GtkCTreeNode *addressbook_node_add_group (GtkCTreeNode  *node,
                                                AddressDataSource *ds,
-                                               ItemGroup       *itemGroup );
-/* static GtkCTreeNode *addressbook_node_add_category
-                                               (GtkCTreeNode   *node,
-                                                AddressDataSource *ds,
-                                                ItemFolder     *itemFolder );  */
+                                               ItemGroup       *itemGroup);
 static void addressbook_tree_remove_children   (GtkCTree       *ctree,
-                                               GtkCTreeNode    *parent );
+                                               GtkCTreeNode    *parent);
 static void addressbook_move_nodes_up          (GtkCTree       *ctree,
-                                               GtkCTreeNode    *node );
+                                               GtkCTreeNode    *node);
 static GtkCTreeNode *addressbook_find_group_node (GtkCTreeNode *parent,
-                                               ItemGroup       *group );
-
-static void addressbook_delete_object          (AddressObject  *obj);
-
+                                                  ItemGroup    *group);
 static void key_pressed                                (GtkWidget      *widget,
                                                 GdkEventKey    *event,
                                                 gpointer        data);
+static gint addressbook_treenode_compare_func  (GtkCList       *clist,
+                                                gconstpointer   ptr1,
+                                                gconstpointer   ptr2);
 static gint addressbook_list_compare_func      (GtkCList       *clist,
                                                 gconstpointer   ptr1,
                                                 gconstpointer   ptr2);
-/* static gint addressbook_obj_name_compare    (gconstpointer   a,
-                                                gconstpointer   b);  */
 
-static void addressbook_book_show_message      ( AddressBookFile *book );
-static void addressbook_vcard_show_message     ( VCardFile *vcf );
-#ifdef USE_JPILOT
-static void addressbook_jpilot_show_message    ( JPilotFile *jpf );
-#endif
 #ifdef USE_LDAP
-static void addressbook_ldap_show_message      ( SyldapServer *server );
+static void addressbook_ldap_show_message      (SyldapServer *server);
 #endif
 
-/* LUT's and IF stuff  */
-static void addressbook_free_adapter           ( GtkCTreeNode  *node );
-static void addressbook_free_child_adapters    ( GtkCTreeNode  *node );
-AddressTypeControlItem *addrbookctl_lookup     ( gint          ot );
-AddressTypeControlItem *addrbookctl_lookup_iface( AddressIfType        ifType );
+/* LUT's and IF stuff */
+static void addressbook_free_treenode          ( gpointer data );
+AddressTypeControlItem *addrbookctl_lookup     (gint            ot);
+AddressTypeControlItem *addrbookctl_lookup_iface(AddressIfType  ifType);
 
-void addrbookctl_build_map                     ( GtkWidget     *window );
-void addrbookctl_build_iflist                  ( void );
-AdapterInterface *addrbookctl_find_interface   ( AddressIfType ifType );
-void addrbookctl_build_ifselect();
+void addrbookctl_build_map                     (GtkWidget      *window);
+void addrbookctl_build_iflist                  (void);
+AdapterInterface *addrbookctl_find_interface   (AddressIfType   ifType);
+void addrbookctl_build_ifselect                        (void);
 
-static void addrbookctl_free_interface         ( AdapterInterface      *adapter );
-static void addrbookctl_free_datasource                ( AdapterDSource        *adapter );
-static void addrbookctl_free_folder            ( AdapterFolder *adapter );
-static void addrbookctl_free_group             ( AdapterGroup  *adapter );
+static void addrbookctl_free_interface         (AdapterInterface *adapter);
+static void addrbookctl_free_datasource                (AdapterDSource   *adapter);
+static void addrbookctl_free_folder            (AdapterFolder    *adapter);
+static void addrbookctl_free_group             (AdapterGroup     *adapter);
 
 static void addressbook_list_select_clear      ( void );
-static void addressbook_list_select_add                ( AddressObject *obj );
-static void addressbook_list_select_remove     ( AddressObject *obj );
+static void addressbook_list_select_add                ( AddrItemObject    *aio,
+                                                 AddressDataSource *ds );
+static void addressbook_list_select_remove     ( AddrItemObject    *aio );
+
+static void addressbook_import_ldif_cb         ( void );
+static void addressbook_import_mutt_cb         ( void );
+static void addressbook_import_pine_cb         ( void );
+static void addressbook_export_html_cb         ( void );
+static void addressbook_clip_cut_cb            ( void );
+static void addressbook_clip_copy_cb           ( void );
+static void addressbook_clip_paste_cb          ( void );
+static void addressbook_clip_paste_address_cb  ( void );
+static void addressbook_treenode_cut_cb                ( void );
+static void addressbook_treenode_copy_cb       ( void );
+static void addressbook_treenode_paste_cb      ( void );
 
 static GtkItemFactoryEntry addressbook_entries[] =
 {
        {N_("/_File"),                  NULL,           NULL, 0, "<Branch>"},
        {N_("/_File/New _Book"),        "<alt>B",       addressbook_new_book_cb,        0, NULL},
-       {N_("/_File/New _V-Card"),      "<alt>D",       addressbook_new_vcard_cb,       0, NULL},
+       {N_("/_File/New _vCard"),       "<alt>D",       addressbook_new_vcard_cb,       0, NULL},
 #ifdef USE_JPILOT
-       {N_("/_File/New _J-Pilot"),     "<alt>J",       addressbook_new_jpilot_cb,      0, NULL},
+       {N_("/_File/New _JPilot"),      "<alt>J",       addressbook_new_jpilot_cb,      0, NULL},
 #endif
 #ifdef USE_LDAP
        {N_("/_File/New _Server"),      "<alt>S",       addressbook_new_ldap_cb,        0, NULL},
@@ -351,7 +354,13 @@ static GtkItemFactoryEntry addressbook_entries[] =
        {N_("/_File/_Delete"),          NULL,           addressbook_treenode_delete_cb, 0, NULL},
        {N_("/_File/---"),              NULL,           NULL, 0, "<Separator>"},
        {N_("/_File/_Save"),            "<alt>S",       addressbook_file_save_cb,       0, NULL},
-       {N_("/_File/_Close"),           "<alt>W",       close_cb, 0, NULL},
+       {N_("/_File/_Close"),           "<alt>W",       close_cb,                       0, NULL},
+       {N_("/_Edit"),                  NULL,           NULL, 0, "<Branch>"},
+       {N_("/_Edit/C_ut"),             "<ctl>X",       addressbook_clip_cut_cb,        0, NULL},
+       {N_("/_Edit/_Copy"),            "<ctl>C",       addressbook_clip_copy_cb,       0, NULL},
+       {N_("/_Edit/_Paste"),           "<ctl>V",       addressbook_clip_paste_cb,      0, NULL},
+       {N_("/_Edit/---"),              NULL,           NULL, 0, "<Separator>"},
+       {N_("/_Edit/Pa_ste Address"),   NULL,           addressbook_clip_paste_address_cb, 0, NULL},
        {N_("/_Address"),               NULL,           NULL, 0, "<Branch>"},
        {N_("/_Address/New _Address"),  "<alt>N",       addressbook_new_address_cb,     0, NULL},
        {N_("/_Address/New _Group"),    "<alt>G",       addressbook_new_group_cb,       0, NULL},
@@ -359,25 +368,16 @@ static GtkItemFactoryEntry addressbook_entries[] =
        {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>"},
        {N_("/_Address/_Edit"),         "<alt>Return",  addressbook_edit_address_cb,    0, NULL},
        {N_("/_Address/_Delete"),       NULL,           addressbook_delete_address_cb,  0, NULL},
+       {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>"},
+       {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_("/_Help"),                  NULL,           NULL, 0, "<LastBranch>"},
        {N_("/_Help/_About"),           NULL,           about_show, 0, NULL}
 };
 
-/* New options to be added. */
-/*
-       {N_("/_Edit"),                  NULL,           NULL, 0, "<Branch>"},
-       {N_("/_Edit/C_ut"),             "<ctl>X",       NULL,                           0, NULL},
-       {N_("/_Edit/_Copy"),            "<ctl>C",       NULL,                           0, NULL},
-       {N_("/_Edit/_Paste"),           "<ctl>V",       NULL,                           0, NULL},
-       {N_("/_Tools"),                 NULL,           NULL, 0, "<Branch>"},
-       {N_("/_Tools/Import _Mozilla"), NULL,           NULL,                           0, NULL},
-       {N_("/_Tools/Import _LDIF"),    NULL,           NULL,                           0, NULL},
-       {N_("/_Tools/Import _V-Card"),  NULL,           NULL,                           0, NULL},
-       {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>"},
-       {N_("/_Tools/Export _LDIF"),    NULL,           NULL,                           0, NULL},
-       {N_("/_Tools/Export V-_Card"),  NULL,           NULL,                           0, NULL},
-*/
-
 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
 {
        {N_("/New _Address"),   NULL, addressbook_new_address_cb, 0, NULL},
@@ -385,39 +385,75 @@ static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
        {N_("/New _Folder"),    NULL, addressbook_new_folder_cb,  0, NULL},
        {N_("/---"),            NULL, NULL, 0, "<Separator>"},
        {N_("/_Edit"),          NULL, addressbook_treenode_edit_cb,   0, NULL},
-       {N_("/_Delete"),        NULL, addressbook_treenode_delete_cb, 0, NULL}
+       {N_("/_Delete"),        NULL, addressbook_treenode_delete_cb, 0, NULL},
+       {N_("/---"),            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}
 };
 
 static GtkItemFactoryEntry addressbook_list_popup_entries[] =
 {
-       {N_("/New _Address"),   NULL, addressbook_new_address_cb,  0, NULL},
-       {N_("/New _Group"),     NULL, addressbook_new_group_cb,    0, NULL},
-       {N_("/New _Folder"),    NULL, addressbook_new_folder_cb,   0, NULL},
+       {N_("/New _Address"),   NULL, addressbook_new_address_cb,    0, NULL},
+       {N_("/New _Group"),     NULL, addressbook_new_group_cb,      0, NULL},
+       {N_("/New _Folder"),    NULL, addressbook_new_folder_cb,     0, NULL},
        {N_("/---"),            NULL, NULL, 0, "<Separator>"},
        {N_("/_Edit"),          NULL, addressbook_edit_address_cb,   0, NULL},
-       {N_("/_Delete"),        NULL, addressbook_delete_address_cb, 0, NULL}
+       {N_("/_Delete"),        NULL, addressbook_delete_address_cb, 0, NULL},
+       {N_("/---"),            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},
+       {N_("/---"),            NULL, NULL, 0, "<Separator>"},
+       {N_("/Pa_ste Address"), NULL, addressbook_clip_paste_address_cb,     0, NULL}
 };
 
 void addressbook_open(Compose *target)
 {
+       /* Initialize all static members */
+       if( _clipBoard_ == NULL ) {
+               _clipBoard_ = addrclip_create();
+       }
+       if( _addressIndex_ != NULL ) {
+               addrclip_set_index( _clipBoard_, _addressIndex_ );
+       }
+       if( _addressSelect_ == NULL ) {
+               _addressSelect_ = addrselect_list_create();
+       }
        if (!addrbook.window) {
                addressbook_read_file();
                addressbook_create();
                addressbook_load_tree();
                gtk_ctree_select(GTK_CTREE(addrbook.ctree),
                                 GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
-       } else
+       }
+       else {
                gtk_widget_hide(addrbook.window);
+       }
 
        gtk_widget_show_all(addrbook.window);
-
        addressbook_set_target_compose(target);
 }
 
+void addressbook_destroy() {
+       /* Free up address stuff */
+       if( _addressSelect_ != NULL ) {
+               addrselect_list_free( _addressSelect_ );
+       }
+       if( _clipBoard_ != NULL ) {
+               addrclip_free( _clipBoard_ );
+       }
+       if( _addressIndex_ != NULL ) {
+              addrindex_free_index( _addressIndex_ );
+       }
+       _addressSelect_ = NULL;
+       _clipBoard_ = NULL;
+       _addressIndex_ = NULL;
+}
+
 void addressbook_set_target_compose(Compose *target)
 {
        addrbook.target_compose = target;
-
        addressbook_button_set_sensitive();
 }
 
@@ -426,10 +462,12 @@ Compose *addressbook_get_target_compose(void)
        return addrbook.target_compose;
 }
 
-void addressbook_refresh( void ) {
-       if( addrbook.window ) {
-               if( addrbook.treeSelected ) {
-                       gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.treeSelected );
+void addressbook_refresh(void)
+{
+       if (addrbook.window) {
+               if (addrbook.treeSelected) {
+                       gtk_ctree_select(GTK_CTREE(addrbook.ctree),
+                                        addrbook.treeSelected);
                }
        }
        addressbook_export_to_file();
@@ -450,7 +488,8 @@ void addressbook_refresh( void ) {
 * In the tradition of MVC architecture, the data stores have been separated from the
 * GUI components. The addrindex.c file provides the interface to all data stores.
 */
-static void addressbook_create( void ) {
+static void addressbook_create(void)
+{
        GtkWidget *window;
        GtkWidget *vbox;
        GtkWidget *menubar;
@@ -465,7 +504,6 @@ static void addressbook_create( void ) {
        GtkWidget *label;
        GtkWidget *entry;
        GtkWidget *statusbar;
-       /* GtkWidget *hmbox;  */
        GtkWidget *hbbox;
        GtkWidget *hsbox;
        GtkWidget *del_btn;
@@ -481,31 +519,29 @@ static void addressbook_create( void ) {
        GtkItemFactory *menu_factory;
        gint n_entries;
        GList *nodeIf;
-       AdapterInterface *adapter;
-       AddressTypeControlItem *atci;
-       AddressInterface *iface;
 
-       gchar *titles[N_COLS] = {_("Name"), _("E-Mail address"), _("Remarks")};
+       gchar *titles[N_COLS];
        gchar *text;
        gint i;
 
        debug_print("Creating addressbook window...\n");
 
+       titles[COL_NAME]    = _("Name");
+       titles[COL_ADDRESS] = _("E-Mail address");
+       titles[COL_REMARKS] = _("Remarks");
+
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title(GTK_WINDOW(window), _("Address book"));
        gtk_widget_set_usize(window, ADDRESSBOOK_WIDTH, ADDRESSBOOK_HEIGHT);
-       gtk_container_set_border_width(GTK_CONTAINER(window), BORDER_WIDTH);
        gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
+       gtk_window_set_wmclass(GTK_WINDOW(window), "addressbook", "Sylpheed");
        gtk_widget_realize(window);
 
        gtk_signal_connect(GTK_OBJECT(window), "delete_event",
                           GTK_SIGNAL_FUNC(addressbook_close), NULL);
        gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
                           GTK_SIGNAL_FUNC(key_pressed), NULL);
-       gtk_signal_connect(GTK_OBJECT(window), "focus_in_event",
-                          GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
-       gtk_signal_connect(GTK_OBJECT(window), "focus_out_event",
-                          GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
+       MANAGE_WINDOW_SIGNALS_CONNECT(window);
 
        vbox = gtk_vbox_new(FALSE, 0);
        gtk_container_add(GTK_CONTAINER(window), vbox);
@@ -527,7 +563,7 @@ static void addressbook_create( void ) {
                                       GTK_POLICY_ALWAYS);
        gtk_widget_set_usize(ctree_swin, COL_FOLDER_WIDTH + 40, -1);
 
-       /* Address index  */
+       /* Address index */
        ctree = gtk_ctree_new(1, 0);
        gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
        gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
@@ -537,7 +573,7 @@ static void addressbook_create( void ) {
                                     GTK_CTREE_EXPANDER_SQUARE);
        gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
        gtk_clist_set_compare_func(GTK_CLIST(ctree),
-                                  addressbook_list_compare_func);
+                                  addressbook_treenode_compare_func);
 
        gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
                           GTK_SIGNAL_FUNC(addressbook_tree_selected), NULL);
@@ -556,11 +592,11 @@ static void addressbook_create( void ) {
                                       GTK_POLICY_ALWAYS);
        gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
 
-       /* Address list  */
+       /* Address list */
        clist = gtk_ctree_new_with_titles(N_COLS, 0, 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_DOTTED);
+       gtk_ctree_set_line_style(GTK_CTREE(clist), GTK_CTREE_LINES_NONE);
        gtk_ctree_set_expander_style(GTK_CTREE(clist), GTK_CTREE_EXPANDER_SQUARE);
        gtk_ctree_set_indent(GTK_CTREE(clist), CTREE_INDENT);
        gtk_clist_set_column_width(GTK_CLIST(clist), COL_NAME,
@@ -666,22 +702,24 @@ static void addressbook_create( void ) {
                           GTK_SIGNAL_FUNC(addressbook_to_clicked),
                           GINT_TO_POINTER(COMPOSE_BCC));
 
-       /* Build icons for interface  */
-       PIXMAP_CREATE( window, interfacexpm, interfacexpmmask, interface_xpm );
+       /* Build icons for interface */
+       stock_pixmap_gdk( window, STOCK_PIXMAP_INTERFACE,
+                         &interfacexpm, &interfacexpmmask );
 
        /* Build control tables */
-       addrbookctl_build_map( window );
+       addrbookctl_build_map(window);
        addrbookctl_build_iflist();
        addrbookctl_build_ifselect();
 
-       /* Add each interface into the tree as a root level folder  */
+       /* Add each interface into the tree as a root level folder */
        nodeIf = _addressInterfaceList_;
        while( nodeIf ) {
-               adapter = nodeIf->data;
-               nodeIf = g_list_next( nodeIf );
-               iface = adapter->interface;
-               if( iface->useInterface ) {
-                       atci = adapter->atci;
+               AdapterInterface *adapter = nodeIf->data;
+               AddressInterface *iface = adapter->interface;
+               nodeIf = g_list_next(nodeIf);
+
+               if(iface->useInterface) {
+                       AddressTypeControlItem *atci = adapter->atci;
                        text = atci->displayName;
                        adapter->treeNode =
                                gtk_ctree_insert_node( GTK_CTREE(ctree),
@@ -690,7 +728,9 @@ static void addressbook_create( void ) {
                                        interfacexpm, interfacexpmmask,
                                        FALSE, FALSE );
                        menu_set_sensitive( menu_factory, atci->menuCommand, adapter->haveLibrary );
-                       gtk_ctree_node_set_row_data( GTK_CTREE(ctree), adapter->treeNode, adapter );
+                       gtk_ctree_node_set_row_data_full(
+                               GTK_CTREE(ctree), adapter->treeNode, adapter,
+                               addressbook_free_treenode );
                }
        }
 
@@ -716,7 +756,8 @@ static void addressbook_create( void ) {
        addrbook.clist   = clist;
        addrbook.entry   = entry;
        addrbook.statusbar = statusbar;
-       addrbook.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Addressbook Window" );
+       addrbook.status_cid = gtk_statusbar_get_context_id(
+                       GTK_STATUSBAR(statusbar), "Addressbook Window" );
 
        addrbook.del_btn = del_btn;
        addrbook.reg_btn = reg_btn;
@@ -737,8 +778,7 @@ static void addressbook_create( void ) {
 
 }
 
-static gint addressbook_close(void)
-{
+static gint addressbook_close( void ) {
        gtk_widget_hide(addrbook.window);
        addressbook_export_to_file();
        return TRUE;
@@ -746,13 +786,31 @@ static gint addressbook_close(void)
 
 static void addressbook_status_show( gchar *msg ) {
        if( addrbook.statusbar != NULL ) {
-               gtk_statusbar_pop( GTK_STATUSBAR(addrbook.statusbar), addrbook.status_cid );
+               gtk_statusbar_pop(
+                       GTK_STATUSBAR(addrbook.statusbar),
+                       addrbook.status_cid );
                if( msg ) {
-                       gtk_statusbar_push( GTK_STATUSBAR(addrbook.statusbar), addrbook.status_cid, msg );
+                       gtk_statusbar_push(
+                               GTK_STATUSBAR(addrbook.statusbar),
+                               addrbook.status_cid, msg );
                }
        }
 }
 
+static void addressbook_ds_status_message( AddressDataSource *ds, gchar *msg ) {
+       *addressbook_msgbuf = '\0';
+       if( ds ) {
+               gchar *name;
+
+               name = addrindex_ds_get_name( ds );
+               sprintf( addressbook_msgbuf, "%s: %s", name, msg );
+       }
+       else {
+               sprintf( addressbook_msgbuf, "%s", msg );
+       }
+       addressbook_status_show( addressbook_msgbuf );
+}
+
 static void addressbook_ds_show_message( AddressDataSource *ds ) {
        gint retVal;
        gchar *name;
@@ -761,17 +819,11 @@ static void addressbook_ds_show_message( AddressDataSource *ds ) {
                name = addrindex_ds_get_name( ds );
                retVal = addrindex_ds_get_status_code( ds );
                if( retVal == MGU_SUCCESS ) {
-                       if( ds ) {
-                               sprintf( addressbook_msgbuf, "%s", name );
-                       }
+                       sprintf( addressbook_msgbuf, "%s", name );
                }
                else {
-                       if( ds == NULL ) {
-                               sprintf( addressbook_msgbuf, "%s", mgu_error2string( retVal ) );
-                       }
-                       else {
-                               sprintf( addressbook_msgbuf, "%s: %s", name, mgu_error2string( retVal ) );
-                       }
+                       sprintf( addressbook_msgbuf, "%s: %s", name,
+                               mgu_error2string( retVal ) );
                }
        }
        addressbook_status_show( addressbook_msgbuf );
@@ -788,7 +840,9 @@ static void addressbook_button_set_sensitive(void)
        if (addrbook.target_compose) {
                to_sens = TRUE;
                cc_sens = TRUE;
+#ifndef CLAWS          
                if (addrbook.target_compose->use_bcc)
+#endif                 
                        bcc_sens = TRUE;
        }
 
@@ -804,23 +858,38 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
 {
        GtkCTree *clist = GTK_CTREE(addrbook.clist);
        GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
-       AddressObject *pobj, *obj;
+       AddressObject *pobj;
        AdapterDSource *ads = NULL;
        GtkCTreeNode *nodeList;
        gboolean procFlag;
        AlertValue aval;
        AddressBookFile *abf = NULL;
        AddressDataSource *ds = NULL;
+       AddressInterface *iface;
+       AddrItemObject *aio;
+       AddrSelectItem *item;
+       GList *list, *node;
 
        pobj = gtk_ctree_node_get_row_data(ctree, addrbook.opened );
        g_return_if_fail(pobj != NULL);
 
+       /* Test whether anything selected for deletion */
        nodeList = addrbook.listSelected;
-       obj = gtk_ctree_node_get_row_data( clist, nodeList );
-       if( obj == NULL) return;
+       aio = gtk_ctree_node_get_row_data( clist, nodeList );
+       if( aio == NULL) return;
        ds = addressbook_find_datasource( addrbook.treeSelected );
        if( ds == NULL ) return;
 
+       /* Test for read only */
+       iface = ds->interface;
+       if( iface->readOnly ) {
+               alertpanel( _("Delete address(es)"),
+                       _("This address data is readonly and cannot be deleted."),
+                       _("Close"), NULL, NULL );
+               return;
+       }
+
+       /* Test whether Ok to proceed */
        procFlag = FALSE;
        if( pobj->type == ADDR_DATASOURCE ) {
                ads = ADAPTER_DSOURCE(pobj);
@@ -845,11 +914,12 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
        /* Process deletions */
        if( pobj->type == ADDR_DATASOURCE || pobj->type == ADDR_ITEM_FOLDER ) {
                /* Items inside folders */
-               GList *node;
-               node = _addressListSelection_;
+               list = addrselect_get_list( _addressSelect_ );
+               node = list;
                while( node ) {
-                       AddrItemObject *aio = node->data;
+                       item = node->data;
                        node = g_list_next( node );
+                       aio = ( AddrItemObject * ) item->addressItem;
                        if( aio->type == ADDR_ITEM_GROUP ) {
                                ItemGroup *item = ( ItemGroup * ) aio;
                                GtkCTreeNode *nd = NULL;
@@ -858,9 +928,8 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
                                item = addrbook_remove_group( abf, item );
                                if( item ) {
                                        addritem_free_item_group( item );
-                                       item = NULL;
                                }
-                               /* Remove group from parent node  */
+                               /* Remove group from parent node */
                                gtk_ctree_remove_node( ctree, nd );
                        }
                        else if( aio->type == ADDR_ITEM_PERSON ) {
@@ -868,7 +937,6 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
                                item = addrbook_remove_person( abf, item );
                                if( item ) {
                                        addritem_free_item_person( item );
-                                       item = NULL;
                                }
                        }
                        else if( aio->type == ADDR_ITEM_EMAIL ) {
@@ -877,32 +945,32 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
                                item = addrbook_person_remove_email( abf, person, item );
                                if( item ) {
                                        addritem_free_item_email( item );
-                                       item = NULL;
                                }
                        }
                }
+               g_list_free( list );
                addressbook_list_select_clear();
                gtk_ctree_select( ctree, addrbook.opened);
                return;
        }
        else if( pobj->type == ADDR_ITEM_GROUP ) {
                /* Items inside groups */
-               GList *node;
-               node = _addressListSelection_;
+               list = addrselect_get_list( _addressSelect_ );
+               node = list;
                while( node ) {
-                       AddrItemObject *aio = node->data;
+                       item = node->data;
                        node = g_list_next( node );
+                       aio = ( AddrItemObject * ) item->addressItem;
                        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 ) {
-                                       addritem_print_item_email( item, stdout );
                                        addritem_free_item_email( item );
-                                       item = NULL;
                                }
                        }
                }
+               g_list_free( list );
                addressbook_list_select_clear();
                gtk_ctree_select( ctree, addrbook.opened);
                return;
@@ -910,7 +978,6 @@ static void addressbook_del_clicked(GtkButton *button, gpointer data)
 
        gtk_ctree_node_set_row_data( clist, nodeList, NULL );
        gtk_ctree_remove_node( clist, nodeList );
-       addressbook_list_select_remove( obj );
 
 }
 
@@ -919,14 +986,14 @@ static void addressbook_reg_clicked(GtkButton *button, gpointer data)
        addressbook_new_address_cb( NULL, 0, NULL );
 }
 
-gchar *addressbook_format_address( AddressObject * obj ) {
+gchar *addressbook_format_address( AddrItemObject * aio ) {
        gchar *buf = NULL;
        gchar *name = NULL;
        gchar *address = NULL;
 
-       if( obj->type == ADDR_ITEM_EMAIL ) {
+       if( aio->type == ADDR_ITEM_EMAIL ) {
                ItemPerson *person = NULL;
-               ItemEMail *email = ( ItemEMail * ) obj;
+               ItemEMail *email = ( ItemEMail * ) aio;
 
                person = ( ItemPerson * ) ADDRITEM_PARENT(email);
                if( email->address ) {
@@ -945,8 +1012,8 @@ gchar *addressbook_format_address( AddressObject * obj ) {
                        address = email->address;
                }
        }
-       else if( obj->type == ADDR_ITEM_PERSON ) {
-               ItemPerson *person = ( ItemPerson * ) obj;
+       else if( aio->type == ADDR_ITEM_PERSON ) {
+               ItemPerson *person = ( ItemPerson * ) aio;
                GList *node = person->listEMail;
 
                name = ADDRITEM_NAME(person);
@@ -956,8 +1023,11 @@ gchar *addressbook_format_address( AddressObject * obj ) {
                }
        }
        if( address ) {
-               if( name ) {
-                       buf = g_strdup_printf( "%s <%s>", name, address );
+               if( name && name[0] != '\0' ) {
+                       if( strchr_with_skip_quote( name, '"', ',' ) )
+                               buf = g_strdup_printf( "\"%s\" <%s>", name, address );
+                       else
+                               buf = g_strdup_printf( "%s <%s>", name, address );
                }
                else {
                        buf = g_strdup( address );
@@ -969,43 +1039,62 @@ gchar *addressbook_format_address( AddressObject * obj ) {
 
 static void addressbook_to_clicked(GtkButton *button, gpointer data)
 {
-       GList *node = _addressListSelection_;
-       if (!addrbook.target_compose) return;
+       GList *list, *node;
+       Compose *compose;
+       AddrSelectItem *item;
+       AddrItemObject *aio;
+       gchar *addr;
+
+       compose = addrbook.target_compose;
+       if( ! compose ) return;
+
+       /* Nothing selected, but maybe there is something in text entry */
+       addr = gtk_entry_get_text( GTK_ENTRY( addrbook.entry) );
+       if ( addr ) {
+               compose_entry_append(
+                       compose, addr, (ComposeEntryType)data );
+       }
+
+       /* Select from address list */
+       list = addrselect_get_list( _addressSelect_ );
+       node = list;
        while( node ) {
-               AddressObject *obj = node->data;
-               Compose *compose = addrbook.target_compose;
+               item = node->data;
                node = g_list_next( node );
-               if( obj->type == ADDR_ITEM_PERSON || obj->type == ADDR_ITEM_EMAIL ) {
-                       gchar *addr = addressbook_format_address( obj );
-                       compose_entry_append( compose, addr, (ComposeEntryType) data );
+               aio = item->addressItem;
+               if( aio->type == ADDR_ITEM_PERSON ||
+                   aio->type == ADDR_ITEM_EMAIL ) {
+                       addr = addressbook_format_address( aio );
+                       compose_entry_append(
+                               compose, addr, (ComposeEntryType) data );
                        g_free( addr );
-                       addr = NULL;
                }
-               else if( obj->type == ADDR_ITEM_GROUP ) {
-                       ItemGroup *group = ( ItemGroup * ) obj;
+               else if( aio->type == ADDR_ITEM_GROUP ) {
+                       ItemGroup *group = ( ItemGroup * ) aio;
                        GList *nodeMail = group->listEMail;
                        while( nodeMail ) {
                                ItemEMail *email = nodeMail->data;
-                               gchar *addr = addressbook_format_address( ( AddressObject * ) email );
-                               compose_entry_append( compose, addr, (ComposeEntryType) data );
+
+                               addr = addressbook_format_address(
+                                               ( AddrItemObject * ) email );
+                               compose_entry_append(
+                                       compose, addr, (ComposeEntryType) data );
                                g_free( addr );
                                nodeMail = g_list_next( nodeMail );
                        }
                }
        }
+       g_list_free( list );
 }
 
 static void addressbook_menubar_set_sensitive( gboolean sensitive ) {
-       menu_set_sensitive( addrbook.menu_factory, "/File/New Book",    sensitive );
-       menu_set_sensitive( addrbook.menu_factory, "/File/New V-Card",  sensitive );
-#ifdef USE_JPILOT
-       menu_set_sensitive( addrbook.menu_factory, "/File/New J-Pilot", sensitive );
-#endif
-#ifdef USE_LDAP
-       menu_set_sensitive( addrbook.menu_factory, "/File/New Server",  sensitive );
-#endif
-       menu_set_sensitive( addrbook.menu_factory, "/File/Edit",        sensitive );
-       menu_set_sensitive( addrbook.menu_factory, "/File/Delete",      sensitive );
+       menu_set_sensitive( addrbook.menu_factory, "/File/Edit",   sensitive );
+       menu_set_sensitive( addrbook.menu_factory, "/File/Delete", sensitive );
+
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/Cut",    sensitive );
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/Copy",   sensitive );
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste",  sensitive );
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste Address",  sensitive );
 
        menu_set_sensitive( addrbook.menu_factory, "/Address/New Address", sensitive );
        menu_set_sensitive( addrbook.menu_factory, "/Address/New Group",   sensitive );
@@ -1019,6 +1108,7 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
        gboolean canAdd = FALSE;
        gboolean canEditTr = TRUE;
        gboolean editAddress = FALSE;
+       gboolean canExport = TRUE;
        AddressTypeControlItem *atci = NULL;
        AddressDataSource *ds = NULL;
        AddressInterface *iface = NULL;
@@ -1034,7 +1124,7 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
                                menu_set_sensitive( addrbook.menu_factory, atci->menuCommand, TRUE );
                        }
                }
-               canEditTr = FALSE;
+               canEditTr = canExport = FALSE;
        }
        else if( obj->type == ADDR_DATASOURCE ) {
                AdapterDSource *ads = ADAPTER_DSOURCE(obj);
@@ -1044,14 +1134,17 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
                        canAdd = canEdit = editAddress = TRUE;
                }
                if( ! iface->haveLibrary ) {
-                       canAdd = canEdit = editAddress = FALSE;
+                       canAdd = canEdit = editAddress = canExport = FALSE;
                }
        }
        else if( obj->type == ADDR_ITEM_FOLDER ) {
                ds = addressbook_find_datasource( addrbook.treeSelected );
                if( ds ) {
                        iface = ds->interface;
-                       if( ! iface->readOnly ) {
+                       if( iface->readOnly ) {
+                               canEditTr = FALSE;
+                       }
+                       else {
                                canAdd = editAddress = TRUE;
                        }
                }
@@ -1068,7 +1161,7 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
 
        if( addrbook.listSelected == NULL ) canEdit = FALSE;
 
-       /* Enable add  */
+       /* Enable add */
        menu_set_sensitive( addrbook.menu_factory, "/Address/New Address", editAddress );
        menu_set_sensitive( addrbook.menu_factory, "/Address/New Group",   canAdd );
        menu_set_sensitive( addrbook.menu_factory, "/Address/New Folder",  canAdd );
@@ -1081,6 +1174,9 @@ static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode
 
        menu_set_sensitive( addrbook.menu_factory, "/File/Edit",      canEditTr );
        menu_set_sensitive( addrbook.menu_factory, "/File/Delete",    canEditTr );
+
+       /* Export data */
+       menu_set_sensitive( addrbook.menu_factory, "/Tools/Export HTML...", canExport );
 }
 
 static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
@@ -1103,13 +1199,13 @@ static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
        addrbook.opened = node;
 
        if( obj->type == ADDR_DATASOURCE ) {
-               /*  Read from file */
+               /* Read from file */
                static gboolean tVal = TRUE;
 
                ads = ADAPTER_DSOURCE(obj);
                if( ads == NULL ) return;
                ds = ads->dataSource;
-               if( ds == NULL ) return;
+               if( ds == NULL ) return;                
 
                if( addrindex_ds_get_modify_flag( ds ) ) {
                        addrindex_ds_read_data( ds );
@@ -1139,7 +1235,7 @@ static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
                }
        }
 
-       /* Update address list  */
+       /* Update address list */
        addressbook_set_clist( obj );
 
        /* Setup main menu selections */
@@ -1147,7 +1243,96 @@ static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
        addressbook_menuitem_set_sensitive( obj, node );
 
        addressbook_list_select_clear();
+}
+
+/*
+* Setup address list popup menu.
+* Enter: pobj  Parent address object in address tree.
+*        obj   Address object in address list.
+*/
+static void addressbook_list_menu_setup( void ) {
+       GtkCTree *clist = NULL;
+       AddressObject *pobj = NULL;
+       AddressObject *obj = NULL;
+       AdapterDSource *ads = NULL;
+       AddressInterface *iface = NULL;
+       AddressDataSource *ds = NULL;
+       gboolean canEdit = FALSE;
+       gboolean canDelete = FALSE;
+       gboolean canCut = FALSE;
+       gboolean canCopy = FALSE;
+       gboolean canPaste = FALSE;
+
+       pobj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), addrbook.treeSelected );
+       if( pobj == NULL ) return;
+
+       clist = GTK_CTREE(addrbook.clist);
+       obj = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
+       if( obj == NULL ) canEdit = FALSE;
+
+       menu_set_insensitive_all( GTK_MENU_SHELL(addrbook.list_popup) );
+
+       if( pobj->type == ADDR_DATASOURCE ) {
+               ads = ADAPTER_DSOURCE(pobj);
+               ds = ads->dataSource;
+               iface = ds->interface;
+               if( ! iface->readOnly ) {
+                       menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
+                       menu_set_sensitive( addrbook.list_factory, "/New Folder", TRUE );
+                       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;
+               }
+       }
+       else if( pobj->type != ADDR_INTERFACE ) {
+               ds = addressbook_find_datasource( addrbook.treeSelected );
+               iface = ds->interface;
+               if( ! iface->readOnly ) {
+                       if( pobj->type == ADDR_ITEM_FOLDER || pobj->type == ADDR_ITEM_GROUP ) {
+                               menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
+                               gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
+                               if( obj ) canEdit = TRUE;
+                       }
+                       if( pobj->type == ADDR_ITEM_FOLDER ) {
+                               menu_set_sensitive( addrbook.list_factory, "/New Folder", TRUE );
+                               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;
+               }
+       }
+       if( ! addrselect_test_empty( _addressSelect_ ) ) canCopy = TRUE;
+
+       canDelete = canEdit;
+       canDelete = canEdit;
+
+       /* Disable edit if more than one row selected */
+       if( GTK_CLIST(clist)->selection && GTK_CLIST(clist)->selection->next ) canEdit = FALSE;
+
+       menu_set_sensitive( addrbook.list_factory, "/Edit",   canEdit );
+       menu_set_sensitive( addrbook.list_factory, "/Delete", canDelete );
+
+       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.menu_factory, "/Edit/Cut",           canCut );
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/Copy",          canCopy );
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/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 );
 
+       gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
 }
 
 static void addressbook_list_selected(GtkCList *clist, gint row, gint column,
@@ -1163,145 +1348,335 @@ static void addressbook_list_selected(GtkCList *clist, gint row, gint column,
        }
 }
 
-#if 0
-static void addressbook_list_select_show() {
-       GList *node = _addressListSelection_;
-       gchar *addr = NULL;
-       printf( "show selection...>>>\n" );
-       while( node != NULL ) {
-               AddressObject *obj = ( AddressObject * ) node->data;
-               if( obj ) {
-                       printf( "- %d : '%s'\n", obj->type, obj->name );
-                       if( obj->type == ADDR_ITEM_GROUP ) {
-                               ItemGroup *group = ( ItemGroup * ) obj;
-                               GList *node = group->listEMail;
-                               while( node ) {
-                                       ItemEMail *email = node->data;
-                                       addr = addressbook_format_address( ( AddressObject * ) email );
-                                       if( addr ) {
-                                               printf( "\tgrp >%s<\n", addr );
-                                               g_free( addr );
-                                       }
-                                       node = g_list_next( node );
-                               }
-                       }
-                       else {
-                               addr = addressbook_format_address( obj );
-                               if( addr ) {
-                                       printf( "\t>%s<\n", addr );
-                                       g_free( addr );
-                               }
-                       }
+/*
+ * Add list of items into tree node below specified tree node.
+ * Enter: treeNode  Tree node.
+ *        ds        Data source.
+ *        listItems List of items.
+ */
+static void addressbook_treenode_add_list(
+       GtkCTreeNode *treeNode, AddressDataSource *ds, GList *listItems )
+{
+       GList *node;
+
+       node = listItems;
+       while( node ) {
+               AddrItemObject *aio;
+               GtkCTreeNode *nn;
+
+               aio = node->data;
+               if( ADDRESS_OBJECT_TYPE(aio) == ITEMTYPE_GROUP ) {
+                       ItemGroup *group;
+
+                       group = ( ItemGroup * ) aio;
+                       nn = addressbook_node_add_group( treeNode, ds, group );
                }
-               else {
-                       printf( "- NULL" );
+               else if( ADDRESS_OBJECT_TYPE(aio) == ITEMTYPE_FOLDER ) {
+                       ItemFolder *folder;
+
+                       folder = ( ItemFolder * ) aio;
+                       nn = addressbook_node_add_folder(
+                               treeNode, ds, folder, ADDR_ITEM_FOLDER );
                }
                node = g_list_next( node );
        }
-       printf( "show selection...<<<\n" );
 }
-#endif
 
-static void addressbook_list_select_clear() {
-       if( _addressListSelection_ ) {
-               g_list_free( _addressListSelection_ );
+/*
+* Cut from address list widget.
+*/
+static void addressbook_clip_cut_cb( void ) {
+       _clipBoard_->cutFlag = TRUE;
+       addrclip_clear( _clipBoard_ );
+       addrclip_add( _clipBoard_, _addressSelect_ );
+       /* addrclip_list_show( _clipBoard_, stdout ); */
+}
+
+/*
+* Copy from address list widget.
+*/
+static void addressbook_clip_copy_cb( void ) {
+       _clipBoard_->cutFlag = FALSE;
+       addrclip_clear( _clipBoard_ );
+       addrclip_add( _clipBoard_, _addressSelect_ );
+       /* addrclip_list_show( _clipBoard_, stdout ); */
+}
+
+/*
+* Paste clipboard into address list widget.
+*/
+static void addressbook_clip_paste_cb( void ) {
+       GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
+       AddressObject *pobj = NULL;
+       AddressDataSource *ds = NULL;
+       AddressBookFile *abf = NULL;
+       ItemFolder *folder = NULL;
+       GList *folderGroup = NULL;
+
+       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." ) );
+               return;
+       }
+
+       pobj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
+       if( pobj ) {
+               if( pobj->type == ADDR_ITEM_FOLDER ) {
+                       folder = ADAPTER_FOLDER(pobj)->itemFolder;
+               }
+               else if( pobj->type == ADDR_ITEM_GROUP ) {
+                       addressbook_ds_status_message(
+                               ds, _( "Cannot paste into an address group." ) );
+                       return;
+               }
+       }
+
+       /* Get an address book */
+       abf = addressbook_get_book_file();
+       if( abf == NULL ) return;
+
+       if( _clipBoard_->cutFlag ) {
+               /* Paste/Cut */
+               folderGroup = addrclip_paste_cut( _clipBoard_, abf, folder );
+
+               /* Remove all groups and folders in clipboard from tree node */
+               addressbook_treenode_remove_item();
+
+               /* Remove all "cut" items */
+               addrclip_delete_item( _clipBoard_ );
+
+               /* Clear clipboard - cut items??? */
+               addrclip_clear( _clipBoard_ );
        }
-       _addressListSelection_ = NULL;
+       else {
+               /* Paste/Copy */
+               folderGroup = addrclip_paste_copy( _clipBoard_, abf, folder );
+       }
+
+       /* addrclip_list_show( _clipBoard_, stdout ); */
+       if( folderGroup ) {
+               /* Update tree by inserting node for each folder or group */
+               addressbook_treenode_add_list(
+                       addrbook.treeSelected, ds, folderGroup );
+               gtk_ctree_expand( ctree, addrbook.treeSelected );
+               g_list_free( folderGroup );
+               folderGroup = NULL;
+       }
+
+       /* Display items pasted */
+       gtk_ctree_select( ctree, addrbook.opened );
+
 }
 
-static void addressbook_list_select_add( AddressObject *obj ) {
-       if( obj ) {
-               if(     obj->type == ADDR_ITEM_PERSON ||
-                       obj->type == ADDR_ITEM_EMAIL ||
-                       obj->type == ADDR_ITEM_GROUP ) {
-                       if( ! g_list_find( _addressListSelection_, obj ) ) {
-                               _addressListSelection_ = g_list_append( _addressListSelection_, obj );
-                       }
+/*
+* Paste clipboard email addresses only into address list widget.
+*/
+static void addressbook_clip_paste_address_cb( void ) {
+       GtkCTree *clist = GTK_CTREE(addrbook.clist);
+       GtkCTree *ctree;
+       AddressObject *pobj = NULL;
+       AddressDataSource *ds = NULL;
+       AddressBookFile *abf = NULL;
+       ItemFolder *folder = NULL;
+       AddrItemObject *aio;
+       gint cnt;
+
+       if( addrbook.listSelected == NULL ) return;
+
+               ctree = GTK_CTREE( addrbook.ctree );
+       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." ) );
+               return;
+       }
+
+       pobj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
+       if( pobj ) {
+               if( pobj->type == ADDR_ITEM_FOLDER ) {
+                       folder = ADAPTER_FOLDER(pobj)->itemFolder;
+               }
+       }
+
+       abf = addressbook_get_book_file();
+       if( abf == NULL ) return;
+
+       cnt = 0;
+       aio = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
+       if( aio->type == ADDR_ITEM_PERSON ) {
+               ItemPerson *person;
+
+               person = ( ItemPerson * ) aio;
+               if( _clipBoard_->cutFlag ) {
+                       /* Paste/Cut */
+                       cnt = addrclip_paste_person_cut( _clipBoard_, abf, person );
+
+                       /* Remove all "cut" items */
+                       addrclip_delete_address( _clipBoard_ );
+
+                       /* Clear clipboard */
+                       addrclip_clear( _clipBoard_ );
+               }
+               else {
+                       /* Paste/Copy */
+                       cnt = addrclip_paste_person_copy( _clipBoard_, abf, person );
                }
+               if( cnt > 0 ) {
+                       addritem_person_set_opened( person, TRUE );
+               }
+       }
+
+       /* Display items pasted */
+       if( cnt > 0 ) {
+               gtk_ctree_select( ctree, addrbook.opened );
        }
-       /* addressbook_list_select_show(); */
 }
 
-static void addressbook_list_select_remove( AddressObject *obj ) {
+/*
+* Add current treenode object to clipboard. Note that widget only allows
+* one entry from the tree list to be selected.
+*/
+static void addressbook_treenode_to_clipboard( void ) {
+       AddressObject *obj = NULL;
+       AddressDataSource *ds = NULL;
+       AddrSelectItem *item;
+       GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
+       GtkCTreeNode *node;
+
+       node = addrbook.treeSelected;
+       if( node == NULL ) return;
+       obj = gtk_ctree_node_get_row_data( ctree, node );
        if( obj == NULL ) return;
-       if( _addressListSelection_ ) {
-               _addressListSelection_ = g_list_remove( _addressListSelection_, obj );
+
+       ds = addressbook_find_datasource( node );
+       if( ds == NULL ) return;
+
+       item = NULL;
+       if( obj->type == ADDR_ITEM_FOLDER ) {
+               AdapterFolder *adapter = ADAPTER_FOLDER(obj);
+               ItemFolder *folder = adapter->itemFolder;
+
+               item = addrselect_create_node( obj );
+               item->uid = g_strdup( ADDRITEM_ID(folder) );
+       }
+       else if( obj->type == ADDR_ITEM_GROUP ) {
+               AdapterGroup *adapter = ADAPTER_GROUP(obj);
+               ItemGroup *group = adapter->itemGroup;
+
+               item = addrselect_create_node( obj );
+               item->uid = g_strdup( ADDRITEM_ID(group) );
+       }
+       else if( obj->type == ADDR_DATASOURCE ) {
+               /* Data source */
+               item = addrselect_create_node( obj );
+               item->uid = NULL;
+       }
+
+       if( item ) {
+               /* Clear existing list and add item into list */
+               gchar *cacheID;
+
+               addressbook_list_select_clear();
+               cacheID = addrindex_get_cache_id( _addressIndex_, ds );
+               addrselect_list_add( _addressSelect_, item, cacheID );
+               g_free( cacheID );
        }
-       /* addressbook_list_select_show(); */
 }
 
-static void addressbook_list_row_selected( GtkCTree *clist, GtkCTreeNode *node, gint column, gpointer data ) {
+/*
+* Cut from tree widget.
+*/
+static void addressbook_treenode_cut_cb( void ) {
+       _clipBoard_->cutFlag = TRUE;
+       addressbook_treenode_to_clipboard();
+       addrclip_clear( _clipBoard_ );
+       addrclip_add( _clipBoard_, _addressSelect_ );
+       /* addrclip_list_show( _clipBoard_, stdout ); */
+}
+
+/*
+* Copy from tree widget.
+*/
+static void addressbook_treenode_copy_cb( void ) {
+       _clipBoard_->cutFlag = FALSE;
+       addressbook_treenode_to_clipboard();
+       addrclip_clear( _clipBoard_ );
+       addrclip_add( _clipBoard_, _addressSelect_ );
+       /* addrclip_list_show( _clipBoard_, stdout ); */
+}
+
+/*
+* Paste clipboard into address tree widget.
+*/
+static void addressbook_treenode_paste_cb( void ) {
+       addressbook_clip_paste_cb();
+}
+
+static void addressbook_list_select_clear( void ) {
+       addrselect_list_clear( _addressSelect_ );
+}
+
+static void addressbook_list_select_add( AddrItemObject *aio, AddressDataSource *ds ) {
+       gchar *cacheID;
+
+       if( ds == NULL ) return;
+       cacheID = addrindex_get_cache_id( _addressIndex_, ds );
+       addrselect_list_add_obj( _addressSelect_, aio, cacheID );
+       g_free( cacheID );
+}
+
+static void addressbook_list_select_remove( AddrItemObject *aio ) {
+       addrselect_list_remove( _addressSelect_, aio );
+}
+
+static void addressbook_list_row_selected( GtkCTree *clist,
+                                          GtkCTreeNode *node,
+                                          gint column,
+                                          gpointer data )
+{
        GtkEntry *entry = GTK_ENTRY(addrbook.entry);
-       AddressObject *obj = NULL;
+       AddrItemObject *aio = NULL;
        AddressObject *pobj = NULL;
        AdapterDSource *ads = NULL;
-       AddressInterface *iface = NULL;
        AddressDataSource *ds = NULL;
-       gboolean canEdit = FALSE;
-       gboolean canDelete = FALSE;
 
        gtk_entry_set_text( entry, "" );
        addrbook.listSelected = node;
-       obj = gtk_ctree_node_get_row_data( clist, node );
-       if( obj != NULL ) {
-               /* printf( "list select: %d : '%s'\n", obj->type, obj->name ); */
-               addressbook_list_select_add( obj );
-       }
 
        pobj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), addrbook.treeSelected );
        if( pobj == NULL ) return;
 
-       menu_set_insensitive_all( GTK_MENU_SHELL(addrbook.list_popup) );
-
        if( pobj->type == ADDR_DATASOURCE ) {
                ads = ADAPTER_DSOURCE(pobj);
                ds = ads->dataSource;
-               iface = ds->interface;
-               if( ! iface->readOnly ) {
-                       canEdit = TRUE;
-                       menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
-                       menu_set_sensitive( addrbook.list_factory, "/New Folder", TRUE );
-                       menu_set_sensitive( addrbook.list_factory, "/New Group", TRUE );
-                       gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
-               }
        }
        else if( pobj->type != ADDR_INTERFACE ) {
                ds = addressbook_find_datasource( addrbook.treeSelected );
-               iface = ds->interface;
-               if( ! iface->readOnly ) {
-                       if( pobj->type == ADDR_ITEM_FOLDER || pobj->type == ADDR_ITEM_GROUP ) {
-                               canEdit = TRUE;
-                               menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
-                               gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
-                       }
-                       if( pobj->type == ADDR_ITEM_FOLDER ) {
-                               canEdit = TRUE;
-                               menu_set_sensitive( addrbook.list_factory, "/New Folder", TRUE );
-                               menu_set_sensitive( addrbook.list_factory, "/New Group", TRUE );
-                       }
-               }
        }
-       if( obj == NULL ) canEdit = FALSE;
-       canDelete = canEdit;
-       if( GTK_CLIST(clist)->selection && GTK_CLIST(clist)->selection->next ) canEdit = FALSE;
-
-       menu_set_sensitive( addrbook.list_factory, "/Edit",   canEdit );
-       menu_set_sensitive( addrbook.list_factory, "/Delete", canDelete );
-
-       menu_set_sensitive( addrbook.menu_factory, "/Address/Edit",   canEdit );
-       menu_set_sensitive( addrbook.menu_factory, "/Address/Delete", canDelete );
 
-       gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
+       aio = gtk_ctree_node_get_row_data( clist, node );
+       if( aio ) {
+               /* printf( "list select: %d : '%s'\n", aio->type, aio->name ); */
+               addressbook_list_select_add( aio, ds );
+       }
 
+       addressbook_list_menu_setup();
 }
 
-static void addressbook_list_row_unselected( GtkCTree *ctree, GtkCTreeNode *node, gint column, gpointer data ) {
-       AddressObject *obj;
+static void addressbook_list_row_unselected( GtkCTree *ctree,
+                                            GtkCTreeNode *node,
+                                            gint column,
+                                            gpointer data )
+{
+       AddrItemObject *aio;
 
-       obj = gtk_ctree_node_get_row_data( ctree, node );
-       if( obj != NULL ) {
-               /* g_print( "list unselect: %d : '%s'\n", obj->type, obj->name ); */
-               addressbook_list_select_remove( obj );
+       aio = gtk_ctree_node_get_row_data( ctree, node );
+       if( aio != NULL ) {
+               /* printf( "list unselect: %d : '%s'\n", aio->type, aio->name ); */
+               addressbook_list_select_remove( aio );
        }
 }
 
@@ -1314,6 +1689,9 @@ static void addressbook_list_button_pressed(GtkWidget *widget,
                                            gpointer data)
 {
        if( ! event ) return;
+
+       addressbook_list_menu_setup();
+
        if( event->button == 3 ) {
                gtk_menu_popup( GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
                       event->button, event->time );
@@ -1333,47 +1711,65 @@ static void addressbook_tree_button_pressed(GtkWidget *ctree,
        GtkCList *clist = GTK_CLIST(ctree);
        gint row, column;
        AddressObject *obj = NULL;
-       /* GtkCTreeNode *node; */
        AdapterDSource *ads = NULL;
        AddressInterface *iface = NULL;
        AddressDataSource *ds = NULL;
-       /* AddressTypeControlItem *atci = NULL; */
        gboolean canEdit = FALSE;
+       gboolean canCut = FALSE;
+       gboolean canCopy = FALSE;
+       gboolean canPaste = FALSE;
+       gboolean canTreeCut = FALSE;
+       gboolean canTreeCopy = FALSE;
+       gboolean canTreePaste = FALSE;
 
        if( ! event ) return;
        addressbook_menubar_set_sensitive( FALSE );
-/* */
+
        if( gtk_clist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
                gtk_clist_select_row( clist, row, column );
                gtkut_clist_set_focus_row(clist, row);
                obj = gtk_clist_get_row_data( clist, row );
        }
-/* */
+
        menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
 
        if( obj == NULL ) return;
+
+       if( ! addrclip_is_empty( _clipBoard_ ) ) {
+               canTreePaste = TRUE;
+       }
+
        if (obj->type == ADDR_DATASOURCE) {
                ads = ADAPTER_DSOURCE(obj);
                ds = ads->dataSource;
                iface = ds->interface;
                canEdit = TRUE;
-               if( ! iface->readOnly ) {
+               if( iface->readOnly ) {
+                       canTreePaste = FALSE;
+               }
+               else {
                        menu_set_sensitive( addrbook.tree_factory, "/New Address", 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 );
                }
+               canTreeCopy = TRUE;
        }
        else if (obj->type == ADDR_ITEM_FOLDER) {
                ds = addressbook_find_datasource( addrbook.treeSelected );
                iface = ds->interface;
-               if( ! iface->readOnly ) {
+               if( iface->readOnly ) {
+                       canTreePaste = FALSE;
+               }
+               else {
                        canEdit = TRUE;
+                       canTreeCut = TRUE;
                        menu_set_sensitive( addrbook.tree_factory, "/New Address", 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 );
                }
+               canTreeCopy = TRUE;
        }
        else if (obj->type == ADDR_ITEM_GROUP) {
                ds = addressbook_find_datasource( addrbook.treeSelected );
@@ -1384,12 +1780,29 @@ static void 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( ! addrselect_test_empty( _addressSelect_ ) ) canCopy = TRUE;
+       if( ! addrclip_is_empty( _clipBoard_ ) ) canPaste = TRUE;
 
        /* Enable edit */
        menu_set_sensitive( addrbook.tree_factory, "/Edit",   canEdit );
        menu_set_sensitive( addrbook.tree_factory, "/Delete", canEdit );
-       menu_set_sensitive( addrbook.menu_factory, "/File/Edit",   canEdit );
-       menu_set_sensitive( addrbook.menu_factory, "/File/Delete", canEdit );
+       menu_set_sensitive( addrbook.tree_factory, "/Cut",    canTreeCut );
+       menu_set_sensitive( addrbook.tree_factory, "/Copy",   canTreeCopy );
+       menu_set_sensitive( addrbook.tree_factory, "/Paste",  canTreePaste );
+
+       menu_set_sensitive( addrbook.menu_factory, "/File/Edit",          canEdit );
+       menu_set_sensitive( addrbook.menu_factory, "/File/Delete",        canEdit );
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/Cut",           canCut );
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/Copy",          canCopy );
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste",         canPaste );
+       menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste Address", canPaste );
 
        if( event->button == 3 ) {
                gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
@@ -1526,6 +1939,10 @@ static gchar *addressbook_edit_datasource( AddressObject *obj, GtkCTreeNode *nod
        if( ! iface->haveLibrary ) return NULL;
 
        /* Read data from data source */
+       if( addrindex_ds_get_modify_flag( ds ) ) {
+               addrindex_ds_read_data( ds );
+       }
+
        if( ! addrindex_ds_get_read_flag( ds ) ) {
                addrindex_ds_read_data( ds );
        }
@@ -1605,7 +2022,7 @@ static void addressbook_treenode_edit_cb(gpointer data, guint action,
        if( name && parentNode ) {
                /* Update node in tree view */
                addressbook_change_node_name( node, name );
-               gtk_ctree_sort_node(ctree, parentNode);
+               gtk_sctree_sort_node(ctree, parentNode);
                gtk_ctree_expand( ctree, node );
                gtk_ctree_select( ctree, node );
        }
@@ -1642,7 +2059,7 @@ static void addressbook_treenode_delete_cb(gpointer data, guint action,
                if( ds == NULL ) return;
        }
        else {
-               /* Must be folder or something else  */
+               /* Must be folder or something else */
                ds = addressbook_find_datasource( node );
                if( ds == NULL ) return;
 
@@ -1654,10 +2071,11 @@ static void addressbook_treenode_delete_cb(gpointer data, guint action,
        /* Confirm deletion */
        if( obj->type == ADDR_ITEM_FOLDER ) {
                message = g_strdup_printf( _(
-                               "Do you want to delete the folder AND all addresses in `%s' ? \n" \
-                               "If deleting the folder only, addresses will be moved into parent folder." ),
-                               obj->name );
-               aval = alertpanel( _("Delete"), message, _("Folder only"), _("Folder and Addresses"), _("Cancel") );
+                       "Do you want to delete the folder AND all addresses in `%s' ? \n" \
+                       "If deleting the folder only, addresses will be moved into parent folder." ),
+                       obj->name );
+               aval = alertpanel( _("Delete"), message,
+                       _("Folder only"), _("Folder and Addresses"), _("Cancel") );
                g_free(message);
                if( aval == G_ALERTOTHER ) return;
        }
@@ -1670,9 +2088,9 @@ static void addressbook_treenode_delete_cb(gpointer data, guint action,
 
        /* Proceed with deletion */
        if( obj->type == ADDR_DATASOURCE ) {
-               /* Remove data source */
+               /* Remove data source. */
                if( addrindex_index_remove_datasource( _addressIndex_, ds ) ) {
-                       addressbook_free_child_adapters( node );
+                       addrindex_free_datasource( ds );
                        remFlag = TRUE;
                }
        }
@@ -1698,7 +2116,6 @@ static void addressbook_treenode_delete_cb(gpointer data, guint action,
                        item = addrbook_remove_folder_delete( abf, item );
                        if( item ) {
                                addritem_free_item_folder( item );
-                               addressbook_free_child_adapters( node );
                                remFlag = TRUE;
                        }
                }
@@ -1715,8 +2132,7 @@ static void addressbook_treenode_delete_cb(gpointer data, guint action,
        }
 
        if( remFlag ) {
-               /* Free up adapter and remove node. */
-               addressbook_free_adapter( node );
+               /* Remove node. */
                gtk_ctree_remove_node(ctree, node );
        }
 }
@@ -1746,7 +2162,7 @@ static void addressbook_new_address_cb( gpointer data, guint action, GtkWidget *
                }
        }
        else if( pobj->type == ADDR_ITEM_FOLDER ) {
-               /* New address  */
+               /* New address */
                ItemFolder *folder = ADAPTER_FOLDER(pobj)->itemFolder;
                ItemPerson *person = addressbook_edit_person( abf, folder, NULL, FALSE );
                if( person ) {
@@ -1768,7 +2184,9 @@ static void addressbook_new_address_cb( gpointer data, guint action, GtkWidget *
 }
 
 /*
-* Search for specified group in address index tree.
+* Search for specified child group node in address index tree.
+* Enter: parent Parent node.
+*        group  Group to find.
 */
 static GtkCTreeNode *addressbook_find_group_node( GtkCTreeNode *parent, ItemGroup *group ) {
        GtkCTreeNode *node = NULL;
@@ -1778,7 +2196,9 @@ static GtkCTreeNode *addressbook_find_group_node( GtkCTreeNode *parent, ItemGrou
        if( currRow ) {
                node = currRow->children;
                while( node ) {
-                       AddressObject *obj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), node );
+                       AddressObject *obj;
+
+                       obj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), node );
                        if( obj->type == ADDR_ITEM_GROUP ) {
                                ItemGroup *g = ADAPTER_GROUP(obj)->itemGroup;
                                if( g == group ) return node;
@@ -1822,7 +2242,7 @@ static void addressbook_move_nodes_up( GtkCTree *ctree, GtkCTreeNode *node ) {
                while( (child = currRow->children) ) {
                        gtk_ctree_move( ctree, child, parent, node );
                }
-               gtk_ctree_sort_node( ctree, parent );
+               gtk_sctree_sort_node( ctree, parent );
        }
 }
 
@@ -1850,10 +2270,9 @@ static void addressbook_edit_address_cb( gpointer data, guint action, GtkWidget
        if( abf == NULL ) return;
        if( obj->type == ADDR_ITEM_EMAIL ) {
                ItemEMail *email = ( ItemEMail * ) obj;
-               ItemPerson *person;
                if( email == NULL ) return;
                if( pobj && pobj->type == ADDR_ITEM_GROUP ) {
-                       /* Edit parent group  */
+                       /* Edit parent group */
                        AdapterGroup *adapter = ADAPTER_GROUP(pobj);
                        ItemGroup *itemGrp = adapter->itemGroup;
                        if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
@@ -1863,9 +2282,11 @@ static void addressbook_edit_address_cb( gpointer data, guint action, GtkWidget
                }
                else {
                        /* Edit person - email page */
+                       ItemPerson *person;
                        person = ( ItemPerson * ) ADDRITEM_PARENT(email);
                        if( addressbook_edit_person( abf, NULL, person, TRUE ) == NULL ) return;
                        gtk_ctree_select( ctree, addrbook.opened );
+                       invalidate_address_completion();
                        return;
                }
        }
@@ -1874,6 +2295,7 @@ static void addressbook_edit_address_cb( gpointer data, guint action, GtkWidget
                ItemPerson *person = ( ItemPerson * ) obj;
                if( addressbook_edit_person( abf, NULL, person, FALSE ) == NULL ) return;
                gtk_ctree_select( ctree, addrbook.opened);
+               invalidate_address_completion();
                return;
        }
        else if( obj->type == ADDR_ITEM_GROUP ) {
@@ -1890,7 +2312,7 @@ static void addressbook_edit_address_cb( gpointer data, guint action, GtkWidget
        /* Update tree node with node name */
        if( node == NULL ) return;
        addressbook_change_node_name( node, name );
-       gtk_ctree_sort_node( ctree, parentNode );
+       gtk_sctree_sort_node( ctree, parentNode );
        gtk_ctree_select( ctree, addrbook.opened );
 }
 
@@ -1979,14 +2401,13 @@ static void addressbook_folder_load_person( GtkCTree *clist, ItemFolder *itemFol
        if( atci == NULL ) return;
        if( atciMail == NULL ) return;
 
-       /* Load email addresses  */
+       /* Load email addresses */
        items = addritem_folder_get_person_list( itemFolder );
        for( ; items != NULL; items = g_list_next( items ) ) {
                GtkCTreeNode *nodePerson = NULL;
                GtkCTreeNode *nodeEMail = NULL;
                gchar *text[N_COLS];
                gboolean flgFirst = TRUE, haveAddr = FALSE;
-               /* gint row; */
                ItemPerson *person;
                GList *node;
 
@@ -2003,9 +2424,9 @@ static void addressbook_folder_load_person( GtkCTree *clist, ItemFolder *itemFol
                        text[COL_ADDRESS] = email->address;
                        text[COL_REMARKS] = email->remarks;
                        eMailAddr = ADDRITEM_NAME(email);
-                       if( *eMailAddr == '\0' ) eMailAddr = NULL;
+                       if( eMailAddr && *eMailAddr == '\0' ) eMailAddr = NULL;
                        if( flgFirst ) {
-                               /* First email belongs with person  */
+                               /* First email belongs with person */
                                gchar *str = addressbook_format_item_clist( person, email );
                                if( str ) {
                                        text[COL_NAME] = str;
@@ -2050,7 +2471,7 @@ static void addressbook_folder_load_person( GtkCTree *clist, ItemFolder *itemFol
                                        FALSE, person->isOpened );
                        gtk_ctree_node_set_row_data(clist, nodePerson, person );
                }
-               gtk_ctree_sort_node(GTK_CTREE(clist), NULL);
+               gtk_sctree_sort_node(GTK_CTREE(clist), NULL);
        }
        /* Free up the list */
        mgu_clear_list( items );
@@ -2078,7 +2499,7 @@ static void addressbook_folder_load_group( GtkCTree *clist, ItemFolder *itemFold
                                      atci->iconXpmOpen, atci->maskXpmOpen,
                                      FALSE, FALSE);
                gtk_ctree_node_set_row_data(clist, nodeGroup, group );
-               gtk_ctree_sort_node(clist, NULL);
+               gtk_sctree_sort_node(clist, NULL);
        }
        /* Free up the list */
        mgu_clear_list( items );
@@ -2086,48 +2507,97 @@ static void addressbook_folder_load_group( GtkCTree *clist, ItemFolder *itemFold
 }
 
 /*
- * Load data sources into list.
- */
-static void addressbook_node_load_datasource( GtkCTree *clist, AddressObject *obj ) {
-       AdapterInterface *adapter;
-       AddressInterface *iface;
-       AddressTypeControlItem *atci = NULL;
-       /* AddressDataSource *ds; */
-       GtkCTreeNode *newNode, *node;
-       GtkCTreeRow *row;
-       GtkCell *cell = NULL;
-       gchar *text[N_COLS];
+* Search ctree widget callback function.
+* Enter: pA Pointer to node.
+*        pB Pointer to data item being sought.
+* Return: Zero (0) if group found.
+*/
+static int addressbook_treenode_find_group_cb( gconstpointer pA, gconstpointer pB ) {
+       AddressObject *aoA;
 
-       adapter = ADAPTER_INTERFACE(obj);
-       if( adapter == NULL ) return;
-       iface = adapter->interface;
-       atci = adapter->atci;
-       if( atci == NULL ) return;
+       aoA = ( AddressObject * ) pA;
+       if( aoA->type == ADDR_ITEM_GROUP ) {
+               ItemGroup *group, *grp;
+
+               grp = ADAPTER_GROUP(aoA)->itemGroup;
+               group = ( ItemGroup * ) pB;
+               if( grp == group ) return 0;    /* Found group */
+       }
+       return 1;
+}
+
+/*
+* Search ctree widget callback function.
+* Enter: pA Pointer to node.
+*        pB Pointer to data item being sought.
+* Return: Zero (0) if folder found.
+*/
+static int addressbook_treenode_find_folder_cb( gconstpointer pA, gconstpointer pB ) {
+       AddressObject *aoA;
+
+       aoA = ( AddressObject * ) pA;
+       if( aoA->type == ADDR_ITEM_FOLDER ) {
+               ItemFolder *folder, *fld;
+
+               fld = ADAPTER_FOLDER(aoA)->itemFolder;
+               folder = ( ItemFolder * ) pB;
+               if( fld == folder ) return 0;   /* Found folder */
+       }
+       return 1;
+}
+
+/*
+* Remove folder and group nodes from tree widget for items contained ("cut")
+* in clipboard.
+*/
+static void addressbook_treenode_remove_item( void ) {
+       GList *node;
+       AddrSelectItem *cutItem;
+       AddressCache *cache;
+       AddrItemObject *aio;
+       GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
+       GtkCTreeNode *tn;
+
+       node = _clipBoard_->objectList;
+       while( node ) {
+               cutItem = node->data;
+               node = g_list_next( node );
+               cache = addrindex_get_cache(
+                       _clipBoard_->addressIndex, cutItem->cacheID );
+               if( cache == NULL ) continue;
+               aio = addrcache_get_object( cache, cutItem->uid );
+               if( aio ) {
+                       tn = NULL;
+                       if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
+                               ItemFolder *folder;
+
+                               folder = ( ItemFolder * ) aio;
+                               tn = gtk_ctree_find_by_row_data_custom(
+                                       ctree, NULL, folder,
+                                       addressbook_treenode_find_folder_cb );
+                       }
+                       else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
+                               ItemGroup *group;
 
-       /* Create nodes in list copying values for data sources in tree  */
-       row = GTK_CTREE_ROW( adapter->treeNode );
-       if( row ) {
-               node = row->children;
-               while( node ) {
-                       gpointer data = gtk_ctree_node_get_row_data( clist, node );
-                       row = GTK_CTREE_ROW( node );
-                       cell = ( ( GtkCListRow * )row )->cell;
-                       text[COL_NAME] = cell->u.text;
-                       text[COL_ADDRESS] = NULL;
-                       text[COL_REMARKS] = NULL;
-                       newNode = gtk_ctree_insert_node( clist, NULL, NULL,
-                                     text, FOLDER_SPACING,
-                                     atci->iconXpm, atci->maskXpm,
-                                     atci->iconXpmOpen, atci->maskXpmOpen,
-                                     FALSE, FALSE);
-                       gtk_ctree_node_set_row_data( clist, newNode, data );
-                       node = row->sibling;
+                               group = ( ItemGroup * ) aio;
+                               tn = gtk_ctree_find_by_row_data_custom(
+                                       ctree, NULL, group,
+                                       addressbook_treenode_find_group_cb );
+                       }
 
+                       if( tn ) {
+                               /* Free up adapter and remove node. */
+                               gtk_ctree_remove_node( ctree, tn );
+                       }
                }
        }
-       gtk_ctree_sort_node( clist, NULL );
 }
 
+/*
+* Find parent datasource for specified tree node.
+* Enter: node Node to test.
+* Return: Data source, or NULL if not found.
+*/
 static AddressDataSource *addressbook_find_datasource( GtkCTreeNode *node ) {
        AddressDataSource *ds = NULL;
        AddressObject *ao;
@@ -2138,10 +2608,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 );   */
+                       /* printf( "ao->type = %d\n", ao->type ); */
                        if( ao->type == ADDR_DATASOURCE ) {
                                AdapterDSource *ads = ADAPTER_DSOURCE(ao);
-/*                             printf( "found it\n" );           */
+                               /* printf( "found it\n" ); */
                                ds = ads->dataSource;
                                break;
                        }
@@ -2167,8 +2637,8 @@ static void addressbook_set_clist( AddressObject *obj ) {
        }
 
        if( obj->type == ADDR_INTERFACE ) {
-               /* printf( "set_clist: loading datasource...\n" );
-                addressbook_node_load_datasource( clist, obj ); */
+               /* printf( "set_clist: loading datasource...\n" ); */
+               /* addressbook_node_load_datasource( GTK_CTREE(clist), obj ); */
                return;
        }
 
@@ -2193,70 +2663,49 @@ static void addressbook_set_clist( AddressObject *obj ) {
                        addressbook_load_group( ctreelist, itemGroup );
                }
                else if( obj->type == ADDR_ITEM_FOLDER ) {
-                       /* Load folders  */
+                       /* Load folders */
                        ItemFolder *itemFolder = ADAPTER_FOLDER(obj)->itemFolder;
                        addressbook_folder_load_person( ctreelist, itemFolder );
                        addressbook_folder_load_group( ctreelist, itemFolder );
                }
        }
-
-       gtk_clist_sort(clist);
+       /* gtk_clist_sort(clist); */
        gtk_clist_thaw(clist);
 }
 
 /*
-* Free adaptor for specified node.
+* Call back function to free adaptor. Call back is setup by function
+* gtk_ctree_node_set_row_data_full() when node is populated. This function is
+* called when the address book tree widget node is removed by calling
+* function gtk_ctree_remove_node().
+* Enter: data Tree node's row data.
 */
-static void addressbook_free_adapter( GtkCTreeNode *node ) {
+static void addressbook_free_treenode( gpointer data ) {
        AddressObject *ao;
 
-       g_return_if_fail(addrbook.ctree != NULL);
-
-       if( node ) {
-               if( GTK_CTREE_ROW(node)->level < 2 ) return;
-               ao = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), node );
-               if( ao == NULL ) return;
-               if( ao->type == ADDR_INTERFACE ) {
-                       AdapterInterface *ai = ADAPTER_INTERFACE(ao);
-                       addrbookctl_free_interface( ai );
-               }
-               else if( ao->type == ADDR_DATASOURCE ) {
-                       AdapterDSource *ads = ADAPTER_DSOURCE(ao);
-                       addrbookctl_free_datasource( ads );
-               }
-               else if( ao->type == ADDR_ITEM_FOLDER ) {
-                       AdapterFolder *af = ADAPTER_FOLDER(ao);
-                       addrbookctl_free_folder( af );
-               }
-               else if( ao->type == ADDR_ITEM_GROUP ) {
-                       AdapterGroup *ag = ADAPTER_GROUP(ao);
-                       addrbookctl_free_group( ag );
-               }
-               gtk_ctree_node_set_row_data( GTK_CTREE(addrbook.ctree), node, NULL );
+       ao = ( AddressObject * ) data;
+       if( ao == NULL ) return;
+       if( ao->type == ADDR_INTERFACE ) {
+               AdapterInterface *ai = ADAPTER_INTERFACE(ao);
+               addrbookctl_free_interface( ai );
+       }
+       else if( ao->type == ADDR_DATASOURCE ) {
+               AdapterDSource *ads = ADAPTER_DSOURCE(ao);
+               addrbookctl_free_datasource( ads );
+       }
+       else if( ao->type == ADDR_ITEM_FOLDER ) {
+               AdapterFolder *af = ADAPTER_FOLDER(ao);
+               addrbookctl_free_folder( af );
+       }
+       else if( ao->type == ADDR_ITEM_GROUP ) {
+               AdapterGroup *ag = ADAPTER_GROUP(ao);
+               addrbookctl_free_group( ag );
        }
 }
 
 /*
-* Free all children adapters.
+* Create new adaptor for specified data source.
 */
-static void addressbook_free_child_adapters( GtkCTreeNode *node ) {
-       GtkCTreeNode *parent, *child;
-       GtkCTreeRow *currRow;
-
-       if( node == NULL ) return;
-       currRow = GTK_CTREE_ROW( node );
-       if( currRow ) {
-               parent = currRow->parent;
-               child = currRow->children;
-               while( child ) {
-                       addressbook_free_child_adapters( child );
-                       addressbook_free_adapter( child );
-                       currRow = GTK_CTREE_ROW( child );
-                       child = currRow->sibling;
-               }
-       }
-}
-
 AdapterDSource *addressbook_create_ds_adapter( AddressDataSource *ds,
                                AddressObjectType otype, gchar *name )
 {
@@ -2294,7 +2743,7 @@ static void addressbook_load_tree( void ) {
                atci = adapter->atci;
                if( iface ) {
                        if( iface->useInterface ) {
-                               /* Load data sources below interface node  */
+                               /* Load data sources below interface node */
                                nodeDS = iface->listSource;
                                while( nodeDS ) {
                                        ds = nodeDS->data;
@@ -2346,7 +2795,7 @@ static gboolean addressbook_convert( AddressIndex *addrIndex ) {
        else {
                /* We have an old file */
                if( addrIndex->wasConverted ) {
-                       /* Converted successfully - save address index  */
+                       /* Converted successfully - save address index */
                        addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
                        addrindex_save_data( addrIndex );
                        if( addrIndex->retVal == MGU_SUCCESS ) {
@@ -2432,119 +2881,6 @@ void addressbook_read_file( void ) {
        debug_print( "done.\n" );
 }
 
-#if 0
-void addressbook_read_file_old( void ) {
-       AddressIndex *addrIndex = NULL;
-       gboolean errFlag = TRUE;
-       gchar *msg = NULL;
-
-       if( _addressIndex_ ) {
-               debug_print( "address book already read!!!\n" );
-               return;
-       }
-
-       addrIndex = addrindex_create_index();
-
-       /* Use use new address book. */
-       /* addrindex_set_file_path( addrIndex, "/home/match/tmp/empty-dir" );  */
-       addrindex_set_file_path( addrIndex, get_rc_dir() );
-       addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
-
-       debug_print( "Reading address index...\n" );
-       addrindex_read_data( addrIndex );
-       if( addrIndex->retVal == MGU_NO_FILE ) {
-               /* Read old address book, performing conversion */
-               debug_print( "Reading and converting old address book...\n" );
-               addrindex_set_file_name( addrIndex, ADDRESSBOOK_OLD_FILE );
-               addrindex_read_data( addrIndex );
-               if( addrIndex->retVal == MGU_NO_FILE ) {
-                       /* We do not have a file - new user */
-                       debug_print( "New user... create new books...\n" );
-                       addrindex_create_new_books( addrIndex );
-                       if( addrIndex->retVal == MGU_SUCCESS ) {
-                               /* Save index file */
-                               addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
-                               addrindex_save_data( addrIndex );
-                               if( addrIndex->retVal == MGU_SUCCESS ) {
-                                       errFlag = FALSE;
-                               }
-                               else {
-                                       msg = g_strdup( _( "New user, could not save index file." ) );
-                               }
-                       }
-                       else {
-                               msg = g_strdup( _( "New user, could not save address book files." ) );
-                       }
-               }
-               else {
-                       /* We have an old file */
-                       if( addrIndex->wasConverted ) {
-                               /* Converted successfully - save address index  */
-                               addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
-                               addrindex_save_data( addrIndex );
-                               if( addrIndex->retVal == MGU_SUCCESS ) {
-                                       msg = g_strdup( _( "Old address book converted successfully." ) );
-                                       errFlag = FALSE;
-                               }
-                               else {
-                                       msg = g_strdup( _(
-                                               "Old address book converted, " \
-                                               "could not save new address index file" ) );
-                               }
-                       }
-                       else {
-                               /* File conversion failed - just create new books */
-                               debug_print( "File conversion failed... just create new books...\n" );
-                               addrindex_create_new_books( addrIndex );
-                               if( addrIndex->retVal == MGU_SUCCESS ) {
-                                       /* Save index */
-                                       addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
-                                       addrindex_save_data( addrIndex );
-                                       if( addrIndex->retVal == MGU_SUCCESS ) {
-                                               msg = g_strdup( _(
-                                                       "Could not convert address book, " \
-                                                       "but created empty new address book files." ) );
-                                               errFlag = FALSE;
-                                       }
-                                       else {
-                                               msg = g_strdup( _(
-                                                       "Could not convert address book, " \
-                                                       "could not create new address book files." ) );
-                                       }
-                               }
-                               else {
-                                       msg = g_strdup( _(
-                                               "Could not convert address book " \
-                                               "and could not create new address book files." ) );
-                               }
-                       }
-               }
-       }
-       else if( addrIndex->retVal == MGU_SUCCESS ) {
-               errFlag = FALSE;
-       }
-       else {
-               debug_print( "Could not read address index.\n" );
-               addrindex_print_index( addrIndex, stdout );
-               msg = g_strdup( _( "Could not read address index" ) );
-       }
-       _addressIndex_ = addrIndex;
-
-       if( errFlag ) {
-               debug_print( "Error\n%s\n", msg );
-               alertpanel( _( "Sylpheed Addressbook Conversion Error" ), msg, _( "Close" ), NULL, NULL );
-       }
-       else {
-               if( msg ) {
-                       debug_print( "Warning\n%s\n", msg );
-                       alertpanel( _( "Sylpheed Addressbook Conversion" ), msg, _( "Close" ), NULL, NULL );
-               }
-       }
-       if( msg ) g_free( msg );
-       debug_print( "done.\n" );
-}
-#endif
-
 /*
 * Add object into the address index tree widget.
 * Enter: node  Parent node.
@@ -2566,7 +2902,7 @@ static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
        pobj = gtk_ctree_node_get_row_data(ctree, node);
        g_return_val_if_fail(pobj != NULL, NULL);
 
-       /* Determine object type to be displayed  */
+       /* Determine object type to be displayed */
        if( obj->type == ADDR_DATASOURCE ) {
                otype = ADAPTER_DSOURCE(obj)->subType;
        }
@@ -2585,11 +2921,12 @@ static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
                        added = gtk_ctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
                                atci->iconXpm, atci->maskXpm, atci->iconXpmOpen, atci->maskXpmOpen,
                                atci->treeLeaf, atci->treeExpand );
-                       gtk_ctree_node_set_row_data(ctree, added, obj);
+                       gtk_ctree_node_set_row_data_full( ctree, added, obj,
+                               addressbook_free_treenode );
                }
        }
 
-       gtk_ctree_sort_node(ctree, node);
+       gtk_sctree_sort_node(ctree, node);
 
        return added;
 }
@@ -2623,8 +2960,9 @@ static GtkCTreeNode *addressbook_node_add_group( GtkCTreeNode *node, AddressData
        newNode = gtk_ctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
                        atci->iconXpm, atci->maskXpm, atci->iconXpm, atci->maskXpm,
                        atci->treeLeaf, atci->treeExpand );
-       gtk_ctree_node_set_row_data( ctree, newNode, adapter );
-       gtk_ctree_sort_node( ctree, node );
+       gtk_ctree_node_set_row_data_full( ctree, newNode, adapter,
+               addressbook_free_treenode );
+       gtk_sctree_sort_node( ctree, node );
        return newNode;
 }
 
@@ -2669,7 +3007,8 @@ static GtkCTreeNode *addressbook_node_add_folder(
                newNode = gtk_ctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
                                atci->iconXpm, atci->maskXpm, atci->iconXpm, atci->maskXpm,
                                atci->treeLeaf, atci->treeExpand );
-               gtk_ctree_node_set_row_data( ctree, newNode, adapter );
+               gtk_ctree_node_set_row_data_full( ctree, newNode, adapter,
+                       addressbook_free_treenode );
        }
 
        listItems = itemFolder->listFolder;
@@ -2684,31 +3023,10 @@ static GtkCTreeNode *addressbook_node_add_folder(
                addressbook_node_add_group( newNode, ds, item );
                listItems = g_list_next( listItems );
        }
-       gtk_ctree_sort_node( ctree, node );
+       gtk_sctree_sort_node( ctree, node );
        return newNode;
 }
 
-static void addressbook_delete_object(AddressObject *obj) {
-       AdapterDSource *ads = NULL;
-       AddressDataSource *ds = NULL;
-       if (!obj) return;
-
-       /* Remove data source. */
-       printf( "Delete obj type : %d\n", obj->type );
-
-       ads = ADAPTER_DSOURCE(obj);
-       if( ads == NULL ) return;
-       ds = ads->dataSource;
-       if( ds == NULL ) return;
-
-       /* Remove data source */
-       if( addrindex_index_remove_datasource( _addressIndex_, ds ) ) {
-               addrindex_free_datasource( _addressIndex_, ds );
-       }
-       /* Free up Adapter object */
-       g_free( ADAPTER_DSOURCE(obj) );
-}
-
 void addressbook_export_to_file( void ) {
        if( _addressIndex_ ) {
                /* Save all new address book data */
@@ -2721,7 +3039,7 @@ void addressbook_export_to_file( void ) {
                        addrindex_print_index( _addressIndex_, stdout );
                }
 
-               /* Notify address completion of new data  */
+               /* Notify address completion of new data */
                invalidate_address_completion();
        }
 }
@@ -2733,28 +3051,12 @@ static void key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
 }
 
 /*
-* Comparsion using names of AddressItem objects.
+* Comparison using cell contents (text in first column). Used for sort
+* address index widget.
 */
-/*
-static gint addressbook_list_compare_func(GtkCList *clist,
-                                         gconstpointer ptr1,
-                                         gconstpointer ptr2)
+static gint addressbook_treenode_compare_func(
+       GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
 {
-       AddressObject *obj1 = ((GtkCListRow *)ptr1)->data;
-       AddressObject *obj2 = ((GtkCListRow *)ptr2)->data;
-       gchar *name1 = NULL, *name2 = NULL;
-       if( obj1 ) name1 = obj1->name;
-       if( obj2 ) name2 = obj2->name;
-       if( ! name1 ) return ( name2 != NULL );
-       if( ! name2 ) return -1;
-       return strcasecmp(name1, name2);
-}
-*/
-
-/*
-* Comparison using cell contents (text in first column).
-*/
-static gint addressbook_list_compare_func( GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 ) {
        GtkCell *cell1 = ((GtkCListRow *)ptr1)->cell;
        GtkCell *cell2 = ((GtkCListRow *)ptr2)->cell;
        gchar *name1 = NULL, *name2 = NULL;
@@ -2765,47 +3067,51 @@ static gint addressbook_list_compare_func( GtkCList *clist, gconstpointer ptr1,
        return strcasecmp( name1, name2 );
 }
 
-/* static */ 
-gint addressbook_obj_name_compare(gconstpointer a, gconstpointer b)
+/*
+* Comparison using object names and types.
+*/
+static gint addressbook_list_compare_func(
+       GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
 {
-       const AddressObject *obj = a;
-       const gchar *name = b;
-       AddressTypeControlItem *atci = NULL;
-
-       if (!obj || !name) return -1;
+       AddrItemObject *aio1 = ((GtkCListRow *)ptr1)->data;
+       AddrItemObject *aio2 = ((GtkCListRow *)ptr2)->data;
+       gchar *name1 = NULL, *name2 = NULL;
 
-       atci = addrbookctl_lookup( obj->type );
-       if( ! atci ) return -1;
-       if( ! obj->name ) return -1;
-       return strcasecmp(obj->name, name);
-}
+       /* Order by cell contents */
+       name1 = ADDRITEM_NAME( aio1 );
+       name2 = ADDRITEM_NAME( aio2 );
 
-static void addressbook_book_show_message( AddressBookFile *abf ) {
-       *addressbook_msgbuf = '\0';
-       if( abf ) {
-               if( abf->retVal == MGU_SUCCESS ) {
-                       sprintf( addressbook_msgbuf, "%s", abf->name );
+       if( aio1->type == aio2->type ) {
+               /* Order by name */
+               if( ! name1 ) return ( name2 != NULL );
+               if( ! name2 ) return -1;
+               return strcasecmp( name1, name2 );
+       }
+       else {
+               /* Order groups before person */
+               if( aio1->type == ITEMTYPE_GROUP ) {
+                       return -1;
                }
-               else {
-                       sprintf( addressbook_msgbuf, "%s: %s", abf->name, mgu_error2string( abf->retVal ) );
+               else if( aio2->type == ITEMTYPE_GROUP ) {
+                       return 1;
                }
+               return 0;
        }
-       addressbook_status_show( addressbook_msgbuf );
 }
 
 static void addressbook_new_book_cb( gpointer data, guint action, GtkWidget *widget ) {
        AdapterDSource *ads;
        AdapterInterface *adapter;
+       GtkCTreeNode *newNode;
 
        adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
        if( adapter == NULL ) return;
-       if( addrbook.treeSelected == NULL ) return;
-       if( addrbook.treeSelected != adapter->treeNode ) return;
        ads = addressbook_edit_book( _addressIndex_, NULL );
        if( ads ) {
-               addressbook_add_object( addrbook.treeSelected, ADDRESS_OBJECT(ads) );
-               if( addrbook.treeSelected == addrbook.opened ) {
-                       gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
+               newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
+               if( newNode ) {
+                       gtk_ctree_select( GTK_CTREE(addrbook.ctree), newNode );
+                       addrbook.treeSelected = newNode;
                }
        }
 }
@@ -2813,30 +3119,18 @@ static void addressbook_new_book_cb( gpointer data, guint action, GtkWidget *wid
 static void addressbook_new_vcard_cb( gpointer data, guint action, GtkWidget *widget ) {
        AdapterDSource *ads;
        AdapterInterface *adapter;
+       GtkCTreeNode *newNode;
 
        adapter = addrbookctl_find_interface( ADDR_IF_VCARD );
        if( adapter == NULL ) return;
-       if( addrbook.treeSelected != adapter->treeNode ) return;
        ads = addressbook_edit_vcard( _addressIndex_, NULL );
        if( ads ) {
-               addressbook_add_object( addrbook.treeSelected, ADDRESS_OBJECT(ads) );
-               if( addrbook.treeSelected == addrbook.opened ) {
-                       gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
-               }
-       }
-}
-
-static void addressbook_vcard_show_message( VCardFile *vcf ) {
-       *addressbook_msgbuf = '\0';
-       if( vcf ) {
-               if( vcf->retVal == MGU_SUCCESS ) {
-                       sprintf( addressbook_msgbuf, "%s", vcf->name );
-               }
-               else {
-                       sprintf( addressbook_msgbuf, "%s: %s", vcf->name, mgu_error2string( vcf->retVal ) );
+               newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
+               if( newNode ) {
+                       gtk_ctree_select( GTK_CTREE(addrbook.ctree), newNode );
+                       addrbook.treeSelected = newNode;
                }
        }
-       addressbook_status_show( addressbook_msgbuf );
 }
 
 #ifdef USE_JPILOT
@@ -2844,34 +3138,21 @@ static void addressbook_new_jpilot_cb( gpointer data, guint action, GtkWidget *w
        AdapterDSource *ads;
        AdapterInterface *adapter;
        AddressInterface *iface;
+       GtkCTreeNode *newNode;
 
        adapter = addrbookctl_find_interface( ADDR_IF_JPILOT );
        if( adapter == NULL ) return;
-       if( addrbook.treeSelected != adapter->treeNode ) return;
        iface = adapter->interface;
        if( ! iface->haveLibrary ) return;
        ads = addressbook_edit_jpilot( _addressIndex_, NULL );
        if( ads ) {
-               addressbook_add_object( addrbook.treeSelected, ADDRESS_OBJECT(ads) );
-               if( addrbook.treeSelected == addrbook.opened ) {
-                       gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
-               }
-       }
-}
-
-static void addressbook_jpilot_show_message( JPilotFile *jpf ) {
-       *addressbook_msgbuf = '\0';
-       if( jpf ) {
-               if( jpf->retVal == MGU_SUCCESS ) {
-                       sprintf( addressbook_msgbuf, "%s", jpf->name );
-               }
-               else {
-                       sprintf( addressbook_msgbuf, "%s: %s", jpf->name, mgu_error2string( jpf->retVal ) );
+               newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
+               if( newNode ) {
+                       gtk_ctree_select( GTK_CTREE(addrbook.ctree), newNode );
+                       addrbook.treeSelected = newNode;
                }
        }
-       addressbook_status_show( addressbook_msgbuf );
 }
-
 #endif
 
 #ifdef USE_LDAP
@@ -2879,33 +3160,36 @@ static void addressbook_new_ldap_cb( gpointer data, guint action, GtkWidget *wid
        AdapterDSource *ads;
        AdapterInterface *adapter;
        AddressInterface *iface;
+       GtkCTreeNode *newNode;
 
        adapter = addrbookctl_find_interface( ADDR_IF_LDAP );
        if( adapter == NULL ) return;
-       if( addrbook.treeSelected != adapter->treeNode ) return;
        iface = adapter->interface;
        if( ! iface->haveLibrary ) return;
        ads = addressbook_edit_ldap( _addressIndex_, NULL );
        if( ads ) {
-               addressbook_add_object( addrbook.treeSelected, ADDRESS_OBJECT(ads) );
-               if( addrbook.treeSelected == addrbook.opened ) {
-                       gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
+               newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
+               if( newNode ) {
+                       gtk_ctree_select( GTK_CTREE(addrbook.ctree), newNode );
+                       addrbook.treeSelected = newNode;
                }
        }
 }
 
 static void addressbook_ldap_show_message( SyldapServer *svr ) {
+       gchar *name;
        *addressbook_msgbuf = '\0';
        if( svr ) {
+               name = syldap_get_name( svr );
                if( svr->busyFlag ) {
-                       sprintf( addressbook_msgbuf, "%s: %s", svr->name, ADDRESSBOOK_LDAP_BUSYMSG );
+                       sprintf( addressbook_msgbuf, "%s: %s", name, ADDRESSBOOK_LDAP_BUSYMSG );
                }
                else {
                        if( svr->retVal == MGU_SUCCESS ) {
-                               sprintf( addressbook_msgbuf, "%s", svr->name );
+                               sprintf( addressbook_msgbuf, "%s", name );
                        }
                        else {
-                               sprintf( addressbook_msgbuf, "%s: %s", svr->name, mgu_error2string( svr->retVal ) );
+                               sprintf( addressbook_msgbuf, "%s: %s", name, mgu_error2string( svr->retVal ) );
                        }
                }
        }
@@ -3000,6 +3284,25 @@ static void addressbook_lup_clicked( GtkButton *button, gpointer data ) {
 * ***********************************************************************
 */
 
+/*
+ * Remap object types.
+ * Enter:  abType AddressObjectType (used in tree node).
+ * Return: ItemObjectType (used in address cache data).
+ */
+ItemObjectType addressbook_type2item( AddressObjectType abType ) {
+       ItemObjectType ioType;
+
+       ioType = ITEMTYPE_NONE;
+       switch( abType ) {
+               case ADDR_ITEM_PERSON: ioType = ITEMTYPE_PERSON;     break;
+               case ADDR_ITEM_EMAIL:  ioType = ITEMTYPE_EMAIL;      break;
+               case ADDR_ITEM_FOLDER: ioType = ITEMTYPE_FOLDER;     break;
+               case ADDR_ITEM_GROUP:  ioType = ITEMTYPE_GROUP;      break;
+               case ADDR_DATASOURCE:  ioType = ITEMTYPE_DATASOURCE; break;
+       }
+       return ioType;
+}
+
 /*
 * Build table that controls the rendering of object types.
 */
@@ -3007,15 +3310,15 @@ void addrbookctl_build_map( GtkWidget *window ) {
        AddressTypeControlItem *atci;
 
        /* Build icons */
-       PIXMAP_CREATE(window, folderxpm, folderxpmmask, DIRECTORY_CLOSE_XPM);
-       PIXMAP_CREATE(window, folderopenxpm, folderopenxpmmask, DIRECTORY_OPEN_XPM);
-       PIXMAP_CREATE(window, groupxpm, groupxpmmask, group_xpm);
-       PIXMAP_CREATE(window, vcardxpm, vcardxpmmask, vcard_xpm);
-       PIXMAP_CREATE(window, bookxpm, bookxpmmask, book_xpm);
-       PIXMAP_CREATE(window, addressxpm, addressxpmmask, address_xpm);
-       PIXMAP_CREATE(window, jpilotxpm, jpilotxpmmask, jpilot_xpm);
-       PIXMAP_CREATE(window, categoryxpm, categoryxpmmask, category_xpm);
-       PIXMAP_CREATE(window, ldapxpm, ldapxpmmask, ldap_xpm);
+       stock_pixmap_gdk(window, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
+       stock_pixmap_gdk(window, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
+       stock_pixmap_gdk(window, STOCK_PIXMAP_GROUP, &groupxpm, &groupxpmmask);
+       stock_pixmap_gdk(window, STOCK_PIXMAP_VCARD, &vcardxpm, &vcardxpmmask);
+       stock_pixmap_gdk(window, STOCK_PIXMAP_BOOK, &bookxpm, &bookxpmmask);
+       stock_pixmap_gdk(window, STOCK_PIXMAP_ADDRESS, &addressxpm, &addressxpmmask);
+       stock_pixmap_gdk(window, STOCK_PIXMAP_JPILOT, &jpilotxpm, &jpilotxpmmask);
+       stock_pixmap_gdk(window, STOCK_PIXMAP_CATEGORY, &categoryxpm, &categoryxpmmask);
+       stock_pixmap_gdk(window, STOCK_PIXMAP_LDAP, &ldapxpm, &ldapxpmmask);
 
        _addressBookTypeHash_ = g_hash_table_new( g_int_hash, g_int_equal );
        _addressBookTypeList_ = NULL;
@@ -3036,7 +3339,7 @@ void addrbookctl_build_map( GtkWidget *window ) {
        g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
        _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
 
-       /* Address book  */
+       /* Address book */
        atci = g_new0( AddressTypeControlItem, 1 );
        atci->objectType = ADDR_BOOK;
        atci->interfaceType = ADDR_IF_BOOK;
@@ -3116,19 +3419,19 @@ void addrbookctl_build_map( GtkWidget *window ) {
        g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
        _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
 
-       /* V-Card */
+       /* vCard */
        atci = g_new0( AddressTypeControlItem, 1 );
        atci->objectType = ADDR_VCARD;
        atci->interfaceType = ADDR_IF_VCARD;
        atci->showInTree = TRUE;
        atci->treeExpand = TRUE;
        atci->treeLeaf = TRUE;
-       atci->displayName = _( "V-Card" );
+       atci->displayName = _( "vCard" );
        atci->iconXpm = vcardxpm;
        atci->maskXpm = vcardxpmmask;
        atci->iconXpmOpen = vcardxpm;
        atci->maskXpmOpen = vcardxpmmask;
-       atci->menuCommand = "/File/New V-Card";
+       atci->menuCommand = "/File/New vCard";
        g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
        _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
 
@@ -3139,12 +3442,12 @@ void addrbookctl_build_map( GtkWidget *window ) {
        atci->showInTree = TRUE;
        atci->treeExpand = TRUE;
        atci->treeLeaf = FALSE;
-       atci->displayName = _( "J-Pilot" );
+       atci->displayName = _( "JPilot" );
        atci->iconXpm = jpilotxpm;
        atci->maskXpm = jpilotxpmmask;
        atci->iconXpmOpen = jpilotxpm;
        atci->maskXpmOpen = jpilotxpmmask;
-       atci->menuCommand = "/File/New J-Pilot";
+       atci->menuCommand = "/File/New JPilot";
        g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
        _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
 
@@ -3155,7 +3458,7 @@ void addrbookctl_build_map( GtkWidget *window ) {
        atci->showInTree = TRUE;
        atci->treeExpand = TRUE;
        atci->treeLeaf = TRUE;
-       atci->displayName = _( "J-Pilot" );
+       atci->displayName = _( "JPilot" );
        atci->iconXpm = categoryxpm;
        atci->maskXpm = categoryxpmmask;
        atci->iconXpmOpen = categoryxpm;
@@ -3249,6 +3552,10 @@ void addrbookctl_build_iflist() {
 
        if( _addressIndex_ == NULL ) {
                _addressIndex_ = addrindex_create_index();
+               if( _clipBoard_ == NULL ) {
+                       _clipBoard_ = addrclip_create();
+               }
+               addrclip_set_index( _clipBoard_, _addressIndex_ );
        }
        _addressInterfaceList_ = NULL;
        list = addrindex_get_interface_list( _addressIndex_ );
@@ -3374,8 +3681,7 @@ gboolean addressbook_add_contact( const gchar *name, const gchar *address, const
 *                     to be loaded.
 * Return: TRUE if data loaded, FALSE if address index not loaded.
 */
-gboolean addressbook_load_completion( gint (*callBackFunc) ( const gchar *, const gchar * ) ) {
-       /* AddressInterface *interface; */
+gboolean addressbook_load_completion( gint (*callBackFunc) ( const gchar *, const gchar *, const gchar * ) ) {
        AddressDataSource *ds;
        GList *nodeIf, *nodeDS;
        GList *listP, *nodeP;
@@ -3394,6 +3700,10 @@ gboolean addressbook_load_completion( gint (*callBackFunc) ( const gchar *, cons
                        ds = nodeDS->data;
 
                        /* Read address book */
+                       if( addrindex_ds_get_modify_flag( ds ) ) {
+                               addrindex_ds_read_data( ds );
+                       }
+
                        if( ! addrindex_ds_get_read_flag( ds ) ) {
                                addrindex_ds_read_data( ds );
                        }
@@ -3422,7 +3732,7 @@ gboolean addressbook_load_completion( gint (*callBackFunc) ( const gchar *, cons
                                                if( sAlias && *sAlias != '\0' ) {
                                                        sFriendly = sAlias;
                                                }
-                                               ( callBackFunc ) ( sFriendly, sAddress );
+                                               ( callBackFunc ) ( sFriendly, sAddress, sName );
                                        }
 
                                        nodeM = g_list_next( nodeM );
@@ -3441,6 +3751,186 @@ gboolean addressbook_load_completion( gint (*callBackFunc) ( const gchar *, cons
        return TRUE;
 }
 
+/* **********************************************************************
+* Address Import.
+* ***********************************************************************
+*/
+
+/*
+* Import LDIF file.
+*/
+static void addressbook_import_ldif_cb() {
+       AddressDataSource *ds = NULL;
+       AdapterDSource *ads = NULL;
+       AddressBookFile *abf = NULL;
+       AdapterInterface *adapter;
+       GtkCTreeNode *newNode;
+
+       adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
+       if( adapter ) {
+               if( adapter->treeNode ) {
+                       abf = addressbook_imp_ldif( _addressIndex_ );
+                       if( abf ) {
+                               ds = addrindex_index_add_datasource(
+                                       _addressIndex_, ADDR_IF_BOOK, abf );
+                               ads = addressbook_create_ds_adapter(
+                                       ds, ADDR_BOOK, NULL );
+                               addressbook_ads_set_name(
+                                       ads, addrbook_get_name( abf ) );
+                               newNode = addressbook_add_object(
+                                       adapter->treeNode,
+                                       ADDRESS_OBJECT(ads) );
+                               if( newNode ) {
+                                       gtk_ctree_select(
+                                               GTK_CTREE(addrbook.ctree),
+                                               newNode );
+                                       addrbook.treeSelected = newNode;
+                               }
+
+                               /* Notify address completion */
+                               invalidate_address_completion();
+                       }
+               }
+       }
+}
+
+/*
+* Import MUTT file.
+*/
+static void addressbook_import_mutt_cb() {
+       AddressDataSource *ds = NULL;
+       AdapterDSource *ads = NULL;
+       AddressBookFile *abf = NULL;
+       AdapterInterface *adapter;
+       GtkCTreeNode *newNode;
+
+       adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
+       if( adapter ) {
+               if( adapter->treeNode ) {
+                       abf = addressbook_imp_mutt( _addressIndex_ );
+                       if( abf ) {
+                               ds = addrindex_index_add_datasource(
+                                       _addressIndex_, ADDR_IF_BOOK, abf );
+                               ads = addressbook_create_ds_adapter(
+                                       ds, ADDR_BOOK, NULL );
+                               addressbook_ads_set_name(
+                                       ads, addrbook_get_name( abf ) );
+                               newNode = addressbook_add_object(
+                                       adapter->treeNode,
+                                       ADDRESS_OBJECT(ads) );
+                               if( newNode ) {
+                                       gtk_ctree_select(
+                                               GTK_CTREE(addrbook.ctree),
+                                               newNode );
+                                       addrbook.treeSelected = newNode;
+                               }
+
+                               /* Notify address completion */
+                               invalidate_address_completion();
+                       }
+               }
+       }
+}
+
+/*
+* Import Pine file.
+*/
+static void addressbook_import_pine_cb() {
+       AddressDataSource *ds = NULL;
+       AdapterDSource *ads = NULL;
+       AddressBookFile *abf = NULL;
+       AdapterInterface *adapter;
+       GtkCTreeNode *newNode;
+
+       adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
+       if( adapter ) {
+               if( adapter->treeNode ) {
+                       abf = addressbook_imp_pine( _addressIndex_ );
+                       if( abf ) {
+                               ds = addrindex_index_add_datasource(
+                                       _addressIndex_, ADDR_IF_BOOK, abf );
+                               ads = addressbook_create_ds_adapter(
+                                       ds, ADDR_BOOK, NULL );
+                               addressbook_ads_set_name(
+                                       ads, addrbook_get_name( abf ) );
+                               newNode = addressbook_add_object(
+                                       adapter->treeNode,
+                                       ADDRESS_OBJECT(ads) );
+                               if( newNode ) {
+                                       gtk_ctree_select(
+                                               GTK_CTREE(addrbook.ctree),
+                                               newNode );
+                                       addrbook.treeSelected = newNode;
+                               }
+
+                               /* Notify address completion */
+                               invalidate_address_completion();
+                       }
+               }
+       }
+}
+
+/*
+ * Harvest addresses.
+ * Enter: folderItem Folder to import.
+ *        sourceInd  Source indicator: FALSE - Folder, TRUE - Messages.
+ *        msgList    List of message numbers, or NULL to process folder.
+ */
+void addressbook_harvest(
+       FolderItem *folderItem, gboolean sourceInd, GList *msgList )
+{
+       AddressDataSource *ds = NULL;
+       AdapterDSource *ads = NULL;
+       AddressBookFile *abf = NULL;
+       AdapterInterface *adapter;
+       GtkCTreeNode *newNode;
+
+       abf = addrgather_dlg_execute(
+               folderItem, _addressIndex_, sourceInd, msgList );
+       if( abf ) {
+               ds = addrindex_index_add_datasource(
+                       _addressIndex_, ADDR_IF_BOOK, abf );
+
+               adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
+               if( adapter ) {
+                       if( adapter->treeNode ) {
+                               ads = addressbook_create_ds_adapter(
+                                       ds, ADDR_BOOK, addrbook_get_name( abf ) );
+                               newNode = addressbook_add_object(
+                                               adapter->treeNode,
+                                               ADDRESS_OBJECT(ads) );
+                       }
+               }
+
+               /* Notify address completion */
+               invalidate_address_completion();
+       }
+}
+
+/*
+* Export HTML file.
+*/
+static void addressbook_export_html_cb( void ) {
+       GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
+       AddressObject *obj;
+       AddressDataSource *ds = NULL;
+       AddrBookBase *adbase;
+       AddressCache *cache;
+       GtkCTreeNode *node = NULL;
+
+       if( ! addrbook.treeSelected ) return;
+       node = addrbook.treeSelected;
+       if( GTK_CTREE_ROW(node)->level == 1 ) return;
+       obj = gtk_ctree_node_get_row_data( ctree, node );
+       if( obj == NULL ) return;
+
+       ds = addressbook_find_datasource( node );
+       if( ds == NULL ) return;
+       adbase = ( AddrBookBase * ) ds->rawDataSource;
+       cache = adbase->addressCache;
+       addressbook_exp_html( cache );
+}
+
 /*
 * End of Source.
 */