talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

addressbook.c (159398B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2024 the Claws Mail team and Hiroyuki Yamamoto
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "defs.h"
     20 
     21 #include <glib.h>
     22 #include <gdk/gdkkeysyms.h>
     23 #include <gtk/gtk.h>
     24 #include <string.h>
     25 #include <sys/stat.h>
     26 #include <sys/types.h>
     27 
     28 #include "main.h"
     29 #include "addressbook.h"
     30 #include "manage_window.h"
     31 #include "prefs_common.h"
     32 #include "alertpanel.h"
     33 #include "inputdialog.h"
     34 #include "menu.h"
     35 #include "stock_pixmap.h"
     36 #include "xml.h"
     37 #include "prefs_gtk.h"
     38 #include "procmime.h"
     39 #include "file-utils.h"
     40 #include "utils.h"
     41 #include "gtkutils.h"
     42 #include "codeconv.h"
     43 #include "addr_compl.h"
     44 
     45 #include "mgutils.h"
     46 #include "addressitem.h"
     47 #include "addritem.h"
     48 #include "addrcache.h"
     49 #include "addrbook.h"
     50 #include "addrindex.h"
     51 #include "addrmerge.h"
     52 #include "addressadd.h"
     53 #include "addrduplicates.h"
     54 #include "addressbook_foldersel.h"
     55 #include "editgroup.h"
     56 #include "editaddress.h"
     57 #include "editbook.h"
     58 #include "manual.h"
     59 
     60 #include "addrquery.h"
     61 #include "addrselect.h"
     62 #include "addrclip.h"
     63 #include "adbookbase.h"
     64 
     65 typedef enum
     66 {
     67 	COL_SOURCES	= 0,
     68 	N_INDEX_COLS	= 1
     69 } AddressIndexColumns;
     70 
     71 typedef enum
     72 {
     73 	COL_NAME	= 0,
     74 	COL_ADDRESS	= 1,
     75 	COL_REMARKS	= 2,
     76 	N_LIST_COLS	= 3
     77 } AddressListColumns;
     78 
     79 typedef struct {
     80 	AddressBookFile	*book;
     81 	ItemFolder	*folder;
     82 } FolderInfo;
     83 
     84 typedef struct {
     85 	gchar **folder_path;
     86 	gboolean matched;
     87 	gint index;
     88 	AddressDataSource *book;
     89 	ItemFolder *folder;
     90 } FolderPathMatch;
     91 
     92 static gchar *list_titles[] = { "Name",
     93                                 "Email Address",
     94                                 "Remarks" };
     95 
     96 #define COL_NAME_WIDTH		164
     97 #define COL_ADDRESS_WIDTH	156
     98 
     99 #define COL_FOLDER_WIDTH	170
    100 #define ADDRESSBOOK_WIDTH	640
    101 #define ADDRESSBOOK_HEIGHT	360
    102 
    103 #define ADDRESSBOOK_MSGBUF_SIZE 2048
    104 
    105 static GdkPixbuf *folderxpm = NULL;
    106 static GdkPixbuf *folderopenxpm = NULL;
    107 static GdkPixbuf *groupxpm = NULL;
    108 static GdkPixbuf *interfacexpm = NULL;
    109 static GdkPixbuf *bookxpm = NULL;
    110 static GdkPixbuf *addressxpm = NULL;
    111 static GdkPixbuf *categoryxpm = NULL;
    112 static GdkPixbuf *addrsearchxpm = NULL;
    113 
    114 /* Message buffer */
    115 static gchar addressbook_msgbuf[ ADDRESSBOOK_MSGBUF_SIZE ];
    116 
    117 /* Address list selection */
    118 static AddrSelectList *_addressSelect_ = NULL;
    119 static AddressClipboard *_clipBoard_ = NULL;
    120 
    121 /* Address index file and interfaces */
    122 static AddressIndex *_addressIndex_ = NULL;
    123 static GList *_addressInterfaceList_ = NULL;
    124 static GList *_addressIFaceSelection_ = NULL;
    125 #define ADDRESSBOOK_IFACE_SELECTION "1/y,3/y,4/y,2/n"
    126 
    127 static AddressBook_win addrbook;
    128 
    129 static GHashTable *_addressBookTypeHash_ = NULL;
    130 static GList *_addressBookTypeList_ = NULL;
    131 
    132 static void addressbook_new_address_from_book_post_cb( ItemPerson *person );
    133 static void addressbook_new_address_from_folder_post_cb( ItemPerson *person );
    134 static void addressbook_edit_address_post_cb( ItemPerson *person );
    135 
    136 static void addressbook_create			(void);
    137 static gint addressbook_close			(void);
    138 
    139 static gboolean address_index_has_focus = FALSE;
    140 static gboolean address_list_has_focus = FALSE;
    141 
    142 /* callback functions */
    143 static void addressbook_del_clicked		(GtkButton	*button,
    144 						 gpointer	 data);
    145 static void addressbook_reg_clicked		(GtkButton	*button,
    146 						 gpointer	 data);
    147 static void addressbook_to_clicked		(GtkButton	*button,
    148 						 gpointer	 data);
    149 static void addressbook_lup_clicked		(GtkButton	*button,
    150 						 gpointer	data);
    151 static void addressbook_close_clicked		(GtkButton	*button,
    152 						 gpointer	data);
    153 
    154 static void addressbook_tree_selected		(GtkCMCTree	*ctree,
    155 						 GtkCMCTreeNode	*node,
    156 						 gint		 column,
    157 						 gpointer	 data);
    158 static void addressbook_select_row_tree		(GtkCMCTree	*ctree,
    159 						 GtkCMCTreeNode	*node,
    160 						 gint		 column,
    161 						 gpointer	 data);
    162 static void addressbook_list_row_selected	(GtkCMCTree	*clist,
    163 						 GtkCMCTreeNode	*node,
    164 						 gint		 column,
    165 						 gpointer	 data);
    166 static void addressbook_list_row_unselected	(GtkCMCTree	*clist,
    167 						 GtkCMCTreeNode	*node,
    168 						 gint		 column,
    169 						 gpointer	 data);
    170 static void addressbook_person_expand_node	(GtkCMCTree	*ctree,
    171 						 GList		*node,
    172 						 gpointer	*data );
    173 static void addressbook_person_collapse_node	(GtkCMCTree	*ctree,
    174 						 GList		*node,
    175 						 gpointer	*data );
    176 
    177 static void addressbook_entry_activated		(GtkWidget	*widget,
    178 						 gpointer	 data);
    179 
    180 static gboolean addressbook_list_button_pressed	(GtkWidget	*widget,
    181 						 GdkEventButton	*event,
    182 						 gpointer	 data);
    183 static gboolean addressbook_list_button_released(GtkWidget	*widget,
    184 						 GdkEventButton	*event,
    185 						 gpointer	 data);
    186 static gboolean addressbook_tree_button_pressed	(GtkWidget	*ctree,
    187 						 GdkEventButton	*event,
    188 						 gpointer	 data);
    189 static gboolean addressbook_tree_button_released(GtkWidget	*ctree,
    190 						 GdkEventButton	*event,
    191 						 gpointer	 data);
    192 
    193 static void addressbook_new_folder_cb		(GtkAction	*action,
    194 						 gpointer	 data);
    195 static void addressbook_new_group_cb		(GtkAction	*action,
    196 						 gpointer	 data);
    197 static void addressbook_treenode_edit_cb	(GtkAction	*action,
    198 						 gpointer	 data);
    199 static void addressbook_treenode_delete_cb	(GtkAction	*action,
    200 						 gpointer	 data);
    201 
    202 static void addressbook_change_node_name	(GtkCMCTreeNode	*node,
    203 						 const gchar	*name);
    204 
    205 static void addressbook_new_address_cb		(GtkAction	*action,
    206 						 gpointer	 data);
    207 static void addressbook_edit_address_cb		(GtkAction	*action,
    208 						 gpointer	 data);
    209 static void addressbook_delete_address_cb	(GtkAction	*action,
    210 						 gpointer	 data);
    211 
    212 static void close_cb				(GtkAction	*action,
    213 						 gpointer	 data);
    214 static void addressbook_file_save_cb		(GtkAction	*action,
    215 						 gpointer	 data);
    216 
    217 /* Data source edit stuff */
    218 static void addressbook_new_book_cb		(GtkAction	*action,
    219 						 gpointer	 data);
    220 
    221 static void addressbook_set_clist		(AddressObject	*obj,
    222 						 gboolean 	 refresh);
    223 
    224 static void addressbook_load_tree		(void);
    225 void addressbook_read_file			(void);
    226 
    227 static GtkCMCTreeNode *addressbook_add_object	(GtkCMCTreeNode	*node,
    228 						 AddressObject	*obj);
    229 static void addressbook_treenode_remove_item	( void );
    230 
    231 static AddressDataSource *addressbook_find_datasource
    232 						(GtkCMCTreeNode	*node );
    233 
    234 static AddressBookFile *addressbook_get_book_file(void);
    235 
    236 static GtkCMCTreeNode *addressbook_node_add_folder
    237 						(GtkCMCTreeNode	*node,
    238 						AddressDataSource *ds,
    239 						ItemFolder	*itemFolder,
    240 						AddressObjectType otype);
    241 static GtkCMCTreeNode *addressbook_node_add_group (GtkCMCTreeNode	*node,
    242 						AddressDataSource *ds,
    243 						ItemGroup	*itemGroup);
    244 static void addressbook_tree_remove_children	(GtkCMCTree	*ctree,
    245 						GtkCMCTreeNode	*parent);
    246 static void addressbook_move_nodes_up		(GtkCMCTree	*ctree,
    247 						GtkCMCTreeNode	*node);
    248 static GtkCMCTreeNode *addressbook_find_group_node (GtkCMCTreeNode	*parent,
    249 						   ItemGroup	*group);
    250 static gboolean addressbook_entry_key_pressed	(GtkWidget	*widget,
    251 						 GdkEventKey	*event,
    252 						 gpointer	 data);
    253 static gint addressbook_treenode_compare_func	(GtkCMCList	*clist,
    254 						 gconstpointer	 ptr1,
    255 						 gconstpointer	 ptr2);
    256 static void addressbook_folder_load_one_person	(GtkCMCTree *clist,
    257 						 ItemPerson *person,
    258 						 AddressTypeControlItem *atci,
    259 						 AddressTypeControlItem *atciMail);
    260 static void addressbook_folder_remove_node	(GtkCMCTree *clist,
    261 						 GtkCMCTreeNode *node);
    262 
    263 static void addressbook_edit_address( gpointer data, guint action, GtkWidget *widget,
    264 									  gboolean force_focus );
    265 
    266 /* LUT's and IF stuff */
    267 static void addressbook_free_treenode		( gpointer data );
    268 static AddressTypeControlItem *addrbookctl_lookup	(gint		 ot);
    269 static AddressTypeControlItem *addrbookctl_lookup_iface(AddressIfType	 ifType);
    270 
    271 static void addrbookctl_build_map			(GtkWidget	*window);
    272 static void addrbookctl_build_iflist			(void);
    273 static AdapterInterface *addrbookctl_find_interface	(AddressIfType	 ifType);
    274 static void addrbookctl_build_ifselect			(void);
    275 
    276 static void addrbookctl_free_interface		(AdapterInterface *adapter);
    277 static void addrbookctl_free_datasource		(AdapterDSource	  *adapter);
    278 static void addrbookctl_free_folder		(AdapterFolder	  *adapter);
    279 static void addrbookctl_free_group		(AdapterGroup	  *adapter);
    280 
    281 static void addressbook_list_select_clear	( void );
    282 static void addressbook_list_select_add		( AddrItemObject    *aio,
    283 						  AddressDataSource *ds );
    284 static void addressbook_list_select_remove	( AddrItemObject    *aio );
    285 
    286 static void addressbook_find_duplicates_cb	( GtkAction *action, gpointer data );
    287 static void addressbook_select_all_cb		( GtkAction *action, gpointer data );
    288 static void addressbook_clip_cut_cb		( GtkAction *action, gpointer data );
    289 static void addressbook_clip_copy_cb		( GtkAction *action, gpointer data );
    290 static void addressbook_clip_paste_cb		( GtkAction *action, gpointer data );
    291 static void addressbook_treenode_cut_cb		( GtkAction *action, gpointer data );
    292 static void addressbook_treenode_copy_cb	( GtkAction *action, gpointer data );
    293 static void addressbook_treenode_paste_cb	( GtkAction *action, gpointer data );
    294 
    295 static void addressbook_mail_to_cb		( GtkAction *action, gpointer data );
    296 static void addressbook_merge_cb		( GtkAction *action, gpointer data );
    297 
    298 static void addressbook_edit_clicked(GtkButton *button, gpointer data);
    299 
    300 static void addressbook_start_drag(GtkWidget *widget, gint button,
    301 				   GdkEvent *event,
    302 			           void *data);
    303 static void addressbook_drag_data_get(GtkWidget        *widget,
    304 				     GdkDragContext   *drag_context,
    305 				     GtkSelectionData *selection_data,
    306 				     guint             info,
    307 				     guint             time,
    308 				     void	      *data);
    309 static gboolean addressbook_drag_motion_cb(GtkWidget      *widget,
    310 					  GdkDragContext *context,
    311 					  gint            x,
    312 					  gint            y,
    313 					  guint           time,
    314 					  void           *data);
    315 static void addressbook_drag_leave_cb(GtkWidget      *widget,
    316 				     GdkDragContext *context,
    317 				     guint           time,
    318 				     void           *data);
    319 static void addressbook_drag_received_cb(GtkWidget        *widget,
    320 					GdkDragContext   *drag_context,
    321 					gint              x,
    322 					gint              y,
    323 					GtkSelectionData *data,
    324 					guint             info,
    325 					guint             time,
    326 					void             *pdata);
    327 static void addressbook_list_menu_setup( void );
    328 
    329 static GtkTargetEntry addressbook_drag_types[] =
    330 {
    331 	{"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY}
    332 };
    333 
    334 static GtkTargetList *addressbook_target_list = NULL;
    335 
    336 static GtkActionEntry addressbook_entries[] =
    337 {
    338 	{"Menu",				NULL, "Menu", NULL, NULL, NULL },
    339 /* menus */
    340 	{"Book",			NULL, "_Book", NULL, NULL, NULL },
    341 	{"Edit",			NULL, "_Edit", NULL, NULL, NULL },
    342 	{"Tools",			NULL, "_Tools", NULL, NULL, NULL },
    343 
    344 /* Book menu */
    345 	{"Book/NewBook",		NULL, "New _Book", "<control>B", NULL, G_CALLBACK(addressbook_new_book_cb) },
    346 	{"Book/NewFolder",		NULL, "New _Folder", "<control>R", NULL, G_CALLBACK(addressbook_new_folder_cb) },
    347 	{"Book/---",			NULL, "---", NULL, NULL, NULL },
    348 
    349 	{"Book/EditBook",		NULL, "_Edit book", NULL, NULL, G_CALLBACK(addressbook_treenode_edit_cb) },
    350 	{"Book/DeleteBook",		NULL, "_Delete book", NULL, NULL, G_CALLBACK(addressbook_treenode_delete_cb) },
    351 	/* {"Book/---",			NULL, "---", NULL, NULL, NULL }, */
    352 	{"Book/Save",			NULL, "_Save", "<control>S", NULL, G_CALLBACK(addressbook_file_save_cb) },
    353 	{"Book/Close",			NULL, "_Close", "<control>W", NULL, G_CALLBACK(close_cb) },
    354 
    355 /* Adress menu */
    356 	{"Address/SelectAll",		NULL, "_Select all", "<control>A", NULL, G_CALLBACK(addressbook_select_all_cb) },
    357 	{"Address/---",			NULL, "---", NULL, NULL, NULL },
    358 	{"Address/Cut",			NULL, "C_ut", "<control>X", NULL, G_CALLBACK(addressbook_clip_cut_cb) },
    359 	{"Address/Copy",		NULL, "_Copy", "<control>C", NULL, G_CALLBACK(addressbook_clip_copy_cb) },
    360 	{"Address/Paste",		NULL, "_Paste", "<control>V", NULL, G_CALLBACK(addressbook_clip_paste_cb) },
    361 	/* {"Address/---",			NULL, "---", NULL, NULL, NULL }, */
    362 	{"Address/Edit",		NULL, "_Edit", "<control>Return", NULL, G_CALLBACK(addressbook_edit_address_cb) },
    363 	{"Address/Delete",		NULL, "_Delete", "<control>D", NULL, G_CALLBACK(addressbook_delete_address_cb) },
    364 	/* {"Address/---",			NULL, "---", NULL, NULL, NULL }, */
    365 	{"Address/NewAddress",		NULL, "New _Address", "<control>N", NULL, G_CALLBACK(addressbook_new_address_cb) },
    366 	{"Address/NewGroup",		NULL, "New _Group", "<control>G", NULL, G_CALLBACK(addressbook_new_group_cb) },
    367 	/* {"Address/---",			NULL, "---", NULL, NULL, NULL }, */
    368 	{"Address/Mailto",		NULL, "_Mail To", "<control>M", NULL, G_CALLBACK(addressbook_mail_to_cb) },
    369 	{"Address/Merge",		NULL, "_Merge", "<control>E", NULL, G_CALLBACK(addressbook_merge_cb) },
    370 
    371 	{"Tools/---",			NULL, "---", NULL, NULL, NULL },
    372 	{"Tools/FindDuplicates",	NULL, "Find duplicates...", NULL, NULL, G_CALLBACK(addressbook_find_duplicates_cb) },
    373 };
    374 
    375 static GtkActionEntry addressbook_tree_popup_entries[] =
    376 {
    377 	{"ABTreePopup",			NULL, "ABTreePopup", NULL, NULL, NULL },
    378 	{"ABTreePopup/EditBook",	NULL, "_Edit", NULL, NULL, G_CALLBACK(addressbook_treenode_edit_cb) },
    379 	{"ABTreePopup/DeleteBook",	NULL, "_Delete", NULL, NULL, G_CALLBACK(addressbook_treenode_delete_cb) },
    380 	{"ABTreePopup/---",		NULL, "---", NULL, NULL, NULL },
    381 	{"ABTreePopup/NewBook",		NULL, "New _Book", NULL, NULL, G_CALLBACK(addressbook_new_book_cb) },
    382 	{"ABTreePopup/NewFolder",	NULL, "New _Folder", NULL, NULL, G_CALLBACK(addressbook_new_folder_cb) },
    383 	{"ABTreePopup/NewGroup",	NULL, "New _Group", NULL, NULL, G_CALLBACK(addressbook_new_group_cb) },
    384 	/* {"ABTreePopup/---",		NULL, "---", NULL, NULL, NULL }, */
    385 	{"ABTreePopup/Cut",		NULL, "C_ut", NULL, NULL, G_CALLBACK(addressbook_treenode_cut_cb) },
    386 	{"ABTreePopup/Copy",		NULL, "_Copy", NULL, NULL, G_CALLBACK(addressbook_treenode_copy_cb) },
    387 	{"ABTreePopup/Paste",		NULL, "_Paste", NULL, NULL, G_CALLBACK(addressbook_treenode_paste_cb) },
    388 };
    389 
    390 static GtkActionEntry addressbook_list_popup_entries[] =
    391 {
    392 	{"ABListPopup",			NULL, "ABListPopup", NULL, NULL, NULL },
    393 	{"ABListPopup/SelectAll",	NULL, "_Select all", NULL, NULL, G_CALLBACK(addressbook_select_all_cb) },
    394 	{"ABListPopup/---",		NULL, "---", NULL, NULL, NULL },
    395 	{"ABListPopup/Edit",		NULL, "_Edit", NULL, NULL, G_CALLBACK(addressbook_edit_address_cb) },
    396 	{"ABListPopup/Delete",		NULL, "_Delete", NULL, NULL, G_CALLBACK(addressbook_delete_address_cb) },
    397 	/* {"ABListPopup/---",		NULL, "---", NULL, NULL, NULL }, */
    398 	{"ABListPopup/NewAddress",	NULL, "New _Address", NULL, NULL, G_CALLBACK(addressbook_new_address_cb) },
    399 	{"ABListPopup/NewGroup",	NULL, "New _Group", NULL, NULL, G_CALLBACK(addressbook_new_group_cb) },
    400 	/* {"ABListPopup/---",		NULL, "---", NULL, NULL, NULL }, */
    401 	{"ABListPopup/Cut",		NULL, "C_ut", NULL, NULL, G_CALLBACK(addressbook_clip_cut_cb) },
    402 	{"ABListPopup/Copy",		NULL, "_Copy", NULL, NULL, G_CALLBACK(addressbook_clip_copy_cb) },
    403 	{"ABListPopup/Paste",		NULL, "_Paste", NULL, NULL, G_CALLBACK(addressbook_clip_paste_cb) },
    404 	/* {"ABListPopup/---",		NULL, "---", NULL, NULL, NULL }, */
    405 	{"ABListPopup/Mailto",		NULL, "_Mail To", NULL, NULL, G_CALLBACK(addressbook_mail_to_cb) },
    406 	{"ABListPopup/Merge",		NULL, "_Merge", NULL, NULL, G_CALLBACK(addressbook_merge_cb) },
    407 };
    408 
    409 /**
    410  * Structure of error message table.
    411  */
    412 typedef struct _ErrMsgTableEntry ErrMsgTableEntry;
    413 struct _ErrMsgTableEntry {
    414 	gint	code;
    415 	gchar	*description;
    416 };
    417 
    418 static gchar *_errMsgUnknown_ = "Unknown" ;
    419 
    420 /**
    421  * Lookup table of error messages for general errors. Note that a NULL
    422  * description signifies the end of the table.
    423  */
    424 static ErrMsgTableEntry _lutErrorsGeneral_[] = {
    425 	{ MGU_SUCCESS,		"Success" },
    426 	{ MGU_NO_FILE,		"File not specified" },
    427 	{ MGU_OPEN_FILE,	"Error opening file" },
    428 	{ MGU_BAD_FORMAT,	"Bad file format" },
    429 	{ MGU_ERROR_WRITE,	"Error writing to file" },
    430 	{ MGU_OPEN_DIRECTORY,	"Error opening directory" },
    431 	{ MGU_NO_PATH,      	"No path specified" },
    432 	{ 0,			NULL }
    433 };
    434 
    435 /**
    436  * Lookup message for specified error code.
    437  * \param lut  Lookup table.
    438  * \param code Code to lookup.
    439  * \return Description associated to code.
    440  */
    441 static gchar *addressbook_err2string( ErrMsgTableEntry lut[], gint code ) {
    442         gchar *desc = NULL;
    443         ErrMsgTableEntry entry;
    444         gint i;
    445 
    446         for( i = 0; ; i++ ) {
    447                 entry = lut[ i ];
    448                 if( entry.description == NULL ) break;
    449                 if( entry.code == code ) {
    450                         desc = entry.description;
    451                         break;
    452                 }
    453         }
    454         if( ! desc ) {
    455 		desc = _errMsgUnknown_;
    456         }
    457         return desc;
    458 }
    459 
    460 static gboolean lastCanLookup = FALSE;
    461 
    462 static void addressbook_show_buttons(gboolean add_and_delete, gboolean lookup, gboolean mail_ops)
    463 {
    464 	if (add_and_delete) {
    465 		gtk_widget_show(addrbook.edit_btn);
    466 		gtk_widget_show(addrbook.del_btn);
    467 		gtk_widget_show(addrbook.reg_btn);
    468 	} else {
    469 		gtk_widget_hide(addrbook.edit_btn);
    470 		gtk_widget_hide(addrbook.del_btn);
    471 		gtk_widget_hide(addrbook.reg_btn);
    472 	}
    473 
    474 	if (lookup) {
    475 		gtk_widget_show(addrbook.lup_btn);
    476 		gtk_widget_show(addrbook.entry);
    477 		gtk_widget_show(addrbook.label);
    478 	} else {
    479 		gtk_widget_hide(addrbook.lup_btn);
    480 		gtk_widget_hide(addrbook.entry);
    481 		gtk_widget_hide(addrbook.label);
    482 	}
    483 
    484 	lastCanLookup = lookup;
    485 
    486 	if (mail_ops) {
    487 		gtk_widget_show(addrbook.to_btn);
    488 		gtk_widget_show(addrbook.cc_btn);
    489 		gtk_widget_show(addrbook.bcc_btn);
    490 	} else {
    491 		gtk_widget_hide(addrbook.to_btn);
    492 		gtk_widget_hide(addrbook.cc_btn);
    493 		gtk_widget_hide(addrbook.bcc_btn);
    494 	}
    495 }
    496 
    497 void addressbook_open(Compose *target)
    498 {
    499 	/* Initialize all static members */
    500 	if( _clipBoard_ == NULL ) {
    501 		_clipBoard_ = addrclip_create();
    502 	}
    503 	if( _addressIndex_ != NULL ) {
    504 		addrclip_set_index( _clipBoard_, _addressIndex_ );
    505 	}
    506 	if( _addressSelect_ == NULL ) {
    507 		_addressSelect_ = addrselect_list_create();
    508 	}
    509 	if (!addrbook.window) {
    510 		addressbook_read_file();
    511 		addressbook_create();
    512 		addressbook_load_tree();
    513 		gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
    514 				 GTK_CMCTREE_NODE(GTK_CMCLIST(addrbook.ctree)->row_list));
    515 	}
    516 	else {
    517 		gtk_widget_hide(addrbook.window);
    518 	}
    519 
    520 	gtk_widget_show_all(addrbook.window);
    521 
    522 	if (!prefs_common.addressbook_use_editaddress_dialog)
    523 		addressbook_edit_person_widgetset_hide();
    524 
    525 	address_completion_start(addrbook.window);
    526 
    527 	addressbook_show_buttons(target == NULL, lastCanLookup, target != NULL);
    528 	addressbook_set_target_compose(target);
    529 }
    530 
    531 /**
    532  * Destroy addressbook.
    533  */
    534 void addressbook_destroy( void ) {
    535 	/* Free up address stuff */
    536 	if( _addressSelect_ != NULL ) {
    537 		addrselect_list_free( _addressSelect_ );
    538 	}
    539 	if( _clipBoard_ != NULL ) {
    540 		addrclip_free( _clipBoard_ );
    541 		_clipBoard_ = NULL;
    542 	}
    543 	if( _addressIndex_ != NULL ) {
    544 		addrindex_free_index( _addressIndex_ );
    545 		addrindex_teardown();
    546 	}
    547 	_addressSelect_ = NULL;
    548 	_clipBoard_ = NULL;
    549 	_addressIndex_ = NULL;
    550 }
    551 
    552 void addressbook_set_target_compose(Compose *target)
    553 {
    554 	addrbook.target_compose = target;
    555 }
    556 
    557 Compose *addressbook_get_target_compose(void)
    558 {
    559 	return addrbook.target_compose;
    560 }
    561 
    562 /**
    563  * Refresh addressbook and save to file(s).
    564  */
    565 void addressbook_refresh( void )
    566 {
    567 	if (addrbook.window) {
    568 		if (addrbook.treeSelected) {
    569 			gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
    570 					 addrbook.treeSelected);
    571 			addressbook_set_clist(
    572 				gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
    573 					addrbook.treeSelected),
    574 				TRUE);
    575 
    576 		}
    577 	}
    578 	addressbook_export_to_file();
    579 }
    580 
    581 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
    582 {
    583 	if (event && event->keyval == GDK_KEY_Delete) {
    584 		/* TODO: enable deletion when focus is in ctree (needs implementation in _del_clicked() */
    585 		if ( /* address_index_has_focus || */ address_list_has_focus )
    586 			addressbook_del_clicked(NULL, NULL);
    587 	}
    588 	return FALSE;
    589 }
    590 
    591 /*!
    592  *\brief	Save Gtk object size to prefs dataset
    593  */
    594 static void addressbook_size_allocate_cb(GtkWidget *widget,
    595 					 GtkAllocation *allocation)
    596 {
    597 	cm_return_if_fail(allocation != NULL);
    598 
    599 	gtk_window_get_size(GTK_WINDOW(widget),
    600 		&prefs_common.addressbookwin_width, &prefs_common.addressbookwin_height);
    601 }
    602 
    603 static gint sort_column_number = 0;
    604 static GtkSortType sort_column_type = GTK_SORT_ASCENDING;
    605 
    606 static gint list_case_sort(
    607 	GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
    608 {
    609 	GtkCMCListRow *row1 = (GtkCMCListRow *) ptr1;
    610 	GtkCMCListRow *row2 = (GtkCMCListRow *) ptr2;
    611 	gchar *name1 = NULL, *name2 = NULL;
    612 	AddrItemObject *aio1 = ((GtkCMCListRow *)ptr1)->data;
    613 	AddrItemObject *aio2 = ((GtkCMCListRow *)ptr2)->data;
    614 
    615 	if( aio1->type == aio2->type ) {
    616 		if( row1 )
    617 			name1 = GTK_CMCELL_TEXT (row1->cell[sort_column_number])->text;
    618 		if( row2 )
    619 			name2 = GTK_CMCELL_TEXT (row2->cell[sort_column_number])->text;
    620 		if( ! name1 ) return ( name2 != NULL );
    621 		if( ! name2 ) return -1;
    622 		return g_utf8_collate( name1, name2 );
    623 	} else {
    624 		/* Order groups before person */
    625 		if( aio1->type == ITEMTYPE_GROUP ) {
    626 			return (sort_column_type==GTK_SORT_ASCENDING) ? -1:+1;
    627 		} else if( aio2->type == ITEMTYPE_GROUP ) {
    628 			return (sort_column_type==GTK_SORT_ASCENDING) ? +1:-1;
    629 		}
    630 		return 0;
    631 	}
    632 }
    633 
    634 static void addressbook_sort_list(GtkCMCList *clist, const gint col,
    635 		const GtkSortType sort_type)
    636 {
    637 	gint pos;
    638 	GtkWidget *hbox, *label, *arrow;
    639 
    640 	sort_column_number = col;
    641 	sort_column_type = sort_type;
    642 	gtk_cmclist_set_compare_func(clist, list_case_sort);
    643 	gtk_cmclist_set_sort_type(clist, sort_type);
    644 	gtk_cmclist_set_sort_column(clist, col);
    645 
    646 	gtk_cmclist_freeze(clist);
    647 	gtk_cmclist_sort(clist);
    648 
    649 	for(pos = 0 ; pos < N_LIST_COLS ; pos++) {
    650 		hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
    651 		label = gtk_label_new(list_titles[pos]);
    652 		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    653 
    654 		if(pos == col) {
    655 			arrow = gtk_image_new_from_icon_name(sort_type == GTK_SORT_ASCENDING ?
    656 				"pan-down-symbolic" : "pan-up-symbolic", GTK_ICON_SIZE_MENU);
    657 			gtk_box_pack_end(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
    658 		}
    659 
    660 		gtk_widget_show_all(hbox);
    661 		gtk_cmclist_set_column_widget(clist, pos, hbox);
    662 	}
    663 
    664 	gtk_cmclist_thaw(clist);
    665 }
    666 
    667 static void addressbook_name_clicked(GtkWidget *button, GtkCMCList *clist)
    668 {
    669 	static GtkSortType sort_type = GTK_SORT_ASCENDING;
    670 
    671 	sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
    672 			GTK_SORT_ASCENDING;
    673 	addressbook_sort_list(clist, COL_NAME, sort_type);
    674 }
    675 
    676 static void addressbook_address_clicked(GtkWidget *button, GtkCMCList *clist)
    677 {
    678 	static GtkSortType sort_type = GTK_SORT_ASCENDING;
    679 
    680 	sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
    681 			GTK_SORT_ASCENDING;
    682 	addressbook_sort_list(clist, COL_ADDRESS, sort_type);
    683 }
    684 
    685 static void addressbook_remarks_clicked(GtkWidget *button, GtkCMCList *clist)
    686 {
    687 	static GtkSortType sort_type = GTK_SORT_ASCENDING;
    688 
    689 	sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
    690 			GTK_SORT_ASCENDING;
    691 	addressbook_sort_list(clist, COL_REMARKS, sort_type);
    692 }
    693 
    694 static gboolean addressbook_address_index_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
    695 											 gpointer data)
    696 {
    697 	address_index_has_focus = TRUE;
    698 	return FALSE;
    699 }
    700 
    701 static gboolean addressbook_address_index_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
    702 											 gpointer data)
    703 {
    704 	address_index_has_focus = FALSE;
    705 	if (!prefs_common.addressbook_use_editaddress_dialog
    706 			&& !address_list_has_focus)
    707 		addressbook_address_list_disable_some_actions();
    708 	return FALSE;
    709 }
    710 
    711 static gboolean addressbook_address_list_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
    712 											 gpointer data)
    713 {
    714 	address_list_has_focus = TRUE;
    715 	return FALSE;
    716 }
    717 
    718 static gboolean addressbook_address_list_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
    719 											 gpointer data)
    720 {
    721 	address_list_has_focus = FALSE;
    722 	if (!prefs_common.addressbook_use_editaddress_dialog
    723 			&& !address_index_has_focus)
    724 		addressbook_address_list_disable_some_actions();
    725 	return FALSE;
    726 }
    727 
    728 /* save hpane and vpane's handle position when it moves */
    729 static void addressbook_pane_save_position(void)
    730 {
    731 	if (addrbook.hpaned)
    732 		prefs_common.addressbook_hpaned_pos =
    733 			gtk_paned_get_position(GTK_PANED(addrbook.hpaned));
    734 	if (addrbook.vpaned)
    735 		prefs_common.addressbook_vpaned_pos =
    736 			gtk_paned_get_position(GTK_PANED(addrbook.vpaned));
    737 }
    738 
    739 /*
    740 * Create the address book widgets. The address book contains two CTree widgets: the
    741 * address index tree on the left and the address list on the right.
    742 *
    743 * The address index tree displays a hierarchy of interfaces and groups. Each node in
    744 * this tree is linked to an address Adapter. Adapters have been created for interfaces,
    745 * data sources and folder objects.
    746 *
    747 * The address list displays group, person and email objects. These items are linked
    748 * directly to ItemGroup, ItemPerson and ItemEMail objects inside the address book data
    749 * sources.
    750 *
    751 * In the tradition of MVC architecture, the data stores have been separated from the
    752 * GUI components. The addrindex.c file provides the interface to all data stores.
    753 */
    754 static void addressbook_create(void)
    755 {
    756 	GtkWidget *window;
    757 	GtkWidget *vbox;
    758 	GtkWidget *menubar;
    759 	GtkWidget *vbox2;
    760 	GtkWidget *ctree_swin;
    761 	GtkWidget *ctree;
    762 	GtkWidget *editaddress_vbox;
    763 	GtkWidget *clist_vbox;
    764 	GtkWidget *clist_swin;
    765 	GtkWidget *clist;
    766 	GtkWidget *hpaned;
    767 	GtkWidget *vpaned;
    768 	GtkWidget *hbox;
    769 	GtkWidget *label;
    770 	GtkWidget *entry;
    771 	GtkWidget *statusbar;
    772 	GtkWidget *hbbox;
    773 	GtkWidget *hsbox;
    774 	GtkWidget *help_btn;
    775 	GtkWidget *del_btn;
    776 	GtkWidget *edit_btn;
    777 	GtkWidget *reg_btn;
    778 	GtkWidget *lup_btn;
    779 	GtkWidget *to_btn;
    780 	GtkWidget *cc_btn;
    781 	GtkWidget *bcc_btn;
    782 	GtkWidget *close_btn;
    783 	GtkWidget *tree_popup;
    784 	GtkWidget *list_popup;
    785 	GList *nodeIf;
    786 	GtkUIManager *ui_manager;
    787 	GtkActionGroup *action_group;
    788 	gchar *index_titles[N_INDEX_COLS];
    789 	gchar *text;
    790 	gint i;
    791 
    792 	static GdkGeometry geometry;
    793 
    794 	debug_print("Creating addressbook window...\n");
    795 
    796 	index_titles[COL_SOURCES] = "Sources";
    797 
    798 	/* Address book window */
    799 	window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "addressbook");
    800 	gtk_window_set_title(GTK_WINDOW(window), "Address book");
    801 	gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
    802 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
    803 	gtk_widget_realize(window);
    804 
    805 	g_signal_connect(G_OBJECT(window), "delete_event",
    806 			 G_CALLBACK(addressbook_close), NULL);
    807 	g_signal_connect(G_OBJECT(window), "size_allocate",
    808 			 G_CALLBACK(addressbook_size_allocate_cb), NULL);
    809 	g_signal_connect(G_OBJECT(window), "key_press_event",
    810 			 G_CALLBACK(key_pressed), NULL);
    811 	MANAGE_WINDOW_SIGNALS_CONNECT(window);
    812 
    813 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    814 	gtk_container_add(GTK_CONTAINER(window), vbox);
    815 
    816 	/* Menu bar */
    817 	ui_manager = gtk_ui_manager_new();
    818 	action_group = cm_menu_create_action_group_full(ui_manager,"Menu", addressbook_entries,
    819 			G_N_ELEMENTS(addressbook_entries), NULL);
    820 	gtk_action_group_add_actions(action_group, addressbook_tree_popup_entries,
    821 			G_N_ELEMENTS(addressbook_tree_popup_entries), NULL);
    822 	gtk_action_group_add_actions(action_group, addressbook_list_popup_entries,
    823 			G_N_ELEMENTS(addressbook_list_popup_entries), NULL);
    824 
    825 	MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
    826 
    827 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Book", "Book", GTK_UI_MANAGER_MENU)
    828 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
    829 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
    830 
    831 /* Book menu */
    832 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewBook", "Book/NewBook", GTK_UI_MANAGER_MENUITEM)
    833 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewFolder", "Book/NewFolder", GTK_UI_MANAGER_MENUITEM)
    834 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Separator1", "Book/---", GTK_UI_MANAGER_SEPARATOR)
    835 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "EditBook", "Book/EditBook", GTK_UI_MANAGER_MENUITEM)
    836 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "DeleteBook", "Book/DeleteBook", GTK_UI_MANAGER_MENUITEM)
    837 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Separator2", "Book/---", GTK_UI_MANAGER_SEPARATOR)
    838 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Save", "Book/Save", GTK_UI_MANAGER_MENUITEM)
    839 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Close", "Book/Close", GTK_UI_MANAGER_MENUITEM)
    840 
    841 /* Address menu */
    842 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "SelectAll", "Address/SelectAll", GTK_UI_MANAGER_MENUITEM)
    843 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator1", "Address/---", GTK_UI_MANAGER_SEPARATOR)
    844 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Cut", "Address/Cut", GTK_UI_MANAGER_MENUITEM)
    845 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Copy", "Address/Copy", GTK_UI_MANAGER_MENUITEM)
    846 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Paste", "Address/Paste", GTK_UI_MANAGER_MENUITEM)
    847 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator2", "Address/---", GTK_UI_MANAGER_SEPARATOR)
    848 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Edit", "Address/Edit", GTK_UI_MANAGER_MENUITEM)
    849 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Delete", "Address/Delete", GTK_UI_MANAGER_MENUITEM)
    850 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator3", "Address/---", GTK_UI_MANAGER_SEPARATOR)
    851 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "NewAddress", "Address/NewAddress", GTK_UI_MANAGER_MENUITEM)
    852 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "NewGroup", "Address/NewGroup", GTK_UI_MANAGER_MENUITEM)
    853 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator4", "Address/---", GTK_UI_MANAGER_SEPARATOR)
    854 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Mailto", "Address/Mailto", GTK_UI_MANAGER_MENUITEM)
    855 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Merge", "Address/Merge", GTK_UI_MANAGER_MENUITEM)
    856 
    857 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "FindDuplicates", "Tools/FindDuplicates", GTK_UI_MANAGER_MENUITEM)
    858 
    859 	gtk_window_add_accel_group(GTK_WINDOW(window),
    860 			gtk_ui_manager_get_accel_group(ui_manager));
    861 
    862 	menubar = gtk_ui_manager_get_widget(ui_manager, "/Menu");
    863 
    864 	gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
    865 
    866 	vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, BORDER_WIDTH);
    867 	gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
    868 	gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
    869 
    870 	ctree_swin = gtk_scrolled_window_new(NULL, NULL);
    871 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ctree_swin),
    872 				       GTK_POLICY_AUTOMATIC,
    873 				       GTK_POLICY_AUTOMATIC);
    874 
    875 	/* Address index */
    876 	ctree = gtk_sctree_new_with_titles(N_INDEX_COLS, 0, index_titles);
    877 	gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[0].button, FALSE);
    878 
    879 	gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
    880 	gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
    881 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), 0, COL_FOLDER_WIDTH);
    882 	gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
    883 			     GTK_CMCTREE_EXPANDER_TRIANGLE);
    884 	gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
    885 	gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
    886 	gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree),
    887 				   addressbook_treenode_compare_func);
    888 
    889 	g_signal_connect(G_OBJECT(ctree), "tree_select_row",
    890 			 G_CALLBACK(addressbook_tree_selected), NULL);
    891 	g_signal_connect(G_OBJECT(ctree), "button_press_event",
    892 			 G_CALLBACK(addressbook_tree_button_pressed),
    893 			 NULL);
    894 	g_signal_connect(G_OBJECT(ctree), "button_release_event",
    895 			 G_CALLBACK(addressbook_tree_button_released),
    896 			 NULL);
    897 	/* TEMPORARY */
    898 	g_signal_connect(G_OBJECT(ctree), "select_row",
    899 			 G_CALLBACK(addressbook_select_row_tree), NULL);
    900 
    901 	gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
    902 			  addressbook_drag_types, 1,
    903 			  GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
    904 	g_signal_connect(G_OBJECT(ctree), "drag_motion",
    905 			 G_CALLBACK(addressbook_drag_motion_cb),
    906 			 ctree);
    907 	g_signal_connect(G_OBJECT(ctree), "drag_leave",
    908 			 G_CALLBACK(addressbook_drag_leave_cb),
    909 			 ctree);
    910 	g_signal_connect(G_OBJECT(ctree), "drag_data_received",
    911 			 G_CALLBACK(addressbook_drag_received_cb),
    912 			 ctree);
    913 	g_signal_connect(G_OBJECT(ctree), "focus_in_event",
    914 		G_CALLBACK(addressbook_address_index_focus_evt_in), NULL);
    915 	g_signal_connect(G_OBJECT(ctree), "focus_out_event",
    916 		G_CALLBACK(addressbook_address_index_focus_evt_out), NULL);
    917 
    918 	clist_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
    919 
    920 	clist_swin = gtk_scrolled_window_new(NULL, NULL);
    921 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
    922 				       GTK_POLICY_AUTOMATIC,
    923 				       GTK_POLICY_AUTOMATIC);
    924 	gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
    925 
    926 	/* Address list */
    927 	clist = gtk_sctree_new_with_titles(N_LIST_COLS, 0, list_titles);
    928 	gtk_container_add(GTK_CONTAINER(clist_swin), clist);
    929 	gtk_cmclist_set_selection_mode(GTK_CMCLIST(clist), GTK_SELECTION_MULTIPLE);
    930 	gtk_cmctree_set_expander_style(GTK_CMCTREE(clist),
    931 			     GTK_CMCTREE_EXPANDER_TRIANGLE);
    932 	gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
    933 	gtk_cmctree_set_indent(GTK_CMCTREE(clist), CTREE_INDENT);
    934 	gtk_cmclist_set_column_width(GTK_CMCLIST(clist), COL_NAME,
    935 				   COL_NAME_WIDTH);
    936 	gtk_cmclist_set_column_width(GTK_CMCLIST(clist), COL_ADDRESS,
    937 				   COL_ADDRESS_WIDTH);
    938 	gtk_widget_set_size_request(clist, -1, 80);
    939 
    940 	addressbook_sort_list(GTK_CMCLIST(clist), COL_NAME, GTK_SORT_ASCENDING);
    941 	g_signal_connect(G_OBJECT(GTK_CMCLIST(clist)->column[COL_NAME].button),
    942 		"clicked", G_CALLBACK(addressbook_name_clicked), clist);
    943 	g_signal_connect(G_OBJECT(GTK_CMCLIST(clist)->column[COL_ADDRESS].button),
    944 		"clicked", G_CALLBACK(addressbook_address_clicked), clist);
    945 	g_signal_connect(G_OBJECT(GTK_CMCLIST(clist)->column[COL_REMARKS].button),
    946 		"clicked", G_CALLBACK(addressbook_remarks_clicked), clist);
    947 	g_signal_connect(G_OBJECT(clist), "focus_in_event",
    948 		G_CALLBACK(addressbook_address_list_focus_evt_in), NULL);
    949 	g_signal_connect(G_OBJECT(clist), "focus_out_event",
    950 		G_CALLBACK(addressbook_address_list_focus_evt_out), NULL);
    951 
    952 	for (i = 0; i < N_LIST_COLS; i++)
    953 		gtk_widget_set_can_focus(GTK_CMCLIST(clist)->column[i].button,
    954 					 FALSE);
    955 
    956 	g_signal_connect(G_OBJECT(clist), "tree_select_row",
    957 			 G_CALLBACK(addressbook_list_row_selected), NULL);
    958 	g_signal_connect(G_OBJECT(clist), "tree_unselect_row",
    959 			 G_CALLBACK(addressbook_list_row_unselected), NULL);
    960 	g_signal_connect(G_OBJECT(clist), "button_press_event",
    961 			 G_CALLBACK(addressbook_list_button_pressed),
    962 			 NULL);
    963 	g_signal_connect(G_OBJECT(clist), "button_release_event",
    964 			 G_CALLBACK(addressbook_list_button_released),
    965 			 NULL);
    966 	g_signal_connect(G_OBJECT(clist), "tree_expand",
    967 			 G_CALLBACK(addressbook_person_expand_node), NULL );
    968 	g_signal_connect(G_OBJECT(clist), "tree_collapse",
    969 			 G_CALLBACK(addressbook_person_collapse_node), NULL );
    970 	g_signal_connect(G_OBJECT(clist), "start_drag",
    971 			 G_CALLBACK(addressbook_start_drag), NULL);
    972 	g_signal_connect(G_OBJECT(clist), "drag_data_get",
    973 			 G_CALLBACK(addressbook_drag_data_get), NULL);
    974 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
    975 	gtk_box_pack_start(GTK_BOX(clist_vbox), hbox, FALSE, FALSE, 0);
    976 
    977 	label = gtk_label_new("Search");
    978 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    979 
    980 	entry = gtk_entry_new();
    981 	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
    982 
    983 	address_completion_register_entry(GTK_ENTRY(entry), FALSE);
    984 
    985 	g_signal_connect(G_OBJECT(entry), "key_press_event",
    986 			 G_CALLBACK(addressbook_entry_key_pressed),
    987 			 NULL);
    988 	g_signal_connect(G_OBJECT(entry), "activate",
    989 			 G_CALLBACK(addressbook_entry_activated), NULL);
    990 
    991 	if (!prefs_common.addressbook_use_editaddress_dialog) {
    992 		editaddress_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
    993 		vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
    994 		gtk_paned_pack1(GTK_PANED(vpaned), clist_vbox, FALSE, FALSE);
    995 		gtk_paned_pack2(GTK_PANED(vpaned), editaddress_vbox, TRUE, FALSE);
    996 	} else {
    997 		vpaned = NULL;
    998 		editaddress_vbox = NULL;
    999 	}
   1000 	hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
   1001 	gtk_box_pack_start(GTK_BOX(vbox2), hpaned, TRUE, TRUE, 0);
   1002 	gtk_paned_pack1(GTK_PANED(hpaned), ctree_swin, FALSE, FALSE);
   1003 	if (prefs_common.addressbook_use_editaddress_dialog)
   1004 		gtk_paned_pack2(GTK_PANED(hpaned), clist_vbox, TRUE, FALSE);
   1005 	else
   1006 		gtk_paned_pack2(GTK_PANED(hpaned), vpaned, TRUE, FALSE);
   1007 
   1008 	/* Status bar */
   1009 	hsbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
   1010 	gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
   1011 	statusbar = gtk_statusbar_new();
   1012 	gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
   1013 
   1014 	/* Button panel */
   1015 	hbbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
   1016 	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
   1017 	gtk_box_set_spacing(GTK_BOX(hbbox), 2);
   1018 	gtk_container_set_border_width(GTK_CONTAINER(hbbox), 4);
   1019 	gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
   1020 
   1021 	gtkut_stock_button_add_help(hbbox, &help_btn);
   1022 
   1023 	edit_btn = gtk_button_new_with_mnemonic("_Edit");
   1024 	gtk_widget_set_can_default(edit_btn, TRUE);
   1025 	gtk_box_pack_start(GTK_BOX(hbbox), edit_btn, TRUE, TRUE, 0);
   1026 	del_btn = gtk_button_new_with_mnemonic("_Delete");
   1027 	gtk_widget_set_can_default(del_btn, TRUE);
   1028 	gtk_box_pack_start(GTK_BOX(hbbox), del_btn, TRUE, TRUE, 0);
   1029 	reg_btn = gtk_button_new_with_mnemonic("_New");
   1030 	gtk_widget_set_can_default(reg_btn, TRUE);
   1031 	gtk_box_pack_start(GTK_BOX(hbbox), reg_btn, TRUE, TRUE, 0);
   1032 
   1033 
   1034 	lup_btn = gtkut_stock_button("edit-find", "_Find");
   1035 	gtk_widget_set_can_default(lup_btn, TRUE);
   1036 	gtk_box_pack_start(GTK_BOX(hbox), lup_btn, TRUE, TRUE, 0);
   1037 
   1038 	g_signal_connect(G_OBJECT(help_btn), "clicked",
   1039 			 G_CALLBACK(manual_open_with_anchor_cb),
   1040 			 MANUAL_ANCHOR_ADDRBOOK);
   1041 
   1042 	g_signal_connect(G_OBJECT(edit_btn), "clicked",
   1043 			 G_CALLBACK(addressbook_edit_clicked), NULL);
   1044 	g_signal_connect(G_OBJECT(del_btn), "clicked",
   1045 			 G_CALLBACK(addressbook_del_clicked), NULL);
   1046 	g_signal_connect(G_OBJECT(reg_btn), "clicked",
   1047 			 G_CALLBACK(addressbook_reg_clicked), NULL);
   1048 	g_signal_connect(G_OBJECT(lup_btn), "clicked",
   1049 			 G_CALLBACK(addressbook_lup_clicked), NULL);
   1050 
   1051 	to_btn = gtk_button_new_with_label
   1052 		("To:");
   1053 	gtk_widget_set_can_default(to_btn, TRUE);
   1054 	gtk_box_pack_start(GTK_BOX(hbbox), to_btn, TRUE, TRUE, 0);
   1055 	cc_btn = gtk_button_new_with_label
   1056 		("Cc:");
   1057 	gtk_widget_set_can_default(cc_btn, TRUE);
   1058 	gtk_box_pack_start(GTK_BOX(hbbox), cc_btn, TRUE, TRUE, 0);
   1059 	bcc_btn = gtk_button_new_with_label
   1060 		("Bcc:");
   1061 	gtk_widget_set_can_default(bcc_btn, TRUE);
   1062 	gtk_box_pack_start(GTK_BOX(hbbox), bcc_btn, TRUE, TRUE, 0);
   1063 
   1064 	close_btn = gtkut_stock_button("window-close", "_Close");
   1065 	gtk_widget_set_can_default(close_btn, TRUE);
   1066 	gtk_box_pack_start(GTK_BOX(hbbox), close_btn, TRUE, TRUE, 0);
   1067 
   1068 	g_signal_connect(G_OBJECT(to_btn), "clicked",
   1069 			 G_CALLBACK(addressbook_to_clicked),
   1070 			 GINT_TO_POINTER(COMPOSE_TO));
   1071 	g_signal_connect(G_OBJECT(cc_btn), "clicked",
   1072 			 G_CALLBACK(addressbook_to_clicked),
   1073 			 GINT_TO_POINTER(COMPOSE_CC));
   1074 	g_signal_connect(G_OBJECT(bcc_btn), "clicked",
   1075 			 G_CALLBACK(addressbook_to_clicked),
   1076 			 GINT_TO_POINTER(COMPOSE_BCC));
   1077 	g_signal_connect(G_OBJECT(close_btn), "clicked",
   1078 			 G_CALLBACK(addressbook_close_clicked), NULL);
   1079 
   1080 	/* Build icons for interface */
   1081 
   1082 	/* Build control tables */
   1083 	addrbookctl_build_map(window);
   1084 	addrbookctl_build_iflist();
   1085 	addrbookctl_build_ifselect();
   1086 
   1087 	addrbook.clist   = NULL;
   1088 
   1089 	/* Add each interface into the tree as a root level folder */
   1090 	nodeIf = _addressInterfaceList_;
   1091 	while( nodeIf ) {
   1092 		AdapterInterface *adapter = nodeIf->data;
   1093 		AddressInterface *iface = adapter->interface;
   1094 		nodeIf = g_list_next(nodeIf);
   1095 
   1096 		if(iface->useInterface) {
   1097 			AddressTypeControlItem *atci = adapter->atci;
   1098 			text = atci->displayName;
   1099 			adapter->treeNode =
   1100 				gtk_sctree_insert_node( GTK_CMCTREE(ctree),
   1101 					NULL, NULL, &text, FOLDER_SPACING,
   1102 					interfacexpm,
   1103 					interfacexpm,
   1104 					FALSE, FALSE );
   1105 			cm_menu_set_sensitive_full(ui_manager, atci->menuCommand, adapter->haveLibrary );
   1106 			gtk_cmctree_node_set_row_data_full(
   1107 				GTK_CMCTREE(ctree), adapter->treeNode, adapter,
   1108 				addressbook_free_treenode );
   1109 		}
   1110 	}
   1111 
   1112 	/* Popup menu */
   1113 
   1114 	MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popups", NULL, GTK_UI_MANAGER_MENUBAR);
   1115 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups", "ABTreePopup", "ABTreePopup", GTK_UI_MANAGER_MENU)
   1116 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "EditBook", "ABTreePopup/EditBook", GTK_UI_MANAGER_MENUITEM)
   1117 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "DeleteBook", "ABTreePopup/DeleteBook", GTK_UI_MANAGER_MENUITEM)
   1118 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Separator1", "ABTreePopup/---", GTK_UI_MANAGER_SEPARATOR)
   1119 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "NewBook", "ABTreePopup/NewBook", GTK_UI_MANAGER_MENUITEM)
   1120 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "NewFolder", "ABTreePopup/NewFolder", GTK_UI_MANAGER_MENUITEM)
   1121 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "NewGroup", "ABTreePopup/NewGroup", GTK_UI_MANAGER_MENUITEM)
   1122 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Separator2", "ABTreePopup/---", GTK_UI_MANAGER_SEPARATOR)
   1123 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Cut", "ABTreePopup/Cut", GTK_UI_MANAGER_MENUITEM)
   1124 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Copy", "ABTreePopup/Copy", GTK_UI_MANAGER_MENUITEM)
   1125 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Paste", "ABTreePopup/Paste", GTK_UI_MANAGER_MENUITEM)
   1126 
   1127 	tree_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
   1128 				gtk_ui_manager_get_widget(ui_manager, "/Popups/ABTreePopup")));
   1129 
   1130 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups", "ABListPopup", "ABListPopup", GTK_UI_MANAGER_MENU)
   1131 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "SelectAll", "ABListPopup/SelectAll", GTK_UI_MANAGER_MENUITEM)
   1132 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator1", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
   1133 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Edit", "ABListPopup/Edit", GTK_UI_MANAGER_MENUITEM)
   1134 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Delete", "ABListPopup/Delete", GTK_UI_MANAGER_MENUITEM)
   1135 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator2", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
   1136 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "NewAddress", "ABListPopup/NewAddress", GTK_UI_MANAGER_MENUITEM)
   1137 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "NewGroup", "ABListPopup/NewGroup", GTK_UI_MANAGER_MENUITEM)
   1138 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator3", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
   1139 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Cut", "ABListPopup/Cut", GTK_UI_MANAGER_MENUITEM)
   1140 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Copy", "ABListPopup/Copy", GTK_UI_MANAGER_MENUITEM)
   1141 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Paste", "ABListPopup/Paste", GTK_UI_MANAGER_MENUITEM)
   1142 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator4", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
   1143 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Mailto", "ABListPopup/Mailto", GTK_UI_MANAGER_MENUITEM)
   1144 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Merge", "ABListPopup/Merge", GTK_UI_MANAGER_MENUITEM)
   1145 	list_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
   1146 				gtk_ui_manager_get_widget(ui_manager, "/Popups/ABListPopup")));
   1147 
   1148 	addrbook.window  = window;
   1149 	addrbook.hpaned  = hpaned;
   1150 	addrbook.vpaned  = vpaned;
   1151 	addrbook.menubar = menubar;
   1152 	addrbook.ctree   = ctree;
   1153 	addrbook.ctree_swin
   1154 			 = ctree_swin;
   1155 	addrbook.editaddress_vbox = editaddress_vbox;
   1156 	addrbook.clist   = clist;
   1157 	addrbook.label	 = label;
   1158 	addrbook.entry   = entry;
   1159 	addrbook.statusbar = statusbar;
   1160 	addrbook.status_cid = gtk_statusbar_get_context_id(
   1161 			GTK_STATUSBAR(statusbar), "Addressbook Window" );
   1162 
   1163 	addrbook.help_btn = help_btn;
   1164 	addrbook.edit_btn = edit_btn;
   1165 	addrbook.del_btn = del_btn;
   1166 	addrbook.reg_btn = reg_btn;
   1167 	addrbook.lup_btn = lup_btn;
   1168 	addrbook.to_btn  = to_btn;
   1169 	addrbook.cc_btn  = cc_btn;
   1170 	addrbook.bcc_btn = bcc_btn;
   1171 
   1172 	addrbook.tree_popup   = tree_popup;
   1173 	addrbook.list_popup   = list_popup;
   1174 	addrbook.ui_manager   = ui_manager;
   1175 
   1176 	addrbook.listSelected = NULL;
   1177 
   1178 	if (!geometry.min_height) {
   1179 		geometry.min_width = ADDRESSBOOK_WIDTH;
   1180 		geometry.min_height = ADDRESSBOOK_HEIGHT;
   1181 	}
   1182 
   1183 	gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
   1184 				      GDK_HINT_MIN_SIZE);
   1185 	gtk_window_set_default_size(GTK_WINDOW(window), prefs_common.addressbookwin_width,
   1186 				    prefs_common.addressbookwin_height);
   1187 
   1188 	if (!prefs_common.addressbook_use_editaddress_dialog) {
   1189 		if (prefs_common.addressbook_vpaned_pos > 0)
   1190 			gtk_paned_set_position(GTK_PANED(vpaned),
   1191 				prefs_common.addressbook_vpaned_pos);
   1192 	}
   1193 	if (prefs_common.addressbook_hpaned_pos > 0)
   1194 		gtk_paned_set_position(GTK_PANED(hpaned),
   1195 			prefs_common.addressbook_hpaned_pos);
   1196 
   1197 
   1198 	gtk_widget_show_all(window);
   1199 }
   1200 
   1201 /**
   1202  * Close address book window and save to file(s).
   1203  */
   1204 static gint addressbook_close( void ) {
   1205 	address_completion_end(addrbook.window);
   1206 	if (!prefs_common.addressbook_use_editaddress_dialog)
   1207 		addressbook_edit_person_invalidate(NULL, NULL, NULL);
   1208 
   1209 	addressbook_pane_save_position();
   1210 
   1211 	gtk_widget_hide(addrbook.window);
   1212 	addressbook_export_to_file();
   1213 	return TRUE;
   1214 }
   1215 
   1216 /**
   1217  * Display message in status line.
   1218  * \param msg Message to display.
   1219  */
   1220 static void addressbook_status_show( gchar *msg ) {
   1221 	if( addrbook.statusbar != NULL ) {
   1222 		gtk_statusbar_pop(
   1223 			GTK_STATUSBAR(addrbook.statusbar),
   1224 			addrbook.status_cid );
   1225 		if( msg ) {
   1226 			gtk_statusbar_push(
   1227 				GTK_STATUSBAR(addrbook.statusbar),
   1228 				addrbook.status_cid, msg );
   1229 		}
   1230 	}
   1231 }
   1232 
   1233 static void addressbook_ds_show_message( AddressDataSource *ds ) {
   1234 	gint retVal;
   1235 	gchar *name;
   1236 	gchar *desc;
   1237 	*addressbook_msgbuf = '\0';
   1238 	if( ds ) {
   1239 		name = addrindex_ds_get_name( ds );
   1240 		retVal = addrindex_ds_get_status_code( ds );
   1241 		if( retVal == MGU_SUCCESS ) {
   1242 			g_snprintf( addressbook_msgbuf,
   1243 				    sizeof(addressbook_msgbuf), "%s", name );
   1244 		}
   1245 		else {
   1246 			desc = addressbook_err2string( _lutErrorsGeneral_, retVal );
   1247 			g_snprintf( addressbook_msgbuf,
   1248 			    sizeof(addressbook_msgbuf), "%s: %s", name, desc );
   1249 		}
   1250 	}
   1251 	addressbook_status_show( addressbook_msgbuf );
   1252 }
   1253 
   1254 static void addressbook_edit_clicked(GtkButton *button, gpointer data)
   1255 {
   1256 	addressbook_edit_address_cb(NULL, NULL);
   1257 }
   1258 
   1259 static gboolean find_person(AddrSelectItem *item_a, ItemPerson *person)
   1260 {
   1261 	return ((ItemPerson *)item_a->addressItem == person)?0:-1;
   1262 }
   1263 
   1264 /*
   1265 * Delete one or more objects from address list.
   1266 */
   1267 static void addressbook_del_clicked(GtkButton *button, gpointer data)
   1268 {
   1269 	GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
   1270 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   1271 	AddressObject *pobj;
   1272 	AdapterDSource *ads = NULL;
   1273 	GtkCMCTreeNode *nodeList;
   1274 	gboolean procFlag;
   1275 	AlertValue aval;
   1276 	AddressBookFile *abf = NULL;
   1277 	AddressDataSource *ds = NULL;
   1278 	AddressInterface *iface;
   1279 	AddrItemObject *aio;
   1280 	AddrSelectItem *item;
   1281 	GList *list, *node;
   1282 	gboolean refreshList = FALSE;
   1283 
   1284 	pobj = gtk_cmctree_node_get_row_data(ctree, addrbook.opened );
   1285 	cm_return_if_fail(pobj != NULL);
   1286 
   1287 	/* Test whether anything selected for deletion */
   1288 	nodeList = addrbook.listSelected;
   1289 
   1290 	aio = gtk_cmctree_node_get_row_data( clist, nodeList );
   1291 	if( aio == NULL) return;
   1292 	ds = addressbook_find_datasource( addrbook.treeSelected );
   1293 	if( ds == NULL ) return;
   1294 
   1295 	/* Test for read only */
   1296 	iface = ds->interface;
   1297 	if( iface->readOnly ) {
   1298 		alertpanel("Delete address(es)",
   1299 			"This address data is read-only and cannot be deleted.",
   1300 			"window-close", "_Close", NULL, NULL, NULL, NULL, ALERTFOCUS_FIRST);
   1301 		return;
   1302 	}
   1303 
   1304 	/* Test whether Ok to proceed */
   1305 	procFlag = FALSE;
   1306 	if( pobj->type == ADDR_DATASOURCE ) {
   1307 		ads = ADAPTER_DSOURCE(pobj);
   1308 		if( ads->subType == ADDR_BOOK ) procFlag = TRUE;
   1309 	}
   1310 	else if( pobj->type == ADDR_ITEM_FOLDER ) {
   1311 		procFlag = TRUE;
   1312 	}
   1313 	else if( pobj->type == ADDR_ITEM_GROUP ) {
   1314 		procFlag = TRUE;
   1315 	}
   1316 	if( ! procFlag ) return;
   1317 	abf = ds->rawDataSource;
   1318 	if( abf == NULL ) return;
   1319 
   1320 	gtk_cmclist_freeze(GTK_CMCLIST(addrbook.clist));
   1321 	g_signal_handlers_block_by_func
   1322 		(G_OBJECT(addrbook.clist),
   1323 		 G_CALLBACK(addressbook_list_row_unselected), NULL);
   1324 
   1325 	/* Process deletions */
   1326 	if( pobj->type == ADDR_DATASOURCE || pobj->type == ADDR_ITEM_FOLDER ) {
   1327 		GList *groups = NULL, *persons = NULL, *emails = NULL;
   1328 		gboolean group_delete = TRUE;
   1329 		/* Items inside folders */
   1330 		list = addrselect_get_list( _addressSelect_ );
   1331 		/* Confirm deletion */
   1332 		node = list;
   1333 		while( node ) {
   1334 			item = node->data;
   1335 			node = g_list_next( node );
   1336 			aio = ( AddrItemObject * ) item->addressItem;
   1337 			if( aio->type == ITEMTYPE_PERSON || aio->type == ITEMTYPE_EMAIL ) {
   1338 				group_delete = FALSE;
   1339 				break;
   1340 			}
   1341 		}
   1342 		if (group_delete) {
   1343 			aval = alertpanel("Delete group",
   1344 					"Really delete the group(s)?\nThe addresses it contains will not be lost.",
   1345 					NULL, "_Cancel", "edit-delete", "D_elete", NULL, NULL,
   1346 					ALERTFOCUS_SECOND );
   1347 			if( aval != G_ALERTALTERNATE ) {
   1348 				goto thaw_ret;
   1349 			}
   1350 		} else {
   1351 			aval = alertpanel("Delete address(es)",
   1352 					"Really delete the address(es)?",
   1353 					NULL, "_Cancel", "edit-delete", "D_elete", NULL, NULL,
   1354 					ALERTFOCUS_SECOND );
   1355 			if( aval != G_ALERTALTERNATE ) {
   1356 				goto thaw_ret;
   1357 			}
   1358 		}
   1359 
   1360 	/* first, set lists of groups and persons to remove */
   1361 		node = list;
   1362 		while( node ) {
   1363 			item = node->data;
   1364 			node = g_list_next( node );
   1365 			aio = ( AddrItemObject * ) item->addressItem;
   1366 			if (!aio)
   1367 				continue;
   1368 			if( aio->type == ITEMTYPE_GROUP ) {
   1369 				groups = g_list_prepend(groups, item);
   1370 			}
   1371 			else if( aio->type == ITEMTYPE_PERSON ) {
   1372 				persons = g_list_prepend(persons, item);
   1373 			}
   1374 		}
   1375 	/* then set list of emails to remove *if* they're not children of
   1376 	 * persons to remove */
   1377 		node = list;
   1378 		while( node ) {
   1379 			item = node->data;
   1380 			node = g_list_next( node );
   1381 			aio = ( AddrItemObject * ) item->addressItem;
   1382 			if (!aio)
   1383 				continue;
   1384 			if( aio->type == ITEMTYPE_EMAIL ) {
   1385 				ItemEMail *sitem = ( ItemEMail * ) aio;
   1386 				ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
   1387 				if (!g_list_find_custom(persons, person, (GCompareFunc)(find_person))) {
   1388 					emails = g_list_prepend(emails, item);
   1389 				}
   1390 				/* else, the email will be removed via the parent person */
   1391 			}
   1392 		}
   1393 	/* then delete groups */
   1394 		node = groups;
   1395 		while( node ) {
   1396 			item = node->data;
   1397 			node = g_list_next( node );
   1398 			aio = ( AddrItemObject * ) item->addressItem;
   1399 			if (!aio)
   1400 				continue;
   1401 			if( aio->type == ITEMTYPE_GROUP ) {
   1402 				ItemGroup *item = ( ItemGroup * ) aio;
   1403 				GtkCMCTreeNode *nd = NULL;
   1404 				nd = addressbook_find_group_node( addrbook.opened, item );
   1405 				item = addrbook_remove_group( abf, item );
   1406 				if( item ) {
   1407 					addritem_free_item_group( item );
   1408 				}
   1409 				/* Remove group from parent node */
   1410 				gtk_cmctree_remove_node( ctree, nd );
   1411 				refreshList = TRUE;
   1412 			}
   1413 		}
   1414 	/* then delete persons */
   1415 		node = persons;
   1416 		while( node ) {
   1417 			item = node->data;
   1418 			node = g_list_next( node );
   1419 			aio = ( AddrItemObject * ) item->addressItem;
   1420 			if (!aio)
   1421 				continue;
   1422 			if( aio->type == ITEMTYPE_PERSON ) {
   1423 				ItemPerson *item = ( ItemPerson * ) aio;
   1424 				item->status = DELETE_ENTRY;
   1425 				addressbook_folder_remove_one_person( clist, item );
   1426 				if (pobj->type == ADDR_ITEM_FOLDER)
   1427 					addritem_folder_remove_person(ADAPTER_FOLDER(pobj)->itemFolder, item);
   1428 				item = addrbook_remove_person( abf, item );
   1429 				if( item ) {
   1430 					addritem_person_remove_picture(item);
   1431 					addritem_free_item_person( item );
   1432 				}
   1433 			}
   1434 		}
   1435 	/* then delete emails */
   1436 		node = emails;
   1437 		while( node ) {
   1438 			item = node->data;
   1439 			node = g_list_next( node );
   1440 			aio = ( AddrItemObject * ) item->addressItem;
   1441 			if (!aio)
   1442 				continue;
   1443 
   1444 			if( aio->type == ITEMTYPE_EMAIL ) {
   1445 				ItemEMail *sitem = ( ItemEMail * ) aio;
   1446 				ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
   1447 				sitem = addrbook_person_remove_email( abf, person, sitem );
   1448 				if( sitem ) {
   1449 					addrcache_remove_email(abf->addressCache, sitem);
   1450 					addritem_free_item_email( sitem );
   1451 				}
   1452 				addressbook_folder_refresh_one_person( clist, person );
   1453 			}
   1454 		}
   1455 		g_list_free( groups );
   1456 		g_list_free( persons );
   1457 		g_list_free( emails );
   1458 		g_list_free( list );
   1459 		addressbook_list_select_clear();
   1460 		if( refreshList ) {
   1461 			gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened);
   1462 			addressbook_set_clist(
   1463 				gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
   1464 					addrbook.opened),
   1465 				TRUE);
   1466 		}
   1467 		addrbook_set_dirty(abf, TRUE);
   1468 		addressbook_export_to_file();
   1469 		addressbook_list_menu_setup();
   1470 		goto thaw_ret;
   1471 	}
   1472 	else if( pobj->type == ADDR_ITEM_GROUP ) {
   1473 		/* Items inside groups */
   1474 		list = addrselect_get_list( _addressSelect_ );
   1475 		node = list;
   1476 		while( node ) {
   1477 			item = node->data;
   1478 			node = g_list_next( node );
   1479 			aio = ( AddrItemObject * ) item->addressItem;
   1480 			if( aio->type == ITEMTYPE_EMAIL ) {
   1481 				ItemEMail *item = ( ItemEMail * ) aio;
   1482 				ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(item);
   1483 				item = addrbook_person_remove_email( abf, person, item );
   1484 				if( item ) {
   1485 					addritem_free_item_email( item );
   1486 				}
   1487 			}
   1488 		}
   1489 		g_list_free( list );
   1490 		addressbook_list_select_clear();
   1491 		gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened);
   1492 		addressbook_set_clist(
   1493 			gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
   1494 				addrbook.opened),
   1495 			TRUE);
   1496 
   1497 		addrbook_set_dirty(abf, TRUE);
   1498 		addressbook_export_to_file();
   1499 		addressbook_list_menu_setup();
   1500 		goto thaw_ret;
   1501 	}
   1502 
   1503 	gtk_cmctree_node_set_row_data( clist, nodeList, NULL );
   1504 	gtk_cmctree_remove_node( clist, nodeList );
   1505 thaw_ret:
   1506 	gtk_cmclist_thaw(GTK_CMCLIST(addrbook.clist));
   1507 	g_signal_handlers_unblock_by_func
   1508 		(G_OBJECT(addrbook.clist),
   1509 		 G_CALLBACK(addressbook_list_row_unselected), NULL);
   1510 }
   1511 
   1512 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
   1513 {
   1514 	addressbook_new_address_cb( NULL, NULL );
   1515 }
   1516 
   1517 static gchar *addressbook_format_address( AddrItemObject * aio ) {
   1518 	gchar *buf = NULL;
   1519 	gchar *name = NULL;
   1520 	gchar *address = NULL;
   1521 
   1522 	if( aio->type == ITEMTYPE_EMAIL ) {
   1523 		ItemPerson *person = NULL;
   1524 		ItemEMail *email = ( ItemEMail * ) aio;
   1525 
   1526 		person = ( ItemPerson * ) ADDRITEM_PARENT(email);
   1527 		if( email->address ) {
   1528 			if( ADDRITEM_NAME(email) ) {
   1529 				name = ADDRITEM_NAME(email);
   1530 				if( *name == '\0' ) {
   1531 					name = ADDRITEM_NAME(person);
   1532 				}
   1533 			}
   1534 			else if( ADDRITEM_NAME(person) ) {
   1535 				name = ADDRITEM_NAME(person);
   1536 			}
   1537 			else {
   1538 				buf = g_strdup( email->address );
   1539 			}
   1540 			address = email->address;
   1541 		}
   1542 	}
   1543 	else if( aio->type == ITEMTYPE_PERSON ) {
   1544 		ItemPerson *person = ( ItemPerson * ) aio;
   1545 		GList *node = person->listEMail;
   1546 
   1547 		name = ADDRITEM_NAME(person);
   1548 		if( node ) {
   1549 			ItemEMail *email = ( ItemEMail * ) node->data;
   1550 			address = email->address;
   1551 		}
   1552 	}
   1553 	if( address ) {
   1554 		if( name && name[0] != '\0' ) {
   1555 			if( strchr_with_skip_quote( name, '"', ',' ) )
   1556 				buf = g_strdup_printf( "\"%s\" <%s>", name, address );
   1557 			else
   1558 				buf = g_strdup_printf( "%s <%s>", name, address );
   1559 		}
   1560 		else {
   1561 			buf = g_strdup( address );
   1562 		}
   1563 	}
   1564 
   1565 	return buf;
   1566 }
   1567 
   1568 static void addressbook_to_clicked(GtkButton *button, gpointer data)
   1569 {
   1570 	GList *list, *node;
   1571 	Compose *compose;
   1572 	AddrSelectItem *item;
   1573 	AddrItemObject *aio;
   1574 	gchar *addr;
   1575 
   1576 	compose = addrbook.target_compose;
   1577 	if( ! compose ) return;
   1578 
   1579 	/* Nothing selected, but maybe there is something in text entry */
   1580 	addr = (char *)gtk_entry_get_text( GTK_ENTRY( addrbook.entry) );
   1581 	if ( addr ) {
   1582 		compose_entry_append(
   1583 			compose, addr, (ComposeEntryType)data , PREF_NONE);
   1584 	}
   1585 
   1586 	/* Select from address list */
   1587 	list = addrselect_get_list( _addressSelect_ );
   1588 	node = list;
   1589 	if (node) {
   1590 		while( node ) {
   1591 			item = node->data;
   1592 			node = g_list_next( node );
   1593 			aio = item->addressItem;
   1594 			if( aio->type == ITEMTYPE_PERSON ||
   1595 			    aio->type == ITEMTYPE_EMAIL ) {
   1596 				addr = addressbook_format_address( aio );
   1597 				compose_entry_append(
   1598 					compose, addr, (ComposeEntryType) data, PREF_NONE );
   1599 				g_free( addr );
   1600 			}
   1601 			else if( aio->type == ITEMTYPE_GROUP ) {
   1602 				ItemGroup *group = ( ItemGroup * ) aio;
   1603 				GList *nodeMail = group->listEMail;
   1604 				while( nodeMail ) {
   1605 					ItemEMail *email = nodeMail->data;
   1606 
   1607 					addr = addressbook_format_address(
   1608 							( AddrItemObject * ) email );
   1609 					compose_entry_append(
   1610 						compose, addr, (ComposeEntryType) data, PREF_NONE );
   1611 					g_free( addr );
   1612 					nodeMail = g_list_next( nodeMail );
   1613 				}
   1614 			}
   1615 		}
   1616 	} else {
   1617 		AddressObject *obj = NULL;
   1618 
   1619 		obj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
   1620 
   1621 		if( obj && obj->type == ADDR_ITEM_GROUP ) {
   1622 			ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup;
   1623 			GList *nodeMail = itemGroup->listEMail;
   1624 			while( nodeMail ) {
   1625 				ItemEMail *email = nodeMail->data;
   1626 
   1627 				addr = addressbook_format_address(
   1628 						( AddrItemObject * ) email );
   1629 				compose_entry_append(
   1630 					compose, addr, (ComposeEntryType) data, PREF_NONE );
   1631 				g_free( addr );
   1632 				nodeMail = g_list_next( nodeMail );
   1633 			}
   1634 		}
   1635 	}
   1636 	g_list_free( list );
   1637 }
   1638 
   1639 static void addressbook_menubar_set_sensitive( gboolean sensitive ) {
   1640 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook",   sensitive );
   1641 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook", sensitive );
   1642 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/NewFolder",  sensitive );
   1643 
   1644 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/SelectAll",    TRUE );
   1645 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Cut",    sensitive );
   1646 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Copy",   sensitive );
   1647 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Paste",  sensitive );
   1648 
   1649 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/NewAddress", sensitive );
   1650 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/NewGroup",   sensitive );
   1651 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Mailto",     sensitive );
   1652 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Merge",      sensitive );
   1653 	gtk_widget_set_sensitive( addrbook.edit_btn, sensitive );
   1654 	gtk_widget_set_sensitive( addrbook.del_btn, sensitive );
   1655 }
   1656 
   1657 static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCMCTreeNode *node ) {
   1658 	gboolean canEdit = FALSE;
   1659 	gboolean canDelete = TRUE;
   1660 	gboolean canAdd = FALSE;
   1661 	gboolean canEditTr = TRUE;
   1662 	gboolean editAddress = FALSE;
   1663 	gboolean canExport = TRUE;
   1664 	AddressTypeControlItem *atci = NULL;
   1665 	AddressDataSource *ds = NULL;
   1666 	AddressInterface *iface = NULL;
   1667 
   1668 	if( obj == NULL ) return;
   1669 	if( obj->type == ADDR_INTERFACE ) {
   1670 		AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
   1671 		iface = adapter->interface;
   1672 		if( iface ) {
   1673 			if( iface->haveLibrary ) {
   1674 				/* Enable appropriate File / New command */
   1675 				atci = adapter->atci;
   1676 				cm_menu_set_sensitive_full(addrbook.ui_manager, atci->menuCommand, TRUE );
   1677 			}
   1678 		}
   1679 		canEditTr = canExport = FALSE;
   1680 	}
   1681 	else if( obj->type == ADDR_DATASOURCE ) {
   1682 		AdapterDSource *ads = ADAPTER_DSOURCE(obj);
   1683 		ds = ads->dataSource;
   1684 		iface = ds->interface;
   1685 		if( ! iface->readOnly ) {
   1686 			canAdd = canEdit = editAddress = canDelete = TRUE;
   1687 		}
   1688 		if( ! iface->haveLibrary ) {
   1689 			canAdd = canEdit = editAddress = canExport = canDelete = FALSE;
   1690 		}
   1691 	}
   1692 	else if( obj->type == ADDR_ITEM_FOLDER ) {
   1693 		ds = addressbook_find_datasource( addrbook.treeSelected );
   1694 		if( ds ) {
   1695 			iface = ds->interface;
   1696 			if( iface->readOnly ) {
   1697 				canEditTr = FALSE;
   1698 				canDelete = FALSE;
   1699 			}
   1700 			else {
   1701 				canAdd = editAddress = TRUE;
   1702 			}
   1703 		}
   1704 	}
   1705 	else if( obj->type == ADDR_ITEM_GROUP ) {
   1706 		ds = addressbook_find_datasource( addrbook.treeSelected );
   1707 		if( ds ) {
   1708 			iface = ds->interface;
   1709 			if( ! iface->readOnly ) {
   1710 				editAddress = TRUE;
   1711 			}
   1712 		}
   1713 	}
   1714 
   1715 	if( addrbook.listSelected == NULL )
   1716 		canEdit = FALSE;
   1717 
   1718 	/* Enable add */
   1719 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/NewAddress", editAddress );
   1720 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/NewGroup",   canAdd );
   1721 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/NewFolder",  canAdd );
   1722 	gtk_widget_set_sensitive( addrbook.reg_btn, editAddress );
   1723 
   1724 	/* Enable edit */
   1725 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Edit",   canEdit );
   1726 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Delete", canDelete );
   1727 	gtk_widget_set_sensitive( addrbook.edit_btn, canEdit );
   1728 	gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
   1729 
   1730 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook",      canEditTr );
   1731 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook",    canEditTr );
   1732 }
   1733 
   1734 /**
   1735  * Address book tree callback function that responds to selection of tree
   1736  * items.
   1737  *
   1738  * \param ctree  Tree widget.
   1739  * \param node   Node that was selected.
   1740  * \param column Column number where selected occurred.
   1741  * \param data   Pointer to user data.
   1742  */
   1743 static void addressbook_tree_selected(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   1744 				      gint column, gpointer data)
   1745 {
   1746 	AddressObject *obj = NULL;
   1747 	AdapterDSource *ads = NULL;
   1748 	AddressDataSource *ds = NULL;
   1749 	ItemFolder *rootFolder = NULL;
   1750 	AddressObjectType aot;
   1751 
   1752 	addrbook.treeSelected = node;
   1753 	addrbook.listSelected = NULL;
   1754 	addressbook_status_show( "" );
   1755 	if( addrbook.entry != NULL ) gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
   1756 
   1757 	if( node ) obj = gtk_cmctree_node_get_row_data( ctree, node );
   1758 	if( obj == NULL ) {
   1759 		addressbook_set_clist(NULL, TRUE);
   1760 		return;
   1761 	}
   1762 	addrbook.opened = node;
   1763 
   1764 	if( obj->type == ADDR_DATASOURCE ) {
   1765 		/* Read from file */
   1766 		static gboolean tVal = TRUE;
   1767 
   1768 		ads = ADAPTER_DSOURCE(obj);
   1769 
   1770 		ds = ads->dataSource;
   1771 		if( ds == NULL ) return;
   1772 
   1773 		if( addrindex_ds_get_modify_flag( ds ) ) {
   1774 			addrindex_ds_read_data( ds );
   1775 		}
   1776 
   1777 		if( ! addrindex_ds_get_read_flag( ds ) ) {
   1778 			addrindex_ds_read_data( ds );
   1779 		}
   1780 		addressbook_ds_show_message( ds );
   1781 
   1782 		if( ! addrindex_ds_get_access_flag( ds ) ) {
   1783 			/* Remove existing folders and groups */
   1784 			gtk_cmclist_freeze( GTK_CMCLIST(ctree) );
   1785 			addressbook_tree_remove_children( ctree, node );
   1786 			gtk_cmclist_thaw( GTK_CMCLIST(ctree) );
   1787 
   1788 			/* Load folders into the tree */
   1789 			rootFolder = addrindex_ds_get_root_folder( ds );
   1790 			aot = ADDR_ITEM_FOLDER;
   1791 			addressbook_node_add_folder( node, ds, rootFolder, aot );
   1792 			addrindex_ds_set_access_flag( ds, &tVal );
   1793 			gtk_cmctree_expand( ctree, node );
   1794 		}
   1795 	} else {
   1796 		addressbook_set_clist(NULL, TRUE);
   1797 	}
   1798 
   1799 	/* Update address list */
   1800 	g_signal_handlers_block_by_func
   1801 		(G_OBJECT(ctree),
   1802 		 G_CALLBACK(addressbook_tree_selected), NULL);
   1803 	addressbook_set_clist( obj, FALSE );
   1804 	g_signal_handlers_unblock_by_func
   1805 		(G_OBJECT(ctree),
   1806 		 G_CALLBACK(addressbook_tree_selected), NULL);
   1807 	if (!prefs_common.addressbook_use_editaddress_dialog)
   1808 		addressbook_edit_person_invalidate(NULL, NULL, NULL);
   1809 
   1810 	/* Setup main menu selections */
   1811 	addressbook_menubar_set_sensitive( FALSE );
   1812 	addressbook_menuitem_set_sensitive( obj, node );
   1813 	addressbook_list_select_clear();
   1814 	addressbook_list_menu_setup();
   1815 	return;
   1816 }
   1817 
   1818 /**
   1819  * Setup address list popup menu items. Items are enabled or disabled as
   1820  * required.
   1821  */
   1822 static void addressbook_list_menu_setup( void ) {
   1823 	GtkCMCTree *clist = NULL;
   1824 	AddressObject *pobj = NULL;
   1825 	AddressObject *obj = NULL;
   1826 	AdapterDSource *ads = NULL;
   1827 	AddressInterface *iface = NULL;
   1828 	AddressDataSource *ds = NULL;
   1829 	GList *list;
   1830 	AddrItemObject *aio;
   1831 	AddrSelectItem *item;
   1832 	gboolean canEdit = FALSE;
   1833 	gboolean canDelete = FALSE;
   1834 	gboolean canCut = FALSE;
   1835 	gboolean canCopy = FALSE;
   1836 	gboolean canPaste = FALSE;
   1837 	gboolean canMerge = FALSE;
   1838 
   1839 	pobj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
   1840 	if( pobj == NULL ) return;
   1841 
   1842 	clist = GTK_CMCTREE(addrbook.clist);
   1843 	obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
   1844 	if( obj == NULL ) canEdit = FALSE;
   1845 
   1846 	menu_set_insensitive_all( GTK_MENU_SHELL(addrbook.list_popup) );
   1847 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/SelectAll", TRUE );
   1848 
   1849 	if( pobj->type == ADDR_DATASOURCE ) {
   1850 		/* Parent object is a data source */
   1851 		ads = ADAPTER_DSOURCE(pobj);
   1852 		ds = ads->dataSource;
   1853 		if (!ds)
   1854 			return;
   1855 		iface = ds->interface;
   1856 		if (!iface)
   1857 			return;
   1858 		if( ! iface->readOnly ) {
   1859 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewAddress", TRUE );
   1860 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewGroup", TRUE );
   1861 			gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
   1862 			if( obj )
   1863 				canEdit = TRUE;
   1864 			canDelete = canEdit;
   1865 		}
   1866 	}
   1867 	else if( pobj->type != ADDR_INTERFACE ) {
   1868 		/* Parent object is not an interface */
   1869 		ds = addressbook_find_datasource( addrbook.treeSelected );
   1870 		if (!ds)
   1871 			return;
   1872 		iface = ds->interface;
   1873 		if (!iface)
   1874 			return;
   1875 		if( ! iface->readOnly ) {
   1876 			/* Folder or group */
   1877 			if( pobj->type == ADDR_ITEM_FOLDER || pobj->type == ADDR_ITEM_GROUP ) {
   1878 				cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewAddress", TRUE );
   1879 				gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
   1880 				if( obj ) canEdit = TRUE;
   1881 			}
   1882 			/* Folder */
   1883 			if( pobj->type == ADDR_ITEM_FOLDER ) {
   1884 				cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewGroup", TRUE );
   1885 				if( obj ) canEdit = TRUE;
   1886 			}
   1887 			canDelete = canEdit;
   1888 		}
   1889 	}
   1890 
   1891 	if( iface ) {
   1892 		/* Enable cut and paste */
   1893 		if( ! addrclip_is_empty( _clipBoard_ ) )
   1894 			canPaste = TRUE;
   1895 		if( ! addrselect_test_empty( _addressSelect_ ) )
   1896 			canCut = TRUE;
   1897 		/* Enable copy if something is selected */
   1898 		if( ! addrselect_test_empty( _addressSelect_ ) )
   1899 			canCopy = TRUE;
   1900 	}
   1901 
   1902 	/* Disable edit or browse if more than one row selected */
   1903 	if( GTK_CMCLIST(clist)->selection && GTK_CMCLIST(clist)->selection->next ) {
   1904 		canEdit = FALSE;
   1905 	}
   1906 
   1907 	/* Allow merging persons or emails are selected */
   1908 	list = _addressSelect_->listSelect;
   1909 	if (list && list->next ) {
   1910 		item = list->data;
   1911 		aio = ( AddrItemObject * ) item->addressItem;
   1912 		if( aio->type == ITEMTYPE_EMAIL ||
   1913 				aio->type == ITEMTYPE_PERSON ) {
   1914 			canMerge = TRUE;
   1915 		}
   1916 	}
   1917 
   1918 	/* Forbid write changes when read-only */
   1919 	if( iface && iface->readOnly ) {
   1920 		canCut = FALSE;
   1921 		canDelete = FALSE;
   1922 		canPaste = FALSE;
   1923 		canMerge = FALSE;
   1924 	}
   1925 
   1926 	/* Now go finalize menu items */
   1927 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Edit",   canEdit );
   1928 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Delete", canDelete );
   1929 
   1930 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Cut",           canCut );
   1931 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Copy",          canCopy );
   1932 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Paste",         canPaste );
   1933 
   1934 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Mailto",       canCopy );
   1935 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Merge",        canMerge );
   1936 
   1937 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Cut",           canCut );
   1938 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Copy",          canCopy );
   1939 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Paste",         canPaste );
   1940 
   1941 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Edit",    canEdit );
   1942 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Delete",  canDelete );
   1943 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Mailto", canCopy );
   1944 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Merge",  canMerge );
   1945 
   1946 	gtk_widget_set_sensitive( addrbook.edit_btn, canEdit );
   1947 	gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
   1948 
   1949 	if (addrbook.target_compose) {
   1950 		gtk_widget_set_sensitive(addrbook.to_btn, obj ? TRUE : FALSE);
   1951 		gtk_widget_set_sensitive(addrbook.cc_btn, obj ? TRUE : FALSE);
   1952 		gtk_widget_set_sensitive(addrbook.bcc_btn, obj ? TRUE : FALSE);
   1953 	}
   1954 }
   1955 
   1956 static void addressbook_select_row_tree	(GtkCMCTree	*ctree,
   1957 					 GtkCMCTreeNode	*node,
   1958 					 gint		 column,
   1959 					 gpointer	 data)
   1960 {
   1961 }
   1962 
   1963 /**
   1964  * Add list of items into tree node below specified tree node.
   1965  * \param treeNode  Tree node.
   1966  * \param ds        Data source.
   1967  * \param listItems List of items.
   1968  */
   1969 static void addressbook_treenode_add_list(
   1970 	GtkCMCTreeNode *treeNode, AddressDataSource *ds, GList *listItems )
   1971 {
   1972 	GList *node;
   1973 
   1974 	node = listItems;
   1975 	while( node ) {
   1976 		AddrItemObject *aio;
   1977 		GtkCMCTreeNode *nn;
   1978 
   1979 		aio = node->data;
   1980 		if( ADDRESS_OBJECT_TYPE(aio) == ADDR_ITEM_GROUP ) {
   1981 			ItemGroup *group;
   1982 
   1983 			group = ( ItemGroup * ) aio;
   1984 			nn = addressbook_node_add_group( treeNode, ds, group );
   1985 			if (nn == NULL) {
   1986 				g_message("error adding addressbook group\n");
   1987 			}
   1988 		}
   1989 		else if( ADDRESS_OBJECT_TYPE(aio) == ADDR_ITEM_FOLDER ) {
   1990 			ItemFolder *folder;
   1991 
   1992 			folder = ( ItemFolder * ) aio;
   1993 			nn = addressbook_node_add_folder(
   1994 				treeNode, ds, folder, ADDR_ITEM_FOLDER );
   1995 			if (nn == NULL) {
   1996 				g_message("error adding addressbook folder\n");
   1997 			}
   1998 		}
   1999 		node = g_list_next( node );
   2000 	}
   2001 }
   2002 
   2003 static void addressbook_select_all_cb( GtkAction *action, gpointer data ) {
   2004 	gtk_cmclist_select_all(GTK_CMCLIST(addrbook.clist));
   2005 }
   2006 
   2007 /**
   2008  * Cut from address list widget.
   2009  */
   2010 static void addressbook_clip_cut_cb( GtkAction *action, gpointer data ) {
   2011 	_clipBoard_->cutFlag = TRUE;
   2012 	addrclip_clear( _clipBoard_ );
   2013 	addrclip_add( _clipBoard_, _addressSelect_ );
   2014 #ifdef DEBUG_ADDRBOOK
   2015 	addrclip_list_show( _clipBoard_, stdout );
   2016 #endif
   2017 }
   2018 
   2019 /**
   2020  * Copy from address list widget.
   2021  */
   2022 static void addressbook_clip_copy_cb(GtkAction *action, gpointer data) {
   2023 	_clipBoard_->cutFlag = FALSE;
   2024 	addrclip_clear( _clipBoard_ );
   2025 	addrclip_add( _clipBoard_, _addressSelect_ );
   2026 #ifdef DEBUG_ADDRBOOK
   2027 	addrclip_list_show( _clipBoard_, stdout );
   2028 #endif
   2029 }
   2030 
   2031 /**
   2032  * Paste clipboard into address list widget.
   2033  */
   2034 static void addressbook_clip_paste_cb( GtkAction *action, gpointer data ) {
   2035 	GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
   2036 	AddressObject *pobj = NULL;
   2037 	AddressDataSource *ds = NULL;
   2038 	AddressBookFile *abf = NULL;
   2039 	ItemFolder *folder = NULL;
   2040 	GList *folderGroup = NULL;
   2041 
   2042 	ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
   2043 	if( ds == NULL ) return;
   2044 	if( addrindex_ds_get_readonly( ds ) ) {
   2045 		alertpanel_error("Cannot paste. Target address book is read-only.");
   2046 		return;
   2047 	}
   2048 
   2049 	pobj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
   2050 	if( pobj ) {
   2051 		if( pobj->type == ADDR_ITEM_FOLDER ) {
   2052 			folder = ADAPTER_FOLDER(pobj)->itemFolder;
   2053 		}
   2054 		else if( pobj->type == ADDR_ITEM_GROUP ) {
   2055 			alertpanel_error("Cannot paste into an address group.");
   2056 			return;
   2057 		}
   2058 	}
   2059 
   2060 	/* Get an address book */
   2061 	abf = addressbook_get_book_file();
   2062 	if( abf == NULL ) return;
   2063 
   2064 	if( _clipBoard_->cutFlag ) {
   2065 		/* Paste/Cut */
   2066 		folderGroup = addrclip_paste_cut( _clipBoard_, abf, folder );
   2067 
   2068 		/* Remove all groups and folders in clipboard from tree node */
   2069 		addressbook_treenode_remove_item();
   2070 
   2071 		/* Remove all "cut" items */
   2072 		addrclip_delete_item( _clipBoard_ );
   2073 
   2074 		/* Clear clipboard - cut items??? */
   2075 		addrclip_clear( _clipBoard_ );
   2076 	}
   2077 	else {
   2078 		/* Paste/Copy */
   2079 		folderGroup = addrclip_paste_copy( _clipBoard_, abf, folder );
   2080 	}
   2081 
   2082 #ifdef DEBUG_ADDRBOOK
   2083 	addrclip_list_show( _clipBoard_, stdout );
   2084 #endif
   2085 	if( folderGroup ) {
   2086 		/* Update tree by inserting node for each folder or group */
   2087 		addressbook_treenode_add_list(
   2088 			addrbook.treeSelected, ds, folderGroup );
   2089 		gtk_cmctree_expand( ctree, addrbook.treeSelected );
   2090 		g_list_free( folderGroup );
   2091 		folderGroup = NULL;
   2092 	}
   2093 
   2094 	/* Display items pasted */
   2095 	gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened );
   2096 	addressbook_set_clist(
   2097 		gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
   2098 			addrbook.opened),
   2099 		TRUE);
   2100 
   2101 
   2102 }
   2103 
   2104 /**
   2105  * Add current treenode object to clipboard. Note that widget only allows
   2106  * one entry from the tree list to be selected.
   2107  */
   2108 static void addressbook_treenode_to_clipboard( void ) {
   2109 	AddressObject *obj = NULL;
   2110 	AddressDataSource *ds = NULL;
   2111 	AddrSelectItem *item;
   2112 	GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
   2113 	GtkCMCTreeNode *node;
   2114 
   2115 	node = addrbook.treeSelected;
   2116 	if( node == NULL ) return;
   2117 	obj = gtk_cmctree_node_get_row_data( ctree, node );
   2118 	if( obj == NULL ) return;
   2119 
   2120 	ds = addressbook_find_datasource( node );
   2121 	if( ds == NULL ) return;
   2122 
   2123 	item = NULL;
   2124 	if( obj->type == ADDR_ITEM_FOLDER ) {
   2125 		AdapterFolder *adapter = ADAPTER_FOLDER(obj);
   2126 		ItemFolder *folder = adapter->itemFolder;
   2127 
   2128 		item = addrselect_create_node( obj );
   2129 		item->uid = g_strdup( ADDRITEM_ID(folder) );
   2130 	}
   2131 	else if( obj->type == ADDR_ITEM_GROUP ) {
   2132 		AdapterGroup *adapter = ADAPTER_GROUP(obj);
   2133 		ItemGroup *group = adapter->itemGroup;
   2134 
   2135 		item = addrselect_create_node( obj );
   2136 		item->uid = g_strdup( ADDRITEM_ID(group) );
   2137 	}
   2138 	else if( obj->type == ADDR_DATASOURCE ) {
   2139 		/* Data source */
   2140 		item = addrselect_create_node( obj );
   2141 		item->uid = NULL;
   2142 	}
   2143 
   2144 	if( item ) {
   2145 		/* Clear existing list and add item into list */
   2146 		gchar *cacheID;
   2147 
   2148 		addressbook_list_select_clear();
   2149 		cacheID = addrindex_get_cache_id( _addressIndex_, ds );
   2150 		addrselect_list_add( _addressSelect_, item, cacheID );
   2151 		g_free( cacheID );
   2152 	}
   2153 }
   2154 
   2155 /**
   2156  * Cut from tree widget.
   2157  */
   2158 static void addressbook_treenode_cut_cb( GtkAction *action, gpointer data ) {
   2159 	_clipBoard_->cutFlag = TRUE;
   2160 	addressbook_treenode_to_clipboard();
   2161 	addrclip_clear( _clipBoard_ );
   2162 	addrclip_add( _clipBoard_, _addressSelect_ );
   2163 #ifdef DEBUG_ADDRBOOK
   2164 	addrclip_list_show( _clipBoard_, stdout );
   2165 #endif
   2166 }
   2167 
   2168 /**
   2169  * Copy from tree widget.
   2170  */
   2171 static void addressbook_treenode_copy_cb( GtkAction *action, gpointer data ) {
   2172 	_clipBoard_->cutFlag = FALSE;
   2173 	addressbook_treenode_to_clipboard();
   2174 	addrclip_clear( _clipBoard_ );
   2175 	addrclip_add( _clipBoard_, _addressSelect_ );
   2176 #ifdef DEBUG_ADDRBOOK
   2177 	addrclip_list_show( _clipBoard_, stdout );
   2178 #endif
   2179 }
   2180 
   2181 /**
   2182  * Paste clipboard into address tree widget.
   2183  */
   2184 static void addressbook_treenode_paste_cb( GtkAction *action, gpointer data ) {
   2185 	addressbook_clip_paste_cb(NULL,NULL);
   2186 }
   2187 
   2188 /**
   2189  * Clear selected entries in clipboard.
   2190  */
   2191 static void addressbook_list_select_clear( void ) {
   2192 	addrselect_list_clear( _addressSelect_ );
   2193 }
   2194 
   2195 /**
   2196  * Add specified address item to selected address list.
   2197  * \param aio Address item object.
   2198  * \param ds  Datasource.
   2199  */
   2200 static void addressbook_list_select_add( AddrItemObject *aio, AddressDataSource *ds ) {
   2201 	gchar *cacheID;
   2202 
   2203 	if( ds == NULL ) return;
   2204 	cacheID = addrindex_get_cache_id( _addressIndex_, ds );
   2205 	addrselect_list_add_obj( _addressSelect_, aio, cacheID );
   2206 	g_free( cacheID );
   2207 }
   2208 
   2209 /**
   2210  * Remove specified address item from selected address list.
   2211  * \param aio Address item object.
   2212  */
   2213 static void addressbook_list_select_remove( AddrItemObject *aio ) {
   2214 	addrselect_list_remove( _addressSelect_, aio );
   2215 }
   2216 
   2217 /**
   2218  * Invoke EMail compose window with addresses in selected address list.
   2219  */
   2220 static void addressbook_mail_to_cb( GtkAction *action, gpointer data ) {
   2221 	GList *listAddress;
   2222 
   2223 	if( ! addrselect_test_empty( _addressSelect_ ) ) {
   2224 		listAddress = addrselect_build_list( _addressSelect_ );
   2225 		compose_new_with_list( NULL, listAddress );
   2226 		g_list_free_full( listAddress, g_free );
   2227 		listAddress = NULL;
   2228 	}
   2229 }
   2230 
   2231 static void addressbook_merge_list( AddrSelectList *list ) {
   2232 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   2233 	GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
   2234 	AddressObject *pobj;
   2235 	AddressDataSource *ds = NULL;
   2236 
   2237 	pobj = gtk_cmctree_node_get_row_data(ctree, addrbook.opened );
   2238 	cm_return_if_fail(pobj != NULL);
   2239 
   2240 	ds = addressbook_find_datasource( addrbook.treeSelected );
   2241 	if( ds == NULL ) return;
   2242 
   2243 	addrmerge_merge(clist, pobj, ds, list);
   2244 }
   2245 
   2246 /**
   2247  * Merge selected entries in the address list
   2248  */
   2249 static void addressbook_merge_cb( GtkAction *action, gpointer data ) {
   2250 	if( addrselect_test_empty( _addressSelect_ ) )
   2251 		return;
   2252 
   2253 	addressbook_merge_list( _addressSelect_ );
   2254 }
   2255 
   2256 static void addressbook_list_row_selected( GtkCMCTree *clist,
   2257 					   GtkCMCTreeNode *node,
   2258 					   gint column,
   2259 					   gpointer data )
   2260 {
   2261 	AddrItemObject *aio = NULL;
   2262 	AddressObject *pobj = NULL;
   2263 	AdapterDSource *ads = NULL;
   2264 	AddressDataSource *ds = NULL;
   2265 
   2266 	addrbook.listSelected = node;
   2267 
   2268 	pobj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
   2269 	if( pobj == NULL ) return;
   2270 
   2271 	if( pobj->type == ADDR_DATASOURCE ) {
   2272 		ads = ADAPTER_DSOURCE(pobj);
   2273 		ds = ads->dataSource;
   2274 	}
   2275 	else if( pobj->type != ADDR_INTERFACE ) {
   2276 		ds = addressbook_find_datasource( addrbook.treeSelected );
   2277 	}
   2278 
   2279 	aio = gtk_cmctree_node_get_row_data( clist, node );
   2280 	if( aio ) {
   2281 		/* g_print( "list select: %d : '%s'\n", aio->type, aio->name ); */
   2282 		addressbook_list_select_add( aio, ds );
   2283 	}
   2284 
   2285 	addressbook_list_menu_setup();
   2286 
   2287 	if (!addrbook.target_compose && !prefs_common.addressbook_use_editaddress_dialog) {
   2288 		AddressObject *obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
   2289 
   2290 		if (obj && obj->type != ADDR_ITEM_GROUP)
   2291 			addressbook_edit_address(NULL, 0, NULL, FALSE);
   2292 	}
   2293 }
   2294 
   2295 static void addressbook_list_row_unselected( GtkCMCTree *ctree,
   2296 					     GtkCMCTreeNode *node,
   2297 					     gint column,
   2298 					     gpointer data )
   2299 {
   2300 	AddrItemObject *aio;
   2301 
   2302 	aio = gtk_cmctree_node_get_row_data( ctree, node );
   2303 	if( aio != NULL ) {
   2304 		/* g_print( "list unselect: %d : '%s'\n", aio->type, aio->name ); */
   2305 		addressbook_list_select_remove( aio );
   2306 	}
   2307 
   2308 	if (!prefs_common.addressbook_use_editaddress_dialog)
   2309 		addressbook_edit_person_invalidate(NULL, NULL, NULL);
   2310 }
   2311 
   2312 static void addressbook_entry_activated(GtkWidget *widget, gpointer data)
   2313 {
   2314 	addressbook_lup_clicked(NULL, NULL);
   2315 }
   2316 
   2317 static gboolean addressbook_list_button_pressed(GtkWidget *widget,
   2318 						GdkEventButton *event,
   2319 						gpointer data)
   2320 {
   2321 	if( ! event ) return FALSE;
   2322 	if( event->window != GTK_CMCLIST(widget)->clist_window ) return FALSE;
   2323 
   2324 	addressbook_list_menu_setup();
   2325 
   2326 	if( event->button == 3 ) {
   2327 		gtk_menu_popup_at_pointer(GTK_MENU(addrbook.list_popup), NULL);
   2328 	} else if (event->button == 1) {
   2329 		if (event->type == GDK_2BUTTON_PRESS) {
   2330 			if (prefs_common.addressbook_use_editaddress_dialog)
   2331 				addressbook_edit_address_cb(NULL, NULL);
   2332 			else {
   2333 				GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
   2334 				AddressObject *obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
   2335 				if( obj && obj->type == ADDR_ITEM_GROUP )
   2336 					addressbook_edit_address_cb(NULL, NULL);
   2337 			}
   2338 		}
   2339 	}
   2340 
   2341 	return FALSE;
   2342 }
   2343 
   2344 static gboolean addressbook_list_button_released(GtkWidget *widget,
   2345 						 GdkEventButton *event,
   2346 						 gpointer data)
   2347 {
   2348 	return FALSE;
   2349 }
   2350 
   2351 static gboolean addressbook_tree_button_pressed(GtkWidget *ctree,
   2352 						GdkEventButton *event,
   2353 						gpointer data)
   2354 {
   2355 	GtkCMCList *clist = GTK_CMCLIST(ctree);
   2356 	gint row, column;
   2357 	AddressObject *obj = NULL;
   2358 	AdapterDSource *ads = NULL;
   2359 	AddressInterface *iface = NULL;
   2360 	AddressDataSource *ds = NULL;
   2361 	gboolean canEdit = FALSE;
   2362 	gboolean canDelete = FALSE;
   2363 	gboolean canCut = FALSE;
   2364 	gboolean canCopy = FALSE;
   2365 	gboolean canPaste = FALSE;
   2366 	gboolean canTreeCut = FALSE;
   2367 	gboolean canTreeCopy = FALSE;
   2368 	gboolean canTreePaste = FALSE;
   2369 	gboolean canLookup = FALSE;
   2370 	GtkCMCTreeNode *node = NULL;
   2371 
   2372 	if( ! event ) return FALSE;
   2373 /*	if( ! event || event->type != GDK_BUTTON_PRESS) return FALSE;*/
   2374 
   2375 	if( event->window != clist->clist_window )
   2376 		return FALSE;
   2377 
   2378 	if (event->button == 1) {
   2379 		if (event->type == GDK_2BUTTON_PRESS) {
   2380 			if( gtk_cmclist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
   2381 				gtkut_clist_set_focus_row(clist, row);
   2382 				obj = gtk_cmclist_get_row_data( clist, row );
   2383 			}
   2384 			if( obj == NULL )
   2385 				return FALSE;
   2386 
   2387 			if (obj->type == ADDR_ITEM_GROUP ||
   2388 					obj->type == ADDR_DATASOURCE) {
   2389 				/* edit group */
   2390 				addressbook_treenode_edit_cb(NULL, NULL);
   2391 			} else {
   2392 				/* expand pr collapse */
   2393 				node = gtk_cmctree_node_nth(GTK_CMCTREE(ctree), row);
   2394 				gtk_cmctree_toggle_expansion(GTK_CMCTREE(ctree), node);
   2395 			}
   2396 			return FALSE;
   2397 		}
   2398 	}
   2399 
   2400 	addressbook_menubar_set_sensitive( FALSE );
   2401 
   2402 	if( gtk_cmclist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
   2403 		gtkut_clist_set_focus_row(clist, row);
   2404 		obj = gtk_cmclist_get_row_data( clist, row );
   2405 	}
   2406 
   2407 	menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
   2408 
   2409 	if( obj == NULL )
   2410 		return FALSE;
   2411 	node = gtk_cmctree_node_nth(GTK_CMCTREE(clist), row);
   2412 
   2413 	if( ! addrclip_is_empty( _clipBoard_ ) )
   2414 		canTreePaste = TRUE;
   2415 
   2416 	if (obj->type == ADDR_INTERFACE) {
   2417 		AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
   2418 		iface = adapter->interface;
   2419 		if( !iface )
   2420 			goto just_set_sens;
   2421 		if( !iface->readOnly && iface->type == ADDR_IF_BOOK) {
   2422 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewBook", TRUE );
   2423 		}
   2424 		if( iface->externalQuery )
   2425 			canLookup = TRUE;
   2426 	}
   2427 	if (obj->type == ADDR_DATASOURCE) {
   2428 		canLookup = TRUE;
   2429 		ads = ADAPTER_DSOURCE(obj);
   2430 		ds = ads->dataSource;
   2431 		if( !ds )
   2432 			goto just_set_sens;
   2433 		iface = ds->interface;
   2434 		if( !iface )
   2435 			goto just_set_sens;
   2436 		if( !iface->readOnly ) {
   2437 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewFolder", TRUE );
   2438 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewGroup", TRUE );
   2439 			gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
   2440 		}
   2441 		canDelete = TRUE;
   2442 		canEdit = TRUE;
   2443 		canTreeCopy = TRUE;
   2444 	}
   2445 	else if (obj->type == ADDR_ITEM_FOLDER) {
   2446 		canLookup = TRUE;
   2447 		ds = addressbook_find_datasource( node );
   2448 		if( !ds )
   2449 			goto just_set_sens;
   2450 		iface = ds->interface;
   2451 		if( !iface )
   2452 			goto just_set_sens;
   2453 		if( !iface->readOnly ) {
   2454 			canEdit = TRUE;
   2455 			canDelete = TRUE;
   2456 			canTreeCut = TRUE;
   2457 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewFolder", TRUE );
   2458 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewGroup", TRUE );
   2459 			gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
   2460 		}
   2461 		canTreeCopy = TRUE;
   2462 
   2463 		if( iface->externalQuery ) {
   2464 			/* Enable deletion of LDAP folder */
   2465 			canDelete = TRUE;
   2466 		}
   2467 	}
   2468 	else if (obj->type == ADDR_ITEM_GROUP) {
   2469 		canLookup = TRUE;
   2470 		ds = addressbook_find_datasource( node );
   2471 		if( !ds )
   2472 			goto just_set_sens;
   2473 		iface = ds->interface;
   2474 		if( !iface )
   2475 			goto just_set_sens;
   2476 		if( ! iface->readOnly ) {
   2477 			canEdit = TRUE;
   2478 			canDelete = TRUE;
   2479 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewAddress", TRUE );
   2480 			gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
   2481 		}
   2482 	}
   2483 
   2484 	if( canEdit && !addrselect_test_empty( _addressSelect_ ) )
   2485 		canCut = TRUE;
   2486 	if( ! addrselect_test_empty( _addressSelect_ ) )
   2487 		canCopy = TRUE;
   2488 	if( ! addrclip_is_empty( _clipBoard_ ) )
   2489 		canPaste = TRUE;
   2490 
   2491 	/* Forbid write changes when read-only */
   2492 	if( iface && iface->readOnly ) {
   2493 		canTreeCut = FALSE;
   2494 		canTreePaste = FALSE;
   2495 		canCut = FALSE;
   2496 		canPaste = FALSE;
   2497 	}
   2498 
   2499 just_set_sens:
   2500 	/* Enable edit */
   2501 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/EditBook",   canEdit );
   2502 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/DeleteBook", canDelete );
   2503 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/Cut",    canTreeCut );
   2504 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/Copy",   canTreeCopy );
   2505 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/Paste",  canTreePaste );
   2506 
   2507 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook",          canEdit );
   2508 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook",        canDelete );
   2509 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Cut",           canCut );
   2510 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Copy",          canCopy );
   2511 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Paste",         canPaste );
   2512 
   2513 	addressbook_show_buttons(addrbook.target_compose == NULL, canLookup,
   2514 			addrbook.target_compose != NULL);
   2515 
   2516 	if( event->button == 3 )
   2517 		gtk_menu_popup_at_pointer(GTK_MENU(addrbook.tree_popup), NULL);
   2518 
   2519 	return FALSE;
   2520 }
   2521 
   2522 static gboolean addressbook_tree_button_released(GtkWidget *ctree,
   2523 						 GdkEventButton *event,
   2524 						 gpointer data)
   2525 {
   2526 	gtkut_ctree_set_focus_row(GTK_CMCTREE(addrbook.ctree), addrbook.opened);
   2527 	return FALSE;
   2528 }
   2529 
   2530 static void addressbook_new_folder_cb(GtkAction *action, gpointer data)
   2531 {
   2532 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   2533 	AddressObject *obj = NULL;
   2534 	AddressDataSource *ds = NULL;
   2535 	AddressBookFile *abf = NULL;
   2536 	ItemFolder *parentFolder = NULL;
   2537 	ItemFolder *folder = NULL;
   2538 
   2539 	if( ! addrbook.treeSelected ) return;
   2540 	obj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
   2541 	if( obj == NULL ) return;
   2542 	ds = addressbook_find_datasource( addrbook.treeSelected );
   2543 	if( ds == NULL ) return;
   2544 
   2545 	if( obj->type == ADDR_DATASOURCE ) {
   2546 		if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
   2547 	}
   2548 	else if( obj->type == ADDR_ITEM_FOLDER ) {
   2549 		parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
   2550 	}
   2551 	else {
   2552 		return;
   2553 	}
   2554 
   2555 	abf = ds->rawDataSource;
   2556 	if( abf == NULL ) return;
   2557 	folder = addressbook_edit_folder( abf, parentFolder, NULL );
   2558 	if( folder ) {
   2559 		GtkCMCTreeNode *nn;
   2560 		nn = addressbook_node_add_folder(
   2561 			addrbook.treeSelected, ds, folder, ADDR_ITEM_FOLDER );
   2562 		if (nn == NULL) {
   2563 			g_message("error adding addressbook folder\n");
   2564 		}
   2565 		gtk_cmctree_expand( ctree, addrbook.treeSelected );
   2566 		if( addrbook.treeSelected == addrbook.opened )
   2567 			addressbook_set_clist(obj, TRUE);
   2568 	}
   2569 }
   2570 
   2571 static void addressbook_new_group_cb(GtkAction *action, gpointer data)
   2572 {
   2573 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   2574 	AddressObject *obj = NULL;
   2575 	AddressDataSource *ds = NULL;
   2576 	AddressBookFile *abf = NULL;
   2577 	ItemFolder *parentFolder = NULL;
   2578 	ItemGroup *group = NULL;
   2579 
   2580 	if( ! addrbook.treeSelected ) return;
   2581 	obj = gtk_cmctree_node_get_row_data(ctree, addrbook.treeSelected);
   2582 	if( obj == NULL ) return;
   2583 	ds = addressbook_find_datasource( addrbook.treeSelected );
   2584 	if( ds == NULL ) return;
   2585 
   2586 	if( obj->type == ADDR_DATASOURCE ) {
   2587 		if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
   2588 	}
   2589 	else if( obj->type == ADDR_ITEM_FOLDER ) {
   2590 		parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
   2591 	}
   2592 	else {
   2593 		return;
   2594 	}
   2595 
   2596 	abf = ds->rawDataSource;
   2597 	if( abf == NULL ) return;
   2598 	group = addressbook_edit_group( abf, parentFolder, NULL );
   2599 	if( group ) {
   2600 		GtkCMCTreeNode *nn;
   2601 		nn = addressbook_node_add_group( addrbook.treeSelected, ds, group );
   2602 		if (nn == NULL) {
   2603 			g_message("error adding addressbook group\n");
   2604 		}
   2605 		gtk_cmctree_expand( ctree, addrbook.treeSelected );
   2606 		if( addrbook.treeSelected == addrbook.opened )
   2607 			addressbook_set_clist(obj, TRUE);
   2608 	}
   2609 }
   2610 
   2611 static void addressbook_change_node_name(GtkCMCTreeNode *node, const gchar *name)
   2612 {
   2613 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   2614 	gchar *text[1];
   2615 	guint8 spacing;
   2616 	GdkPixbuf *pix_cl, *pix_op;
   2617 	gboolean is_leaf, expanded;
   2618 
   2619 	gtk_cmctree_get_node_info(ctree, node, text, &spacing,
   2620 				&pix_cl, &pix_op,
   2621 				&is_leaf, &expanded);
   2622 	gtk_cmctree_set_node_info(ctree, node, name, spacing,
   2623 				pix_cl, pix_op,
   2624 				is_leaf, expanded);
   2625 }
   2626 
   2627 /**
   2628  * Edit data source.
   2629  * \param obj  Address object to edit.
   2630  * \param node Node in tree.
   2631  * \return New name of data source.
   2632  */
   2633 static gchar *addressbook_edit_datasource( AddressObject *obj, GtkCMCTreeNode *node ) {
   2634 	gchar *newName = NULL;
   2635 	AddressDataSource *ds = NULL;
   2636 	AddressInterface *iface = NULL;
   2637 	AdapterDSource *ads = NULL;
   2638 
   2639 	ds = addressbook_find_datasource( node );
   2640 	if( ds == NULL ) return NULL;
   2641 	iface = ds->interface;
   2642 	if( ! iface->haveLibrary ) return NULL;
   2643 
   2644 	/* Read data from data source */
   2645 	if( addrindex_ds_get_modify_flag( ds ) ) {
   2646 		addrindex_ds_read_data( ds );
   2647 	}
   2648 
   2649 	if( ! addrindex_ds_get_read_flag( ds ) ) {
   2650 		addrindex_ds_read_data( ds );
   2651 	}
   2652 
   2653 	/* Handle edit */
   2654 	ads = ADAPTER_DSOURCE(obj);
   2655 	if( ads->subType == ADDR_BOOK ) {
   2656                 if( addressbook_edit_book( _addressIndex_, ads ) == NULL ) return NULL;
   2657 	} else {
   2658 		return NULL;
   2659 	}
   2660 	newName = obj->name;
   2661 	return newName;
   2662 }
   2663 
   2664 /*
   2665 * Edit an object that is in the address tree area.
   2666 */
   2667 static void addressbook_treenode_edit_cb(GtkAction *action, gpointer data)
   2668 {
   2669 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   2670 	AddressObject *obj;
   2671 	AddressDataSource *ds = NULL;
   2672 	AddressBookFile *abf = NULL;
   2673 	GtkCMCTreeNode *node = NULL, *parentNode = NULL;
   2674 	gchar *name = NULL;
   2675 
   2676 	if( ! addrbook.treeSelected ) return;
   2677 	node = addrbook.treeSelected;
   2678 	if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
   2679 	obj = gtk_cmctree_node_get_row_data( ctree, node );
   2680 	if( obj == NULL ) return;
   2681 	parentNode = GTK_CMCTREE_ROW(node)->parent;
   2682 
   2683 	ds = addressbook_find_datasource( node );
   2684 	if( ds == NULL ) return;
   2685 
   2686 	if( obj->type == ADDR_DATASOURCE ) {
   2687 		name = addressbook_edit_datasource( obj, node );
   2688 		if( name == NULL ) return;
   2689 	}
   2690 	else {
   2691 		abf = ds->rawDataSource;
   2692 		if( abf == NULL ) return;
   2693 		if( obj->type == ADDR_ITEM_FOLDER ) {
   2694 			AdapterFolder *adapter = ADAPTER_FOLDER(obj);
   2695 			ItemFolder *item = adapter->itemFolder;
   2696 			ItemFolder *parentFolder = NULL;
   2697 			parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
   2698 			if( addressbook_edit_folder( abf, parentFolder, item ) == NULL ) return;
   2699 			name = ADDRITEM_NAME(item);
   2700 		}
   2701 		else if( obj->type == ADDR_ITEM_GROUP ) {
   2702 			AdapterGroup *adapter = ADAPTER_GROUP(obj);
   2703 			ItemGroup *item = adapter->itemGroup;
   2704 			ItemFolder *parentFolder = NULL;
   2705 			parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
   2706 			if( addressbook_edit_group( abf, parentFolder, item ) == NULL ) return;
   2707 			name = ADDRITEM_NAME(item);
   2708 		}
   2709 	}
   2710 	if( name && parentNode ) {
   2711 		/* Update node in tree view */
   2712 		addressbook_change_node_name( node, name );
   2713 		gtk_sctree_sort_node(ctree, parentNode);
   2714 		gtk_cmctree_expand( ctree, node );
   2715 		gtk_sctree_select( GTK_SCTREE( ctree), node );
   2716 	}
   2717 }
   2718 
   2719 typedef enum {
   2720 	ADDRTREE_DEL_NONE,
   2721 	ADDRTREE_DEL_DATA,
   2722 	ADDRTREE_DEL_FOLDER_ONLY,
   2723 	ADDRTREE_DEL_FOLDER_ADDR
   2724 } TreeItemDelType ;
   2725 
   2726 /**
   2727  * Delete an item from the tree widget.
   2728  * \param data   Data passed in.
   2729  * \param action Action.
   2730  * \param widget Widget issuing callback.
   2731  */
   2732 static void addressbook_treenode_delete_cb(GtkAction *action, gpointer data)
   2733 {
   2734 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   2735 	GtkCMCTreeNode *node = NULL;
   2736 	AddressObject *obj;
   2737 	gchar *message;
   2738 	AlertValue aval;
   2739 	AddrBookBase *adbase;
   2740 	AddressCache *cache;
   2741 	AdapterDSource *ads = NULL;
   2742 	AddressInterface *iface = NULL;
   2743 	AddressDataSource *ds = NULL;
   2744 	gboolean remFlag = FALSE;
   2745 	TreeItemDelType delType;
   2746 
   2747 	if( ! addrbook.treeSelected ) return;
   2748 	node = addrbook.treeSelected;
   2749 	if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
   2750 
   2751 	obj = gtk_cmctree_node_get_row_data( ctree, node );
   2752 	cm_return_if_fail(obj != NULL);
   2753 
   2754 	if( obj->type == ADDR_DATASOURCE ) {
   2755 		ads = ADAPTER_DSOURCE(obj);
   2756 
   2757 		ds = ads->dataSource;
   2758 		if( ds == NULL ) return;
   2759 	}
   2760 	else {
   2761 		/* Must be folder or something else */
   2762 		ds = addressbook_find_datasource( node );
   2763 		if( ds == NULL ) return;
   2764 
   2765 		/* Only allow deletion from non-readOnly */
   2766 		iface = ds->interface;
   2767 		if( iface->readOnly ) {
   2768 			/* Allow deletion of query results */
   2769 			if( ! iface->externalQuery ) return;
   2770 		}
   2771 	}
   2772 
   2773 	/* Confirm deletion */
   2774 	delType = ADDRTREE_DEL_NONE;
   2775 	if( obj->type == ADDR_ITEM_FOLDER ) {
   2776 		if( iface && iface->externalQuery ) {
   2777 			message = g_strdup_printf("Do you want to delete the query results and addresses in '%s'?",
   2778 				obj->name );
   2779 			aval = alertpanel("Delete", message,
   2780 				NULL, "_Cancel", "edit-delete", "D_elete", NULL, NULL,
   2781 				ALERTFOCUS_SECOND );
   2782 			g_free(message);
   2783 			if( aval == G_ALERTALTERNATE ) {
   2784 				delType = ADDRTREE_DEL_FOLDER_ADDR;
   2785 			}
   2786 		}
   2787 		else {
   2788 			message = g_strdup_printf("Do you want to delete '%s'? If you delete the folder only, the addresses it contains will be moved into the parent folder.", obj->name);
   2789 			aval = alertpanel("Delete folder", message,
   2790 				NULL, "_Cancel", "edit-delete", "Delete _folder only",
   2791 				"edit-delete", "Delete folder and _addresses", ALERTFOCUS_SECOND);
   2792 			g_free(message);
   2793 			if( aval == G_ALERTALTERNATE ) {
   2794 				delType = ADDRTREE_DEL_FOLDER_ONLY;
   2795 			}
   2796 			else if( aval == G_ALERTOTHER ) {
   2797 				delType = ADDRTREE_DEL_FOLDER_ADDR;
   2798 			}
   2799 		}
   2800 	}
   2801 	else if( obj->type == ADDR_ITEM_GROUP ) {
   2802 		message = g_strdup_printf("Do you want to delete '%s'?\nThe addresses it contains will not be lost.", obj->name);
   2803 		aval = alertpanel("Delete", message, NULL, "_Cancel",
   2804 				"edit-delete", "D_elete", NULL, NULL, ALERTFOCUS_SECOND);
   2805 		g_free(message);
   2806 		if( aval == G_ALERTALTERNATE ) delType = ADDRTREE_DEL_FOLDER_ONLY;
   2807 	} else {
   2808 		message = g_strdup_printf("Do you want to delete '%s'?\nThe addresses it contains will be lost.", obj->name);
   2809 		aval = alertpanel("Delete", message, NULL, "_Cancel",
   2810 				"edit-delete", "D_elete", NULL, NULL, ALERTFOCUS_SECOND);
   2811 		g_free(message);
   2812 		if( aval == G_ALERTALTERNATE ) delType = ADDRTREE_DEL_DATA;
   2813 	}
   2814 	if( delType == ADDRTREE_DEL_NONE ) return;
   2815 
   2816 	/* Proceed with deletion */
   2817 	if( obj->type == ADDR_DATASOURCE ) {
   2818 		/* Remove node from tree */
   2819 		gtk_cmctree_remove_node( ctree, node );
   2820 
   2821 		if (delType == ADDRTREE_DEL_DATA &&
   2822 		    ds->interface && ds->interface->type == ADDR_IF_BOOK)
   2823 			addrbook_delete_book_file((AddressBookFile *) ds->rawDataSource);
   2824 
   2825 		/* Remove data source. */
   2826 		if( addrindex_index_remove_datasource( _addressIndex_, ds ) ) {
   2827 			addrindex_free_datasource( ds );
   2828 		}
   2829 		return;
   2830 	}
   2831 
   2832 	/* Get reference to cache */
   2833 	adbase = ( AddrBookBase * ) ds->rawDataSource;
   2834 	if( adbase == NULL ) return;
   2835 	cache = adbase->addressCache;
   2836 
   2837 	/* Remove query results folder */
   2838 	if( iface && iface->externalQuery ) {
   2839 		AdapterFolder *adapter = ADAPTER_FOLDER(obj);
   2840 		ItemFolder *folder = adapter->itemFolder;
   2841 
   2842 		adapter->itemFolder = NULL;
   2843 		/*
   2844 		g_print( "remove folder for ::%s::\n", obj->name );
   2845 		g_print( "      folder name ::%s::\n", ADDRITEM_NAME(folder) );
   2846 		g_print( "-------------- remove results\n" );
   2847 		*/
   2848 		addrindex_remove_results( ds, folder );
   2849 		/* g_print( "-------------- remove node\n" ); */
   2850 		gtk_cmctree_remove_node( ctree, node );
   2851 		return;
   2852 	}
   2853 
   2854 	/* Code below is valid for regular address book deletion */
   2855 	if( obj->type == ADDR_ITEM_FOLDER ) {
   2856 		AdapterFolder *adapter = ADAPTER_FOLDER(obj);
   2857 		ItemFolder *item = adapter->itemFolder;
   2858 
   2859 		if( delType == ADDRTREE_DEL_FOLDER_ONLY ) {
   2860 			/* Remove folder only */
   2861 			item = addrcache_remove_folder( cache, item );
   2862 			if( item ) {
   2863 				addritem_free_item_folder( item );
   2864 				addressbook_move_nodes_up( ctree, node );
   2865 				remFlag = TRUE;
   2866 			}
   2867 		}
   2868 		else if( delType == ADDRTREE_DEL_FOLDER_ADDR ) {
   2869 			/* Remove folder and addresses */
   2870 			item = addrcache_remove_folder_delete( cache, item );
   2871 			if( item ) {
   2872 				addritem_free_item_folder( item );
   2873 				remFlag = TRUE;
   2874 			}
   2875 		}
   2876 	}
   2877 	else if( obj->type == ADDR_ITEM_GROUP ) {
   2878 		AdapterGroup *adapter = ADAPTER_GROUP(obj);
   2879 		ItemGroup *item = adapter->itemGroup;
   2880 
   2881 		item = addrcache_remove_group( cache, item );
   2882 		if( item ) {
   2883 			addritem_free_item_group( item );
   2884 			remFlag = TRUE;
   2885 		}
   2886 	}
   2887 
   2888 	if( remFlag ) {
   2889 		/* Remove node. */
   2890 		gtk_cmctree_remove_node(ctree, node );
   2891 	}
   2892 }
   2893 
   2894 static void addressbook_new_address_from_book_post_cb( ItemPerson *person )
   2895 {
   2896 	if( person && addrbook.treeSelected == addrbook.opened ) {
   2897 		person->status = ADD_ENTRY;
   2898 		gtk_cmclist_unselect_all( GTK_CMCLIST(addrbook.clist) );
   2899 		addressbook_folder_refresh_one_person(
   2900 			GTK_CMCTREE(addrbook.clist), person );
   2901 	}
   2902 	addressbook_address_list_set_focus();
   2903 }
   2904 
   2905 static void addressbook_new_address_from_folder_post_cb( ItemPerson *person )
   2906 {
   2907 	if( person && addrbook.treeSelected == addrbook.opened) {
   2908 		person->status = ADD_ENTRY;
   2909 		gtk_sctree_select( GTK_SCTREE(addrbook.ctree), addrbook.opened );
   2910 		addressbook_set_clist(
   2911 			gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
   2912 				addrbook.opened),
   2913 			TRUE);
   2914 	}
   2915 	addressbook_address_list_set_focus();
   2916 }
   2917 
   2918 /**
   2919  * Label (a format string) that is used to name each folder.
   2920  */
   2921 static gchar *_queryFolderLabel_ = "Search '%s'";
   2922 
   2923 /**
   2924  * Search ctree widget callback function.
   2925  * \param  pA Pointer to node.
   2926  * \param  pB Pointer to data item being sought.
   2927  * \return Zero (0) if folder found.
   2928  */
   2929 static int addressbook_treenode_find_folder_cb( gconstpointer pA, gconstpointer pB ) {
   2930 	AddressObject *aoA;
   2931 
   2932 	aoA = ( AddressObject * ) pA;
   2933 	if( aoA->type == ADDR_ITEM_FOLDER ) {
   2934 		ItemFolder *folder, *fld;
   2935 
   2936 		fld = ADAPTER_FOLDER(aoA)->itemFolder;
   2937 		folder = ( ItemFolder * ) pB;
   2938 		if( fld == folder ) return 0;	/* Found folder */
   2939 	}
   2940 	return 1;
   2941 }
   2942 
   2943 static ItemFolder * addressbook_setup_subf(
   2944 		AddressDataSource *ds, gchar *title,
   2945 		GtkCMCTreeNode *pNode )
   2946 {
   2947 	AddrBookBase *adbase;
   2948 	AddressCache *cache;
   2949 	ItemFolder *folder;
   2950 	GtkCMCTree *ctree;
   2951 	GtkCMCTreeNode *nNode;
   2952 	gchar *name;
   2953 	AddressObjectType aoType = ADDR_NONE;
   2954 	GList *children;
   2955 	/* Setup a query */
   2956 	if( *title == '\0' || strlen( title ) < 1 ) return NULL;
   2957 	return NULL;
   2958 
   2959 	ctree = GTK_CMCTREE(addrbook.ctree);
   2960 	/* Get reference to address cache */
   2961 	adbase = ( AddrBookBase * ) ds->rawDataSource;
   2962 	cache = adbase->addressCache;
   2963 
   2964 	if ((children = addrcache_get_list_folder(cache)) != NULL) {
   2965 		GList *cur = children;
   2966 		for (; cur; cur = cur->next) {
   2967 			ItemFolder *child = (ItemFolder *) cur->data;
   2968 			if (!g_strcmp0(ADDRITEM_NAME(child), title)) {
   2969 				nNode = gtk_cmctree_find_by_row_data_custom(
   2970 					ctree, NULL, child,
   2971 					addressbook_treenode_find_folder_cb );
   2972 				if( nNode ) {
   2973 					addrindex_remove_results( ds, child );
   2974 					while( child->listPerson ) {
   2975 						ItemPerson *item = ( ItemPerson * ) child->listPerson->data;
   2976 						item = addrcache_remove_person( cache, item );
   2977 						if( item ) {
   2978 							addritem_free_item_person( item );
   2979 							item = NULL;
   2980 						}
   2981 					}
   2982 					gtk_sctree_select( GTK_SCTREE(ctree), nNode );
   2983 					addrbook.treeSelected = nNode;
   2984 				}
   2985 				return child;
   2986 			}
   2987 		}
   2988 	}
   2989 
   2990 	/* Create a folder */
   2991 	folder = addrcache_add_new_folder( cache, NULL );
   2992 	name = g_strdup_printf( "%s", title );
   2993 	addritem_folder_set_name( folder, name );
   2994 	addritem_folder_set_remarks( folder, "" );
   2995 	g_free( name );
   2996 
   2997 	/* Now let's see the folder */
   2998 	nNode = addressbook_node_add_folder( pNode, ds, folder, aoType );
   2999 	gtk_cmctree_expand( ctree, pNode );
   3000 	if( nNode ) {
   3001 		gtk_sctree_select( GTK_SCTREE(ctree), nNode );
   3002 		addrbook.treeSelected = nNode;
   3003 		return folder;
   3004 	}
   3005 	return NULL;
   3006 }
   3007 
   3008 static void addressbook_new_address_cb( GtkAction *action, gpointer data ) {
   3009 	AddressObject *pobj = NULL;
   3010 	AddressDataSource *ds = NULL;
   3011 	AddressBookFile *abf = NULL;
   3012 	debug_print("adding address\n");
   3013 	pobj = gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected);
   3014 	if( pobj == NULL ) {
   3015 		debug_print("no row data\n");
   3016 		return;
   3017 	}
   3018 	ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
   3019 	if( ds == NULL ) {
   3020 		debug_print("no datasource\n");
   3021 		return;
   3022 	}
   3023 
   3024 	abf = ds->rawDataSource;
   3025 	if( abf == NULL ) {
   3026 		g_print("no addressbook file\n");
   3027 		return;
   3028 	}
   3029 
   3030 	if( pobj->type == ADDR_DATASOURCE ) {
   3031 		if (ADAPTER_DSOURCE(pobj)->subType == ADDR_BOOK) {
   3032 			ItemPerson *person;
   3033 			ItemFolder *folder = NULL;
   3034 			person = addressbook_edit_person( abf, folder, NULL, FALSE,
   3035 								  addrbook.editaddress_vbox,
   3036 								  addressbook_new_address_from_book_post_cb,
   3037 								  TRUE );
   3038 			if (prefs_common.addressbook_use_editaddress_dialog)
   3039 				addressbook_new_address_from_book_post_cb( person );
   3040 		}
   3041 	}
   3042 	else if( pobj->type == ADDR_ITEM_FOLDER ) {
   3043 		/* New address */
   3044 		ItemFolder *folder = ADAPTER_FOLDER(pobj)->itemFolder;
   3045 		ItemPerson *person;
   3046 		person = addressbook_edit_person( abf, folder, NULL, FALSE,
   3047 							  addrbook.editaddress_vbox,
   3048 							  addressbook_new_address_from_folder_post_cb,
   3049 							  TRUE );
   3050 		if (prefs_common.addressbook_use_editaddress_dialog)
   3051 			addressbook_new_address_from_folder_post_cb( person );
   3052 	}
   3053 	else if( pobj->type == ADDR_ITEM_GROUP ) {
   3054 		/* New address in group */
   3055 		ItemGroup *group = ADAPTER_GROUP(pobj)->itemGroup;
   3056 		if( addressbook_edit_group( abf, NULL, group ) == NULL ) return;
   3057 		if (addrbook.treeSelected == addrbook.opened) {
   3058 			/* Change node name in tree. */
   3059 			addressbook_change_node_name( addrbook.treeSelected, ADDRITEM_NAME(group) );
   3060 			gtk_sctree_select( GTK_SCTREE(addrbook.ctree), addrbook.opened );
   3061 			addressbook_set_clist(
   3062 				gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
   3063 					addrbook.opened),
   3064 				TRUE);
   3065 		}
   3066 	}
   3067 }
   3068 
   3069 /**
   3070  * Search for specified child group node in address index tree.
   3071  * \param parent Parent node.
   3072  * \param group  Group to find.
   3073  */
   3074 static GtkCMCTreeNode *addressbook_find_group_node( GtkCMCTreeNode *parent, ItemGroup *group ) {
   3075 	GtkCMCTreeNode *node = NULL;
   3076 	GtkCMCTreeRow *currRow;
   3077 
   3078 	currRow = GTK_CMCTREE_ROW( parent );
   3079 	if( currRow ) {
   3080 		node = currRow->children;
   3081 		while( node ) {
   3082 			AddressObject *obj;
   3083 
   3084 			obj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), node );
   3085 			if(obj && obj->type == ADDR_ITEM_GROUP ) {
   3086 				ItemGroup *g = ADAPTER_GROUP(obj)->itemGroup;
   3087 				if( g == group ) return node;
   3088 			}
   3089 			currRow = GTK_CMCTREE_ROW(node);
   3090 			node = currRow->sibling;
   3091 		}
   3092 	}
   3093 	return NULL;
   3094 }
   3095 
   3096 static AddressBookFile *addressbook_get_book_file() {
   3097 	AddressBookFile *abf = NULL;
   3098 	AddressDataSource *ds = NULL;
   3099 
   3100 	ds = addressbook_find_datasource( addrbook.treeSelected );
   3101 	if( ds == NULL ) return NULL;
   3102 	if( ds->type == ADDR_IF_BOOK )
   3103 		abf = ds->rawDataSource;
   3104 	return abf;
   3105 }
   3106 
   3107 static void addressbook_tree_remove_children( GtkCMCTree *ctree, GtkCMCTreeNode *parent ) {
   3108 	GtkCMCTreeNode *node;
   3109 	GtkCMCTreeRow *row;
   3110 
   3111 	/* Remove existing folders and groups */
   3112 	row = GTK_CMCTREE_ROW( parent );
   3113 	if( row ) {
   3114 		while( (node = row->children) ) {
   3115 			gtk_cmctree_remove_node( ctree, node );
   3116 		}
   3117 	}
   3118 }
   3119 
   3120 static void addressbook_move_nodes_up( GtkCMCTree *ctree, GtkCMCTreeNode *node ) {
   3121 	GtkCMCTreeNode *parent, *child;
   3122 	GtkCMCTreeRow *currRow;
   3123 	currRow = GTK_CMCTREE_ROW( node );
   3124 	if( currRow ) {
   3125 		parent = currRow->parent;
   3126 		while( (child = currRow->children) ) {
   3127 			gtk_cmctree_move( ctree, child, parent, node );
   3128 		}
   3129 		gtk_sctree_sort_node( ctree, parent );
   3130 	}
   3131 }
   3132 
   3133 static void addressbook_edit_address_post_cb( ItemPerson *person )
   3134 {
   3135 	if( person ) {
   3136 		addressbook_folder_refresh_one_person( GTK_CMCTREE(addrbook.clist), person );
   3137 		invalidate_address_completion();
   3138 	}
   3139 	addressbook_address_list_set_focus();
   3140 }
   3141 
   3142 void addressbook_address_list_set_focus( void )
   3143 {
   3144 	if (!prefs_common.addressbook_use_editaddress_dialog) {
   3145 		gtk_window_set_focus(GTK_WINDOW(addrbook.window), addrbook.clist);
   3146 		addressbook_list_menu_setup();
   3147 	}
   3148 }
   3149 
   3150 void addressbook_address_list_disable_some_actions(void)
   3151 {
   3152 	/* disable address copy/pasting when editing contact's detail (embedded form) */
   3153 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Cut",   FALSE );
   3154 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Copy",  FALSE );
   3155 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Paste", FALSE );
   3156 }
   3157 
   3158 static void addressbook_edit_address_cb( GtkAction *action, gpointer data ) {
   3159 	addressbook_edit_address(data, 0, NULL, TRUE);
   3160 }
   3161 
   3162 static void addressbook_edit_address( gpointer data, guint action, GtkWidget *widget,
   3163 									  gboolean force_focus ) {
   3164 	GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
   3165 	GtkCMCTree *ctree;
   3166 	AddressObject *obj = NULL, *pobj = NULL;
   3167 	AddressDataSource *ds = NULL;
   3168 	GtkCMCTreeNode *node = NULL, *parentNode = NULL;
   3169 	gchar *name = NULL;
   3170 	AddressBookFile *abf = NULL;
   3171 
   3172 	if( addrbook.listSelected == NULL ) return;
   3173 	obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
   3174 	cm_return_if_fail(obj != NULL);
   3175 
   3176        	ctree = GTK_CMCTREE( addrbook.ctree );
   3177 	pobj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
   3178 
   3179 	ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
   3180 	if( ds == NULL ) return;
   3181 
   3182 	abf = addressbook_get_book_file();
   3183 
   3184 	if( obj->type == ADDR_ITEM_EMAIL ) {
   3185 		ItemEMail *email = ( ItemEMail * ) obj;
   3186 
   3187 		if( pobj && pobj->type == ADDR_ITEM_GROUP ) {
   3188 			/* Edit parent group */
   3189 			AdapterGroup *adapter = ADAPTER_GROUP(pobj);
   3190 			ItemGroup *itemGrp = adapter->itemGroup;
   3191 			if( abf == NULL ) return;
   3192 			if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
   3193 			name = ADDRITEM_NAME(itemGrp);
   3194 			node = addrbook.treeSelected;
   3195 			parentNode = GTK_CMCTREE_ROW(node)->parent;
   3196 		}
   3197 		else {
   3198 			/* Edit person - email page */
   3199 			ItemPerson *person;
   3200 			person = ( ItemPerson * ) ADDRITEM_PARENT(email);
   3201 			if  ( addressbook_edit_person( abf, NULL, person, TRUE, addrbook.editaddress_vbox,
   3202 										   addressbook_edit_address_post_cb,
   3203 										   (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
   3204 				  != NULL ) {
   3205 				if (prefs_common.addressbook_use_editaddress_dialog)
   3206 					addressbook_edit_address_post_cb( person );
   3207 			}
   3208 			return;
   3209 		}
   3210 	}
   3211 	else if( obj->type == ADDR_ITEM_PERSON ) {
   3212 		/* Edit person - basic page */
   3213 		ItemPerson *person = ( ItemPerson * ) obj;
   3214 		if( addressbook_edit_person( abf, NULL, person, FALSE, addrbook.editaddress_vbox,
   3215 									  addressbook_edit_address_post_cb,
   3216 									  (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
   3217 			!= NULL ) {
   3218 				if (prefs_common.addressbook_use_editaddress_dialog)
   3219 					addressbook_edit_address_post_cb( person );
   3220 		}
   3221 		return;
   3222 	}
   3223 	else if( obj->type == ADDR_ITEM_GROUP ) {
   3224 		ItemGroup *itemGrp = ( ItemGroup * ) obj;
   3225 		if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
   3226 		parentNode = addrbook.treeSelected;
   3227 		node = addressbook_find_group_node( parentNode, itemGrp );
   3228 		name = ADDRITEM_NAME(itemGrp);
   3229 		invalidate_address_completion();
   3230 	}
   3231 	else {
   3232 		return;
   3233 	}
   3234 
   3235 	/* Update tree node with node name */
   3236 	if( node == NULL ) return;
   3237 	addressbook_change_node_name( node, name );
   3238 	gtk_sctree_sort_node( ctree, parentNode );
   3239 	gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened );
   3240 	addressbook_set_clist(
   3241 		gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
   3242 			addrbook.opened),
   3243 		TRUE);
   3244 }
   3245 
   3246 static void addressbook_delete_address_cb(GtkAction *action, gpointer data)
   3247 {
   3248 	addressbook_del_clicked(NULL, NULL);
   3249 }
   3250 
   3251 static void close_cb(GtkAction *action, gpointer data)
   3252 {
   3253 	addressbook_close();
   3254 }
   3255 
   3256 static void addressbook_file_save_cb( GtkAction *action, gpointer data ) {
   3257 	addressbook_export_to_file();
   3258 }
   3259 
   3260 static void addressbook_person_expand_node( GtkCMCTree *ctree, GList *node, gpointer *data ) {
   3261 	if( node ) {
   3262 		ItemPerson *person = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(node) );
   3263 		if( person ) addritem_person_set_opened( person, TRUE );
   3264 	}
   3265 }
   3266 
   3267 static void addressbook_person_collapse_node( GtkCMCTree *ctree, GList *node, gpointer *data ) {
   3268 	if( node ) {
   3269 		ItemPerson *person = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(node) );
   3270 		if( person ) addritem_person_set_opened( person, FALSE );
   3271 	}
   3272 }
   3273 
   3274 static gchar *addressbook_format_item_clist( ItemPerson *person, ItemEMail *email ) {
   3275 	gchar *str = NULL;
   3276 	gchar *eMailAlias = ADDRITEM_NAME(email);
   3277 	if( eMailAlias && *eMailAlias != '\0' ) {
   3278 		if( person ) {
   3279 			str = g_strdup_printf( "%s - %s", ADDRITEM_NAME(person), eMailAlias );
   3280 		}
   3281 		else {
   3282 			str = g_strdup( eMailAlias );
   3283 		}
   3284 	}
   3285 	return str;
   3286 }
   3287 
   3288 static gboolean addressbook_match_item(const gchar *name,
   3289 				       const gchar *email_alias,
   3290 				       const gchar *addr,
   3291 				       const gchar *remarks,
   3292 				       const gchar *str)
   3293 {
   3294 	if (!name)
   3295 		return FALSE;
   3296 	if (!str || str[0] == '\0')
   3297 		return TRUE;
   3298 	if (strcasestr(name, str))
   3299 		return TRUE;
   3300 	else if (email_alias && strcasestr(email_alias, str))
   3301 		return TRUE;
   3302 	else if (addr && strcasestr(addr, str))
   3303 		return TRUE;
   3304 	else if (remarks && strcasestr(remarks, str))
   3305 		return TRUE;
   3306 
   3307 	return FALSE;
   3308 }
   3309 
   3310 static void addressbook_load_group( GtkCMCTree *clist, ItemGroup *itemGroup ) {
   3311 	GList *items = itemGroup->listEMail;
   3312 	AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_EMAIL );
   3313 	const gchar *search_str = gtk_entry_get_text(GTK_ENTRY(addrbook.entry));
   3314 	for( ; items != NULL; items = g_list_next( items ) ) {
   3315 		GtkCMCTreeNode *nodeEMail = NULL;
   3316 		gchar *text[N_LIST_COLS];
   3317 		ItemEMail *email = items->data;
   3318 		ItemPerson *person;
   3319 		gchar *str = NULL;
   3320 
   3321 		if( ! email ) continue;
   3322 
   3323 		person = ( ItemPerson * ) ADDRITEM_PARENT(email);
   3324 
   3325 		if( !addressbook_match_item(ADDRITEM_NAME(person),
   3326 					    ADDRITEM_NAME(email),
   3327 					    email->address, email->remarks,
   3328 					    search_str))
   3329 			continue;
   3330 
   3331 		str = addressbook_format_item_clist( person, email );
   3332 		if( str ) {
   3333 			text[COL_NAME] = addressbook_set_col_name_guard(str);
   3334 		}
   3335 		else {
   3336 			text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
   3337 		}
   3338 		text[COL_ADDRESS] = email->address;
   3339 		text[COL_REMARKS] = email->remarks;
   3340 		nodeEMail = gtk_sctree_insert_node(
   3341 				clist, NULL, NULL,
   3342 				text, FOLDER_SPACING,
   3343 				atci->iconXpm,
   3344 				atci->iconXpmOpen,
   3345 				FALSE, FALSE );
   3346 		gtk_cmctree_node_set_row_data( clist, nodeEMail, email );
   3347 		g_free( str );
   3348 		str = NULL;
   3349 	}
   3350 }
   3351 
   3352 gchar *addressbook_set_col_name_guard(gchar *value)
   3353 {
   3354 	gchar *ret = "<not set>";
   3355 	gchar *tmp = g_strdup(value);
   3356 	if (tmp) {
   3357 		g_strstrip(tmp);
   3358 		if (*tmp != '\0')
   3359 			ret = value;
   3360 		g_free(tmp);
   3361 	}
   3362 	return ret;
   3363 }
   3364 
   3365 static void addressbook_folder_load_one_person(
   3366 		GtkCMCTree *clist, ItemPerson *person,
   3367 		AddressTypeControlItem *atci,
   3368 		AddressTypeControlItem *atciMail )
   3369 {
   3370 	GtkCMCTreeNode *nodePerson = NULL;
   3371 	GtkCMCTreeNode *nodeEMail = NULL;
   3372 	gchar *text[N_LIST_COLS];
   3373 	gboolean flgFirst = TRUE, haveAddr = FALSE;
   3374 	GList *node;
   3375 	if( person == NULL ) return;
   3376 
   3377 	text[COL_NAME] = "";
   3378 	node = person->listEMail;
   3379 	while( node ) {
   3380 		ItemEMail *email = node->data;
   3381 		gchar *eMailAddr = NULL;
   3382 		node = g_list_next( node );
   3383 
   3384 		text[COL_ADDRESS] = email->address;
   3385 		text[COL_REMARKS] = email->remarks;
   3386 		eMailAddr = ADDRITEM_NAME(email);
   3387 		if( eMailAddr && *eMailAddr == '\0' ) eMailAddr = NULL;
   3388 		if( flgFirst ) {
   3389 			/* First email belongs with person */
   3390 			gchar *str = addressbook_format_item_clist( person, email );
   3391 			if( str ) {
   3392 				text[COL_NAME] = addressbook_set_col_name_guard(str);
   3393 			} else {
   3394 				text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
   3395 			}
   3396 			nodePerson = gtk_sctree_insert_node(
   3397 					clist, NULL, NULL,
   3398 					text, FOLDER_SPACING,
   3399 					atci->iconXpm,
   3400 					atci->iconXpmOpen,
   3401 					FALSE, person->isOpened );
   3402 			g_free( str );
   3403 			str = NULL;
   3404 			gtk_cmctree_node_set_row_data(clist, nodePerson, person );
   3405 		}
   3406 		else {
   3407 			/* Subsequent email is a child node of person */
   3408 			text[COL_NAME] = ADDRITEM_NAME(email);
   3409 			nodeEMail = gtk_sctree_insert_node(
   3410 					clist, nodePerson, NULL,
   3411 					text, FOLDER_SPACING,
   3412 					atciMail->iconXpm,
   3413 					atciMail->iconXpmOpen,
   3414 					FALSE, TRUE );
   3415 			gtk_cmctree_node_set_row_data(clist, nodeEMail, email );
   3416 		}
   3417 		flgFirst = FALSE;
   3418 		haveAddr = TRUE;
   3419 	}
   3420 	if( ! haveAddr ) {
   3421 		/* Have name without EMail */
   3422 		text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
   3423 		text[COL_ADDRESS] = "";
   3424 		text[COL_REMARKS] = "";
   3425 		nodePerson = gtk_sctree_insert_node(
   3426 				clist, NULL, NULL,
   3427 				text, FOLDER_SPACING,
   3428 				atci->iconXpm,
   3429 				atci->iconXpmOpen,
   3430 				FALSE, person->isOpened );
   3431 		gtk_cmctree_node_set_row_data(clist, nodePerson, person );
   3432 	}
   3433 	return;
   3434 }
   3435 
   3436 static void addressbook_folder_load_person( GtkCMCTree *clist, ItemFolder *itemFolder ) {
   3437 	GList *items, *cur;
   3438 	AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
   3439 	AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
   3440 	const gchar *search_str;
   3441 
   3442 	if( atci == NULL ) return;
   3443 	if( atciMail == NULL ) return;
   3444 
   3445 	search_str = gtk_entry_get_text(GTK_ENTRY(addrbook.entry));
   3446 
   3447 	/* Load email addresses */
   3448 	items = addritem_folder_get_person_list( itemFolder );
   3449 	for(cur = items ; cur != NULL; cur = g_list_next( cur ) ) {
   3450 		ItemPerson *person;
   3451 		GList *node;
   3452 		ItemEMail *email;
   3453 
   3454 		person = (ItemPerson *)cur->data;
   3455 		if (!person)
   3456 			continue;
   3457 		node = person->listEMail;
   3458 		if (node && node->data) {
   3459 			email = node->data;
   3460 			if (!addressbook_match_item(ADDRITEM_NAME(person), ADDRITEM_NAME(email), email->address, email->remarks, search_str))
   3461 				continue;
   3462 		} else {
   3463 			if (!addressbook_match_item(ADDRITEM_NAME(person), NULL, NULL, NULL, search_str))
   3464 				continue;
   3465 		}
   3466 
   3467 		addressbook_folder_load_one_person( clist, cur->data, atci, atciMail );
   3468 	}
   3469 	/* Free up the list */
   3470 	g_list_free( items );
   3471 }
   3472 
   3473 static void addressbook_folder_remove_node( GtkCMCTree *clist, GtkCMCTreeNode *node ) {
   3474 	addrbook.listSelected = NULL;
   3475 	gtk_cmctree_remove_node( clist, node );
   3476 	addressbook_menubar_set_sensitive( FALSE );
   3477 	addressbook_menuitem_set_sensitive(
   3478 		gtk_cmctree_node_get_row_data(
   3479 			GTK_CMCTREE(clist), addrbook.treeSelected ),
   3480 		addrbook.treeSelected );
   3481 }
   3482 
   3483 void addressbook_folder_refresh_one_person( GtkCMCTree *clist, ItemPerson *person ) {
   3484 	AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
   3485 	AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
   3486 	GtkCMCTreeNode *node;
   3487 	if( atci == NULL ) return;
   3488 	if( atciMail == NULL ) return;
   3489 	if( person == NULL ) return;
   3490 	/* unload the person */
   3491 
   3492 	node = gtk_cmctree_find_by_row_data( clist, NULL, person );
   3493 	if( node )
   3494 		addressbook_folder_remove_node( clist, node );
   3495 	addressbook_folder_load_one_person( clist, person, atci, atciMail );
   3496 	gtk_sctree_sort_node( clist, NULL );
   3497 	node = gtk_cmctree_find_by_row_data( clist, NULL, person );
   3498 	if( node ) {
   3499 		gtk_sctree_select( GTK_SCTREE(clist), node );
   3500 		if (!gtk_cmctree_node_is_visible( clist, node ) )
   3501 			gtk_cmctree_node_moveto( clist, node, 0, 0, 0 );
   3502 	}
   3503 }
   3504 
   3505 void addressbook_folder_remove_one_person( GtkCMCTree *clist, ItemPerson *person ) {
   3506 	GtkCMCTreeNode *node;
   3507 
   3508 	if( person == NULL ) return;
   3509 	node = gtk_cmctree_find_by_row_data( clist, NULL, person );
   3510 	if( node ) {
   3511 		addressbook_folder_remove_node( clist, node );
   3512 	}
   3513 }
   3514 
   3515 static void addressbook_folder_load_group( GtkCMCTree *clist, ItemFolder *itemFolder ) {
   3516 	GList *items;
   3517 	AddressTypeControlItem *atci =  addrbookctl_lookup( ADDR_ITEM_GROUP );
   3518 	const gchar *search_str;
   3519 
   3520 	/* Load any groups */
   3521 	if( ! atci ) return;
   3522 
   3523 	search_str = gtk_entry_get_text(GTK_ENTRY(addrbook.entry));
   3524 
   3525 	items = addritem_folder_get_group_list( itemFolder );
   3526 	for( ; items != NULL; items = g_list_next( items ) ) {
   3527 		GtkCMCTreeNode *nodeGroup = NULL;
   3528 		gchar *text[N_LIST_COLS];
   3529 		ItemGroup *group = items->data;
   3530 		if( group == NULL ) continue;
   3531 		if( !addressbook_match_item(ADDRITEM_NAME(group),
   3532 					    NULL, NULL, NULL, search_str) )
   3533 			continue;
   3534 
   3535 		text[COL_NAME] = ADDRITEM_NAME(group);
   3536 		text[COL_ADDRESS] = "";
   3537 		text[COL_REMARKS] = "";
   3538 		nodeGroup = gtk_sctree_insert_node(clist, NULL, NULL,
   3539 				      text, FOLDER_SPACING,
   3540 				      atci->iconXpm,
   3541 				      atci->iconXpmOpen,
   3542 				      FALSE, FALSE);
   3543 		gtk_cmctree_node_set_row_data(clist, nodeGroup, group );
   3544 		gtk_sctree_sort_node(clist, NULL);
   3545 	}
   3546 	/* Free up the list */
   3547 	g_list_free( items );
   3548 }
   3549 
   3550 /**
   3551  * Search ctree widget callback function.
   3552  * \param  pA Pointer to node.
   3553  * \param  pB Pointer to data item being sought.
   3554  * \return Zero (0) if group found.
   3555  */
   3556 static int addressbook_treenode_find_group_cb( gconstpointer pA, gconstpointer pB ) {
   3557 	AddressObject *aoA;
   3558 
   3559 	aoA = ( AddressObject * ) pA;
   3560 	if( aoA->type == ADDR_ITEM_GROUP ) {
   3561 		ItemGroup *group, *grp;
   3562 
   3563 		grp = ADAPTER_GROUP(aoA)->itemGroup;
   3564 		group = ( ItemGroup * ) pB;
   3565 		if( grp == group ) return 0;	/* Found group */
   3566 	}
   3567 	return 1;
   3568 }
   3569 
   3570 /*
   3571 * Remove folder and group nodes from tree widget for items contained ("cut")
   3572 * in clipboard.
   3573 */
   3574 static void addressbook_treenode_remove_item( void ) {
   3575 	GList *node;
   3576 	AddrSelectItem *cutItem;
   3577 	AddressCache *cache;
   3578 	AddrItemObject *aio;
   3579 	GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
   3580 	GtkCMCTreeNode *tn;
   3581 
   3582 	node = _clipBoard_->objectList;
   3583 	while( node ) {
   3584 		cutItem = node->data;
   3585 		node = g_list_next( node );
   3586 		cache = addrindex_get_cache(
   3587 			_clipBoard_->addressIndex, cutItem->cacheID );
   3588 		if( cache == NULL ) continue;
   3589 		aio = addrcache_get_object( cache, cutItem->uid );
   3590 		if( aio ) {
   3591 			tn = NULL;
   3592 			if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
   3593 				ItemFolder *folder;
   3594 
   3595 				folder = ( ItemFolder * ) aio;
   3596 				tn = gtk_cmctree_find_by_row_data_custom(
   3597 					ctree, NULL, folder,
   3598 					addressbook_treenode_find_folder_cb );
   3599 			}
   3600 			else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
   3601 				ItemGroup *group;
   3602 
   3603 				group = ( ItemGroup * ) aio;
   3604 				tn = gtk_cmctree_find_by_row_data_custom(
   3605 					ctree, NULL, group,
   3606 					addressbook_treenode_find_group_cb );
   3607 			}
   3608 
   3609 			if( tn ) {
   3610 				/* Free up adapter and remove node. */
   3611 				gtk_cmctree_remove_node( ctree, tn );
   3612 			}
   3613 		}
   3614 	}
   3615 }
   3616 
   3617 /**
   3618  * Find parent datasource for specified tree node.
   3619  * \param  node Node to test.
   3620  * \return Data source, or NULL if not found.
   3621  */
   3622 static AddressDataSource *addressbook_find_datasource( GtkCMCTreeNode *node ) {
   3623 	AddressDataSource *ds = NULL;
   3624 	AddressObject *ao;
   3625 
   3626 	cm_return_val_if_fail(addrbook.ctree != NULL, NULL);
   3627 
   3628 	while( node ) {
   3629 		if( GTK_CMCTREE_ROW(node)->level < 2 ) return NULL;
   3630 		ao = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), node );
   3631 		if( ao ) {
   3632 			/* g_print( "ao->type = %d\n", ao->type ); */
   3633 			if( ao->type == ADDR_DATASOURCE ) {
   3634 				AdapterDSource *ads = ADAPTER_DSOURCE(ao);
   3635 				/* g_print( "found it\n" ); */
   3636 				ds = ads->dataSource;
   3637 				break;
   3638 			}
   3639 		}
   3640 		node = GTK_CMCTREE_ROW(node)->parent;
   3641 	}
   3642 	return ds;
   3643 }
   3644 
   3645 /**
   3646  * Load address list widget with children of specified object.
   3647  * \param obj Parent object to be loaded.
   3648  */
   3649 static void addressbook_set_clist( AddressObject *obj, gboolean refresh ) {
   3650 	GtkCMCTree *ctreelist = GTK_CMCTREE(addrbook.clist);
   3651 	GtkCMCList *clist = GTK_CMCLIST(addrbook.clist);
   3652 	AddressDataSource *ds = NULL;
   3653 	AdapterDSource *ads = NULL;
   3654 	static AddressObject *last_obj = NULL;
   3655 
   3656 	if (addrbook.clist == NULL) {
   3657 		return;
   3658 	}
   3659 	if (obj == last_obj && !refresh)
   3660 		return;
   3661 
   3662 	last_obj = obj;
   3663 	if( obj == NULL ) {
   3664 		gtk_cmclist_clear(clist);
   3665 		return;
   3666 	}
   3667 
   3668 	if( obj->type == ADDR_INTERFACE ) {
   3669 		/* g_print( "set_clist: loading datasource...\n" ); */
   3670 		/* addressbook_node_load_datasource( GTK_CMCTREE(clist), obj ); */
   3671 		return;
   3672 	}
   3673 
   3674 	gtk_cmclist_freeze(clist);
   3675 	gtk_cmclist_clear(clist);
   3676 
   3677 	if( obj->type == ADDR_DATASOURCE ) {
   3678 		ads = ADAPTER_DSOURCE(obj);
   3679 		ds = ads->dataSource;
   3680 		if( ds ) {
   3681 			/* Load root folder */
   3682 			ItemFolder *rootFolder = NULL;
   3683 			rootFolder = addrindex_ds_get_root_folder( ds );
   3684 			addressbook_folder_load_person(
   3685 				ctreelist, rootFolder );
   3686 			addressbook_folder_load_group(
   3687 				ctreelist, rootFolder );
   3688 		}
   3689 	}
   3690 	else {
   3691 		if( obj->type == ADDR_ITEM_GROUP ) {
   3692 			/* Load groups */
   3693 			ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup;
   3694 			addressbook_load_group( ctreelist, itemGroup );
   3695 		}
   3696 		else if( obj->type == ADDR_ITEM_FOLDER ) {
   3697 			/* Load folders */
   3698 			ItemFolder *itemFolder = ADAPTER_FOLDER(obj)->itemFolder;
   3699 			addressbook_folder_load_person( ctreelist, itemFolder );
   3700 			addressbook_folder_load_group( ctreelist, itemFolder );
   3701 		}
   3702 	}
   3703 	gtk_sctree_sort_recursive(GTK_CMCTREE(clist), NULL);
   3704 	clist->focus_row = -1;
   3705 	gtk_cmclist_thaw(clist);
   3706 }
   3707 
   3708 /**
   3709  * Call back function to free adaptor. Call back is setup by function
   3710  * gtk_cmctree_node_set_row_data_full() when node is populated. This function is
   3711  * called when the address book tree widget node is removed by calling
   3712  * function gtk_cmctree_remove_node().
   3713  *
   3714  * \param data Tree node's row data.
   3715  */
   3716 static void addressbook_free_treenode( gpointer data ) {
   3717 	AddressObject *ao;
   3718 
   3719 	ao = ( AddressObject * ) data;
   3720 	if( ao == NULL ) return;
   3721 	if( ao->type == ADDR_INTERFACE ) {
   3722 		AdapterInterface *ai = ADAPTER_INTERFACE(ao);
   3723 		addrbookctl_free_interface( ai );
   3724 	}
   3725 	else if( ao->type == ADDR_DATASOURCE ) {
   3726 		AdapterDSource *ads = ADAPTER_DSOURCE(ao);
   3727 		addrbookctl_free_datasource( ads );
   3728 	}
   3729 	else if( ao->type == ADDR_ITEM_FOLDER ) {
   3730 		AdapterFolder *af = ADAPTER_FOLDER(ao);
   3731 		addrbookctl_free_folder( af );
   3732 	}
   3733 	else if( ao->type == ADDR_ITEM_GROUP ) {
   3734 		AdapterGroup *ag = ADAPTER_GROUP(ao);
   3735 		addrbookctl_free_group( ag );
   3736 	}
   3737 }
   3738 
   3739 /*
   3740 * Create new adaptor for specified data source.
   3741 */
   3742 AdapterDSource *addressbook_create_ds_adapter( AddressDataSource *ds,
   3743 				AddressObjectType otype, gchar *name )
   3744 {
   3745 	AdapterDSource *adapter = g_new0( AdapterDSource, 1 );
   3746 	ADDRESS_OBJECT(adapter)->type = ADDR_DATASOURCE;
   3747 	ADDRESS_OBJECT_NAME(adapter) = g_strdup( name );
   3748 	adapter->dataSource = ds;
   3749 	adapter->subType = otype;
   3750 	return adapter;
   3751 }
   3752 
   3753 void addressbook_ads_set_name( AdapterDSource *adapter, gchar *value ) {
   3754 	ADDRESS_OBJECT_NAME(adapter) =
   3755 		mgu_replace_string( ADDRESS_OBJECT_NAME(adapter), value );
   3756 }
   3757 
   3758 /*
   3759  * Load tree from address index with the initial data.
   3760  */
   3761 static void addressbook_load_tree( void ) {
   3762 	GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
   3763 	GList *nodeIf, *nodeDS;
   3764 	AdapterInterface *adapter;
   3765 	AddressInterface *iface;
   3766 	AddressTypeControlItem *atci;
   3767 	AddressDataSource *ds;
   3768 	AdapterDSource *ads;
   3769 	GtkCMCTreeNode *node, *newNode;
   3770 	gchar *name;
   3771 
   3772 	nodeIf = _addressInterfaceList_;
   3773 	while( nodeIf ) {
   3774 		adapter = nodeIf->data;
   3775 		node = adapter->treeNode;
   3776 		iface = adapter->interface;
   3777 		atci = adapter->atci;
   3778 		if( iface ) {
   3779 			if( iface->useInterface ) {
   3780 				/* Load data sources below interface node */
   3781 				nodeDS = iface->listSource;
   3782 				while( nodeDS ) {
   3783 					ds = nodeDS->data;
   3784 					name = addrindex_ds_get_name( ds );
   3785 					ads = addressbook_create_ds_adapter(
   3786 							ds, atci->objectType, name );
   3787 					newNode = addressbook_add_object(
   3788 							node, ADDRESS_OBJECT(ads) );
   3789 					if (newNode == NULL) {
   3790 						g_message("error adding addressbook object\n");
   3791 					}
   3792 					nodeDS = g_list_next( nodeDS );
   3793 				}
   3794 				gtk_cmctree_expand( ctree, node );
   3795 			}
   3796 		}
   3797 		nodeIf = g_list_next( nodeIf );
   3798 	}
   3799 }
   3800 
   3801 /*
   3802  * Convert the old address book to new format.
   3803  */
   3804 static gboolean addressbook_convert( AddressIndex *addrIndex ) {
   3805 	gboolean retVal = FALSE;
   3806 	gboolean errFlag = TRUE;
   3807 	gchar *msg = NULL;
   3808 
   3809 	/* Read old address book, performing conversion */
   3810 	debug_print( "Reading and converting old address book...\n" );
   3811 	addrindex_set_file_name( addrIndex, ADDRESSBOOK_OLD_FILE );
   3812 	addrindex_read_data( addrIndex );
   3813 	if( addrIndex->retVal == MGU_NO_FILE ) {
   3814 		/* We do not have a file - new user */
   3815 		debug_print( "New user... create new books...\n" );
   3816 		addrindex_create_new_books( addrIndex );
   3817 		if( addrIndex->retVal == MGU_SUCCESS ) {
   3818 			/* Save index file */
   3819 			addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
   3820 			addrindex_save_data( addrIndex );
   3821 			if( addrIndex->retVal == MGU_SUCCESS ) {
   3822 				retVal = TRUE;
   3823 				errFlag = FALSE;
   3824 			}
   3825 			else {
   3826 				msg = "New user, could not save index file.";
   3827 			}
   3828 		}
   3829 		else {
   3830 			msg = "New user, could not save address book files.";
   3831 		}
   3832 	}
   3833 	else {
   3834 		/* We have an old file */
   3835 		if( addrIndex->wasConverted ) {
   3836 			/* Converted successfully - save address index */
   3837 			addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
   3838 			addrindex_save_data( addrIndex );
   3839 			if( addrIndex->retVal == MGU_SUCCESS ) {
   3840 				msg = "Old address book converted successfully.";
   3841 				retVal = TRUE;
   3842 				errFlag = FALSE;
   3843 			}
   3844 			else {
   3845 				msg = "Old address book converted,\ncould not save new address index file.";
   3846 			}
   3847 		}
   3848 		else {
   3849 			/* File conversion failed - just create new books */
   3850 			debug_print( "File conversion failed... just create new books...\n" );
   3851 			addrindex_create_new_books( addrIndex );
   3852 			if( addrIndex->retVal == MGU_SUCCESS ) {
   3853 				/* Save index */
   3854 				addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
   3855 				addrindex_save_data( addrIndex );
   3856 				if( addrIndex->retVal == MGU_SUCCESS ) {
   3857 					msg = "Could not convert address book,\nbut created empty new address book files.";
   3858 					retVal = TRUE;
   3859 					errFlag = FALSE;
   3860 				}
   3861 				else {
   3862 					msg = "Could not convert address book,\ncould not save new address index file.";
   3863 				}
   3864 			}
   3865 			else {
   3866 				msg = "Could not convert address book\nand could not create new address book files.";
   3867 			}
   3868 		}
   3869 	}
   3870 	if (errFlag) {
   3871 		debug_print( "Error\n%s\n", msg );
   3872 		alertpanel_full("Addressbook conversion error", msg,
   3873 				"window-close-symbolic", "_Close", NULL, NULL, NULL, NULL,
   3874 				ALERTFOCUS_FIRST, FALSE, NULL, ALERT_ERROR);
   3875 	} else if (msg) {
   3876 		debug_print( "Warning\n%s\n", msg );
   3877 		alertpanel_full("Addressbook conversion error", msg,
   3878 				"window-close-symbolic", "_Close", NULL, NULL, NULL, NULL,
   3879 				ALERTFOCUS_FIRST, FALSE, NULL, ALERT_WARNING);
   3880 	}
   3881 
   3882 	return retVal;
   3883 }
   3884 
   3885 static gboolean migrate_addrbook(const gchar *origdir, const gchar *destdir)
   3886 {
   3887 	GDir *dp;
   3888 	const gchar *d;
   3889 	gboolean failed = FALSE;
   3890 	GError *error = NULL;
   3891 
   3892 	if( ( dp = g_dir_open( origdir, 0, &error ) ) == NULL ) {
   3893 		debug_print("opening '%s' failed: %d (%s)\n", origdir,
   3894 				error->code, error->message);
   3895 		g_error_free(error);
   3896 		return FALSE;
   3897 	}
   3898 
   3899 	while( ( d = g_dir_read_name( dp ) ) != NULL ) {
   3900 		if (strncmp(d, "addrbook-", strlen("addrbook-")))
   3901 			continue;
   3902 		else {
   3903 			gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S,
   3904 					d, NULL);
   3905 			gchar *dest_file = g_strconcat(destdir, G_DIR_SEPARATOR_S,
   3906 					d, NULL);
   3907 			if (copy_file(orig_file, dest_file, FALSE) < 0) {
   3908 				failed = TRUE;
   3909 			}
   3910 			g_free(orig_file);
   3911 			g_free(dest_file);
   3912 			if (failed) {
   3913 				break;
   3914 			}
   3915 		}
   3916 	}
   3917 	g_dir_close( dp );
   3918 
   3919 	if (!failed) {
   3920 		/* all copies succeeded, we can remove source files */
   3921 		if( ( dp = g_dir_open( origdir, 0, &error ) ) == NULL ) {
   3922 			debug_print("opening '%s' failed: %d (%s)\n", origdir,
   3923 					error->code, error->message);
   3924 			g_error_free(error);
   3925 			return FALSE;
   3926 		}
   3927 		while( ( d = g_dir_read_name( dp ) ) != NULL ) {
   3928 			if (strncmp(d, "addrbook-", strlen("addrbook-")))
   3929 				continue;
   3930 			else {
   3931 				gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S,
   3932 						d, NULL);
   3933 				unlink(orig_file);
   3934 				g_free(orig_file);
   3935 			}
   3936 		}
   3937 		g_dir_close( dp );
   3938 	}
   3939 
   3940 	return !failed;
   3941 }
   3942 
   3943 void addressbook_read_file( void ) {
   3944 	AddressIndex *addrIndex = NULL;
   3945 	gchar *indexdir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRBOOK_DIR, NULL);
   3946 
   3947 	debug_print( "Reading address index...\n" );
   3948 	if( _addressIndex_ ) {
   3949 		debug_print( "address book already read!!!\n" );
   3950 		g_free(indexdir);
   3951 		return;
   3952 	}
   3953 
   3954 	addrIndex = addrindex_create_index();
   3955 	addrindex_initialize();
   3956 
   3957 	/* Use new address book index. */
   3958 
   3959 	if ( !is_dir_exist(indexdir) ) {
   3960 		if (mkdir(indexdir, 0700) < 0 ) {
   3961 			addrindex_set_file_path( addrIndex, get_rc_dir() );
   3962 			g_warning("couldn't create dir '%s'", indexdir);
   3963 		} else {
   3964 			if (!migrate_addrbook(get_rc_dir(), indexdir)) {
   3965 				remove_dir_recursive(indexdir);
   3966 				addrindex_set_file_path( addrIndex, get_rc_dir() );
   3967 				g_error("couldn't migrate dir %s", indexdir);
   3968 			} else {
   3969 				addrindex_set_file_path( addrIndex, indexdir);
   3970 			}
   3971 		}
   3972 	} else {
   3973 		addrindex_set_file_path( addrIndex, indexdir);
   3974 	}
   3975 	g_free(indexdir);
   3976 	addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
   3977 	addrindex_read_data( addrIndex );
   3978 	if( addrIndex->retVal == MGU_NO_FILE ) {
   3979 		/* Conversion required */
   3980 		debug_print( "Converting...\n" );
   3981 		if( addressbook_convert( addrIndex ) ) {
   3982 			_addressIndex_ = addrIndex;
   3983 		}
   3984 	}
   3985 	else if( addrIndex->retVal == MGU_SUCCESS ) {
   3986 		_addressIndex_ = addrIndex;
   3987 	}
   3988 	else {
   3989 		/* Error reading address book */
   3990 		debug_print( "Could not read address index.\n" );
   3991 		addrindex_print_index( addrIndex, stdout );
   3992 		alertpanel_full("Addressbook Error",
   3993 				"Could not read address index",
   3994 				"window-close-symbolic", "_Close", NULL, NULL, NULL, NULL,
   3995 				ALERTFOCUS_FIRST, FALSE, NULL, ALERT_ERROR);
   3996 	}
   3997 	debug_print( "done.\n" );
   3998 }
   3999 
   4000 /*
   4001 * Add object into the address index tree widget.
   4002 * Enter: node	Parent node.
   4003 *        obj	Object to add.
   4004 * Return: Node that was added, or NULL if object not added.
   4005 */
   4006 static GtkCMCTreeNode *addressbook_add_object(GtkCMCTreeNode *node,
   4007 					    AddressObject *obj)
   4008 {
   4009 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   4010 	GtkCMCTreeNode *added;
   4011 	AddressObject *pobj;
   4012 	AddressObjectType otype;
   4013 	AddressTypeControlItem *atci = NULL;
   4014 
   4015 	cm_return_val_if_fail(node != NULL, NULL);
   4016 	cm_return_val_if_fail(obj  != NULL, NULL);
   4017 
   4018 	pobj = gtk_cmctree_node_get_row_data(ctree, node);
   4019 	cm_return_val_if_fail(pobj != NULL, NULL);
   4020 
   4021 	/* Determine object type to be displayed */
   4022 	if( obj->type == ADDR_DATASOURCE ) {
   4023 		otype = ADAPTER_DSOURCE(obj)->subType;
   4024 	}
   4025 	else {
   4026 		otype = obj->type;
   4027 	}
   4028 
   4029 	/* Handle any special conditions. */
   4030 	added = node;
   4031 	atci = addrbookctl_lookup( otype );
   4032 	if( atci ) {
   4033 		if( atci->showInTree ) {
   4034 			/* Add object to tree */
   4035 			gchar **name;
   4036 			name = &obj->name;
   4037 			added = gtk_sctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
   4038 				atci->iconXpm, atci->iconXpmOpen,
   4039 				atci->treeLeaf, atci->treeExpand );
   4040 			gtk_cmctree_node_set_row_data_full( ctree, added, obj,
   4041 				addressbook_free_treenode );
   4042 		}
   4043 	}
   4044 
   4045 	gtk_sctree_sort_node(ctree, node);
   4046 
   4047 	return added;
   4048 }
   4049 
   4050 /**
   4051  * Add group into the address index tree.
   4052  * \param  node      Parent node.
   4053  * \param  ds        Data source.
   4054  * \param  itemGroup Group to add.
   4055  * \return Inserted node.
   4056  */
   4057 static GtkCMCTreeNode *addressbook_node_add_group(
   4058 		GtkCMCTreeNode *node, AddressDataSource *ds,
   4059 		ItemGroup *itemGroup )
   4060 {
   4061 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   4062 	GtkCMCTreeNode *newNode;
   4063 	AdapterGroup *adapter;
   4064 	AddressTypeControlItem *atci = NULL;
   4065 	gchar **name;
   4066 
   4067 	if( ds == NULL ) return NULL;
   4068 	if( node == NULL || itemGroup == NULL ) return NULL;
   4069 
   4070 	name = &itemGroup->obj.name;
   4071 
   4072 	atci = addrbookctl_lookup( ADDR_ITEM_GROUP );
   4073 
   4074 	adapter = g_new0( AdapterGroup, 1 );
   4075 	ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_GROUP;
   4076 	ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemGroup) );
   4077 	adapter->itemGroup = itemGroup;
   4078 
   4079 	newNode = gtk_sctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
   4080 			atci->iconXpm, atci->iconXpm,
   4081 			atci->treeLeaf, atci->treeExpand );
   4082 	gtk_cmctree_node_set_row_data_full( ctree, newNode, adapter,
   4083 		addressbook_free_treenode );
   4084 	gtk_sctree_sort_node( ctree, node );
   4085 	return newNode;
   4086 }
   4087 
   4088 /**
   4089  * Add folder into the address index tree. Only visible folders are loaded into
   4090  * the address index tree. Note that the root folder is not inserted into the
   4091  * tree.
   4092  *
   4093  * \param  node	      Parent node.
   4094  * \param  ds         Data source.
   4095  * \param  itemFolder Folder to add.
   4096  * \param  otype      Object type to display.
   4097  * \return Inserted node for the folder.
   4098 */
   4099 static GtkCMCTreeNode *addressbook_node_add_folder(
   4100 		GtkCMCTreeNode *node, AddressDataSource *ds,
   4101 		ItemFolder *itemFolder, AddressObjectType otype )
   4102 {
   4103 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
   4104 	GtkCMCTreeNode *newNode = NULL;
   4105 	AddressTypeControlItem *atci = NULL;
   4106 	GList *listItems = NULL;
   4107 	gchar *name;
   4108 	ItemFolder *rootFolder;
   4109 
   4110 	/* Only visible folders */
   4111 	if( itemFolder == NULL || itemFolder->isHidden )
   4112 		return NULL;
   4113 
   4114 	if( ds == NULL )
   4115 		return NULL;
   4116 	if( node == NULL || itemFolder == NULL )
   4117 		return NULL;
   4118 
   4119 	/* Determine object type */
   4120 	atci = addrbookctl_lookup( otype );
   4121 	if( atci == NULL )
   4122 		return NULL;
   4123 
   4124 	rootFolder = addrindex_ds_get_root_folder( ds );
   4125 	if( itemFolder == rootFolder ) {
   4126 		newNode = node;
   4127 	}
   4128 	else {
   4129 		AdapterFolder *adapter = g_new0( AdapterFolder, 1 );
   4130 		ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_FOLDER;
   4131 		ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemFolder) );
   4132 		adapter->itemFolder = itemFolder;
   4133 
   4134 		name = ADDRITEM_NAME(itemFolder);
   4135 		newNode = gtk_sctree_insert_node( ctree, node, NULL, &name, FOLDER_SPACING,
   4136 				atci->iconXpm, atci->iconXpm,
   4137 				atci->treeLeaf, atci->treeExpand );
   4138 		if( newNode ) {
   4139 			gtk_cmctree_node_set_row_data_full( ctree, newNode, adapter,
   4140 				addressbook_free_treenode );
   4141 		} else {
   4142 			addrbookctl_free_folder(adapter);
   4143 		}
   4144 	}
   4145 
   4146 	listItems = itemFolder->listFolder;
   4147 	while( listItems ) {
   4148 		ItemFolder *item = listItems->data;
   4149 		addressbook_node_add_folder( newNode, ds, item, otype );
   4150 		listItems = g_list_next( listItems );
   4151 	}
   4152 	listItems = itemFolder->listGroup;
   4153 	while( listItems ) {
   4154 		ItemGroup *item = listItems->data;
   4155 		addressbook_node_add_group( newNode, ds, item );
   4156 		listItems = g_list_next( listItems );
   4157 	}
   4158 	gtk_sctree_sort_node( ctree, node );
   4159 	return newNode;
   4160 }
   4161 
   4162 void addressbook_export_to_file( void ) {
   4163 	if( _addressIndex_ ) {
   4164 		/* Save all new address book data */
   4165 		debug_print( "Saving address books...\n" );
   4166 		addrindex_save_all_books( _addressIndex_ );
   4167 
   4168 		debug_print( "Exporting addressbook to file...\n" );
   4169 		addrindex_save_data( _addressIndex_ );
   4170 		if( _addressIndex_->retVal != MGU_SUCCESS ) {
   4171 			addrindex_print_index( _addressIndex_, stdout );
   4172 		}
   4173 
   4174 		/* Notify address completion of new data */
   4175 		invalidate_address_completion();
   4176 	}
   4177 }
   4178 
   4179 static gboolean addressbook_entry_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
   4180 {
   4181 	if (event && (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter))
   4182 		addressbook_lup_clicked(NULL, NULL);
   4183 	return FALSE;
   4184 }
   4185 
   4186 /*
   4187 * Comparison using cell contents (text in first column). Used for sort
   4188 * address index widget.
   4189 */
   4190 static gint addressbook_treenode_compare_func(
   4191 	GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
   4192 {
   4193 	GtkCMCell *cell1 = ((GtkCMCListRow *)ptr1)->cell;
   4194 	GtkCMCell *cell2 = ((GtkCMCListRow *)ptr2)->cell;
   4195 	gchar *name1 = NULL, *name2 = NULL;
   4196 	if( cell1 ) name1 = cell1->u.text;
   4197 	if( cell2 ) name2 = cell2->u.text;
   4198 	if( ! name1 ) return ( name2 != NULL );
   4199 	if( ! name2 ) return -1;
   4200 	return g_utf8_collate( name1, name2 );
   4201 }
   4202 
   4203 static void addressbook_new_book_cb( GtkAction *action, gpointer data ) {
   4204 	AdapterDSource *ads;
   4205 	AdapterInterface *adapter;
   4206 	GtkCMCTreeNode *newNode;
   4207 
   4208 	adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
   4209 	if( adapter == NULL ) return;
   4210 	ads = addressbook_edit_book( _addressIndex_, NULL );
   4211 	if( ads ) {
   4212 		newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
   4213 		if( newNode ) {
   4214 			gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
   4215 			addrbook.treeSelected = newNode;
   4216 		}
   4217 	}
   4218 }
   4219 
   4220 /**
   4221  * Display address search status message.
   4222  * \param queryType Query type.
   4223  * \param status    Status/Error code.
   4224  */
   4225 static void addressbook_search_message( gint queryType, gint sts ) {
   4226 	gchar *desc = NULL;
   4227 	*addressbook_msgbuf = '\0';
   4228 
   4229 	if( desc ) {
   4230 		g_snprintf( addressbook_msgbuf,
   4231 			sizeof(addressbook_msgbuf), "%s", desc );
   4232 		addressbook_status_show( addressbook_msgbuf );
   4233 	}
   4234 	else {
   4235 		addressbook_status_show( "" );
   4236 	}
   4237 }
   4238 
   4239 /**
   4240  * Refresh addressbook by forcing refresh of current selected object in
   4241  * tree.
   4242  */
   4243 static void addressbook_refresh_current( void ) {
   4244 	AddressObject *obj;
   4245 	GtkCMCTree *ctree;
   4246 
   4247 	ctree = GTK_CMCTREE(addrbook.ctree);
   4248 	obj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
   4249 	if( obj == NULL ) return;
   4250 	addressbook_set_clist( obj, TRUE );
   4251 }
   4252 
   4253 /**
   4254  * Message that is displayed whilst a query is executing in a background
   4255  * thread.
   4256  */
   4257 static gchar *_tempMessage_ = "Busy searching...";
   4258 
   4259 /**
   4260  * Search completion callback function. This removes the query from the idle
   4261  * list.
   4262  *
   4263  * \param sender  Sender of query.
   4264  * \param queryID Query ID of search request.
   4265  * \param status  Search status.
   4266  * \param data    Query data.
   4267  */
   4268 static void addressbook_search_callback_end(
   4269 		gpointer sender, gint queryID, gint status, gpointer data )
   4270 {
   4271 	gpointer ptrQID;
   4272 	QueryRequest *req;
   4273 	AddrQueryObject *aqo;
   4274 
   4275 	/* Remove idler function */
   4276 	ptrQID = GINT_TO_POINTER( queryID );
   4277 	if( ptrQID ) {
   4278 		g_idle_remove_by_data( ptrQID );
   4279 	}
   4280 
   4281 	/* Refresh addressbook contents */
   4282 	addressbook_refresh_current();
   4283 	req = qrymgr_find_request( queryID );
   4284 	if( req != NULL ) {
   4285 		aqo = ( AddrQueryObject * ) req->queryList->data;
   4286 		addressbook_search_message( aqo->queryType, status );
   4287 	}
   4288 
   4289 	/* Stop the search */
   4290 	addrindex_stop_search( queryID );
   4291 }
   4292 
   4293 /**
   4294  * Perform search.
   4295  *
   4296  * \param ds         Data source to search.
   4297  * \param searchTerm String to lookup.
   4298  * \param pNode      Parent data source node.
   4299  */
   4300 static void addressbook_perform_search(
   4301 		AddressDataSource *ds, gchar *searchTerm,
   4302 		GtkCMCTreeNode *pNode )
   4303 {
   4304 	ItemFolder *folder;
   4305 	gchar *name;
   4306 	gint queryID;
   4307 
   4308 	/* Setup a query */
   4309 	if( *searchTerm == '\0' || strlen( searchTerm ) < 1 ) return;
   4310 
   4311 	if( !ds ) return;
   4312 
   4313 	/* Create a folder for the search results */
   4314 	name = g_strdup_printf( _queryFolderLabel_, searchTerm );
   4315 	folder = addressbook_setup_subf(ds, name, pNode);
   4316 	g_free( name );
   4317 
   4318 	/* Setup the search */
   4319 	queryID = addrindex_setup_explicit_search(
   4320 		ds, searchTerm, folder, addressbook_search_callback_end, NULL );
   4321 	if( queryID == 0 ) return;
   4322 
   4323 	/* Start search, sit back and wait for something to happen */
   4324 	addrindex_start_search( queryID );
   4325 
   4326 	addressbook_status_show( _tempMessage_ );
   4327 }
   4328 
   4329 /**
   4330  * Lookup button handler. Address search is only performed against
   4331  * address interfaces for external queries.
   4332  *
   4333  * \param button Lookup button widget.
   4334  * \param data   Data object.
   4335  */
   4336 static void addressbook_lup_clicked( GtkButton *button, gpointer data ) {
   4337 	GtkCMCTree *ctree;
   4338 	AddressObject *obj;
   4339 	AddressDataSource *ds;
   4340 	AddressInterface *iface;
   4341 	gchar *searchTerm;
   4342 	GtkCMCTreeNode *node, *parentNode;
   4343 
   4344 	node = addrbook.treeSelected;
   4345 	if( ! node ) return;
   4346 	if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
   4347 
   4348 	ctree = GTK_CMCTREE(addrbook.ctree);
   4349 	obj = gtk_cmctree_node_get_row_data( ctree, node );
   4350 	if( obj == NULL ) return;
   4351 
   4352 	if (obj->type != ADDR_DATASOURCE) {
   4353 		addressbook_set_clist(
   4354 				gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
   4355 					addrbook.treeSelected),
   4356 				TRUE);
   4357 	}
   4358 
   4359 	ds = addressbook_find_datasource( node );
   4360 	if( ds == NULL ) return;
   4361 
   4362 	/* We must have a datasource that is an external interface */
   4363 	iface = ds->interface;
   4364 	if( ! iface->haveLibrary ) return;
   4365 	if( ! iface->externalQuery ) return;
   4366 
   4367 	searchTerm =
   4368 		gtk_editable_get_chars( GTK_EDITABLE(addrbook.entry), 0, -1 );
   4369 	g_strchomp( searchTerm );
   4370 
   4371 	if( obj->type == ADDR_ITEM_FOLDER ) {
   4372 		parentNode = GTK_CMCTREE_ROW(node)->parent;
   4373 	}
   4374 	else {
   4375 		parentNode = node;
   4376 	}
   4377 	addressbook_perform_search( ds, searchTerm, parentNode );
   4378 
   4379 	gtk_widget_grab_focus( addrbook.entry );
   4380 
   4381 	g_free( searchTerm );
   4382 }
   4383 
   4384 static void addressbook_close_clicked( GtkButton *button, gpointer data ) {
   4385 	addressbook_close();
   4386 }
   4387 
   4388 /* **********************************************************************
   4389 * Build lookup tables.
   4390 * ***********************************************************************
   4391 */
   4392 
   4393 /*
   4394  * Remap object types.
   4395  * Enter:  abType AddressObjectType (used in tree node).
   4396  * Return: ItemObjectType (used in address cache data).
   4397  */
   4398 ItemObjectType addressbook_type2item( AddressObjectType abType ) {
   4399 	ItemObjectType ioType;
   4400 
   4401 	switch( abType ) {
   4402 		case ADDR_ITEM_PERSON: ioType = ITEMTYPE_PERSON;     break;
   4403 		case ADDR_ITEM_EMAIL:  ioType = ITEMTYPE_EMAIL;      break;
   4404 		case ADDR_ITEM_FOLDER: ioType = ITEMTYPE_FOLDER;     break;
   4405 		case ADDR_ITEM_GROUP:  ioType = ITEMTYPE_GROUP;      break;
   4406 		case ADDR_DATASOURCE:  ioType = ITEMTYPE_DATASOURCE; break;
   4407 		default:               ioType = ITEMTYPE_NONE;       break;
   4408 	}
   4409 	return ioType;
   4410 }
   4411 
   4412 #define UPDATE_ICON_ATCI(id,icon,iconopen) {			\
   4413 	atci = addrbookctl_lookup(id);				\
   4414 	if (atci) {						\
   4415 		atci->iconXpm = icon;				\
   4416 		atci->iconXpmOpen = iconopen;			\
   4417 	} else {						\
   4418 		g_warning("can't get atci %d", id);		\
   4419 	}							\
   4420 }
   4421 
   4422 /*
   4423 * Build table that controls the rendering of object types.
   4424 */
   4425 static void addrbookctl_build_icons( GtkWidget *window ) {
   4426 	AddressTypeControlItem *atci;
   4427 
   4428 	/* Build icons */
   4429 	if (interfacexpm)
   4430 		g_object_unref(interfacexpm);
   4431 	if (folderxpm)
   4432 		g_object_unref(folderxpm);
   4433 	if (folderopenxpm)
   4434 		g_object_unref(folderopenxpm);
   4435 	if (groupxpm)
   4436 		g_object_unref(groupxpm);
   4437 	if (bookxpm)
   4438 		g_object_unref(bookxpm);
   4439 	if (addressxpm)
   4440 		g_object_unref(addressxpm);
   4441 	if (categoryxpm)
   4442 		g_object_unref(categoryxpm);
   4443 	if (addrsearchxpm)
   4444 		g_object_unref(addrsearchxpm);
   4445 	stock_pixbuf_gdk(STOCK_PIXMAP_INTERFACE, &interfacexpm );
   4446 	stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
   4447 	stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
   4448 	stock_pixbuf_gdk(STOCK_PIXMAP_GROUP, &groupxpm);
   4449 	stock_pixbuf_gdk(STOCK_PIXMAP_BOOK, &bookxpm);
   4450 	stock_pixbuf_gdk(STOCK_PIXMAP_ADDRESS, &addressxpm);
   4451 	stock_pixbuf_gdk(STOCK_PIXMAP_CATEGORY, &categoryxpm);
   4452 	stock_pixbuf_gdk(STOCK_PIXMAP_ADDRESS_SEARCH, &addrsearchxpm);
   4453 
   4454 	UPDATE_ICON_ATCI(ADDR_INTERFACE,folderxpm,folderopenxpm);
   4455 	UPDATE_ICON_ATCI(ADDR_BOOK,bookxpm,bookxpm);
   4456 	UPDATE_ICON_ATCI(ADDR_ITEM_PERSON,NULL,NULL);
   4457 	UPDATE_ICON_ATCI(ADDR_ITEM_EMAIL,addressxpm,addressxpm);
   4458 	UPDATE_ICON_ATCI(ADDR_ITEM_GROUP,groupxpm,groupxpm);
   4459 	UPDATE_ICON_ATCI(ADDR_ITEM_FOLDER,folderxpm,folderopenxpm);
   4460 	UPDATE_ICON_ATCI(ADDR_CATEGORY,categoryxpm,categoryxpm);
   4461 
   4462 }
   4463 
   4464 /*
   4465 * Build table that controls the rendering of object types.
   4466 */
   4467 static void addrbookctl_build_map( GtkWidget *window ) {
   4468 	AddressTypeControlItem *atci;
   4469 
   4470 	_addressBookTypeHash_ = g_hash_table_new( g_int_hash, g_int_equal );
   4471 	_addressBookTypeList_ = NULL;
   4472 
   4473 	/* Interface */
   4474 	atci = g_new0( AddressTypeControlItem, 1 );
   4475 	atci->objectType = ADDR_INTERFACE;
   4476 	atci->interfaceType = ADDR_IF_NONE;
   4477 	atci->showInTree = TRUE;
   4478 	atci->treeExpand = TRUE;
   4479 	atci->treeLeaf = FALSE;
   4480 	atci->displayName = "Interface";
   4481 	atci->menuCommand = NULL;
   4482 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
   4483 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
   4484 
   4485 	/* Address book */
   4486 	atci = g_new0( AddressTypeControlItem, 1 );
   4487 	atci->objectType = ADDR_BOOK;
   4488 	atci->interfaceType = ADDR_IF_BOOK;
   4489 	atci->showInTree = TRUE;
   4490 	atci->treeExpand = TRUE;
   4491 	atci->treeLeaf = FALSE;
   4492 	atci->displayName = "Address Books";
   4493 	atci->menuCommand = "Menu/Book/NewBook";
   4494 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
   4495 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
   4496 
   4497 	/* Item person */
   4498 	atci = g_new0( AddressTypeControlItem, 1 );
   4499 	atci->objectType = ADDR_ITEM_PERSON;
   4500 	atci->interfaceType = ADDR_IF_NONE;
   4501 	atci->showInTree = FALSE;
   4502 	atci->treeExpand = FALSE;
   4503 	atci->treeLeaf = FALSE;
   4504 	atci->displayName = "Person";
   4505 	atci->menuCommand = NULL;
   4506 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
   4507 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
   4508 
   4509 	/* Item email */
   4510 	atci = g_new0( AddressTypeControlItem, 1 );
   4511 	atci->objectType = ADDR_ITEM_EMAIL;
   4512 	atci->interfaceType = ADDR_IF_NONE;
   4513 	atci->showInTree = FALSE;
   4514 	atci->treeExpand = FALSE;
   4515 	atci->treeLeaf = TRUE;
   4516 	atci->displayName = "Email Address";
   4517 	atci->menuCommand = NULL;
   4518 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
   4519 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
   4520 
   4521 	/* Item group */
   4522 	atci = g_new0( AddressTypeControlItem, 1 );
   4523 	atci->objectType = ADDR_ITEM_GROUP;
   4524 	atci->interfaceType = ADDR_IF_BOOK;
   4525 	atci->showInTree = TRUE;
   4526 	atci->treeExpand = FALSE;
   4527 	atci->treeLeaf = FALSE;
   4528 	atci->displayName = "Group";
   4529 	atci->menuCommand = NULL;
   4530 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
   4531 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
   4532 
   4533 	/* Item folder */
   4534 	atci = g_new0( AddressTypeControlItem, 1 );
   4535 	atci->objectType = ADDR_ITEM_FOLDER;
   4536 	atci->interfaceType = ADDR_IF_BOOK;
   4537 	atci->showInTree = TRUE;
   4538 	atci->treeExpand = FALSE;
   4539 	atci->treeLeaf = FALSE;
   4540 	atci->displayName = "Folder";
   4541 	atci->menuCommand = NULL;
   4542 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
   4543 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
   4544 
   4545 	addrbookctl_build_icons(window);
   4546 }
   4547 
   4548 void addressbook_reflect_prefs_pixmap_theme(void)
   4549 {
   4550 	if (addrbook.window)
   4551 		addrbookctl_build_icons(addrbook.window);
   4552 }
   4553 
   4554 /*
   4555 * Search for specified object type.
   4556 */
   4557 static AddressTypeControlItem *addrbookctl_lookup( gint ot ) {
   4558 	gint objType = ot;
   4559 	return ( AddressTypeControlItem * ) g_hash_table_lookup( _addressBookTypeHash_, &objType );
   4560 }
   4561 
   4562 /*
   4563 * Search for specified interface type.
   4564 */
   4565 static AddressTypeControlItem *addrbookctl_lookup_iface( AddressIfType ifType ) {
   4566 	GList *node = _addressBookTypeList_;
   4567 	while( node ) {
   4568 		AddressTypeControlItem *atci = node->data;
   4569 		if( atci->interfaceType == ifType ) return atci;
   4570 		node = g_list_next( node );
   4571 	}
   4572 	return NULL;
   4573 }
   4574 
   4575 static void addrbookctl_free_address( AddressObject *obj ) {
   4576 	g_free( obj->name );
   4577 	obj->type = ADDR_NONE;
   4578 	obj->name = NULL;
   4579 }
   4580 
   4581 static void addrbookctl_free_interface( AdapterInterface *adapter ) {
   4582 	addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
   4583 	adapter->interface = NULL;
   4584 	adapter->interfaceType = ADDR_IF_NONE;
   4585 	adapter->atci = NULL;
   4586 	adapter->enabled = FALSE;
   4587 	adapter->haveLibrary = FALSE;
   4588 	adapter->treeNode = NULL;
   4589 	g_free( adapter );
   4590 }
   4591 
   4592 static void addrbookctl_free_datasource( AdapterDSource *adapter ) {
   4593 	addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
   4594 	adapter->dataSource = NULL;
   4595 	adapter->subType = ADDR_NONE;
   4596 	g_free( adapter );
   4597 }
   4598 
   4599 static void addrbookctl_free_folder( AdapterFolder *adapter ) {
   4600 	addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
   4601 	adapter->itemFolder = NULL;
   4602 	g_free( adapter );
   4603 }
   4604 
   4605 static void addrbookctl_free_group( AdapterGroup *adapter ) {
   4606 	addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
   4607 	adapter->itemGroup = NULL;
   4608 	g_free( adapter );
   4609 }
   4610 
   4611 /**
   4612  * Build GUI interface list.
   4613  */
   4614 static void addrbookctl_build_iflist( void ) {
   4615 	AddressTypeControlItem *atci;
   4616 	AdapterInterface *adapter;
   4617 	GList *list = NULL;
   4618 
   4619 	if( _addressIndex_ == NULL ) {
   4620 		_addressIndex_ = addrindex_create_index();
   4621 		if( _clipBoard_ == NULL ) {
   4622 			_clipBoard_ = addrclip_create();
   4623 		}
   4624 		addrclip_set_index( _clipBoard_, _addressIndex_ );
   4625 	}
   4626 	_addressInterfaceList_ = NULL;
   4627 	list = addrindex_get_interface_list( _addressIndex_ );
   4628 	while( list ) {
   4629 		AddressInterface *interface = list->data;
   4630 		atci = addrbookctl_lookup_iface( interface->type );
   4631 		if( atci ) {
   4632 			adapter = g_new0( AdapterInterface, 1 );
   4633 			adapter->interfaceType = interface->type;
   4634 			adapter->atci = atci;
   4635 			adapter->interface = interface;
   4636 			adapter->treeNode = NULL;
   4637 			adapter->enabled = TRUE;
   4638 			adapter->haveLibrary = interface->haveLibrary;
   4639 			ADDRESS_OBJECT(adapter)->type = ADDR_INTERFACE;
   4640 			ADDRESS_OBJECT_NAME(adapter) = g_strdup( atci->displayName );
   4641 			_addressInterfaceList_ =
   4642 				g_list_append( _addressInterfaceList_, adapter );
   4643 		}
   4644 		list = g_list_next( list );
   4645 	}
   4646 }
   4647 
   4648 /**
   4649  * Find GUI interface type specified interface type.
   4650  * \param  ifType Interface type.
   4651  * \return Interface item, or NULL if not found.
   4652  */
   4653 static AdapterInterface *addrbookctl_find_interface( AddressIfType ifType ) {
   4654 	GList *node = _addressInterfaceList_;
   4655 	while( node ) {
   4656 		AdapterInterface *adapter = node->data;
   4657 		if( adapter->interfaceType == ifType ) return adapter;
   4658 		node = g_list_next( node );
   4659 	}
   4660 	return NULL;
   4661 }
   4662 
   4663 /**
   4664  * Build interface list selection.
   4665  */
   4666 static void addrbookctl_build_ifselect( void ) {
   4667 	GList *newList = NULL;
   4668 	gchar *selectStr;
   4669 	gchar **splitStr;
   4670 	gint ifType;
   4671 	gint i;
   4672 	gchar *endptr = NULL;
   4673 	/* gboolean enabled; */
   4674 	AdapterInterface *adapter;
   4675 
   4676 	selectStr = g_strdup( ADDRESSBOOK_IFACE_SELECTION );
   4677 
   4678 	/* Parse string */
   4679 	splitStr = g_strsplit( selectStr, ",", -1 );
   4680 	for( i = 0; i < ADDRESSBOOK_MAX_IFACE; i++ ) {
   4681 		if( splitStr[i] ) {
   4682 			/* g_print( "%d : %s\n", i, splitStr[i] ); */
   4683 			ifType = strtol( splitStr[i], &endptr, 10 );
   4684 			/* enabled = TRUE;
   4685 			if( *endptr ) {
   4686 				if( strcmp( endptr, "/n" ) == 0 ) {
   4687 					enabled = FALSE;
   4688 				}
   4689 			}
   4690 			*/
   4691 			/* g_print( "\t%d : %s\n", ifType, enabled ? "yes" : "no" ); */
   4692 			adapter = addrbookctl_find_interface( ifType );
   4693 			if( adapter ) {
   4694 				newList = g_list_append( newList, adapter );
   4695 			}
   4696 		}
   4697 		else {
   4698 			break;
   4699 		}
   4700 	}
   4701 	/* g_print( "i=%d\n", i ); */
   4702 	g_strfreev( splitStr );
   4703 	g_free( selectStr );
   4704 
   4705 	/* Replace existing list */
   4706 	g_list_free( _addressIFaceSelection_ );
   4707 	_addressIFaceSelection_ = newList;
   4708 	newList = NULL;
   4709 }
   4710 
   4711 /* ***********************************************************************
   4712  * Add sender to address book.
   4713  * ***********************************************************************
   4714  */
   4715 
   4716 /*
   4717  * This function is used by the Add sender to address book function.
   4718  */
   4719 gboolean addressbook_add_contact(
   4720 		const gchar *name, const gchar *address, const gchar *remarks,
   4721 		GdkPixbuf *picture )
   4722 {
   4723 	debug_print( "addressbook_add_contact: name/address: %s - %s\n", name, address );
   4724 	if( addressadd_selection( _addressIndex_, name, address, remarks, picture ) ) {
   4725 		debug_print( "addressbook_add_contact - added\n" );
   4726 		addressbook_refresh();
   4727 	}
   4728 	return TRUE;
   4729 }
   4730 
   4731 /* ***********************************************************************
   4732  * Book/folder selection.
   4733  * ***********************************************************************
   4734  */
   4735 
   4736 /*
   4737  * This function is used by the matcher dialog to select a book/folder.
   4738  */
   4739 gchar *addressbook_folder_selection( const gchar *folderpath)
   4740 {
   4741 	AddressBookFile *book = NULL;
   4742 	ItemFolder *folder = NULL;
   4743 	gchar *path = NULL;
   4744 
   4745 	cm_return_val_if_fail( folderpath != NULL, NULL);
   4746 
   4747 	if ( addressbook_foldersel_selection( _addressIndex_, &book, &folder, folderpath )
   4748 		&& book != NULL ) {
   4749 		if ( folder != NULL) {
   4750 			gchar *tmp = NULL;
   4751 			gchar *oldtmp = NULL;
   4752 			AddrItemObject *obj = NULL;
   4753 
   4754 			/* walk thru folder->parent to build the full folder path */
   4755 			/* TODO: wwp: optimize this */
   4756 			obj = &folder->obj;
   4757 			tmp = g_strdup(obj->uid);
   4758 			while ( obj->parent ) {
   4759 				obj = obj->parent;
   4760 				if ( obj->name != NULL ) {
   4761 					oldtmp = g_strdup(tmp);
   4762 					g_free(tmp);
   4763 					tmp = g_strdup_printf("%s/%s", obj->uid, oldtmp);
   4764 					g_free(oldtmp);
   4765 				}
   4766 			}
   4767 			path = g_strdup_printf("%s/%s", book->fileName, tmp);
   4768 			g_free(tmp);
   4769 		} else {
   4770 			path = g_strdup_printf("%s", book->fileName);
   4771 		}
   4772 		debug_print( "addressbook_foldersel: %s\n", path?path:"(null)");
   4773 		return path;
   4774 	}
   4775 	return NULL;
   4776 }
   4777 
   4778 /* ***********************************************************************
   4779  * Book/folder checking.
   4780  * ***********************************************************************
   4781  */
   4782 
   4783 static FolderInfo *addressbook_peek_subfolder_exists_create_folderinfo( AddressBookFile *abf, ItemFolder *folder )
   4784 {
   4785 	FolderInfo *fi = g_new0( FolderInfo, 1 );
   4786 	fi->book   = abf;
   4787 	fi->folder = folder;
   4788 	return fi;
   4789 }
   4790 
   4791 static void addressbook_peek_subfolder_exists_load_folder( ItemFolder *parentFolder,
   4792 					FolderInfo *fiParent, FolderPathMatch *match )
   4793 {
   4794 	GList *list;
   4795 	ItemFolder *folder;
   4796 	gchar *fName;
   4797 	FolderInfo *fi;
   4798 	FolderPathMatch *nextmatch = NULL;
   4799 
   4800 	if (!parentFolder)
   4801 		return;
   4802 
   4803 	list = parentFolder->listFolder;
   4804 	while ( list ) {
   4805 		folder = list->data;
   4806 		fName = g_strdup( ADDRITEM_NAME(folder) );
   4807 
   4808 		/* match folder name, match pointer will be set to NULL if next recursive call
   4809 		   doesn't need to match subfolder name */
   4810 		if ( match != NULL &&
   4811 			 match->matched == FALSE ) {
   4812 			if ( strcmp(match->folder_path[match->index], folder->obj.uid) == 0 ) {
   4813 				/* folder name matches, prepare next subfolder match */
   4814 				debug_print("matched folder name '%s'\n", fName);
   4815 				match->index++;
   4816 				if ( match->folder_path[match->index] == NULL ) {
   4817 					/* we've matched all elements */
   4818 					match->matched = TRUE;
   4819 					match->folder = folder;
   4820 					debug_print("book/folder path matched!\n");
   4821 				} else {
   4822 					/* keep on matching */
   4823 					nextmatch = match;
   4824 				}
   4825 			}
   4826 		}
   4827 
   4828 		g_free( fName );
   4829 
   4830 		fi = addressbook_peek_subfolder_exists_create_folderinfo( fiParent->book, folder );
   4831 		addressbook_peek_subfolder_exists_load_folder( folder, fi, nextmatch );
   4832 		g_free(fi);
   4833 		list = g_list_next( list );
   4834 	}
   4835 }
   4836 
   4837 /*
   4838  * This function is used by to check if a matcher book/folder path corresponds to an
   4839    existing addressbook book/folder ("" or "Any" are considered as valid, NULL invalid).
   4840    Caution: returned book and folder pointers can be NULL even when returning TRUE:
   4841    if book AND folder are NULL this means that folderpath was empty or Any.
   4842    If folderpath is a simple book name (without folder), book will not be NULL and folder
   4843    will be NULL. It's not expected to return book as NULL and folder as non NULL.
   4844  */
   4845 
   4846 gboolean addressbook_peek_folder_exists( gchar *folderpath,
   4847 										 AddressDataSource **book,
   4848 										 ItemFolder **folder )
   4849 {
   4850 	AddressDataSource *ds;
   4851 	GList *list, *nodeDS;
   4852 	ItemFolder *rootFolder;
   4853 	AddressBookFile *abf;
   4854 	FolderInfo *fi;
   4855 	FolderPathMatch folder_path_match = { NULL, FALSE, 0, NULL, NULL };
   4856 
   4857 	if ( book )
   4858 		*book = NULL;
   4859 	if ( folder )
   4860 		*folder = NULL;
   4861 
   4862 	if ( folderpath == NULL )
   4863 		return FALSE;
   4864 
   4865 	if ( strcasecmp(folderpath, "Any") == 0 || *folderpath == '\0' )
   4866 		return TRUE;
   4867 
   4868 	/* split the folder path we've received, we'll try to match this path, subpath by
   4869 	   subpath against the book/folder structure in order */
   4870 	folder_path_match.folder_path = g_strsplit( folderpath, "/", 256 );
   4871 	if (!folder_path_match.folder_path)
   4872 		return FALSE;
   4873 
   4874 	list = addrindex_get_interface_list( _addressIndex_ );
   4875 	while ( list && !folder_path_match.matched ) {
   4876 		AddressInterface *interface = list->data;
   4877 		if ( interface && interface->type == ADDR_IF_BOOK ) {
   4878 			nodeDS = interface->listSource;
   4879 			while ( nodeDS && !folder_path_match.matched ) {
   4880 				ds = nodeDS->data;
   4881 
   4882 				/* Read address book */
   4883 				if( ! addrindex_ds_get_read_flag( ds ) ) {
   4884 					addrindex_ds_read_data( ds );
   4885 				}
   4886 
   4887 				/* Add node for address book */
   4888 				abf = ds->rawDataSource;
   4889 
   4890 				/* match book name */
   4891 				if ( abf && abf->fileName &&
   4892 				    strcmp(folder_path_match.folder_path[0], abf->fileName) == 0 ) {
   4893 
   4894 					debug_print("matched book name '%s'\n", abf->fileName);
   4895 					folder_path_match.book = ds;
   4896 
   4897 					if ( folder_path_match.folder_path[1] == NULL ) {
   4898 						/* no folder part to match */
   4899 
   4900 						folder_path_match.matched = TRUE;
   4901 						folder_path_match.folder = NULL;
   4902 						debug_print("book path matched!\n");
   4903 
   4904 					} else {
   4905 						/* match folder part */
   4906 
   4907 						fi = addressbook_peek_subfolder_exists_create_folderinfo( abf, NULL );
   4908 						rootFolder = addrindex_ds_get_root_folder( ds );
   4909 
   4910 						/* prepare for recursive call */
   4911 						folder_path_match.index = 1;
   4912 						/* this call will set folder_path_match.matched and folder_path_match.folder */
   4913 						addressbook_peek_subfolder_exists_load_folder( rootFolder, fi, &folder_path_match );
   4914 						g_free(fi);
   4915 					}
   4916 				}
   4917 
   4918 				nodeDS = g_list_next( nodeDS );
   4919 			}
   4920 		}
   4921 		list = g_list_next( list );
   4922 	}
   4923 
   4924 	g_strfreev( folder_path_match.folder_path );
   4925 
   4926 	if ( book )
   4927 		*book = folder_path_match.book;
   4928 	if ( folder )
   4929 		*folder = folder_path_match.folder;
   4930 	return folder_path_match.matched;
   4931 }
   4932 
   4933 static void addressbook_find_duplicates_cb(GtkAction *action, gpointer data)
   4934 {
   4935 	addrduplicates_find(GTK_WINDOW(addrbook.window));
   4936 }
   4937 
   4938 static void addressbook_start_drag(GtkWidget *widget, gint button,
   4939 				   GdkEvent *event,
   4940 			           void *data)
   4941 {
   4942 	GdkDragContext *context;
   4943 	if (addressbook_target_list == NULL)
   4944 		addressbook_target_list = gtk_target_list_new(
   4945 				addressbook_drag_types, 1);
   4946 	context = gtk_drag_begin_with_coordinates(widget, addressbook_target_list,
   4947 				 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event,
   4948 				 -1, -1);
   4949 	gtk_drag_set_icon_default(context);
   4950 }
   4951 
   4952 static void addressbook_drag_data_get(GtkWidget        *widget,
   4953 				     GdkDragContext   *drag_context,
   4954 				     GtkSelectionData *selection_data,
   4955 				     guint             info,
   4956 				     guint             time,
   4957 				     void	      *data)
   4958 {
   4959 	AddrItemObject *aio = NULL;
   4960 	AddressObject *pobj = NULL;
   4961 	AdapterDSource *ads = NULL;
   4962 	AddressDataSource *ds = NULL;
   4963 	GList *cur;
   4964 
   4965 	pobj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
   4966 
   4967 	if( pobj == NULL ) return;
   4968 
   4969 	if( pobj->type == ADDR_DATASOURCE ) {
   4970 		ads = ADAPTER_DSOURCE(pobj);
   4971 		ds = ads->dataSource;
   4972 	} else if (pobj->type == ADDR_ITEM_GROUP) {
   4973 
   4974 		return;
   4975 	}
   4976 
   4977 	else if( pobj->type != ADDR_INTERFACE ) {
   4978 		ds = addressbook_find_datasource( addrbook.treeSelected );
   4979 
   4980 		if (!ds)
   4981 			return;
   4982 	}
   4983 
   4984 	for(cur = GTK_CMCLIST(addrbook.clist)->selection; cur; cur = cur->next) {
   4985 		aio = (AddrItemObject *)gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.clist),
   4986 			GTK_CMCTREE_NODE(cur->data));
   4987 		while (aio && aio->type != ITEMTYPE_PERSON) {
   4988 			aio = aio->parent;
   4989 		}
   4990        }
   4991 
   4992 	if (aio && aio->type == ITEMTYPE_PERSON) {
   4993 		if( ds && ds->interface && ds->interface->readOnly)
   4994 			gtk_selection_data_set(selection_data,
   4995 				       gtk_selection_data_get_target(selection_data), 8,
   4996 				       (const guchar *)"Dummy_addr_copy", 15);
   4997 		else
   4998 			gtk_selection_data_set(selection_data,
   4999 				       gtk_selection_data_get_target(selection_data), 8,
   5000 				       (const guchar *)"Dummy_addr_move", 15);
   5001 	}
   5002 }
   5003 
   5004 static gboolean addressbook_drag_motion_cb(GtkWidget      *widget,
   5005 					  GdkDragContext *context,
   5006 					  gint            x,
   5007 					  gint            y,
   5008 					  guint           time,
   5009 					  void            *data)
   5010 {
   5011 	GtkAllocation allocation;
   5012 	GtkRequisition requisition;
   5013 	gint row, column;
   5014 	GtkCMCTreeNode *node = NULL;
   5015 	gboolean acceptable = FALSE;
   5016 	gtk_widget_get_allocation(GTK_WIDGET(addrbook.ctree), &allocation);
   5017 	gint height = allocation.height;
   5018 	gtk_widget_get_requisition(GTK_WIDGET(addrbook.ctree), &requisition);
   5019 	gint total_height = requisition.height;
   5020 	GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
   5021 				GTK_SCROLLED_WINDOW(addrbook.ctree_swin));
   5022 	gfloat vpos = gtk_adjustment_get_value(pos);
   5023 
   5024 	if (gtk_cmclist_get_selection_info
   5025 		(GTK_CMCLIST(widget), x - 24, y - 24, &row, &column)) {
   5026 
   5027 		if (y > height - 24 && height + vpos < total_height) {
   5028 			gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
   5029 		}
   5030 		if (y < 24 && y > 0) {
   5031 			gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
   5032 		}
   5033 		node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
   5034 
   5035 		if (node != NULL) {
   5036 			AddressObject *obj = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node );
   5037 			if (obj == NULL)
   5038 				return FALSE;
   5039 			if( obj->type == ADDR_ITEM_FOLDER
   5040 			|| obj->type == ADDR_ITEM_GROUP)
   5041 				acceptable = TRUE;
   5042 			else {
   5043 				AdapterDSource *ads = NULL;
   5044 				AddressDataSource *ds = NULL;
   5045 				ads = ADAPTER_DSOURCE(obj);
   5046 				ds = ads->dataSource;
   5047 				if (ds == NULL ) { return FALSE;}
   5048 
   5049 				acceptable = TRUE;
   5050 			}
   5051 		}
   5052 	}
   5053 
   5054 	if (acceptable) {
   5055 		g_signal_handlers_block_by_func
   5056 			(G_OBJECT(widget),
   5057 			 G_CALLBACK(addressbook_tree_selected), NULL);
   5058 		gtk_sctree_select( GTK_SCTREE(widget), node);
   5059 		g_signal_handlers_unblock_by_func
   5060 			(G_OBJECT(widget),
   5061 			 G_CALLBACK(addressbook_tree_selected), NULL);
   5062 		gdk_drag_status(context,
   5063 					(gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
   5064 					GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
   5065 	} else {
   5066 		gdk_drag_status(context, 0, time);
   5067 	}
   5068 	return acceptable;
   5069 }
   5070 
   5071 static void addressbook_drag_leave_cb(GtkWidget      *widget,
   5072 				     GdkDragContext *context,
   5073 				     guint           time,
   5074 				     void           *data)
   5075 {
   5076 	if (addrbook.treeSelected) {
   5077 		g_signal_handlers_block_by_func
   5078 			(G_OBJECT(widget),
   5079 			 G_CALLBACK(addressbook_tree_selected), NULL);
   5080 		gtk_sctree_select( GTK_SCTREE(widget), addrbook.opened);
   5081 		g_signal_handlers_unblock_by_func
   5082 			(G_OBJECT(widget),
   5083 			 G_CALLBACK(addressbook_tree_selected), NULL);
   5084 	}
   5085 
   5086 }
   5087 
   5088 static void addressbook_drag_received_cb(GtkWidget        *widget,
   5089 					GdkDragContext   *drag_context,
   5090 					gint              x,
   5091 					gint              y,
   5092 					GtkSelectionData *data,
   5093 					guint             info,
   5094 					guint             time,
   5095 					void             *pdata)
   5096 {
   5097 	gint row, column;
   5098 	GtkCMCTreeNode *node;
   5099 	GtkCMCTreeNode *lastopened = addrbook.opened;
   5100 
   5101 	if (!strncmp(gtk_selection_data_get_data(data), "Dummy_addr", 10)) {
   5102 		if (gtk_cmclist_get_selection_info
   5103 			(GTK_CMCLIST(widget), x - 24, y - 24, &row, &column) == 0) {
   5104 			return;
   5105 		}
   5106 
   5107 		node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
   5108 		if( !node || !gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree), node))
   5109 			return;
   5110 
   5111 		gtk_cmclist_freeze(GTK_CMCLIST(addrbook.clist));
   5112 		if (gdk_drag_context_get_selected_action(drag_context) == GDK_ACTION_COPY ||
   5113 		    !strcmp(gtk_selection_data_get_data(data), "Dummy_addr_copy"))
   5114 			addressbook_clip_copy_cb(NULL, NULL);
   5115 		else
   5116 			addressbook_clip_cut_cb(NULL, NULL);
   5117 		gtk_sctree_select( GTK_SCTREE(addrbook.ctree), node);
   5118 		addressbook_clip_paste_cb(NULL,NULL);
   5119 		gtk_sctree_select( GTK_SCTREE(addrbook.ctree), lastopened);
   5120 		gtk_cmclist_thaw(GTK_CMCLIST(addrbook.clist));
   5121 		gtk_drag_finish(drag_context, TRUE, TRUE, time);
   5122 	}
   5123 }