sync with 0.7.5
[claws.git] / src / addressbook.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 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 2 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, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtkwindow.h>
29 #include <gtk/gtksignal.h>
30 #include <gtk/gtkvbox.h>
31 #include <gtk/gtkscrolledwindow.h>
32 #include <gtk/gtkhpaned.h>
33 #include <gtk/gtkhbox.h>
34 #include <gtk/gtklabel.h>
35 #include <gtk/gtkentry.h>
36 #include <gtk/gtkctree.h>
37 #include <gtk/gtkclist.h>
38 #include <gtk/gtktable.h>
39 #include <gtk/gtkhbbox.h>
40 #include <gtk/gtkbutton.h>
41 #include <gtk/gtkmenu.h>
42 #include <gtk/gtkmenuitem.h>
43 #include <gtk/gtkitemfactory.h>
44 #include <string.h>
45 #include <setjmp.h>
46
47 #include "intl.h"
48 #include "main.h"
49 #include "addressbook.h"
50 #include "manage_window.h"
51 #include "prefs_common.h"
52 #include "alertpanel.h"
53 #include "inputdialog.h"
54 #include "menu.h"
55 #include "stock_pixmap.h"
56 #include "xml.h"
57 #include "prefs.h"
58 #include "procmime.h"
59 #include "utils.h"
60 #include "gtkutils.h"
61 #include "codeconv.h"
62 #include "about.h"
63 #include "addr_compl.h"
64
65 #include "mgutils.h"
66 #include "addressitem.h"
67 #include "addritem.h"
68 #include "addrcache.h"
69 #include "addrbook.h"
70 #include "addrindex.h"
71 #include "addressadd.h"
72 #include "vcard.h"
73 #include "editvcard.h"
74 #include "editgroup.h"
75 #include "editaddress.h"
76 #include "editbook.h"
77 #include "ldif.h"
78 #include "importldif.h"
79 #include "mutt.h"
80 #include "importmutt.h"
81 #include "pine.h"
82 #include "importpine.h"
83
84 #ifdef USE_JPILOT
85 #include "jpilot.h"
86 #include "editjpilot.h"
87 #endif
88
89 #ifdef USE_LDAP
90 #include <pthread.h>
91 #include "syldap.h"
92 #include "editldap.h"
93
94 #define ADDRESSBOOK_LDAP_BUSYMSG "Busy"
95 #endif
96
97 #include "addrselect.h"
98 #include "addrclip.h"
99 #include "addrgather.h"
100
101 typedef enum
102 {
103         COL_NAME        = 0,
104         COL_ADDRESS     = 1,
105         COL_REMARKS     = 2
106 } AddressBookColumnPos;
107
108 #define N_COLS  3
109 #define COL_NAME_WIDTH          164
110 #define COL_ADDRESS_WIDTH       156
111
112 #define COL_FOLDER_WIDTH        170
113 #define ADDRESSBOOK_WIDTH       640
114 #define ADDRESSBOOK_HEIGHT      360
115
116 #define ADDRESSBOOK_MSGBUF_SIZE 2048
117
118 static GdkPixmap *folderxpm;
119 static GdkBitmap *folderxpmmask;
120 static GdkPixmap *folderopenxpm;
121 static GdkBitmap *folderopenxpmmask;
122 static GdkPixmap *groupxpm;
123 static GdkBitmap *groupxpmmask;
124 static GdkPixmap *interfacexpm;
125 static GdkBitmap *interfacexpmmask;
126 static GdkPixmap *bookxpm;
127 static GdkBitmap *bookxpmmask;
128 static GdkPixmap *addressxpm;
129 static GdkBitmap *addressxpmmask;
130 static GdkPixmap *vcardxpm;
131 static GdkBitmap *vcardxpmmask;
132 static GdkPixmap *jpilotxpm;
133 static GdkBitmap *jpilotxpmmask;
134 static GdkPixmap *categoryxpm;
135 static GdkBitmap *categoryxpmmask;
136 static GdkPixmap *ldapxpm;
137 static GdkBitmap *ldapxpmmask;
138
139 /* Message buffer */
140 static gchar addressbook_msgbuf[ ADDRESSBOOK_MSGBUF_SIZE ];
141
142 /* Address list selection */
143 static AddrSelectList *_addressSelect_ = NULL;
144 static AddressClipboard *_clipBoard_ = NULL;
145
146 /* Address index file and interfaces */
147 static AddressIndex *_addressIndex_ = NULL;
148 static GList *_addressInterfaceList_ = NULL;
149 static GList *_addressIFaceSelection_ = NULL;
150 #define ADDRESSBOOK_IFACE_SELECTION "1/y,3/y,4/y,2/n"
151
152 static AddressBook_win addrbook;
153
154 static GHashTable *_addressBookTypeHash_ = NULL;
155 static GList *_addressBookTypeList_ = NULL;
156
157 static void addressbook_create                  (void);
158 static gint addressbook_close                   (void);
159 static void addressbook_button_set_sensitive    (void);
160
161 /* callback functions */
162 static void addressbook_del_clicked             (GtkButton      *button,
163                                                  gpointer        data);
164 static void addressbook_reg_clicked             (GtkButton      *button,
165                                                  gpointer        data);
166 static void addressbook_to_clicked              (GtkButton      *button,
167                                                  gpointer        data);
168 static void addressbook_lup_clicked             (GtkButton      *button,
169                                                  gpointer       data);
170
171 static void addressbook_tree_selected           (GtkCTree       *ctree,
172                                                  GtkCTreeNode   *node,
173                                                  gint            column,
174                                                  gpointer        data);
175 static void addressbook_list_selected           (GtkCList       *clist,
176                                                  gint            row,
177                                                  gint            column,
178                                                  GdkEvent       *event,
179                                                  gpointer        data);
180 static void addressbook_list_row_selected       (GtkCTree       *clist,
181                                                  GtkCTreeNode   *node,
182                                                  gint            column,
183                                                  gpointer        data);
184 static void addressbook_list_row_unselected     (GtkCTree       *clist,
185                                                  GtkCTreeNode   *node,
186                                                  gint            column,
187                                                  gpointer        data);
188 static void addressbook_person_expand_node      (GtkCTree       *ctree,
189                                                  GList          *node,
190                                                  gpointer       *data );
191 static void addressbook_person_collapse_node    (GtkCTree       *ctree,
192                                                  GList          *node,
193                                                  gpointer       *data );
194 static void addressbook_entry_gotfocus          (GtkWidget      *widget);
195
196 #if 0
197 static void addressbook_entry_changed           (GtkWidget      *widget);
198 #endif
199
200 static void addressbook_list_button_pressed     (GtkWidget      *widget,
201                                                  GdkEventButton *event,
202                                                  gpointer        data);
203 static void addressbook_list_button_released    (GtkWidget      *widget,
204                                                  GdkEventButton *event,
205                                                  gpointer        data);
206 static void addressbook_tree_button_pressed     (GtkWidget      *ctree,
207                                                  GdkEventButton *event,
208                                                  gpointer        data);
209 static void addressbook_tree_button_released    (GtkWidget      *ctree,
210                                                  GdkEventButton *event,
211                                                  gpointer        data);
212 static void addressbook_popup_close             (GtkMenuShell   *menu_shell,
213                                                  gpointer        data);
214
215 static void addressbook_new_folder_cb           (gpointer        data,
216                                                  guint           action,
217                                                  GtkWidget      *widget);
218 static void addressbook_new_group_cb            (gpointer        data,
219                                                  guint           action,
220                                                  GtkWidget      *widget);
221 static void addressbook_treenode_edit_cb        (gpointer        data,
222                                                  guint           action,
223                                                  GtkWidget      *widget);
224 static void addressbook_treenode_delete_cb      (gpointer        data,
225                                                  guint           action,
226                                                  GtkWidget      *widget);
227
228 static void addressbook_change_node_name        (GtkCTreeNode   *node,
229                                                  const gchar    *name);
230
231 static void addressbook_new_address_cb          (gpointer        data,
232                                                  guint           action,
233                                                  GtkWidget      *widget);
234 static void addressbook_edit_address_cb         (gpointer        data,
235                                                  guint           action,
236                                                  GtkWidget      *widget);
237 static void addressbook_delete_address_cb       (gpointer        data,
238                                                  guint           action,
239                                                  GtkWidget      *widget);
240
241 static void close_cb                            (gpointer        data,
242                                                  guint           action,
243                                                  GtkWidget      *widget);
244 static void addressbook_file_save_cb            (gpointer        data,
245                                                  guint           action,
246                                                  GtkWidget      *widget);
247
248 /* Data source edit stuff */
249 static void addressbook_new_book_cb             (gpointer        data,
250                                                  guint           action,
251                                                  GtkWidget      *widget);
252 static void addressbook_new_vcard_cb            (gpointer        data,
253                                                  guint           action,
254                                                  GtkWidget      *widget);
255
256 #ifdef USE_JPILOT
257 static void addressbook_new_jpilot_cb           (gpointer        data,
258                                                  guint           action,
259                                                  GtkWidget      *widget);
260 #endif
261
262 #ifdef USE_LDAP
263 static void addressbook_new_ldap_cb             (gpointer        data,
264                                                  guint           action,
265                                                  GtkWidget      *widget);
266 #endif
267
268 static void addressbook_set_clist               (AddressObject  *obj);
269
270 static void addressbook_load_tree               (void);
271 void addressbook_read_file                      (void);
272
273 static GtkCTreeNode *addressbook_add_object     (GtkCTreeNode   *node,
274                                                  AddressObject  *obj);
275 static void addressbook_treenode_remove_item    ( void );
276
277 static AddressDataSource *addressbook_find_datasource
278                                                 (GtkCTreeNode   *node );
279
280 static AddressBookFile *addressbook_get_book_file(void);
281
282 static GtkCTreeNode *addressbook_node_add_folder
283                                                 (GtkCTreeNode   *node,
284                                                 AddressDataSource *ds,
285                                                 ItemFolder      *itemFolder,
286                                                 AddressObjectType otype);
287 static GtkCTreeNode *addressbook_node_add_group (GtkCTreeNode   *node,
288                                                 AddressDataSource *ds,
289                                                 ItemGroup       *itemGroup);
290 static void addressbook_tree_remove_children    (GtkCTree       *ctree,
291                                                 GtkCTreeNode    *parent);
292 static void addressbook_move_nodes_up           (GtkCTree       *ctree,
293                                                 GtkCTreeNode    *node);
294 static GtkCTreeNode *addressbook_find_group_node (GtkCTreeNode  *parent,
295                                                    ItemGroup    *group);
296 static GtkCTreeNode *addressbook_find_folder_node( GtkCTreeNode *parent,
297                                                    ItemFolder   *folder );
298
299 static void addressbook_delete_object           (AddressObject  *obj);
300
301 static void key_pressed                         (GtkWidget      *widget,
302                                                  GdkEventKey    *event,
303                                                  gpointer        data);
304 static gint addressbook_treenode_compare_func   (GtkCList       *clist,
305                                                  gconstpointer   ptr1,
306                                                  gconstpointer   ptr2);
307 static gint addressbook_list_compare_func       (GtkCList       *clist,
308                                                  gconstpointer   ptr1,
309                                                  gconstpointer   ptr2);
310
311 #ifdef USE_LDAP
312 static void addressbook_ldap_show_message       (SyldapServer *server);
313 #endif
314
315 /* LUT's and IF stuff */
316 static void addressbook_free_treenode           ( gpointer data );
317 AddressTypeControlItem *addrbookctl_lookup      (gint            ot);
318 AddressTypeControlItem *addrbookctl_lookup_iface(AddressIfType   ifType);
319
320 void addrbookctl_build_map                      (GtkWidget      *window);
321 void addrbookctl_build_iflist                   (void);
322 AdapterInterface *addrbookctl_find_interface    (AddressIfType   ifType);
323 void addrbookctl_build_ifselect                 (void);
324
325 static void addrbookctl_free_interface          (AdapterInterface *adapter);
326 static void addrbookctl_free_datasource         (AdapterDSource   *adapter);
327 static void addrbookctl_free_folder             (AdapterFolder    *adapter);
328 static void addrbookctl_free_group              (AdapterGroup     *adapter);
329
330 static void addressbook_list_select_clear       ( void );
331 static void addressbook_list_select_add         ( AddrItemObject    *aio,
332                                                   AddressDataSource *ds );
333 static void addressbook_list_select_remove      ( AddrItemObject    *aio );
334
335 static void addressbook_import_ldif_cb          ( void );
336 static void addressbook_import_mutt_cb          ( void );
337 static void addressbook_import_pine_cb          ( void );
338 static void addressbook_clip_cut_cb             ( void );
339 static void addressbook_clip_copy_cb            ( void );
340 static void addressbook_clip_paste_cb           ( void );
341 static void addressbook_clip_paste_address_cb   ( void );
342 static void addressbook_treenode_cut_cb         ( void );
343 static void addressbook_treenode_copy_cb        ( void );
344 static void addressbook_treenode_paste_cb       ( void );
345
346 static GtkItemFactoryEntry addressbook_entries[] =
347 {
348         {N_("/_File"),                  NULL,           NULL, 0, "<Branch>"},
349         {N_("/_File/New _Book"),        "<alt>B",       addressbook_new_book_cb,        0, NULL},
350         {N_("/_File/New _vCard"),       "<alt>D",       addressbook_new_vcard_cb,       0, NULL},
351 #ifdef USE_JPILOT
352         {N_("/_File/New _JPilot"),      "<alt>J",       addressbook_new_jpilot_cb,      0, NULL},
353 #endif
354 #ifdef USE_LDAP
355         {N_("/_File/New _Server"),      "<alt>S",       addressbook_new_ldap_cb,        0, NULL},
356 #endif
357         {N_("/_File/---"),              NULL,           NULL, 0, "<Separator>"},
358         {N_("/_File/_Edit"),            NULL,           addressbook_treenode_edit_cb,   0, NULL},
359         {N_("/_File/_Delete"),          NULL,           addressbook_treenode_delete_cb, 0, NULL},
360         {N_("/_File/---"),              NULL,           NULL, 0, "<Separator>"},
361         {N_("/_File/_Save"),            "<alt>S",       addressbook_file_save_cb,       0, NULL},
362         {N_("/_File/_Close"),           "<alt>W",       close_cb,                       0, NULL},
363         {N_("/_Edit"),                  NULL,           NULL, 0, "<Branch>"},
364         {N_("/_Edit/C_ut"),             "<ctl>X",       addressbook_clip_cut_cb,        0, NULL},
365         {N_("/_Edit/_Copy"),            "<ctl>C",       addressbook_clip_copy_cb,       0, NULL},
366         {N_("/_Edit/_Paste"),           "<ctl>V",       addressbook_clip_paste_cb,      0, NULL},
367         {N_("/_Edit/---"),              NULL,           NULL, 0, "<Separator>"},
368         {N_("/_Edit/Pa_ste Address"),   NULL,           addressbook_clip_paste_address_cb, 0, NULL},
369         {N_("/_Address"),               NULL,           NULL, 0, "<Branch>"},
370         {N_("/_Address/New _Address"),  "<alt>N",       addressbook_new_address_cb,     0, NULL},
371         {N_("/_Address/New _Group"),    "<alt>G",       addressbook_new_group_cb,       0, NULL},
372         {N_("/_Address/New _Folder"),   "<alt>R",       addressbook_new_folder_cb,      0, NULL},
373         {N_("/_Address/---"),           NULL,           NULL, 0, "<Separator>"},
374         {N_("/_Address/_Edit"),         "<alt>Return",  addressbook_edit_address_cb,    0, NULL},
375         {N_("/_Address/_Delete"),       NULL,           addressbook_delete_address_cb,  0, NULL},
376         {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>"},
377         {N_("/_Tools/Import _LDIF file"), NULL,         addressbook_import_ldif_cb,     0, NULL},
378         {N_("/_Tools/Import M_utt file"), NULL,         addressbook_import_mutt_cb,     0, NULL},
379         {N_("/_Tools/Import _Pine file"), NULL,         addressbook_import_pine_cb,     0, NULL},
380         {N_("/_Help"),                  NULL,           NULL, 0, "<LastBranch>"},
381         {N_("/_Help/_About"),           NULL,           about_show, 0, NULL}
382 };
383
384 /* New options to be added. */
385 /*
386         {N_("/_Tools"),                 NULL,           NULL, 0, "<Branch>"},
387         {N_("/_Tools/Import _Mozilla"), NULL,           NULL,                           0, NULL},
388         {N_("/_Tools/Import _vCard"),   NULL,           NULL,                           0, NULL},
389         {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>"},
390         {N_("/_Tools/Export _LDIF file"), NULL,         NULL,                           0, NULL},
391         {N_("/_Tools/Export v_Card"),   NULL,           NULL,                           0, NULL},
392 */
393
394 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
395 {
396         {N_("/New _Address"),   NULL, addressbook_new_address_cb, 0, NULL},
397         {N_("/New _Group"),     NULL, addressbook_new_group_cb,   0, NULL},
398         {N_("/New _Folder"),    NULL, addressbook_new_folder_cb,  0, NULL},
399         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
400         {N_("/_Edit"),          NULL, addressbook_treenode_edit_cb,   0, NULL},
401         {N_("/_Delete"),        NULL, addressbook_treenode_delete_cb, 0, NULL},
402         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
403         {N_("/C_ut"),           NULL, addressbook_treenode_cut_cb,    0, NULL},
404         {N_("/_Copy"),          NULL, addressbook_treenode_copy_cb,   0, NULL},
405         {N_("/_Paste"),         NULL, addressbook_treenode_paste_cb,  0, NULL}
406 };
407
408 static GtkItemFactoryEntry addressbook_list_popup_entries[] =
409 {
410         {N_("/New _Address"),   NULL, addressbook_new_address_cb,    0, NULL},
411         {N_("/New _Group"),     NULL, addressbook_new_group_cb,      0, NULL},
412         {N_("/New _Folder"),    NULL, addressbook_new_folder_cb,     0, NULL},
413         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
414         {N_("/_Edit"),          NULL, addressbook_edit_address_cb,   0, NULL},
415         {N_("/_Delete"),        NULL, addressbook_delete_address_cb, 0, NULL},
416         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
417         {N_("/C_ut"),           NULL, addressbook_clip_cut_cb,       0, NULL},
418         {N_("/_Copy"),          NULL, addressbook_clip_copy_cb,      0, NULL},
419         {N_("/_Paste"),         NULL, addressbook_clip_paste_cb,     0, NULL},
420         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
421         {N_("/Pa_ste Address"), NULL, addressbook_clip_paste_address_cb,     0, NULL}
422 };
423
424 void addressbook_open(Compose *target)
425 {
426         /* Initialize all static members */
427         if( _clipBoard_ == NULL ) {
428                 _clipBoard_ = addrclip_create();
429         }
430         if( _addressIndex_ != NULL ) {
431                 addrclip_set_index( _clipBoard_, _addressIndex_ );
432         }
433         if( _addressSelect_ == NULL ) {
434                 _addressSelect_ = addrselect_list_create();
435         }
436         if (!addrbook.window) {
437                 addressbook_read_file();
438                 addressbook_create();
439                 addressbook_load_tree();
440                 gtk_ctree_select(GTK_CTREE(addrbook.ctree),
441                                  GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
442         } else
443                 gtk_widget_hide(addrbook.window);
444
445         gtk_widget_show_all(addrbook.window);
446
447         addressbook_set_target_compose(target);
448 }
449
450 void addressbook_destroy() {
451         /* Free up address stuff */
452         if( _addressSelect_ != NULL ) {
453                 addrselect_list_free( _addressSelect_ );
454         }
455         if( _clipBoard_ != NULL ) {
456                 addrclip_free( _clipBoard_ );
457         }
458         if( _addressIndex_ != NULL ) {
459                addrindex_free_index( _addressIndex_ );
460         }
461         _addressSelect_ = NULL;
462         _clipBoard_ = NULL;
463         _addressIndex_ = NULL;
464 }
465
466 void addressbook_set_target_compose(Compose *target)
467 {
468         addrbook.target_compose = target;
469
470         addressbook_button_set_sensitive();
471 }
472
473 Compose *addressbook_get_target_compose(void)
474 {
475         return addrbook.target_compose;
476 }
477
478 void addressbook_refresh(void)
479 {
480         if (addrbook.window) {
481                 if (addrbook.treeSelected) {
482                         gtk_ctree_select(GTK_CTREE(addrbook.ctree),
483                                          addrbook.treeSelected);
484                 }
485         }
486         addressbook_export_to_file();
487 }
488
489 /*
490 * Create the address book widgets. The address book contains two CTree widgets: the
491 * address index tree on the left and the address list on the right.
492 *
493 * The address index tree displays a hierarchy of interfaces and groups. Each node in
494 * this tree is linked to an address Adapter. Adapters have been created for interfaces,
495 * data sources and folder objects.
496 *
497 * The address list displays group, person and email objects. These items are linked
498 * directly to ItemGroup, ItemPerson and ItemEMail objects inside the address book data
499 * sources.
500 *
501 * In the tradition of MVC architecture, the data stores have been separated from the
502 * GUI components. The addrindex.c file provides the interface to all data stores.
503 */
504 static void addressbook_create(void)
505 {
506         GtkWidget *window;
507         GtkWidget *vbox;
508         GtkWidget *menubar;
509         GtkWidget *vbox2;
510         GtkWidget *ctree_swin;
511         GtkWidget *ctree;
512         GtkWidget *clist_vbox;
513         GtkWidget *clist_swin;
514         GtkWidget *clist;
515         GtkWidget *paned;
516         GtkWidget *hbox;
517         GtkWidget *label;
518         GtkWidget *entry;
519         GtkWidget *statusbar;
520         GtkWidget *hbbox;
521         GtkWidget *hsbox;
522         GtkWidget *del_btn;
523         GtkWidget *reg_btn;
524         GtkWidget *lup_btn;
525         GtkWidget *to_btn;
526         GtkWidget *cc_btn;
527         GtkWidget *bcc_btn;
528         GtkWidget *tree_popup;
529         GtkWidget *list_popup;
530         GtkItemFactory *tree_factory;
531         GtkItemFactory *list_factory;
532         GtkItemFactory *menu_factory;
533         gint n_entries;
534         GList *nodeIf;
535
536         gchar *titles[N_COLS];
537         gchar *text;
538         gint i;
539
540         debug_print("Creating addressbook window...\n");
541
542         titles[COL_NAME]    = _("Name");
543         titles[COL_ADDRESS] = _("E-Mail address");
544         titles[COL_REMARKS] = _("Remarks");
545
546         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
547         gtk_window_set_title(GTK_WINDOW(window), _("Address book"));
548         gtk_widget_set_usize(window, ADDRESSBOOK_WIDTH, ADDRESSBOOK_HEIGHT);
549         gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
550         gtk_window_set_wmclass(GTK_WINDOW(window), "addressbook", "Sylpheed");
551         gtk_widget_realize(window);
552
553         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
554                            GTK_SIGNAL_FUNC(addressbook_close), NULL);
555         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
556                            GTK_SIGNAL_FUNC(key_pressed), NULL);
557         MANAGE_WINDOW_SIGNALS_CONNECT(window);
558
559         vbox = gtk_vbox_new(FALSE, 0);
560         gtk_container_add(GTK_CONTAINER(window), vbox);
561
562         n_entries = sizeof(addressbook_entries) /
563                 sizeof(addressbook_entries[0]);
564         menubar = menubar_create(window, addressbook_entries, n_entries,
565                                  "<AddressBook>", NULL);
566         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
567         menu_factory = gtk_item_factory_from_widget(menubar);
568
569         vbox2 = gtk_vbox_new(FALSE, 4);
570         gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
571         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
572
573         ctree_swin = gtk_scrolled_window_new(NULL, NULL);
574         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ctree_swin),
575                                        GTK_POLICY_AUTOMATIC,
576                                        GTK_POLICY_ALWAYS);
577         gtk_widget_set_usize(ctree_swin, COL_FOLDER_WIDTH + 40, -1);
578
579         /* Address index */
580         ctree = gtk_ctree_new(1, 0);
581         gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
582         gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
583         gtk_clist_set_column_width(GTK_CLIST(ctree), 0, COL_FOLDER_WIDTH);
584         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
585         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
586                                      GTK_CTREE_EXPANDER_SQUARE);
587         gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
588         gtk_clist_set_compare_func(GTK_CLIST(ctree),
589                                    addressbook_treenode_compare_func);
590
591         gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
592                            GTK_SIGNAL_FUNC(addressbook_tree_selected), NULL);
593         gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
594                            GTK_SIGNAL_FUNC(addressbook_tree_button_pressed),
595                            NULL);
596         gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
597                            GTK_SIGNAL_FUNC(addressbook_tree_button_released),
598                            NULL);
599
600         clist_vbox = gtk_vbox_new(FALSE, 4);
601
602         clist_swin = gtk_scrolled_window_new(NULL, NULL);
603         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
604                                        GTK_POLICY_AUTOMATIC,
605                                        GTK_POLICY_ALWAYS);
606         gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
607
608         /* Address list */
609         clist = gtk_ctree_new_with_titles(N_COLS, 0, titles);
610         gtk_container_add(GTK_CONTAINER(clist_swin), clist);
611         gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_EXTENDED);
612         gtk_ctree_set_line_style(GTK_CTREE(clist), GTK_CTREE_LINES_NONE);
613         gtk_ctree_set_expander_style(GTK_CTREE(clist), GTK_CTREE_EXPANDER_SQUARE);
614         gtk_ctree_set_indent(GTK_CTREE(clist), CTREE_INDENT);
615         gtk_clist_set_column_width(GTK_CLIST(clist), COL_NAME,
616                                    COL_NAME_WIDTH);
617         gtk_clist_set_column_width(GTK_CLIST(clist), COL_ADDRESS,
618                                    COL_ADDRESS_WIDTH);
619         gtk_clist_set_compare_func(GTK_CLIST(clist),
620                                    addressbook_list_compare_func);
621
622         for (i = 0; i < N_COLS; i++)
623                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button,
624                                        GTK_CAN_FOCUS);
625
626         gtk_signal_connect(GTK_OBJECT(clist), "tree_select_row",
627                            GTK_SIGNAL_FUNC(addressbook_list_row_selected), NULL);
628         gtk_signal_connect(GTK_OBJECT(clist), "tree_unselect_row",
629                            GTK_SIGNAL_FUNC(addressbook_list_row_unselected), NULL);
630         gtk_signal_connect(GTK_OBJECT(clist), "button_press_event",
631                            GTK_SIGNAL_FUNC(addressbook_list_button_pressed),
632                            NULL);
633         gtk_signal_connect(GTK_OBJECT(clist), "button_release_event",
634                            GTK_SIGNAL_FUNC(addressbook_list_button_released),
635                            NULL);
636         gtk_signal_connect(GTK_OBJECT(clist), "select_row",
637                            GTK_SIGNAL_FUNC(addressbook_list_selected), NULL);
638         gtk_signal_connect(GTK_OBJECT(clist), "tree_expand",
639                            GTK_SIGNAL_FUNC(addressbook_person_expand_node), NULL );
640         gtk_signal_connect(GTK_OBJECT(clist), "tree_collapse",
641                            GTK_SIGNAL_FUNC(addressbook_person_collapse_node), NULL );
642
643         hbox = gtk_hbox_new(FALSE, 4);
644         gtk_box_pack_start(GTK_BOX(clist_vbox), hbox, FALSE, FALSE, 0);
645
646         label = gtk_label_new(_("Name:"));
647         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
648
649         entry = gtk_entry_new();
650         gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
651
652         address_completion_register_entry(GTK_ENTRY(entry));
653         gtk_signal_connect(GTK_OBJECT(entry), "focus_in_event",
654                            GTK_SIGNAL_FUNC(addressbook_entry_gotfocus), NULL);
655
656 #if 0
657         gtk_signal_connect(GTK_OBJECT(entry), "changed",
658                            GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
659 #endif
660
661         paned = gtk_hpaned_new();
662         gtk_box_pack_start(GTK_BOX(vbox2), paned, TRUE, TRUE, 0);
663         gtk_paned_add1(GTK_PANED(paned), ctree_swin);
664         gtk_paned_add2(GTK_PANED(paned), clist_vbox);
665
666         /* Status bar */
667         hsbox = gtk_hbox_new(FALSE, 0);
668         gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
669         statusbar = gtk_statusbar_new();
670         gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
671
672         /* Button panel */
673         hbbox = gtk_hbutton_box_new();
674         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
675         gtk_button_box_set_spacing(GTK_BUTTON_BOX(hbbox), 2);
676         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
677
678         del_btn = gtk_button_new_with_label(_("Delete"));
679         GTK_WIDGET_SET_FLAGS(del_btn, GTK_CAN_DEFAULT);
680         gtk_box_pack_start(GTK_BOX(hbbox), del_btn, TRUE, TRUE, 0);
681         reg_btn = gtk_button_new_with_label(_("Add"));
682         GTK_WIDGET_SET_FLAGS(reg_btn, GTK_CAN_DEFAULT);
683         gtk_box_pack_start(GTK_BOX(hbbox), reg_btn, TRUE, TRUE, 0);
684         lup_btn = gtk_button_new_with_label(_("Lookup"));
685         GTK_WIDGET_SET_FLAGS(lup_btn, GTK_CAN_DEFAULT);
686         gtk_box_pack_start(GTK_BOX(hbbox), lup_btn, TRUE, TRUE, 0);
687
688         gtk_signal_connect(GTK_OBJECT(del_btn), "clicked",
689                            GTK_SIGNAL_FUNC(addressbook_del_clicked), NULL);
690         gtk_signal_connect(GTK_OBJECT(reg_btn), "clicked",
691                            GTK_SIGNAL_FUNC(addressbook_reg_clicked), NULL);
692         gtk_signal_connect(GTK_OBJECT(lup_btn), "clicked",
693                            GTK_SIGNAL_FUNC(addressbook_lup_clicked), NULL);
694
695         to_btn = gtk_button_new_with_label
696                 (prefs_common.trans_hdr ? _("To:") : "To:");
697         GTK_WIDGET_SET_FLAGS(to_btn, GTK_CAN_DEFAULT);
698         gtk_box_pack_start(GTK_BOX(hbbox), to_btn, TRUE, TRUE, 0);
699         cc_btn = gtk_button_new_with_label
700                 (prefs_common.trans_hdr ? _("Cc:") : "Cc:");
701         GTK_WIDGET_SET_FLAGS(cc_btn, GTK_CAN_DEFAULT);
702         gtk_box_pack_start(GTK_BOX(hbbox), cc_btn, TRUE, TRUE, 0);
703         bcc_btn = gtk_button_new_with_label
704                 (prefs_common.trans_hdr ? _("Bcc:") : "Bcc:");
705         GTK_WIDGET_SET_FLAGS(bcc_btn, GTK_CAN_DEFAULT);
706         gtk_box_pack_start(GTK_BOX(hbbox), bcc_btn, TRUE, TRUE, 0);
707
708         gtk_signal_connect(GTK_OBJECT(to_btn), "clicked",
709                            GTK_SIGNAL_FUNC(addressbook_to_clicked),
710                            GINT_TO_POINTER(COMPOSE_TO));
711         gtk_signal_connect(GTK_OBJECT(cc_btn), "clicked",
712                            GTK_SIGNAL_FUNC(addressbook_to_clicked),
713                            GINT_TO_POINTER(COMPOSE_CC));
714         gtk_signal_connect(GTK_OBJECT(bcc_btn), "clicked",
715                            GTK_SIGNAL_FUNC(addressbook_to_clicked),
716                            GINT_TO_POINTER(COMPOSE_BCC));
717
718         /* Build icons for interface */
719         stock_pixmap_gdk( window, STOCK_PIXMAP_INTERFACE,
720                           &interfacexpm, &interfacexpmmask );
721
722         /* Build control tables */
723         addrbookctl_build_map(window);
724         addrbookctl_build_iflist();
725         addrbookctl_build_ifselect();
726
727         /* Add each interface into the tree as a root level folder */
728         nodeIf = _addressInterfaceList_;
729         while( nodeIf ) {
730                 AdapterInterface *adapter = nodeIf->data;
731                 AddressInterface *iface = adapter->interface;
732                 nodeIf = g_list_next(nodeIf);
733
734                 if(iface->useInterface) {
735                         AddressTypeControlItem *atci = adapter->atci;
736                         text = atci->displayName;
737                         adapter->treeNode =
738                                 gtk_ctree_insert_node( GTK_CTREE(ctree),
739                                         NULL, NULL, &text, FOLDER_SPACING,
740                                         interfacexpm, interfacexpmmask,
741                                         interfacexpm, interfacexpmmask,
742                                         FALSE, FALSE );
743                         menu_set_sensitive( menu_factory, atci->menuCommand, adapter->haveLibrary );
744                         gtk_ctree_node_set_row_data_full(
745                                 GTK_CTREE(ctree), adapter->treeNode, adapter,
746                                 addressbook_free_treenode );
747                 }
748         }
749
750         /* Popup menu */
751         n_entries = sizeof(addressbook_tree_popup_entries) /
752                 sizeof(addressbook_tree_popup_entries[0]);
753         tree_popup = menu_create_items(addressbook_tree_popup_entries,
754                                        n_entries,
755                                        "<AddressBookTree>", &tree_factory,
756                                        NULL);
757         gtk_signal_connect(GTK_OBJECT(tree_popup), "selection_done",
758                            GTK_SIGNAL_FUNC(addressbook_popup_close), NULL);
759         n_entries = sizeof(addressbook_list_popup_entries) /
760                 sizeof(addressbook_list_popup_entries[0]);
761         list_popup = menu_create_items(addressbook_list_popup_entries,
762                                        n_entries,
763                                        "<AddressBookList>", &list_factory,
764                                        NULL);
765
766         addrbook.window  = window;
767         addrbook.menubar = menubar;
768         addrbook.ctree   = ctree;
769         addrbook.clist   = clist;
770         addrbook.entry   = entry;
771         addrbook.statusbar = statusbar;
772         addrbook.status_cid = gtk_statusbar_get_context_id(
773                         GTK_STATUSBAR(statusbar), "Addressbook Window" );
774
775         addrbook.del_btn = del_btn;
776         addrbook.reg_btn = reg_btn;
777         addrbook.lup_btn = lup_btn;
778         addrbook.to_btn  = to_btn;
779         addrbook.cc_btn  = cc_btn;
780         addrbook.bcc_btn = bcc_btn;
781
782         addrbook.tree_popup   = tree_popup;
783         addrbook.list_popup   = list_popup;
784         addrbook.tree_factory = tree_factory;
785         addrbook.list_factory = list_factory;
786         addrbook.menu_factory = menu_factory;
787
788         addrbook.listSelected = NULL;
789         address_completion_start(window);
790         gtk_widget_show_all(window);
791
792 }
793
794 static gint addressbook_close(void)
795 {
796         gtk_widget_hide(addrbook.window);
797         addressbook_export_to_file();
798         return TRUE;
799 }
800
801 static void addressbook_status_show( gchar *msg ) {
802         if( addrbook.statusbar != NULL ) {
803                 gtk_statusbar_pop(
804                         GTK_STATUSBAR(addrbook.statusbar),
805                         addrbook.status_cid );
806                 if( msg ) {
807                         gtk_statusbar_push(
808                                 GTK_STATUSBAR(addrbook.statusbar),
809                                 addrbook.status_cid, msg );
810                 }
811         }
812 }
813
814 static void addressbook_ds_status_message( AddressDataSource *ds, gchar *msg ) {
815         *addressbook_msgbuf = '\0';
816         if( ds ) {
817                 gchar *name;
818
819                 name = addrindex_ds_get_name( ds );
820                 sprintf( addressbook_msgbuf, "%s: %s", name, msg );
821         }
822         else {
823                 sprintf( addressbook_msgbuf, "%s", msg );
824         }
825         addressbook_status_show( addressbook_msgbuf );
826 }
827
828 static void addressbook_ds_show_message( AddressDataSource *ds ) {
829         gint retVal;
830         gchar *name;
831         *addressbook_msgbuf = '\0';
832         if( ds ) {
833                 name = addrindex_ds_get_name( ds );
834                 retVal = addrindex_ds_get_status_code( ds );
835                 if( retVal == MGU_SUCCESS ) {
836                         sprintf( addressbook_msgbuf, "%s", name );
837                 }
838                 else {
839                         sprintf( addressbook_msgbuf, "%s: %s", name,
840                                 mgu_error2string( retVal ) );
841                 }
842         }
843         addressbook_status_show( addressbook_msgbuf );
844 }
845
846 static void addressbook_button_set_sensitive(void)
847 {
848         gboolean to_sens  = FALSE;
849         gboolean cc_sens  = FALSE;
850         gboolean bcc_sens = FALSE;
851
852         if (!addrbook.window) return;
853
854         if (addrbook.target_compose) {
855                 to_sens = TRUE;
856                 cc_sens = TRUE;
857 #ifndef CLAWS           
858                 if (addrbook.target_compose->use_bcc)
859 #endif                  
860                         bcc_sens = TRUE;
861         }
862
863         gtk_widget_set_sensitive(addrbook.to_btn, to_sens);
864         gtk_widget_set_sensitive(addrbook.cc_btn, cc_sens);
865         gtk_widget_set_sensitive(addrbook.bcc_btn, bcc_sens);
866 }
867
868 /*
869 * Delete one or more objects from address list.
870 */
871 static void addressbook_del_clicked(GtkButton *button, gpointer data)
872 {
873         GtkCTree *clist = GTK_CTREE(addrbook.clist);
874         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
875         AddressObject *pobj;
876         AdapterDSource *ads = NULL;
877         GtkCTreeNode *nodeList;
878         gboolean procFlag;
879         AlertValue aval;
880         AddressBookFile *abf = NULL;
881         AddressDataSource *ds = NULL;
882         AddressInterface *iface;
883         AddrItemObject *aio;
884         AddrSelectItem *item;
885         GList *list, *node;
886
887         pobj = gtk_ctree_node_get_row_data(ctree, addrbook.opened );
888         g_return_if_fail(pobj != NULL);
889
890         /* Test whether anything selected for deletion */
891         nodeList = addrbook.listSelected;
892         aio = gtk_ctree_node_get_row_data( clist, nodeList );
893         if( aio == NULL) return;
894         ds = addressbook_find_datasource( addrbook.treeSelected );
895         if( ds == NULL ) return;
896
897         /* Test for read only */
898         iface = ds->interface;
899         if( iface->readOnly ) {
900                 aval = alertpanel( _("Delete address(es)"),
901                         _("This address data is readonly and cannot be deleted."),
902                         _("Close"), NULL, NULL );
903                 return;
904         }
905
906         /* Test whether Ok to proceed */
907         procFlag = FALSE;
908         if( pobj->type == ADDR_DATASOURCE ) {
909                 ads = ADAPTER_DSOURCE(pobj);
910                 if( ads->subType == ADDR_BOOK ) procFlag = TRUE;
911         }
912         else if( pobj->type == ADDR_ITEM_FOLDER ) {
913                 procFlag = TRUE;
914         }
915         else if( pobj->type == ADDR_ITEM_GROUP ) {
916                 procFlag = TRUE;
917         }
918         if( ! procFlag ) return;
919         abf = ds->rawDataSource;
920         if( abf == NULL ) return;
921
922         /* Confirm deletion */
923         aval = alertpanel( _("Delete address(es)"),
924                         _("Really delete the address(es)?"),
925                         _("Yes"), _("No"), NULL );
926         if( aval != G_ALERTDEFAULT ) return;
927
928         /* Process deletions */
929         if( pobj->type == ADDR_DATASOURCE || pobj->type == ADDR_ITEM_FOLDER ) {
930                 /* Items inside folders */
931                 list = addrselect_get_list( _addressSelect_ );
932                 node = list;
933                 while( node ) {
934                         item = node->data;
935                         node = g_list_next( node );
936                         aio = ( AddrItemObject * ) item->addressItem;
937                         if( aio->type == ADDR_ITEM_GROUP ) {
938                                 ItemGroup *item = ( ItemGroup * ) aio;
939                                 GtkCTreeNode *nd = NULL;
940
941                                 nd = addressbook_find_group_node( addrbook.opened, item );
942                                 item = addrbook_remove_group( abf, item );
943                                 if( item ) {
944                                         addritem_free_item_group( item );
945                                 }
946                                 /* Remove group from parent node */
947                                 gtk_ctree_remove_node( ctree, nd );
948                         }
949                         else if( aio->type == ADDR_ITEM_PERSON ) {
950                                 ItemPerson *item = ( ItemPerson * ) aio;
951                                 item = addrbook_remove_person( abf, item );
952                                 if( item ) {
953                                         addritem_free_item_person( item );
954                                 }
955                         }
956                         else if( aio->type == ADDR_ITEM_EMAIL ) {
957                                 ItemEMail *item = ( ItemEMail * ) aio;
958                                 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(item);
959                                 item = addrbook_person_remove_email( abf, person, item );
960                                 if( item ) {
961                                         addritem_free_item_email( item );
962                                 }
963                         }
964                 }
965                 g_list_free( list );
966                 addressbook_list_select_clear();
967                 gtk_ctree_select( ctree, addrbook.opened);
968                 return;
969         }
970         else if( pobj->type == ADDR_ITEM_GROUP ) {
971                 /* Items inside groups */
972                 list = addrselect_get_list( _addressSelect_ );
973                 node = list;
974                 while( node ) {
975                         item = node->data;
976                         node = g_list_next( node );
977                         aio = ( AddrItemObject * ) item->addressItem;
978                         if( aio->type == ADDR_ITEM_EMAIL ) {
979                                 ItemEMail *item = ( ItemEMail * ) aio;
980                                 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(item);
981                                 item = addrbook_person_remove_email( abf, person, item );
982                                 if( item ) {
983                                         addritem_free_item_email( item );
984                                 }
985                         }
986                 }
987                 g_list_free( list );
988                 addressbook_list_select_clear();
989                 gtk_ctree_select( ctree, addrbook.opened);
990                 return;
991         }
992
993         gtk_ctree_node_set_row_data( clist, nodeList, NULL );
994         gtk_ctree_remove_node( clist, nodeList );
995
996 }
997
998 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
999 {
1000         addressbook_new_address_cb( NULL, 0, NULL );
1001 }
1002
1003 gchar *addressbook_format_address( AddrItemObject * aio ) {
1004         gchar *buf = NULL;
1005         gchar *name = NULL;
1006         gchar *address = NULL;
1007
1008         if( aio->type == ADDR_ITEM_EMAIL ) {
1009                 ItemPerson *person = NULL;
1010                 ItemEMail *email = ( ItemEMail * ) aio;
1011
1012                 person = ( ItemPerson * ) ADDRITEM_PARENT(email);
1013                 if( email->address ) {
1014                         if( ADDRITEM_NAME(email) ) {
1015                                 name = ADDRITEM_NAME(email);
1016                                 if( *name == '\0' ) {
1017                                         name = ADDRITEM_NAME(person);
1018                                 }
1019                         }
1020                         else if( ADDRITEM_NAME(person) ) {
1021                                 name = ADDRITEM_NAME(person);
1022                         }
1023                         else {
1024                                 buf = g_strdup( email->address );
1025                         }
1026                         address = email->address;
1027                 }
1028         }
1029         else if( aio->type == ADDR_ITEM_PERSON ) {
1030                 ItemPerson *person = ( ItemPerson * ) aio;
1031                 GList *node = person->listEMail;
1032
1033                 name = ADDRITEM_NAME(person);
1034                 if( node ) {
1035                         ItemEMail *email = ( ItemEMail * ) node->data;
1036                         address = email->address;
1037                 }
1038         }
1039         if( address ) {
1040                 if( name && name[0] != '\0' ) {
1041                         if( strchr_with_skip_quote( name, '"', ',' ) )
1042                                 buf = g_strdup_printf( "\"%s\" <%s>", name, address );
1043                         else
1044                                 buf = g_strdup_printf( "%s <%s>", name, address );
1045                 }
1046                 else {
1047                         buf = g_strdup( address );
1048                 }
1049         }
1050
1051         return buf;
1052 }
1053
1054 static void addressbook_to_clicked(GtkButton *button, gpointer data)
1055 {
1056         GList *list, *node;
1057         Compose *compose;
1058         AddrSelectItem *item;
1059         AddrItemObject *aio;
1060         gchar *addr;
1061
1062         compose = addrbook.target_compose;
1063         if( ! compose ) return;
1064
1065         list = addrselect_get_list( _addressSelect_ );
1066         node = list;
1067         while( node ) {
1068                 item = node->data;
1069                 node = g_list_next( node );
1070                 aio = item->addressItem;
1071                 if( aio->type == ADDR_ITEM_PERSON ||
1072                     aio->type == ADDR_ITEM_EMAIL ) {
1073                         addr = addressbook_format_address( aio );
1074                         compose_entry_append(
1075                                 compose, addr, (ComposeEntryType) data );
1076                         g_free( addr );
1077                 }
1078                 else if( aio->type == ADDR_ITEM_GROUP ) {
1079                         ItemGroup *group = ( ItemGroup * ) aio;
1080                         GList *nodeMail = group->listEMail;
1081                         while( nodeMail ) {
1082                                 ItemEMail *email = nodeMail->data;
1083
1084                                 addr = addressbook_format_address(
1085                                                 ( AddrItemObject * ) email );
1086                                 compose_entry_append(
1087                                         compose, addr, (ComposeEntryType) data );
1088                                 g_free( addr );
1089                                 nodeMail = g_list_next( nodeMail );
1090                         }
1091                 }
1092         }
1093         g_list_free( list );
1094 }
1095
1096 static void addressbook_menubar_set_sensitive( gboolean sensitive ) {
1097         menu_set_sensitive( addrbook.menu_factory, "/File/Edit",   sensitive );
1098         menu_set_sensitive( addrbook.menu_factory, "/File/Delete", sensitive );
1099
1100         menu_set_sensitive( addrbook.menu_factory, "/Edit/Cut",    sensitive );
1101         menu_set_sensitive( addrbook.menu_factory, "/Edit/Copy",   sensitive );
1102         menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste",  sensitive );
1103         menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste Address",  sensitive );
1104
1105         menu_set_sensitive( addrbook.menu_factory, "/Address/New Address", sensitive );
1106         menu_set_sensitive( addrbook.menu_factory, "/Address/New Group",   sensitive );
1107         menu_set_sensitive( addrbook.menu_factory, "/Address/New Folder",  sensitive );
1108         gtk_widget_set_sensitive( addrbook.reg_btn, sensitive );
1109         gtk_widget_set_sensitive( addrbook.del_btn, sensitive );
1110 }
1111
1112 static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode *node ) {
1113         gboolean canEdit = FALSE;
1114         gboolean canAdd = FALSE;
1115         gboolean canEditTr = TRUE;
1116         gboolean editAddress = FALSE;
1117         AddressTypeControlItem *atci = NULL;
1118         AddressDataSource *ds = NULL;
1119         AddressInterface *iface = NULL;
1120
1121         if( obj == NULL ) return;
1122         if( obj->type == ADDR_INTERFACE ) {
1123                 AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
1124                 iface = adapter->interface;
1125                 if( iface ) {
1126                         if( iface->haveLibrary ) {
1127                                 /* Enable appropriate File / New command */
1128                                 atci = adapter->atci;
1129                                 menu_set_sensitive( addrbook.menu_factory, atci->menuCommand, TRUE );
1130                         }
1131                 }
1132                 canEditTr = FALSE;
1133         }
1134         else if( obj->type == ADDR_DATASOURCE ) {
1135                 AdapterDSource *ads = ADAPTER_DSOURCE(obj);
1136                 ds = ads->dataSource;
1137                 iface = ds->interface;
1138                 if( ! iface->readOnly ) {
1139                         canAdd = canEdit = editAddress = TRUE;
1140                 }
1141                 if( ! iface->haveLibrary ) {
1142                         canAdd = canEdit = editAddress = FALSE;
1143                 }
1144         }
1145         else if( obj->type == ADDR_ITEM_FOLDER ) {
1146                 ds = addressbook_find_datasource( addrbook.treeSelected );
1147                 if( ds ) {
1148                         iface = ds->interface;
1149                         if( iface->readOnly ) {
1150                                 canEditTr = FALSE;
1151                         }
1152                         else {
1153                                 canAdd = editAddress = TRUE;
1154                         }
1155                 }
1156         }
1157         else if( obj->type == ADDR_ITEM_GROUP ) {
1158                 ds = addressbook_find_datasource( addrbook.treeSelected );
1159                 if( ds ) {
1160                         iface = ds->interface;
1161                         if( ! iface->readOnly ) {
1162                                 editAddress = TRUE;
1163                         }
1164                 }
1165         }
1166
1167         if( addrbook.listSelected == NULL ) canEdit = FALSE;
1168
1169         /* Enable add */
1170         menu_set_sensitive( addrbook.menu_factory, "/Address/New Address", editAddress );
1171         menu_set_sensitive( addrbook.menu_factory, "/Address/New Group",   canAdd );
1172         menu_set_sensitive( addrbook.menu_factory, "/Address/New Folder",  canAdd );
1173         gtk_widget_set_sensitive( addrbook.reg_btn, editAddress );
1174
1175         /* Enable edit */
1176         menu_set_sensitive( addrbook.menu_factory, "/Address/Edit",   canEdit );
1177         menu_set_sensitive( addrbook.menu_factory, "/Address/Delete", canEdit );
1178         gtk_widget_set_sensitive( addrbook.del_btn, canEdit );
1179
1180         menu_set_sensitive( addrbook.menu_factory, "/File/Edit",      canEditTr );
1181         menu_set_sensitive( addrbook.menu_factory, "/File/Delete",    canEditTr );
1182 }
1183
1184 static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
1185                                       gint column, gpointer data)
1186 {
1187         AddressObject *obj = NULL;
1188         AdapterDSource *ads = NULL;
1189         AddressDataSource *ds = NULL;
1190         ItemFolder *rootFolder = NULL;
1191
1192         addrbook.treeSelected = node;
1193         addrbook.listSelected = NULL;
1194         addressbook_status_show( "" );
1195         if( addrbook.entry != NULL ) gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
1196
1197         if( addrbook.clist ) gtk_clist_clear( GTK_CLIST(addrbook.clist) );
1198         if( node ) obj = gtk_ctree_node_get_row_data( ctree, node );
1199         if( obj == NULL ) return;
1200
1201         addrbook.opened = node;
1202
1203         if( obj->type == ADDR_DATASOURCE ) {
1204                 /* Read from file */
1205                 static gboolean tVal = TRUE;
1206
1207                 ads = ADAPTER_DSOURCE(obj);
1208                 if( ads == NULL ) return;
1209                 ds = ads->dataSource;
1210                 if( ds == NULL ) return;                
1211
1212                 if( addrindex_ds_get_modify_flag( ds ) ) {
1213                         addrindex_ds_read_data( ds );
1214                 }
1215
1216                 if( ! addrindex_ds_get_read_flag( ds ) ) {
1217                         addrindex_ds_read_data( ds );
1218                 }
1219                 addressbook_ds_show_message( ds );
1220
1221                 if( ! addrindex_ds_get_access_flag( ds ) ) {
1222                         /* Remove existing folders and groups */
1223                         gtk_clist_freeze( GTK_CLIST(ctree) );
1224                         addressbook_tree_remove_children( ctree, node );
1225                         gtk_clist_thaw( GTK_CLIST(ctree) );
1226
1227                         /* Load folders into the tree */
1228                         rootFolder = addrindex_ds_get_root_folder( ds );
1229                         if( ds->type == ADDR_IF_JPILOT ) {
1230                                 addressbook_node_add_folder( node, ds, rootFolder, ADDR_CATEGORY );
1231                         }
1232                         else {
1233                                 addressbook_node_add_folder( node, ds, rootFolder, ADDR_ITEM_FOLDER );
1234                         }
1235                         addrindex_ds_set_access_flag( ds, &tVal );
1236                         gtk_ctree_expand( ctree, node );
1237                 }
1238         }
1239
1240         /* Update address list */
1241         addressbook_set_clist( obj );
1242
1243         /* Setup main menu selections */
1244         addressbook_menubar_set_sensitive( FALSE );
1245         addressbook_menuitem_set_sensitive( obj, node );
1246
1247         addressbook_list_select_clear();
1248 }
1249
1250 /*
1251 * Setup address list popup menu.
1252 * Enter: pobj  Parent address object in address tree.
1253 *        obj   Address object in address list.
1254 */
1255 static void addressbook_list_menu_setup( void ) {
1256         GtkCTree *clist = NULL;
1257         AddressObject *pobj = NULL;
1258         AddressObject *obj = NULL;
1259         AdapterDSource *ads = NULL;
1260         AddressInterface *iface = NULL;
1261         AddressDataSource *ds = NULL;
1262         gboolean canEdit = FALSE;
1263         gboolean canDelete = FALSE;
1264         gboolean canCut = FALSE;
1265         gboolean canCopy = FALSE;
1266         gboolean canPaste = FALSE;
1267
1268         pobj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), addrbook.treeSelected );
1269         if( pobj == NULL ) return;
1270
1271         clist = GTK_CTREE(addrbook.clist);
1272         obj = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
1273         if( obj == NULL ) canEdit = FALSE;
1274
1275         menu_set_insensitive_all( GTK_MENU_SHELL(addrbook.list_popup) );
1276
1277         if( pobj->type == ADDR_DATASOURCE ) {
1278                 ads = ADAPTER_DSOURCE(pobj);
1279                 ds = ads->dataSource;
1280                 iface = ds->interface;
1281                 if( ! iface->readOnly ) {
1282                         menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
1283                         menu_set_sensitive( addrbook.list_factory, "/New Folder", TRUE );
1284                         menu_set_sensitive( addrbook.list_factory, "/New Group", TRUE );
1285                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1286                         if( ! addrclip_is_empty( _clipBoard_ ) ) canPaste = TRUE;
1287                         if( ! addrselect_test_empty( _addressSelect_ ) ) canCut = TRUE;
1288                         if( obj ) canEdit = TRUE;
1289                 }
1290         }
1291         else if( pobj->type != ADDR_INTERFACE ) {
1292                 ds = addressbook_find_datasource( addrbook.treeSelected );
1293                 iface = ds->interface;
1294                 if( ! iface->readOnly ) {
1295                         if( pobj->type == ADDR_ITEM_FOLDER || pobj->type == ADDR_ITEM_GROUP ) {
1296                                 menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
1297                                 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1298                                 if( obj ) canEdit = TRUE;
1299                         }
1300                         if( pobj->type == ADDR_ITEM_FOLDER ) {
1301                                 menu_set_sensitive( addrbook.list_factory, "/New Folder", TRUE );
1302                                 menu_set_sensitive( addrbook.list_factory, "/New Group", TRUE );
1303                                 if( obj ) canEdit = TRUE;
1304                         }
1305                         if( ! addrclip_is_empty( _clipBoard_ ) ) canPaste = TRUE;
1306                         if( ! addrselect_test_empty( _addressSelect_ ) ) canCut = TRUE;
1307                 }
1308         }
1309         if( ! addrselect_test_empty( _addressSelect_ ) ) canCopy = TRUE;
1310
1311         canDelete = canEdit;
1312         canDelete = canEdit;
1313
1314         /* Disable edit if more than one row selected */
1315         if( GTK_CLIST(clist)->selection && GTK_CLIST(clist)->selection->next ) canEdit = FALSE;
1316
1317         menu_set_sensitive( addrbook.list_factory, "/Edit",   canEdit );
1318         menu_set_sensitive( addrbook.list_factory, "/Delete", canDelete );
1319
1320         menu_set_sensitive( addrbook.list_factory, "/Cut",           canCut );
1321         menu_set_sensitive( addrbook.list_factory, "/Copy",          canCopy );
1322         menu_set_sensitive( addrbook.list_factory, "/Paste",         canPaste );
1323         menu_set_sensitive( addrbook.list_factory, "/Paste Address", canPaste );
1324
1325         menu_set_sensitive( addrbook.menu_factory, "/Edit/Cut",           canCut );
1326         menu_set_sensitive( addrbook.menu_factory, "/Edit/Copy",          canCopy );
1327         menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste",         canPaste );
1328         menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste Address", canPaste );
1329
1330         menu_set_sensitive( addrbook.tree_factory, "/Cut",           canCut );
1331         menu_set_sensitive( addrbook.tree_factory, "/Copy",          canCopy );
1332         menu_set_sensitive( addrbook.tree_factory, "/Paste",         canPaste );
1333
1334         menu_set_sensitive( addrbook.menu_factory, "/Address/Edit",   canEdit );
1335         menu_set_sensitive( addrbook.menu_factory, "/Address/Delete", canDelete );
1336
1337         gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
1338 }
1339
1340 static void addressbook_list_selected(GtkCList *clist, gint row, gint column,
1341                                       GdkEvent *event, gpointer data)
1342 {
1343         if (event && event->type == GDK_2BUTTON_PRESS) {
1344                 /* Handle double click */
1345                 if (prefs_common.add_address_by_click &&
1346                     addrbook.target_compose)
1347                         addressbook_to_clicked(NULL, NULL);
1348                 else
1349                         addressbook_edit_address_cb(NULL, 0, NULL);
1350         }
1351 }
1352
1353 /*
1354  * Add list of items into tree node below specified tree node.
1355  * Enter: treeNode  Tree node.
1356  *        ds        Data source.
1357  *        listItems List of items.
1358  */
1359 static void addressbook_treenode_add_list(
1360         GtkCTreeNode *treeNode, AddressDataSource *ds, GList *listItems )
1361 {
1362         GList *node;
1363
1364         node = listItems;
1365         while( node ) {
1366                 AddrItemObject *aio;
1367                 GtkCTreeNode *nn;
1368
1369                 aio = node->data;
1370                 if( ADDRESS_OBJECT_TYPE(aio) == ITEMTYPE_GROUP ) {
1371                         ItemGroup *group;
1372
1373                         group = ( ItemGroup * ) aio;
1374                         nn = addressbook_node_add_group( treeNode, ds, group );
1375                 }
1376                 else if( ADDRESS_OBJECT_TYPE(aio) == ITEMTYPE_FOLDER ) {
1377                         ItemFolder *folder;
1378
1379                         folder = ( ItemFolder * ) aio;
1380                         nn = addressbook_node_add_folder(
1381                                 treeNode, ds, folder, ADDR_ITEM_FOLDER );
1382                 }
1383                 node = g_list_next( node );
1384         }
1385 }
1386
1387 /*
1388 * Cut from address list widget.
1389 */
1390 static void addressbook_clip_cut_cb( void ) {
1391         _clipBoard_->cutFlag = TRUE;
1392         addrclip_clear( _clipBoard_ );
1393         addrclip_add( _clipBoard_, _addressSelect_ );
1394         /* addrclip_list_show( _clipBoard_, stdout ); */
1395 }
1396
1397 /*
1398 * Copy from address list widget.
1399 */
1400 static void addressbook_clip_copy_cb( void ) {
1401         _clipBoard_->cutFlag = FALSE;
1402         addrclip_clear( _clipBoard_ );
1403         addrclip_add( _clipBoard_, _addressSelect_ );
1404         /* addrclip_list_show( _clipBoard_, stdout ); */
1405 }
1406
1407 /*
1408 * Paste clipboard into address list widget.
1409 */
1410 static void addressbook_clip_paste_cb( void ) {
1411         GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
1412         AddressObject *pobj = NULL;
1413         AddressDataSource *ds = NULL;
1414         AddressBookFile *abf = NULL;
1415         ItemFolder *folder = NULL;
1416         GList *folderGroup = NULL;
1417
1418         ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
1419         if( ds == NULL ) return;
1420         if( addrindex_ds_get_readonly( ds ) ) {
1421                 addressbook_ds_status_message(
1422                         ds, _( "Cannot paste. Target address book is readonly." ) );
1423                 return;
1424         }
1425
1426         pobj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
1427         if( pobj ) {
1428                 if( pobj->type == ADDR_ITEM_FOLDER ) {
1429                         folder = ADAPTER_FOLDER(pobj)->itemFolder;
1430                 }
1431                 else if( pobj->type == ADDR_ITEM_GROUP ) {
1432                         addressbook_ds_status_message(
1433                                 ds, _( "Cannot paste into an address group." ) );
1434                         return;
1435                 }
1436         }
1437
1438         /* Get an address book */
1439         abf = addressbook_get_book_file();
1440         if( abf == NULL ) return;
1441
1442         if( _clipBoard_->cutFlag ) {
1443                 /* Paste/Cut */
1444                 folderGroup = addrclip_paste_cut( _clipBoard_, abf, folder );
1445
1446                 /* Remove all groups and folders in clipboard from tree node */
1447                 addressbook_treenode_remove_item();
1448
1449                 /* Remove all "cut" items */
1450                 addrclip_delete_item( _clipBoard_ );
1451
1452                 /* Clear clipboard - cut items??? */
1453                 addrclip_clear( _clipBoard_ );
1454         }
1455         else {
1456                 /* Paste/Copy */
1457                 folderGroup = addrclip_paste_copy( _clipBoard_, abf, folder );
1458         }
1459
1460         /* addrclip_list_show( _clipBoard_, stdout ); */
1461         if( folderGroup ) {
1462                 /* Update tree by inserting node for each folder or group */
1463                 addressbook_treenode_add_list(
1464                         addrbook.treeSelected, ds, folderGroup );
1465                 gtk_ctree_expand( ctree, addrbook.treeSelected );
1466                 g_list_free( folderGroup );
1467                 folderGroup = NULL;
1468         }
1469
1470         /* Display items pasted */
1471         gtk_ctree_select( ctree, addrbook.opened );
1472
1473 }
1474
1475 /*
1476 * Paste clipboard email addresses only into address list widget.
1477 */
1478 static void addressbook_clip_paste_address_cb( void ) {
1479         GtkCTree *clist = GTK_CTREE(addrbook.clist);
1480         GtkCTree *ctree;
1481         AddressObject *pobj = NULL;
1482         AddressDataSource *ds = NULL;
1483         AddressBookFile *abf = NULL;
1484         ItemFolder *folder = NULL;
1485         AddrItemObject *aio;
1486         gint cnt;
1487
1488         if( addrbook.listSelected == NULL ) return;
1489
1490         ctree = GTK_CTREE( addrbook.ctree );
1491         ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
1492         if( ds == NULL ) return;
1493         if( addrindex_ds_get_readonly( ds ) ) {
1494                 addressbook_ds_status_message(
1495                         ds, _( "Cannot paste. Target address book is readonly." ) );
1496                 return;
1497         }
1498
1499         pobj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
1500         if( pobj ) {
1501                 if( pobj->type == ADDR_ITEM_FOLDER ) {
1502                         folder = ADAPTER_FOLDER(pobj)->itemFolder;
1503                 }
1504         }
1505
1506         abf = addressbook_get_book_file();
1507         if( abf == NULL ) return;
1508
1509         cnt = 0;
1510         aio = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
1511         if( aio->type == ADDR_ITEM_PERSON ) {
1512                 ItemPerson *person;
1513
1514                 person = ( ItemPerson * ) aio;
1515                 if( _clipBoard_->cutFlag ) {
1516                         /* Paste/Cut */
1517                         cnt = addrclip_paste_person_cut( _clipBoard_, abf, person );
1518
1519                         /* Remove all "cut" items */
1520                         addrclip_delete_address( _clipBoard_ );
1521
1522                         /* Clear clipboard */
1523                         addrclip_clear( _clipBoard_ );
1524                 }
1525                 else {
1526                         /* Paste/Copy */
1527                         cnt = addrclip_paste_person_copy( _clipBoard_, abf, person );
1528                 }
1529                 if( cnt > 0 ) {
1530                         addritem_person_set_opened( person, TRUE );
1531                 }
1532         }
1533
1534         /* Display items pasted */
1535         if( cnt > 0 ) {
1536                 gtk_ctree_select( ctree, addrbook.opened );
1537         }
1538 }
1539
1540 /*
1541 * Add current treenode object to clipboard. Note that widget only allows
1542 * one entry from the tree list to be selected.
1543 */
1544 static void addressbook_treenode_to_clipboard( void ) {
1545         AddressObject *obj = NULL;
1546         AddressDataSource *ds = NULL;
1547         AddrSelectItem *item;
1548         GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
1549         GtkCTreeNode *node;
1550
1551         node = addrbook.treeSelected;
1552         if( node == NULL ) return;
1553         obj = gtk_ctree_node_get_row_data( ctree, node );
1554         if( obj == NULL ) return;
1555
1556         ds = addressbook_find_datasource( node );
1557         if( ds == NULL ) return;
1558
1559         item = NULL;
1560         if( obj->type == ADDR_ITEM_FOLDER ) {
1561                 AdapterFolder *adapter = ADAPTER_FOLDER(obj);
1562                 ItemFolder *folder = adapter->itemFolder;
1563
1564                 item = addrselect_create_node( obj );
1565                 item->uid = g_strdup( ADDRITEM_ID(folder) );
1566         }
1567         else if( obj->type == ADDR_ITEM_GROUP ) {
1568                 AdapterGroup *adapter = ADAPTER_GROUP(obj);
1569                 ItemGroup *group = adapter->itemGroup;
1570
1571                 item = addrselect_create_node( obj );
1572                 item->uid = g_strdup( ADDRITEM_ID(group) );
1573         }
1574         else if( obj->type == ADDR_DATASOURCE ) {
1575                 /* Data source */
1576                 item = addrselect_create_node( obj );
1577                 item->uid = NULL;
1578         }
1579
1580         if( item ) {
1581                 /* Clear existing list and add item into list */
1582                 gchar *cacheID;
1583
1584                 addressbook_list_select_clear();
1585                 cacheID = addrindex_get_cache_id( _addressIndex_, ds );
1586                 addrselect_list_add( _addressSelect_, item, cacheID );
1587                 g_free( cacheID );
1588         }
1589 }
1590
1591 /*
1592 * Cut from tree widget.
1593 */
1594 static void addressbook_treenode_cut_cb( void ) {
1595         _clipBoard_->cutFlag = TRUE;
1596         addressbook_treenode_to_clipboard();
1597         addrclip_clear( _clipBoard_ );
1598         addrclip_add( _clipBoard_, _addressSelect_ );
1599         /* addrclip_list_show( _clipBoard_, stdout ); */
1600 }
1601
1602 /*
1603 * Copy from tree widget.
1604 */
1605 static void addressbook_treenode_copy_cb( void ) {
1606         _clipBoard_->cutFlag = FALSE;
1607         addressbook_treenode_to_clipboard();
1608         addrclip_clear( _clipBoard_ );
1609         addrclip_add( _clipBoard_, _addressSelect_ );
1610         /* addrclip_list_show( _clipBoard_, stdout ); */
1611 }
1612
1613 /*
1614 * Paste clipboard into address tree widget.
1615 */
1616 static void addressbook_treenode_paste_cb( void ) {
1617         addressbook_clip_paste_cb();
1618 }
1619
1620 static void addressbook_list_select_clear( void ) {
1621         addrselect_list_clear( _addressSelect_ );
1622 }
1623
1624 static void addressbook_list_select_add( AddrItemObject *aio, AddressDataSource *ds ) {
1625         gchar *cacheID;
1626
1627         if( ds == NULL ) return;
1628         cacheID = addrindex_get_cache_id( _addressIndex_, ds );
1629         addrselect_list_add_obj( _addressSelect_, aio, cacheID );
1630         g_free( cacheID );
1631 }
1632
1633 static void addressbook_list_select_remove( AddrItemObject *aio ) {
1634         addrselect_list_remove( _addressSelect_, aio );
1635 }
1636
1637 static void addressbook_list_row_selected( GtkCTree *clist,
1638                                            GtkCTreeNode *node,
1639                                            gint column,
1640                                            gpointer data )
1641 {
1642         GtkEntry *entry = GTK_ENTRY(addrbook.entry);
1643         AddrItemObject *aio = NULL;
1644         AddressObject *pobj = NULL;
1645         AdapterDSource *ads = NULL;
1646         AddressDataSource *ds = NULL;
1647
1648         gtk_entry_set_text( entry, "" );
1649         addrbook.listSelected = node;
1650
1651         pobj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), addrbook.treeSelected );
1652         if( pobj == NULL ) return;
1653
1654         if( pobj->type == ADDR_DATASOURCE ) {
1655                 ads = ADAPTER_DSOURCE(pobj);
1656                 ds = ads->dataSource;
1657         }
1658         else if( pobj->type != ADDR_INTERFACE ) {
1659                 ds = addressbook_find_datasource( addrbook.treeSelected );
1660         }
1661
1662         aio = gtk_ctree_node_get_row_data( clist, node );
1663         if( aio ) {
1664                 /* printf( "list select: %d : '%s'\n", aio->type, aio->name ); */
1665                 addressbook_list_select_add( aio, ds );
1666         }
1667
1668         addressbook_list_menu_setup();
1669 }
1670
1671 static void addressbook_list_row_unselected( GtkCTree *ctree,
1672                                              GtkCTreeNode *node,
1673                                              gint column,
1674                                              gpointer data )
1675 {
1676         AddrItemObject *aio;
1677
1678         aio = gtk_ctree_node_get_row_data( ctree, node );
1679         if( aio != NULL ) {
1680                 /* printf( "list unselect: %d : '%s'\n", aio->type, aio->name ); */
1681                 addressbook_list_select_remove( aio );
1682         }
1683 }
1684
1685 static void addressbook_entry_gotfocus( GtkWidget *widget ) {
1686         gtk_editable_select_region( GTK_EDITABLE(addrbook.entry), 0, -1 );
1687 }
1688
1689 static void addressbook_list_button_pressed(GtkWidget *widget,
1690                                             GdkEventButton *event,
1691                                             gpointer data)
1692 {
1693         if( ! event ) return;
1694
1695         addressbook_list_menu_setup();
1696
1697         if( event->button == 3 ) {
1698                 gtk_menu_popup( GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
1699                        event->button, event->time );
1700         }
1701 }
1702
1703 static void addressbook_list_button_released(GtkWidget *widget,
1704                                              GdkEventButton *event,
1705                                              gpointer data)
1706 {
1707 }
1708
1709 static void addressbook_tree_button_pressed(GtkWidget *ctree,
1710                                             GdkEventButton *event,
1711                                             gpointer data)
1712 {
1713         GtkCList *clist = GTK_CLIST(ctree);
1714         gint row, column;
1715         AddressObject *obj = NULL;
1716         AdapterDSource *ads = NULL;
1717         AddressInterface *iface = NULL;
1718         AddressDataSource *ds = NULL;
1719         gboolean canEdit = FALSE;
1720         gboolean canCut = FALSE;
1721         gboolean canCopy = FALSE;
1722         gboolean canPaste = FALSE;
1723         gboolean canTreeCut = FALSE;
1724         gboolean canTreeCopy = FALSE;
1725         gboolean canTreePaste = FALSE;
1726
1727         if( ! event ) return;
1728         addressbook_menubar_set_sensitive( FALSE );
1729
1730         if( gtk_clist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
1731                 gtk_clist_select_row( clist, row, column );
1732                 gtkut_clist_set_focus_row(clist, row);
1733                 obj = gtk_clist_get_row_data( clist, row );
1734         }
1735
1736         menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
1737
1738         if( obj == NULL ) return;
1739
1740         if( ! addrclip_is_empty( _clipBoard_ ) ) {
1741                 canTreePaste = TRUE;
1742         }
1743
1744         if (obj->type == ADDR_DATASOURCE) {
1745                 ads = ADAPTER_DSOURCE(obj);
1746                 ds = ads->dataSource;
1747                 iface = ds->interface;
1748                 canEdit = TRUE;
1749                 if( iface->readOnly ) {
1750                         canTreePaste = FALSE;
1751                 }
1752                 else {
1753                         menu_set_sensitive( addrbook.tree_factory, "/New Address", TRUE );
1754                         menu_set_sensitive( addrbook.tree_factory, "/New Folder", TRUE );
1755                         menu_set_sensitive( addrbook.tree_factory, "/New Group", TRUE );
1756                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1757                 }
1758                 canTreeCopy = TRUE;
1759         }
1760         else if (obj->type == ADDR_ITEM_FOLDER) {
1761                 ds = addressbook_find_datasource( addrbook.treeSelected );
1762                 iface = ds->interface;
1763                 if( iface->readOnly ) {
1764                         canTreePaste = FALSE;
1765                 }
1766                 else {
1767                         canEdit = TRUE;
1768                         canTreeCut = TRUE;
1769                         menu_set_sensitive( addrbook.tree_factory, "/New Address", TRUE );
1770                         menu_set_sensitive( addrbook.tree_factory, "/New Folder", TRUE );
1771                         menu_set_sensitive( addrbook.tree_factory, "/New Group", TRUE );
1772                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1773                 }
1774                 canTreeCopy = TRUE;
1775         }
1776         else if (obj->type == ADDR_ITEM_GROUP) {
1777                 ds = addressbook_find_datasource( addrbook.treeSelected );
1778                 iface = ds->interface;
1779                 if( ! iface->readOnly ) {
1780                         canEdit = TRUE;
1781                         menu_set_sensitive( addrbook.tree_factory, "/New Address", TRUE );
1782                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1783                 }
1784         }
1785         else if (obj->type == ADDR_INTERFACE) {
1786                 canTreePaste = FALSE;
1787         }
1788
1789         if( canEdit ) {
1790                 if( ! addrselect_test_empty( _addressSelect_ ) ) canCut = TRUE;
1791         }
1792         if( ! addrselect_test_empty( _addressSelect_ ) ) canCopy = TRUE;
1793         if( ! addrclip_is_empty( _clipBoard_ ) ) canPaste = TRUE;
1794
1795         /* Enable edit */
1796         menu_set_sensitive( addrbook.tree_factory, "/Edit",   canEdit );
1797         menu_set_sensitive( addrbook.tree_factory, "/Delete", canEdit );
1798         menu_set_sensitive( addrbook.tree_factory, "/Cut",    canTreeCut );
1799         menu_set_sensitive( addrbook.tree_factory, "/Copy",   canTreeCopy );
1800         menu_set_sensitive( addrbook.tree_factory, "/Paste",  canTreePaste );
1801
1802         menu_set_sensitive( addrbook.menu_factory, "/File/Edit",          canEdit );
1803         menu_set_sensitive( addrbook.menu_factory, "/File/Delete",        canEdit );
1804         menu_set_sensitive( addrbook.menu_factory, "/Edit/Cut",           canCut );
1805         menu_set_sensitive( addrbook.menu_factory, "/Edit/Copy",          canCopy );
1806         menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste",         canPaste );
1807         menu_set_sensitive( addrbook.menu_factory, "/Edit/Paste Address", canPaste );
1808
1809         if( event->button == 3 ) {
1810                 gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
1811                                event->button, event->time);
1812         }
1813
1814 }
1815
1816 static void addressbook_tree_button_released(GtkWidget *ctree,
1817                                              GdkEventButton *event,
1818                                              gpointer data)
1819 {
1820         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1821         gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree), addrbook.opened);
1822 }
1823
1824 static void addressbook_popup_close(GtkMenuShell *menu_shell, gpointer data)
1825 {
1826         if (!addrbook.opened) return;
1827
1828         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1829         gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree),
1830                                   addrbook.opened);
1831 }
1832
1833 static void addressbook_new_folder_cb(gpointer data, guint action,
1834                                       GtkWidget *widget)
1835 {
1836         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1837         AddressObject *obj = NULL;
1838         AddressDataSource *ds = NULL;
1839         AddressBookFile *abf = NULL;
1840         ItemFolder *parentFolder = NULL;
1841         ItemFolder *folder = NULL;
1842
1843         if( ! addrbook.treeSelected ) return;
1844         obj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
1845         if( obj == NULL ) return;
1846         ds = addressbook_find_datasource( addrbook.treeSelected );
1847         if( ds == NULL ) return;
1848
1849         if( obj->type == ADDR_DATASOURCE ) {
1850                 if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
1851         }
1852         else if( obj->type == ADDR_ITEM_FOLDER ) {
1853                 parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
1854         }
1855         else {
1856                 return;
1857         }
1858
1859         abf = ds->rawDataSource;
1860         if( abf == NULL ) return;
1861         folder = addressbook_edit_folder( abf, parentFolder, NULL );
1862         if( folder ) {
1863                 GtkCTreeNode *nn;
1864                 nn = addressbook_node_add_folder( addrbook.treeSelected, ds, folder, ADDR_ITEM_FOLDER );
1865                 gtk_ctree_expand( ctree, addrbook.treeSelected );
1866                 if( addrbook.treeSelected == addrbook.opened ) addressbook_set_clist(obj);
1867         }
1868
1869 }
1870
1871 static void addressbook_new_group_cb(gpointer data, guint action,
1872                                      GtkWidget *widget)
1873 {
1874         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1875         AddressObject *obj = NULL;
1876         AddressDataSource *ds = NULL;
1877         AddressBookFile *abf = NULL;
1878         ItemFolder *parentFolder = NULL;
1879         ItemGroup *group = NULL;
1880
1881         if( ! addrbook.treeSelected ) return;
1882         obj = gtk_ctree_node_get_row_data(ctree, addrbook.treeSelected);
1883         if( obj == NULL ) return;
1884         ds = addressbook_find_datasource( addrbook.treeSelected );
1885         if( ds == NULL ) return;
1886
1887         if( obj->type == ADDR_DATASOURCE ) {
1888                 if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
1889         }
1890         else if( obj->type == ADDR_ITEM_FOLDER ) {
1891                 parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
1892         }
1893         else {
1894                 return;
1895         }
1896
1897         abf = ds->rawDataSource;
1898         if( abf == NULL ) return;
1899         group = addressbook_edit_group( abf, parentFolder, NULL );
1900         if( group ) {
1901                 GtkCTreeNode *nn;
1902                 nn = addressbook_node_add_group( addrbook.treeSelected, ds, group );
1903                 gtk_ctree_expand( ctree, addrbook.treeSelected );
1904                 if( addrbook.treeSelected == addrbook.opened ) addressbook_set_clist(obj);
1905         }
1906
1907 }
1908
1909 static void addressbook_change_node_name(GtkCTreeNode *node, const gchar *name)
1910 {
1911         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1912         gchar *text[1];
1913         guint8 spacing;
1914         GdkPixmap *pix_cl, *pix_op;
1915         GdkBitmap *mask_cl, *mask_op;
1916         gboolean is_leaf, expanded;
1917
1918         gtk_ctree_get_node_info(ctree, node, text, &spacing,
1919                                 &pix_cl, &mask_cl, &pix_op, &mask_op,
1920                                 &is_leaf, &expanded);
1921         gtk_ctree_set_node_info(ctree, node, name, spacing,
1922                                 pix_cl, mask_cl, pix_op, mask_op,
1923                                 is_leaf, expanded);
1924 }
1925
1926 /*
1927 * Edit data source.
1928 * Enter: obj   Address object to edit.
1929 *        node  Node in tree.
1930 * Return: New name of data source.
1931 */
1932 static gchar *addressbook_edit_datasource( AddressObject *obj, GtkCTreeNode *node ) {
1933         gchar *newName = NULL;
1934         AddressDataSource *ds = NULL;
1935         AddressInterface *iface = NULL;
1936         AdapterDSource *ads = NULL;
1937
1938         ds = addressbook_find_datasource( node );
1939         if( ds == NULL ) return NULL;
1940         iface = ds->interface;
1941         if( ! iface->haveLibrary ) return NULL;
1942
1943         /* Read data from data source */
1944         if( addrindex_ds_get_modify_flag( ds ) ) {
1945                 addrindex_ds_read_data( ds );
1946         }
1947
1948         if( ! addrindex_ds_get_read_flag( ds ) ) {
1949                 addrindex_ds_read_data( ds );
1950         }
1951
1952         /* Handle edit */
1953         ads = ADAPTER_DSOURCE(obj);
1954         if( ads->subType == ADDR_BOOK ) {
1955                 if( addressbook_edit_book( _addressIndex_, ads ) == NULL ) return NULL;
1956         }
1957         else if( ads->subType == ADDR_VCARD ) {
1958                 if( addressbook_edit_vcard( _addressIndex_, ads ) == NULL ) return NULL;
1959         }
1960 #ifdef USE_JPILOT
1961         else if( ads->subType == ADDR_JPILOT ) {
1962                 if( addressbook_edit_jpilot( _addressIndex_, ads ) == NULL ) return NULL;
1963         }
1964 #endif
1965 #ifdef USE_LDAP
1966         else if( ads->subType == ADDR_LDAP ) {
1967                 if( addressbook_edit_ldap( _addressIndex_, ads ) == NULL ) return NULL;
1968         }
1969 #endif
1970         else {
1971                 return NULL;
1972         }
1973         newName = obj->name;
1974         return newName;
1975 }
1976
1977 /*
1978 * Edit an object that is in the address tree area.
1979 */
1980 static void addressbook_treenode_edit_cb(gpointer data, guint action,
1981                                        GtkWidget *widget)
1982 {
1983         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1984         AddressObject *obj;
1985         AddressDataSource *ds = NULL;
1986         AddressBookFile *abf = NULL;
1987         GtkCTreeNode *node = NULL, *parentNode = NULL;
1988         gchar *name = NULL;
1989
1990         if( ! addrbook.treeSelected ) return;
1991         node = addrbook.treeSelected;
1992         if( GTK_CTREE_ROW(node)->level == 1 ) return;
1993         obj = gtk_ctree_node_get_row_data( ctree, node );
1994         if( obj == NULL ) return;
1995         parentNode = GTK_CTREE_ROW(node)->parent;
1996
1997         ds = addressbook_find_datasource( node );
1998         if( ds == NULL ) return;
1999
2000         if( obj->type == ADDR_DATASOURCE ) {
2001                 name = addressbook_edit_datasource( obj, node );
2002                 if( name == NULL ) return;
2003         }
2004         else {
2005                 abf = ds->rawDataSource;
2006                 if( abf == NULL ) return;
2007                 if( obj->type == ADDR_ITEM_FOLDER ) {
2008                         AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2009                         ItemFolder *item = adapter->itemFolder;
2010                         ItemFolder *parentFolder = NULL;
2011                         parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
2012                         if( addressbook_edit_folder( abf, parentFolder, item ) == NULL ) return;
2013                         name = ADDRITEM_NAME(item);
2014                 }
2015                 else if( obj->type == ADDR_ITEM_GROUP ) {
2016                         AdapterGroup *adapter = ADAPTER_GROUP(obj);
2017                         ItemGroup *item = adapter->itemGroup;
2018                         ItemFolder *parentFolder = NULL;
2019                         parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
2020                         if( addressbook_edit_group( abf, parentFolder, item ) == NULL ) return;
2021                         name = ADDRITEM_NAME(item);
2022                 }
2023         }
2024         if( name && parentNode ) {
2025                 /* Update node in tree view */
2026                 addressbook_change_node_name( node, name );
2027                 gtk_ctree_sort_node(ctree, parentNode);
2028                 gtk_ctree_expand( ctree, node );
2029                 gtk_ctree_select( ctree, node );
2030         }
2031 }
2032
2033 /*
2034 * Delete an item from the tree widget.
2035 */
2036 static void addressbook_treenode_delete_cb(gpointer data, guint action,
2037                                          GtkWidget *widget)
2038 {
2039         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2040         GtkCTreeNode *node = NULL;
2041         AddressObject *obj;
2042         gchar *message;
2043         AlertValue aval;
2044         AddressBookFile *abf = NULL;
2045         AdapterDSource *ads = NULL;
2046         AddressInterface *iface = NULL;
2047         AddressDataSource *ds = NULL;
2048         gboolean remFlag = FALSE;
2049
2050         if( ! addrbook.treeSelected ) return;
2051         node = addrbook.treeSelected;
2052         if( GTK_CTREE_ROW(node)->level == 1 ) return;
2053
2054         obj = gtk_ctree_node_get_row_data( ctree, node );
2055         g_return_if_fail(obj != NULL);
2056
2057         if( obj->type == ADDR_DATASOURCE ) {
2058                 ads = ADAPTER_DSOURCE(obj);
2059                 if( ads == NULL ) return;
2060                 ds = ads->dataSource;
2061                 if( ds == NULL ) return;
2062         }
2063         else {
2064                 /* Must be folder or something else */
2065                 ds = addressbook_find_datasource( node );
2066                 if( ds == NULL ) return;
2067
2068                 /* Only allow deletion from non-readOnly data sources */
2069                 iface = ds->interface;
2070                 if( iface->readOnly ) return;
2071         }
2072
2073         /* Confirm deletion */
2074         if( obj->type == ADDR_ITEM_FOLDER ) {
2075                 message = g_strdup_printf( _(
2076                         "Do you want to delete the folder AND all addresses in `%s' ? \n" \
2077                         "If deleting the folder only, addresses will be moved into parent folder." ),
2078                         obj->name );
2079                 aval = alertpanel( _("Delete"), message,
2080                         _("Folder only"), _("Folder and Addresses"), _("Cancel") );
2081                 g_free(message);
2082                 if( aval == G_ALERTOTHER ) return;
2083         }
2084         else {
2085                 message = g_strdup_printf(_("Really delete `%s' ?"), obj->name);
2086                 aval = alertpanel(_("Delete"), message, _("Yes"), _("No"), NULL);
2087                 g_free(message);
2088                 if (aval != G_ALERTDEFAULT) return;
2089         }
2090
2091         /* Proceed with deletion */
2092         if( obj->type == ADDR_DATASOURCE ) {
2093                 /* Remove data source. */
2094                 if( addrindex_index_remove_datasource( _addressIndex_, ds ) ) {
2095                         addrindex_free_datasource( ds );
2096                         remFlag = TRUE;
2097                 }
2098         }
2099         else {
2100                 abf = addressbook_get_book_file();
2101                 if( abf == NULL ) return;
2102         }
2103
2104         if( obj->type == ADDR_ITEM_FOLDER ) {
2105                 AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2106                 ItemFolder *item = adapter->itemFolder;
2107                 if( aval == G_ALERTDEFAULT ) {
2108                         /* Remove folder only */
2109                         item = addrbook_remove_folder( abf, item );
2110                         if( item ) {
2111                                 addritem_free_item_folder( item );
2112                                 addressbook_move_nodes_up( ctree, node );
2113                                 remFlag = TRUE;
2114                         }
2115                 }
2116                 else if( aval == G_ALERTALTERNATE ) {
2117                         /* Remove folder and addresses */
2118                         item = addrbook_remove_folder_delete( abf, item );
2119                         if( item ) {
2120                                 addritem_free_item_folder( item );
2121                                 remFlag = TRUE;
2122                         }
2123                 }
2124         }
2125         else if( obj->type == ADDR_ITEM_GROUP ) {
2126                 AdapterGroup *adapter = ADAPTER_GROUP(obj);
2127                 ItemGroup *item = adapter->itemGroup;
2128
2129                 item = addrbook_remove_group( abf, item );
2130                 if( item ) {
2131                         addritem_free_item_group( item );
2132                         remFlag = TRUE;
2133                 }
2134         }
2135
2136         if( remFlag ) {
2137                 /* Remove node. */
2138                 gtk_ctree_remove_node(ctree, node );
2139         }
2140 }
2141
2142 static void addressbook_new_address_cb( gpointer data, guint action, GtkWidget *widget ) {
2143         AddressObject *pobj = NULL;
2144         AddressDataSource *ds = NULL;
2145         AddressBookFile *abf = NULL;
2146
2147         pobj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), addrbook.treeSelected);
2148         if( pobj == NULL ) return;
2149         ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
2150         if( ds == NULL ) return;
2151
2152         abf = ds->rawDataSource;
2153         if( abf == NULL ) return;
2154
2155         if( pobj->type == ADDR_DATASOURCE ) {
2156                 if( ADAPTER_DSOURCE(pobj)->subType == ADDR_BOOK ) {
2157                         /* New address */
2158                         ItemPerson *person = addressbook_edit_person( abf, NULL, NULL, FALSE );
2159                         if( person ) {
2160                                 if( addrbook.treeSelected == addrbook.opened ) {
2161                                         gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
2162                                 }
2163                         }
2164                 }
2165         }
2166         else if( pobj->type == ADDR_ITEM_FOLDER ) {
2167                 /* New address */
2168                 ItemFolder *folder = ADAPTER_FOLDER(pobj)->itemFolder;
2169                 ItemPerson *person = addressbook_edit_person( abf, folder, NULL, FALSE );
2170                 if( person ) {
2171                         if (addrbook.treeSelected == addrbook.opened) {
2172                                 gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
2173                         }
2174                 }
2175         }
2176         else if( pobj->type == ADDR_ITEM_GROUP ) {
2177                 /* New address in group */
2178                 ItemGroup *group = ADAPTER_GROUP(pobj)->itemGroup;
2179                 if( addressbook_edit_group( abf, NULL, group ) == NULL ) return;
2180                 if (addrbook.treeSelected == addrbook.opened) {
2181                         /* Change node name in tree. */
2182                         addressbook_change_node_name( addrbook.treeSelected, ADDRITEM_NAME(group) );
2183                         gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
2184                 }
2185         }
2186 }
2187
2188 /*
2189 * Search for specified child group node in address index tree.
2190 * Enter: parent Parent node.
2191 *        group  Group to find.
2192 */
2193 static GtkCTreeNode *addressbook_find_group_node( GtkCTreeNode *parent, ItemGroup *group ) {
2194         GtkCTreeNode *node = NULL;
2195         GtkCTreeRow *currRow;
2196
2197         currRow = GTK_CTREE_ROW( parent );
2198         if( currRow ) {
2199                 node = currRow->children;
2200                 while( node ) {
2201                         AddressObject *obj;
2202
2203                         obj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), node );
2204                         if( obj->type == ADDR_ITEM_GROUP ) {
2205                                 ItemGroup *g = ADAPTER_GROUP(obj)->itemGroup;
2206                                 if( g == group ) return node;
2207                         }
2208                         currRow = GTK_CTREE_ROW(node);
2209                         node = currRow->sibling;
2210                 }
2211         }
2212         return NULL;
2213 }
2214
2215 /*
2216 * Search for specified child folder node in address index tree.
2217 * Enter: parent Parent node.
2218 *        folder Folder to find.
2219 */
2220 static GtkCTreeNode *addressbook_find_folder_node( GtkCTreeNode *parent, ItemFolder *folder ) {
2221         GtkCTreeNode *node = NULL;
2222         GtkCTreeRow *currRow;
2223
2224         currRow = GTK_CTREE_ROW( parent );
2225         if( currRow ) {
2226                 node = currRow->children;
2227                 while( node ) {
2228                         AddressObject *obj;
2229
2230                         obj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), node );
2231                         if( obj->type == ADDR_ITEM_FOLDER ) {
2232                                 ItemFolder *f = ADAPTER_FOLDER(obj)->itemFolder;
2233                                 if( f == folder ) return node;
2234                         }
2235                         currRow = GTK_CTREE_ROW(node);
2236                         node = currRow->sibling;
2237                 }
2238         }
2239         return NULL;
2240 }
2241
2242 static AddressBookFile *addressbook_get_book_file() {
2243         AddressBookFile *abf = NULL;
2244         AddressDataSource *ds = NULL;
2245
2246         ds = addressbook_find_datasource( addrbook.treeSelected );
2247         if( ds == NULL ) return NULL;
2248         if( ds->type == ADDR_IF_BOOK ) abf = ds->rawDataSource;
2249         return abf;
2250 }
2251
2252 static void addressbook_tree_remove_children( GtkCTree *ctree, GtkCTreeNode *parent ) {
2253         GtkCTreeNode *node;
2254         GtkCTreeRow *row;
2255
2256         /* Remove existing folders and groups */
2257         row = GTK_CTREE_ROW( parent );
2258         if( row ) {
2259                 while( (node = row->children) ) {
2260                         gtk_ctree_remove_node( ctree, node );
2261                 }
2262         }
2263 }
2264
2265 static void addressbook_move_nodes_up( GtkCTree *ctree, GtkCTreeNode *node ) {
2266         GtkCTreeNode *parent, *child;
2267         GtkCTreeRow *currRow;
2268         currRow = GTK_CTREE_ROW( node );
2269         if( currRow ) {
2270                 parent = currRow->parent;
2271                 while( (child = currRow->children) ) {
2272                         gtk_ctree_move( ctree, child, parent, node );
2273                 }
2274                 gtk_ctree_sort_node( ctree, parent );
2275         }
2276 }
2277
2278 static void addressbook_edit_address_cb( gpointer data, guint action, GtkWidget *widget ) {
2279         GtkCTree *clist = GTK_CTREE(addrbook.clist);
2280         GtkCTree *ctree;
2281         AddressObject *obj = NULL, *pobj = NULL;
2282         AddressDataSource *ds = NULL;
2283         GtkCTreeNode *node = NULL, *parentNode = NULL;
2284         gchar *name = NULL;
2285         AddressBookFile *abf = NULL;
2286
2287         if( addrbook.listSelected == NULL ) return;
2288         obj = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
2289         g_return_if_fail(obj != NULL);
2290
2291         ctree = GTK_CTREE( addrbook.ctree );
2292         pobj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
2293         node = gtk_ctree_find_by_row_data( ctree, addrbook.treeSelected, obj );
2294
2295         ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
2296         if( ds == NULL ) return;
2297
2298         abf = addressbook_get_book_file();
2299         if( abf == NULL ) return;
2300         if( obj->type == ADDR_ITEM_EMAIL ) {
2301                 ItemEMail *email = ( ItemEMail * ) obj;
2302                 if( email == NULL ) return;
2303                 if( pobj && pobj->type == ADDR_ITEM_GROUP ) {
2304                         /* Edit parent group */
2305                         AdapterGroup *adapter = ADAPTER_GROUP(pobj);
2306                         ItemGroup *itemGrp = adapter->itemGroup;
2307                         if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
2308                         name = ADDRITEM_NAME(itemGrp);
2309                         node = addrbook.treeSelected;
2310                         parentNode = GTK_CTREE_ROW(node)->parent;
2311                 }
2312                 else {
2313                         /* Edit person - email page */
2314                         ItemPerson *person;
2315                         person = ( ItemPerson * ) ADDRITEM_PARENT(email);
2316                         if( addressbook_edit_person( abf, NULL, person, TRUE ) == NULL ) return;
2317                         gtk_ctree_select( ctree, addrbook.opened );
2318                         invalidate_address_completion();
2319                         return;
2320                 }
2321         }
2322         else if( obj->type == ADDR_ITEM_PERSON ) {
2323                 /* Edit person - basic page */
2324                 ItemPerson *person = ( ItemPerson * ) obj;
2325                 if( addressbook_edit_person( abf, NULL, person, FALSE ) == NULL ) return;
2326                 gtk_ctree_select( ctree, addrbook.opened);
2327                 invalidate_address_completion();
2328                 return;
2329         }
2330         else if( obj->type == ADDR_ITEM_GROUP ) {
2331                 ItemGroup *itemGrp = ( ItemGroup * ) obj;
2332                 if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
2333                 parentNode = addrbook.treeSelected;
2334                 node = addressbook_find_group_node( parentNode, itemGrp );
2335                 name = ADDRITEM_NAME(itemGrp);
2336         }
2337         else {
2338                 return;
2339         }
2340
2341         /* Update tree node with node name */
2342         if( node == NULL ) return;
2343         addressbook_change_node_name( node, name );
2344         gtk_ctree_sort_node( ctree, parentNode );
2345         gtk_ctree_select( ctree, addrbook.opened );
2346 }
2347
2348 static void addressbook_delete_address_cb(gpointer data, guint action,
2349                                           GtkWidget *widget)
2350 {
2351         addressbook_del_clicked(NULL, NULL);
2352 }
2353
2354 static void close_cb(gpointer data, guint action, GtkWidget *widget)
2355 {
2356         addressbook_close();
2357 }
2358
2359 static void addressbook_file_save_cb( gpointer data, guint action, GtkWidget *widget ) {
2360         addressbook_export_to_file();
2361 }
2362
2363 static void addressbook_person_expand_node( GtkCTree *ctree, GList *node, gpointer *data ) {
2364         if( node ) {
2365                 ItemPerson *person = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
2366                 if( person ) addritem_person_set_opened( person, TRUE );
2367         }
2368 }
2369
2370 static void addressbook_person_collapse_node( GtkCTree *ctree, GList *node, gpointer *data ) {
2371         if( node ) {
2372                 ItemPerson *person = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
2373                 if( person ) addritem_person_set_opened( person, FALSE );
2374         }
2375 }
2376
2377 static gchar *addressbook_format_item_clist( ItemPerson *person, ItemEMail *email ) {
2378         gchar *str = NULL;
2379         gchar *eMailAlias = ADDRITEM_NAME(email);
2380         if( eMailAlias && *eMailAlias != '\0' ) {
2381                 if( person ) {
2382                         str = g_strdup_printf( "%s - %s", ADDRITEM_NAME(person), eMailAlias );
2383                 }
2384                 else {
2385                         str = g_strdup( eMailAlias );
2386                 }
2387         }
2388         return str;
2389 }
2390
2391 static void addressbook_load_group( GtkCTree *clist, ItemGroup *itemGroup ) {
2392         GList *items = itemGroup->listEMail;
2393         AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_EMAIL );
2394         for( ; items != NULL; items = g_list_next( items ) ) {
2395                 GtkCTreeNode *nodeEMail = NULL;
2396                 gchar *text[N_COLS];
2397                 ItemEMail *email = items->data;
2398                 ItemPerson *person;
2399                 gchar *str = NULL;
2400
2401                 if( ! email ) continue;
2402
2403                 person = ( ItemPerson * ) ADDRITEM_PARENT(email);
2404                 str = addressbook_format_item_clist( person, email );
2405                 if( str ) {
2406                         text[COL_NAME] = str;
2407                 }
2408                 else {
2409                         text[COL_NAME] = ADDRITEM_NAME(person);
2410                 }
2411                 text[COL_ADDRESS] = email->address;
2412                 text[COL_REMARKS] = email->remarks;
2413                 nodeEMail = gtk_ctree_insert_node(
2414                                 clist, NULL, NULL,
2415                                 text, FOLDER_SPACING,
2416                                 atci->iconXpm, atci->maskXpm,
2417                                 atci->iconXpmOpen, atci->maskXpmOpen,
2418                                 FALSE, FALSE );
2419                 gtk_ctree_node_set_row_data( clist, nodeEMail, email );
2420                 g_free( str );
2421                 str = NULL;
2422         }
2423 }
2424
2425 static void addressbook_folder_load_person( GtkCTree *clist, ItemFolder *itemFolder ) {
2426         GList *items;
2427         AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
2428         AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
2429
2430         if( atci == NULL ) return;
2431         if( atciMail == NULL ) return;
2432
2433         /* Load email addresses */
2434         items = addritem_folder_get_person_list( itemFolder );
2435         for( ; items != NULL; items = g_list_next( items ) ) {
2436                 GtkCTreeNode *nodePerson = NULL;
2437                 GtkCTreeNode *nodeEMail = NULL;
2438                 gchar *text[N_COLS];
2439                 gboolean flgFirst = TRUE, haveAddr = FALSE;
2440                 ItemPerson *person;
2441                 GList *node;
2442
2443                 person = ( ItemPerson * ) items->data;
2444                 if( person == NULL ) continue;
2445
2446                 text[COL_NAME] = NULL;
2447                 node = person->listEMail;
2448                 while( node ) {
2449                         ItemEMail *email = node->data;
2450                         gchar *eMailAddr = NULL;
2451                         node = g_list_next( node );
2452
2453                         text[COL_ADDRESS] = email->address;
2454                         text[COL_REMARKS] = email->remarks;
2455                         eMailAddr = ADDRITEM_NAME(email);
2456                         if( eMailAddr && *eMailAddr == '\0' ) eMailAddr = NULL;
2457                         if( flgFirst ) {
2458                                 /* First email belongs with person */
2459                                 gchar *str = addressbook_format_item_clist( person, email );
2460                                 if( str ) {
2461                                         text[COL_NAME] = str;
2462                                 }
2463                                 else {
2464                                         text[COL_NAME] = ADDRITEM_NAME(person);
2465                                 }
2466                                 nodePerson = gtk_ctree_insert_node(
2467                                                 clist, NULL, NULL,
2468                                                 text, FOLDER_SPACING,
2469                                                 atci->iconXpm, atci->maskXpm,
2470                                                 atci->iconXpmOpen, atci->maskXpmOpen,
2471                                                 FALSE, person->isOpened );
2472                                 g_free( str );
2473                                 str = NULL;
2474                                 gtk_ctree_node_set_row_data(clist, nodePerson, person );
2475                         }
2476                         else {
2477                                 /* Subsequent email is a child node of person */
2478                                 text[COL_NAME] = ADDRITEM_NAME(email);
2479                                 nodeEMail = gtk_ctree_insert_node(
2480                                                 clist, nodePerson, NULL,
2481                                                 text, FOLDER_SPACING,
2482                                                 atciMail->iconXpm, atciMail->maskXpm,
2483                                                 atciMail->iconXpmOpen, atciMail->maskXpmOpen,
2484                                                 FALSE, TRUE );
2485                                 gtk_ctree_node_set_row_data(clist, nodeEMail, email );
2486                         }
2487                         flgFirst = FALSE;
2488                         haveAddr = TRUE;
2489                 }
2490                 if( ! haveAddr ) {
2491                         /* Have name without EMail */
2492                         text[COL_NAME] = ADDRITEM_NAME(person);
2493                         text[COL_ADDRESS] = NULL;
2494                         text[COL_REMARKS] = NULL;
2495                         nodePerson = gtk_ctree_insert_node(
2496                                         clist, NULL, NULL,
2497                                         text, FOLDER_SPACING,
2498                                         atci->iconXpm, atci->maskXpm,
2499                                         atci->iconXpmOpen, atci->maskXpmOpen,
2500                                         FALSE, person->isOpened );
2501                         gtk_ctree_node_set_row_data(clist, nodePerson, person );
2502                 }
2503                 gtk_ctree_sort_node(GTK_CTREE(clist), NULL);
2504         }
2505         /* Free up the list */
2506         mgu_clear_list( items );
2507         g_list_free( items );
2508 }
2509
2510 static void addressbook_folder_load_group( GtkCTree *clist, ItemFolder *itemFolder ) {
2511         GList *items;
2512         AddressTypeControlItem *atci =  addrbookctl_lookup( ADDR_ITEM_GROUP );
2513
2514         /* Load any groups */
2515         if( ! atci ) return;
2516         items = addritem_folder_get_group_list( itemFolder );
2517         for( ; items != NULL; items = g_list_next( items ) ) {
2518                 GtkCTreeNode *nodeGroup = NULL;
2519                 gchar *text[N_COLS];
2520                 ItemGroup *group = items->data;
2521                 if( group == NULL ) continue;
2522                 text[COL_NAME] = ADDRITEM_NAME(group);
2523                 text[COL_ADDRESS] = NULL;
2524                 text[COL_REMARKS] = NULL;
2525                 nodeGroup = gtk_ctree_insert_node(clist, NULL, NULL,
2526                                       text, FOLDER_SPACING,
2527                                       atci->iconXpm, atci->maskXpm,
2528                                       atci->iconXpmOpen, atci->maskXpmOpen,
2529                                       FALSE, FALSE);
2530                 gtk_ctree_node_set_row_data(clist, nodeGroup, group );
2531                 gtk_ctree_sort_node(clist, NULL);
2532         }
2533         /* Free up the list */
2534         mgu_clear_list( items );
2535         g_list_free( items );
2536 }
2537
2538 /*
2539  * Load data sources into list.
2540  */
2541 static void addressbook_node_load_datasource( GtkCTree *clist, AddressObject *obj ) {
2542         AdapterInterface *adapter;
2543         AddressInterface *iface;
2544         AddressTypeControlItem *atci = NULL;
2545         GtkCTreeNode *newNode, *node;
2546         GtkCTreeRow *row;
2547         GtkCell *cell = NULL;
2548         gchar *text[N_COLS];
2549
2550         adapter = ADAPTER_INTERFACE(obj);
2551         if( adapter == NULL ) return;
2552         iface = adapter->interface;
2553         atci = adapter->atci;
2554         if( atci == NULL ) return;
2555
2556         /* Create nodes in list copying values for data sources in tree */
2557         row = GTK_CTREE_ROW( adapter->treeNode );
2558         if( row ) {
2559                 node = row->children;
2560                 while( node ) {
2561                         gpointer data;
2562
2563                         data = gtk_ctree_node_get_row_data( clist, node );
2564                         row = GTK_CTREE_ROW( node );
2565                         cell = ( ( GtkCListRow * )row )->cell;
2566                         text[COL_NAME] = cell->u.text;
2567                         text[COL_ADDRESS] = NULL;
2568                         text[COL_REMARKS] = NULL;
2569                         newNode = gtk_ctree_insert_node( clist, NULL, NULL,
2570                                       text, FOLDER_SPACING,
2571                                       atci->iconXpm, atci->maskXpm,
2572                                       atci->iconXpmOpen, atci->maskXpmOpen,
2573                                       FALSE, FALSE);
2574                         /*
2575                         gtk_ctree_node_set_row_data( clist, newNode, data );
2576                         gtk_ctree_node_set_row_data_full( clist, newNode, NULL, NULL );
2577                         */
2578                         node = row->sibling;
2579                 }
2580         }
2581         gtk_ctree_sort_node( clist, NULL );
2582 }
2583
2584 /*
2585 * Search ctree widget callback function.
2586 * Enter: pA Pointer to node.
2587 *        pB Pointer to data item being sought.
2588 * Return: Zero (0) if group found.
2589 */
2590 static int addressbook_treenode_find_group_cb( gconstpointer pA, gconstpointer pB ) {
2591         AddressObject *aoA;
2592
2593         aoA = ( AddressObject * ) pA;
2594         if( aoA->type == ADDR_ITEM_GROUP ) {
2595                 ItemGroup *group, *grp;
2596
2597                 grp = ADAPTER_GROUP(aoA)->itemGroup;
2598                 group = ( ItemGroup * ) pB;
2599                 if( grp == group ) return 0;    /* Found group */
2600         }
2601         return 1;
2602 }
2603
2604 /*
2605 * Search ctree widget callback function.
2606 * Enter: pA Pointer to node.
2607 *        pB Pointer to data item being sought.
2608 * Return: Zero (0) if folder found.
2609 */
2610 static int addressbook_treenode_find_folder_cb( gconstpointer pA, gconstpointer pB ) {
2611         AddressObject *aoA;
2612
2613         aoA = ( AddressObject * ) pA;
2614         if( aoA->type == ADDR_ITEM_FOLDER ) {
2615                 ItemFolder *folder, *fld;
2616
2617                 fld = ADAPTER_FOLDER(aoA)->itemFolder;
2618                 folder = ( ItemFolder * ) pB;
2619                 if( fld == folder ) return 0;   /* Found folder */
2620         }
2621         return 1;
2622 }
2623
2624 /*
2625 * Remove folder and group nodes from tree widget for items contained ("cut")
2626 * in clipboard.
2627 */
2628 static void addressbook_treenode_remove_item( void ) {
2629         GList *node;
2630         AddrSelectItem *cutItem;
2631         AddressCache *cache;
2632         AddrItemObject *aio;
2633         GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
2634         GtkCTreeNode *tn;
2635
2636         node = _clipBoard_->objectList;
2637         while( node ) {
2638                 cutItem = node->data;
2639                 node = g_list_next( node );
2640                 cache = addrindex_get_cache(
2641                         _clipBoard_->addressIndex, cutItem->cacheID );
2642                 if( cache == NULL ) continue;
2643                 aio = addrcache_get_object( cache, cutItem->uid );
2644                 if( aio ) {
2645                         tn = NULL;
2646                         if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
2647                                 ItemFolder *folder;
2648
2649                                 folder = ( ItemFolder * ) aio;
2650                                 tn = gtk_ctree_find_by_row_data_custom(
2651                                         ctree, NULL, folder,
2652                                         addressbook_treenode_find_folder_cb );
2653                         }
2654                         else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
2655                                 ItemGroup *group;
2656
2657                                 group = ( ItemGroup * ) aio;
2658                                 tn = gtk_ctree_find_by_row_data_custom(
2659                                         ctree, NULL, group,
2660                                         addressbook_treenode_find_group_cb );
2661                         }
2662
2663                         if( tn ) {
2664                                 /* Free up adapter and remove node. */
2665                                 gtk_ctree_remove_node( ctree, tn );
2666                         }
2667                 }
2668         }
2669 }
2670
2671 /*
2672 * Find parent datasource for specified tree node.
2673 * Enter: node Node to test.
2674 * Return: Data source, or NULL if not found.
2675 */
2676 static AddressDataSource *addressbook_find_datasource( GtkCTreeNode *node ) {
2677         AddressDataSource *ds = NULL;
2678         AddressObject *ao;
2679
2680         g_return_val_if_fail(addrbook.ctree != NULL, NULL);
2681
2682         while( node ) {
2683                 if( GTK_CTREE_ROW(node)->level < 2 ) return NULL;
2684                 ao = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), node );
2685                 if( ao ) {
2686                         /* printf( "ao->type = %d\n", ao->type ); */
2687                         if( ao->type == ADDR_DATASOURCE ) {
2688                                 AdapterDSource *ads = ADAPTER_DSOURCE(ao);
2689                                 /* printf( "found it\n" ); */
2690                                 ds = ads->dataSource;
2691                                 break;
2692                         }
2693                 }
2694                 node = GTK_CTREE_ROW(node)->parent;
2695         }
2696         return ds;
2697 }
2698
2699 /*
2700 * Load address list widget with children of specified object.
2701 * Enter: obj    Parent object to be loaded.
2702 */
2703 static void addressbook_set_clist( AddressObject *obj ) {
2704         GtkCTree *ctreelist = GTK_CTREE(addrbook.clist);
2705         GtkCList *clist = GTK_CLIST(addrbook.clist);
2706         AddressDataSource *ds = NULL;
2707         AdapterDSource *ads = NULL;
2708
2709         if( obj == NULL ) {
2710                 gtk_clist_clear(clist);
2711                 return;
2712         }
2713
2714         if( obj->type == ADDR_INTERFACE ) {
2715                 /* printf( "set_clist: loading datasource...\n" ); */
2716                 /* addressbook_node_load_datasource( GTK_CTREE(clist), obj ); */
2717                 return;
2718         }
2719
2720         gtk_clist_freeze(clist);
2721         gtk_clist_clear(clist);
2722
2723         if( obj->type == ADDR_DATASOURCE ) {
2724                 ads = ADAPTER_DSOURCE(obj);
2725                 ds = ADAPTER_DSOURCE(obj)->dataSource;
2726                 if( ds ) {
2727                         /* Load root folder */
2728                         ItemFolder *rootFolder = NULL;
2729                         rootFolder = addrindex_ds_get_root_folder( ds );
2730                         addressbook_folder_load_person( ctreelist, addrindex_ds_get_root_folder( ds ) );
2731                         addressbook_folder_load_group( ctreelist, addrindex_ds_get_root_folder( ds ) );
2732                 }
2733         }
2734         else {
2735                 if( obj->type == ADDR_ITEM_GROUP ) {
2736                         /* Load groups */
2737                         ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup;
2738                         addressbook_load_group( ctreelist, itemGroup );
2739                 }
2740                 else if( obj->type == ADDR_ITEM_FOLDER ) {
2741                         /* Load folders */
2742                         ItemFolder *itemFolder = ADAPTER_FOLDER(obj)->itemFolder;
2743                         addressbook_folder_load_person( ctreelist, itemFolder );
2744                         addressbook_folder_load_group( ctreelist, itemFolder );
2745                 }
2746         }
2747         /* gtk_clist_sort(clist); */
2748         gtk_clist_thaw(clist);
2749 }
2750
2751 /*
2752 * Call back function to free adaptor. Call back is setup by function
2753 * gtk_ctree_node_set_row_data_full() when node is populated. This function is
2754 * called when the address book tree widget node is removed by calling
2755 * function gtk_ctree_remove_node().
2756 * Enter: data Tree node's row data.
2757 */
2758 static void addressbook_free_treenode( gpointer data ) {
2759         AddressObject *ao;
2760
2761         ao = ( AddressObject * ) data;
2762         if( ao == NULL ) return;
2763         if( ao->type == ADDR_INTERFACE ) {
2764                 AdapterInterface *ai = ADAPTER_INTERFACE(ao);
2765                 addrbookctl_free_interface( ai );
2766         }
2767         else if( ao->type == ADDR_DATASOURCE ) {
2768                 AdapterDSource *ads = ADAPTER_DSOURCE(ao);
2769                 addrbookctl_free_datasource( ads );
2770         }
2771         else if( ao->type == ADDR_ITEM_FOLDER ) {
2772                 AdapterFolder *af = ADAPTER_FOLDER(ao);
2773                 addrbookctl_free_folder( af );
2774         }
2775         else if( ao->type == ADDR_ITEM_GROUP ) {
2776                 AdapterGroup *ag = ADAPTER_GROUP(ao);
2777                 addrbookctl_free_group( ag );
2778         }
2779 }
2780
2781 /*
2782 * Create new adaptor for specified data source.
2783 */
2784 AdapterDSource *addressbook_create_ds_adapter( AddressDataSource *ds,
2785                                 AddressObjectType otype, gchar *name )
2786 {
2787         AdapterDSource *adapter = g_new0( AdapterDSource, 1 );
2788         ADDRESS_OBJECT(adapter)->type = ADDR_DATASOURCE;
2789         ADDRESS_OBJECT_NAME(adapter) = g_strdup( name );
2790         adapter->dataSource = ds;
2791         adapter->subType = otype;
2792         return adapter;
2793 }
2794
2795 void addressbook_ads_set_name( AdapterDSource *adapter, gchar *value ) {
2796         ADDRESS_OBJECT_NAME(adapter) = mgu_replace_string( ADDRESS_OBJECT_NAME(adapter), value );
2797 }
2798
2799 /*
2800  * Load tree from address index with the initial data.
2801  */
2802 static void addressbook_load_tree( void ) {
2803         GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
2804         GList *nodeIf, *nodeDS;
2805         AdapterInterface *adapter;
2806         AddressInterface *iface;
2807         AddressTypeControlItem *atci;
2808         AddressDataSource *ds;
2809         AdapterDSource *ads;
2810         GtkCTreeNode *node, *newNode;
2811         gchar *name;
2812
2813         nodeIf = _addressInterfaceList_;
2814         while( nodeIf ) {
2815                 adapter = nodeIf->data;
2816                 node = adapter->treeNode;
2817                 iface = adapter->interface;
2818                 atci = adapter->atci;
2819                 if( iface ) {
2820                         if( iface->useInterface ) {
2821                                 /* Load data sources below interface node */
2822                                 nodeDS = iface->listSource;
2823                                 while( nodeDS ) {
2824                                         ds = nodeDS->data;
2825                                         newNode = NULL;
2826                                         name = addrindex_ds_get_name( ds );
2827                                         ads = addressbook_create_ds_adapter( ds, atci->objectType, name );
2828                                         newNode = addressbook_add_object( node, ADDRESS_OBJECT(ads) );
2829                                         nodeDS = g_list_next( nodeDS );
2830                                 }
2831                                 gtk_ctree_expand( ctree, node );
2832                         }
2833                 }
2834                 nodeIf = g_list_next( nodeIf );
2835         }
2836 }
2837
2838 /*
2839  * Convert the old address book to new format.
2840  */
2841 static gboolean addressbook_convert( AddressIndex *addrIndex ) {
2842         gboolean retVal = FALSE;
2843         gboolean errFlag = TRUE;
2844         gchar *msg = NULL;
2845
2846         /* Read old address book, performing conversion */
2847         debug_print( "Reading and converting old address book...\n" );
2848         addrindex_set_file_name( addrIndex, ADDRESSBOOK_OLD_FILE );
2849         addrindex_read_data( addrIndex );
2850         if( addrIndex->retVal == MGU_NO_FILE ) {
2851                 /* We do not have a file - new user */
2852                 debug_print( "New user... create new books...\n" );
2853                 addrindex_create_new_books( addrIndex );
2854                 if( addrIndex->retVal == MGU_SUCCESS ) {
2855                         /* Save index file */
2856                         addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
2857                         addrindex_save_data( addrIndex );
2858                         if( addrIndex->retVal == MGU_SUCCESS ) {
2859                                 retVal = TRUE;
2860                                 errFlag = FALSE;
2861                         }
2862                         else {
2863                                 msg = _( "New user, could not save index file." );
2864                         }
2865                 }
2866                 else {
2867                         msg = _( "New user, could not save address book files." );
2868                 }
2869         }
2870         else {
2871                 /* We have an old file */
2872                 if( addrIndex->wasConverted ) {
2873                         /* Converted successfully - save address index */
2874                         addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
2875                         addrindex_save_data( addrIndex );
2876                         if( addrIndex->retVal == MGU_SUCCESS ) {
2877                                 msg = _( "Old address book converted successfully." );
2878                                 retVal = TRUE;
2879                                 errFlag = FALSE;
2880                         }
2881                         else {
2882                                 msg = _("Old address book converted,\n"
2883                                         "could not save new address index file" );
2884                         }
2885                 }
2886                 else {
2887                         /* File conversion failed - just create new books */
2888                         debug_print( "File conversion failed... just create new books...\n" );
2889                         addrindex_create_new_books( addrIndex );
2890                         if( addrIndex->retVal == MGU_SUCCESS ) {
2891                                 /* Save index */
2892                                 addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
2893                                 addrindex_save_data( addrIndex );
2894                                 if( addrIndex->retVal == MGU_SUCCESS ) {
2895                                         msg = _("Could not convert address book,\n"
2896                                                 "but created empty new address book files." );
2897                                         retVal = TRUE;
2898                                         errFlag = FALSE;
2899                                 }
2900                                 else {
2901                                         msg = _("Could not convert address book,\n"
2902                                                 "could not create new address book files." );
2903                                 }
2904                         }
2905                         else {
2906                                 msg = _("Could not convert address book\n"
2907                                         "and could not create new address book files." );
2908                         }
2909                 }
2910         }
2911         if( errFlag ) {
2912                 debug_print( "Error\n%s\n", msg );
2913                 alertpanel( _( "Addressbook conversion error" ), msg, _( "Close" ), NULL, NULL );
2914         }
2915         else if( msg ) {
2916                 debug_print( "Warning\n%s\n", msg );
2917                 alertpanel( _( "Addressbook conversion" ), msg, _( "Close" ), NULL, NULL );
2918         }
2919
2920         return retVal;
2921 }
2922
2923 void addressbook_read_file( void ) {
2924         AddressIndex *addrIndex = NULL;
2925
2926         debug_print( "Reading address index...\n" );
2927         if( _addressIndex_ ) {
2928                 debug_print( "address book already read!!!\n" );
2929                 return;
2930         }
2931
2932         addrIndex = addrindex_create_index();
2933
2934         /* Use new address book index. */
2935         addrindex_set_file_path( addrIndex, get_rc_dir() );
2936         addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
2937         addrindex_read_data( addrIndex );
2938         if( addrIndex->retVal == MGU_NO_FILE ) {
2939                 /* Conversion required */
2940                 debug_print( "Converting...\n" );
2941                 if( addressbook_convert( addrIndex ) ) {
2942                         _addressIndex_ = addrIndex;
2943                 }
2944         }
2945         else if( addrIndex->retVal == MGU_SUCCESS ) {
2946                 _addressIndex_ = addrIndex;
2947         }
2948         else {
2949                 /* Error reading address book */
2950                 debug_print( "Could not read address index.\n" );
2951                 addrindex_print_index( addrIndex, stdout );
2952                 alertpanel( _( "Addressbook Error" ),
2953                             _( "Could not read address index" ),
2954                             _( "Close" ), NULL, NULL );
2955         }
2956         debug_print( "done.\n" );
2957 }
2958
2959 /*
2960 * Add object into the address index tree widget.
2961 * Enter: node   Parent node.
2962 *        obj    Object to add.
2963 * Return: Node that was added, or NULL if object not added.
2964 */
2965 static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
2966                                             AddressObject *obj)
2967 {
2968         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2969         GtkCTreeNode *added;
2970         AddressObject *pobj;
2971         AddressObjectType otype;
2972         AddressTypeControlItem *atci = NULL;
2973
2974         g_return_val_if_fail(node != NULL, NULL);
2975         g_return_val_if_fail(obj  != NULL, NULL);
2976
2977         pobj = gtk_ctree_node_get_row_data(ctree, node);
2978         g_return_val_if_fail(pobj != NULL, NULL);
2979
2980         /* Determine object type to be displayed */
2981         if( obj->type == ADDR_DATASOURCE ) {
2982                 otype = ADAPTER_DSOURCE(obj)->subType;
2983         }
2984         else {
2985                 otype = obj->type;
2986         }
2987
2988         /* Handle any special conditions. */
2989         added = node;
2990         atci = addrbookctl_lookup( otype );
2991         if( atci ) {
2992                 if( atci->showInTree ) {
2993                         /* Add object to tree */
2994                         gchar **name;
2995                         name = &obj->name;
2996                         added = gtk_ctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
2997                                 atci->iconXpm, atci->maskXpm, atci->iconXpmOpen, atci->maskXpmOpen,
2998                                 atci->treeLeaf, atci->treeExpand );
2999                         gtk_ctree_node_set_row_data_full( ctree, added, obj,
3000                                 addressbook_free_treenode );
3001                 }
3002         }
3003
3004         gtk_ctree_sort_node(ctree, node);
3005
3006         return added;
3007 }
3008
3009 /*
3010 * Add group into the address index tree.
3011 * Enter: node      Parent node.
3012 *        ds        Data source.
3013 *        itemGroup Group to add.
3014 * Return: Inserted node.
3015 */
3016 static GtkCTreeNode *addressbook_node_add_group( GtkCTreeNode *node, AddressDataSource *ds, ItemGroup *itemGroup ) {
3017         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
3018         GtkCTreeNode *newNode;
3019         AdapterGroup *adapter;
3020         AddressTypeControlItem *atci = NULL;
3021         gchar **name;
3022
3023         if( ds == NULL ) return NULL;
3024         if( node == NULL || itemGroup == NULL ) return NULL;
3025
3026         name = &itemGroup->obj.name;
3027
3028         atci = addrbookctl_lookup( ADDR_ITEM_GROUP );
3029
3030         adapter = g_new0( AdapterGroup, 1 );
3031         ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_GROUP;
3032         ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemGroup) );
3033         adapter->itemGroup = itemGroup;
3034
3035         newNode = gtk_ctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
3036                         atci->iconXpm, atci->maskXpm, atci->iconXpm, atci->maskXpm,
3037                         atci->treeLeaf, atci->treeExpand );
3038         gtk_ctree_node_set_row_data_full( ctree, newNode, adapter,
3039                 addressbook_free_treenode );
3040         gtk_ctree_sort_node( ctree, node );
3041         return newNode;
3042 }
3043
3044 /*
3045 * Add folder into the address index tree.
3046 * Enter: node       Parent node.
3047 *        ds         Data source.
3048 *        itemFolder Folder to add.
3049 *        otype      Object type to display.
3050 * Return: Inserted node.
3051 */
3052 static GtkCTreeNode *addressbook_node_add_folder(
3053                 GtkCTreeNode *node, AddressDataSource *ds, ItemFolder *itemFolder, AddressObjectType otype )
3054 {
3055         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
3056         GtkCTreeNode *newNode = NULL;
3057         AdapterFolder *adapter;
3058         AddressTypeControlItem *atci = NULL;
3059         GList *listItems = NULL;
3060         gchar **name;
3061         ItemFolder *rootFolder;
3062
3063         if( ds == NULL ) return NULL;
3064         if( node == NULL || itemFolder == NULL ) return NULL;
3065
3066         /* Determine object type */
3067         atci = addrbookctl_lookup( otype );
3068         if( atci == NULL ) return NULL;
3069
3070         rootFolder = addrindex_ds_get_root_folder( ds );
3071         if( itemFolder == rootFolder ) {
3072                 newNode = node;
3073         }
3074         else {
3075                 name = &itemFolder->obj.name;
3076
3077                 adapter = g_new0( AdapterFolder, 1 );
3078                 ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_FOLDER;
3079                 ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemFolder) );
3080                 adapter->itemFolder = itemFolder;
3081
3082                 newNode = gtk_ctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
3083                                 atci->iconXpm, atci->maskXpm, atci->iconXpm, atci->maskXpm,
3084                                 atci->treeLeaf, atci->treeExpand );
3085                 gtk_ctree_node_set_row_data_full( ctree, newNode, adapter,
3086                         addressbook_free_treenode );
3087         }
3088
3089         listItems = itemFolder->listFolder;
3090         while( listItems ) {
3091                 ItemFolder *item = listItems->data;
3092                 addressbook_node_add_folder( newNode, ds, item, otype );
3093                 listItems = g_list_next( listItems );
3094         }
3095         listItems = itemFolder->listGroup;
3096         while( listItems ) {
3097                 ItemGroup *item = listItems->data;
3098                 addressbook_node_add_group( newNode, ds, item );
3099                 listItems = g_list_next( listItems );
3100         }
3101         gtk_ctree_sort_node( ctree, node );
3102         return newNode;
3103 }
3104
3105 static void addressbook_delete_object(AddressObject *obj) {
3106         AdapterDSource *ads = NULL;
3107         AddressDataSource *ds = NULL;
3108         if (!obj) return;
3109
3110         /* Remove data source. */
3111         /* printf( "Delete obj type : %d\n", obj->type ); */
3112
3113         ads = ADAPTER_DSOURCE(obj);
3114         if( ads == NULL ) return;
3115         ds = ads->dataSource;
3116         if( ds == NULL ) return;
3117
3118         /* Remove data source */
3119         if( addrindex_index_remove_datasource( _addressIndex_, ds ) ) {
3120                 addrindex_free_datasource( ds );
3121         }
3122         /* Free up Adapter object */
3123         g_free( ads );
3124 }
3125
3126 void addressbook_export_to_file( void ) {
3127         if( _addressIndex_ ) {
3128                 /* Save all new address book data */
3129                 debug_print( "Saving address books...\n" );
3130                 addrindex_save_all_books( _addressIndex_ );
3131
3132                 debug_print( "Exporting addressbook to file...\n" );
3133                 addrindex_save_data( _addressIndex_ );
3134                 if( _addressIndex_->retVal != MGU_SUCCESS ) {
3135                         addrindex_print_index( _addressIndex_, stdout );
3136                 }
3137
3138                 /* Notify address completion of new data */
3139                 invalidate_address_completion();
3140         }
3141 }
3142
3143 static void key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
3144 {
3145         if (event && event->keyval == GDK_Escape)
3146                 addressbook_close();
3147 }
3148
3149 /*
3150 * Comparison using cell contents (text in first column). Used for sort
3151 * address index widget.
3152 */
3153 static gint addressbook_treenode_compare_func(
3154         GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
3155 {
3156         GtkCell *cell1 = ((GtkCListRow *)ptr1)->cell;
3157         GtkCell *cell2 = ((GtkCListRow *)ptr2)->cell;
3158         gchar *name1 = NULL, *name2 = NULL;
3159         if( cell1 ) name1 = cell1->u.text;
3160         if( cell2 ) name2 = cell2->u.text;
3161         if( ! name1 ) return ( name2 != NULL );
3162         if( ! name2 ) return -1;
3163         return strcasecmp( name1, name2 );
3164 }
3165
3166 /*
3167 * Comparison using object names and types.
3168 */
3169 static gint addressbook_list_compare_func(
3170         GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
3171 {
3172         AddrItemObject *aio1 = ((GtkCListRow *)ptr1)->data;
3173         AddrItemObject *aio2 = ((GtkCListRow *)ptr2)->data;
3174         gchar *name1 = NULL, *name2 = NULL;
3175
3176         /* Order by cell contents */
3177         name1 = ADDRITEM_NAME( aio1 );
3178         name2 = ADDRITEM_NAME( aio2 );
3179
3180         if( aio1->type == aio2->type ) {
3181                 /* Order by name */
3182                 if( ! name1 ) return ( name2 != NULL );
3183                 if( ! name2 ) return -1;
3184                 return strcasecmp( name1, name2 );
3185         }
3186         else {
3187                 /* Order groups before person */
3188                 if( aio1->type == ITEMTYPE_GROUP ) {
3189                         return -1;
3190                 }
3191                 else if( aio2->type == ITEMTYPE_GROUP ) {
3192                         return 1;
3193                 }
3194                 return 0;
3195         }
3196 }
3197
3198 static void addressbook_new_book_cb( gpointer data, guint action, GtkWidget *widget ) {
3199         AdapterDSource *ads;
3200         AdapterInterface *adapter;
3201         GtkCTreeNode *newNode;
3202
3203         adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
3204         if( adapter == NULL ) return;
3205         ads = addressbook_edit_book( _addressIndex_, NULL );
3206         if( ads ) {
3207                 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
3208                 if( newNode ) {
3209                         gtk_ctree_select( GTK_CTREE(addrbook.ctree), newNode );
3210                         addrbook.treeSelected = newNode;
3211                 }
3212         }
3213 }
3214
3215 static void addressbook_new_vcard_cb( gpointer data, guint action, GtkWidget *widget ) {
3216         AdapterDSource *ads;
3217         AdapterInterface *adapter;
3218         GtkCTreeNode *newNode;
3219
3220         adapter = addrbookctl_find_interface( ADDR_IF_VCARD );
3221         if( adapter == NULL ) return;
3222         ads = addressbook_edit_vcard( _addressIndex_, NULL );
3223         if( ads ) {
3224                 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
3225                 if( newNode ) {
3226                         gtk_ctree_select( GTK_CTREE(addrbook.ctree), newNode );
3227                         addrbook.treeSelected = newNode;
3228                 }
3229         }
3230 }
3231
3232 #ifdef USE_JPILOT
3233 static void addressbook_new_jpilot_cb( gpointer data, guint action, GtkWidget *widget ) {
3234         AdapterDSource *ads;
3235         AdapterInterface *adapter;
3236         AddressInterface *iface;
3237         GtkCTreeNode *newNode;
3238
3239         adapter = addrbookctl_find_interface( ADDR_IF_JPILOT );
3240         if( adapter == NULL ) return;
3241         iface = adapter->interface;
3242         if( ! iface->haveLibrary ) return;
3243         ads = addressbook_edit_jpilot( _addressIndex_, NULL );
3244         if( ads ) {
3245                 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
3246                 if( newNode ) {
3247                         gtk_ctree_select( GTK_CTREE(addrbook.ctree), newNode );
3248                         addrbook.treeSelected = newNode;
3249                 }
3250         }
3251 }
3252 #endif
3253
3254 #ifdef USE_LDAP
3255 static void addressbook_new_ldap_cb( gpointer data, guint action, GtkWidget *widget ) {
3256         AdapterDSource *ads;
3257         AdapterInterface *adapter;
3258         AddressInterface *iface;
3259         GtkCTreeNode *newNode;
3260
3261         adapter = addrbookctl_find_interface( ADDR_IF_LDAP );
3262         if( adapter == NULL ) return;
3263         iface = adapter->interface;
3264         if( ! iface->haveLibrary ) return;
3265         ads = addressbook_edit_ldap( _addressIndex_, NULL );
3266         if( ads ) {
3267                 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
3268                 if( newNode ) {
3269                         gtk_ctree_select( GTK_CTREE(addrbook.ctree), newNode );
3270                         addrbook.treeSelected = newNode;
3271                 }
3272         }
3273 }
3274
3275 static void addressbook_ldap_show_message( SyldapServer *svr ) {
3276         gchar *name;
3277         *addressbook_msgbuf = '\0';
3278         if( svr ) {
3279                 name = syldap_get_name( svr );
3280                 if( svr->busyFlag ) {
3281                         sprintf( addressbook_msgbuf, "%s: %s", name, ADDRESSBOOK_LDAP_BUSYMSG );
3282                 }
3283                 else {
3284                         if( svr->retVal == MGU_SUCCESS ) {
3285                                 sprintf( addressbook_msgbuf, "%s", name );
3286                         }
3287                         else {
3288                                 sprintf( addressbook_msgbuf, "%s: %s", name, mgu_error2string( svr->retVal ) );
3289                         }
3290                 }
3291         }
3292         addressbook_status_show( addressbook_msgbuf );
3293 }
3294
3295 static void ldapsearch_callback( SyldapServer *sls ) {
3296         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
3297         AddressObject *obj;
3298         AdapterDSource *ads = NULL;
3299         AddressDataSource *ds = NULL;
3300         AddressInterface *iface = NULL;
3301
3302         if( sls == NULL ) return;
3303         if( ! addrbook.treeSelected ) return;
3304         if( GTK_CTREE_ROW( addrbook.treeSelected )->level == 1 ) return;
3305
3306         obj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
3307         if( obj == NULL ) return;
3308         if( obj->type == ADDR_DATASOURCE ) {
3309                 ads = ADAPTER_DSOURCE(obj);
3310                 if( ads->subType == ADDR_LDAP ) {
3311                         SyldapServer *server;
3312
3313                         ds = ads->dataSource;
3314                         if( ds == NULL ) return;
3315                         iface = ds->interface;
3316                         if( ! iface->haveLibrary ) return;
3317                         server = ds->rawDataSource;
3318                         if( server == sls ) {
3319                                 /* Read from cache */
3320                                 gtk_widget_show_all(addrbook.window);
3321                                 addressbook_set_clist( obj );
3322                                 addressbook_ldap_show_message( sls );
3323                                 gtk_widget_show_all(addrbook.window);
3324                                 gtk_entry_set_text( GTK_ENTRY(addrbook.entry), "" );
3325                         }
3326                 }
3327         }
3328 }
3329 #endif
3330
3331 /*
3332  * Lookup button handler.
3333  */
3334 static void addressbook_lup_clicked( GtkButton *button, gpointer data ) {
3335         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
3336         AddressObject *obj;
3337 #ifdef USE_LDAP
3338         AdapterDSource *ads = NULL;
3339         AddressDataSource *ds = NULL;
3340         AddressInterface *iface = NULL;
3341 #endif
3342         gchar *sLookup;
3343
3344         sLookup = gtk_editable_get_chars( GTK_EDITABLE(addrbook.entry), 0, -1 );
3345         g_strchomp( sLookup );
3346
3347         if( ! addrbook.treeSelected ) return;
3348         if( GTK_CTREE_ROW( addrbook.treeSelected )->level == 1 ) return;
3349
3350         obj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
3351         if( obj == NULL ) return;
3352
3353 #ifdef USE_LDAP
3354         if( obj->type == ADDR_DATASOURCE ) {
3355                 ads = ADAPTER_DSOURCE(obj);
3356                 if( ads->subType == ADDR_LDAP ) {
3357                         SyldapServer *server;
3358
3359                         ds = ads->dataSource;
3360                         if( ds == NULL ) return;
3361                         iface = ds->interface;
3362                         if( ! iface->haveLibrary ) return;
3363                         server = ds->rawDataSource;
3364                         if( server ) {
3365                                 syldap_cancel_read( server );
3366                                 if( *sLookup == '\0' || strlen( sLookup ) < 1 ) return;
3367                                 syldap_set_search_value( server, sLookup );
3368                                 syldap_set_callback( server, ldapsearch_callback );
3369                                 syldap_read_data_th( server );
3370                                 addressbook_ldap_show_message( server );
3371                         }
3372                 }
3373         }
3374 #endif
3375
3376 }
3377
3378 /* **********************************************************************
3379 * Build lookup tables.
3380 * ***********************************************************************
3381 */
3382
3383 /*
3384  * Remap object types.
3385  * Enter:  abType AddressObjectType (used in tree node).
3386  * Return: ItemObjectType (used in address cache data).
3387  */
3388 ItemObjectType addressbook_type2item( AddressObjectType abType ) {
3389         ItemObjectType ioType;
3390
3391         ioType = ITEMTYPE_NONE;
3392         switch( abType ) {
3393                 case ADDR_ITEM_PERSON: ioType = ITEMTYPE_PERSON;     break;
3394                 case ADDR_ITEM_EMAIL:  ioType = ITEMTYPE_EMAIL;      break;
3395                 case ADDR_ITEM_FOLDER: ioType = ITEMTYPE_FOLDER;     break;
3396                 case ADDR_ITEM_GROUP:  ioType = ITEMTYPE_GROUP;      break;
3397                 case ADDR_DATASOURCE:  ioType = ITEMTYPE_DATASOURCE; break;
3398         }
3399         return ioType;
3400 }
3401
3402 /*
3403 * Build table that controls the rendering of object types.
3404 */
3405 void addrbookctl_build_map( GtkWidget *window ) {
3406         AddressTypeControlItem *atci;
3407
3408         /* Build icons */
3409         stock_pixmap_gdk(window, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
3410         stock_pixmap_gdk(window, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
3411         stock_pixmap_gdk(window, STOCK_PIXMAP_GROUP, &groupxpm, &groupxpmmask);
3412         stock_pixmap_gdk(window, STOCK_PIXMAP_VCARD, &vcardxpm, &vcardxpmmask);
3413         stock_pixmap_gdk(window, STOCK_PIXMAP_BOOK, &bookxpm, &bookxpmmask);
3414         stock_pixmap_gdk(window, STOCK_PIXMAP_ADDRESS, &addressxpm, &addressxpmmask);
3415         stock_pixmap_gdk(window, STOCK_PIXMAP_JPILOT, &jpilotxpm, &jpilotxpmmask);
3416         stock_pixmap_gdk(window, STOCK_PIXMAP_CATEGORY, &categoryxpm, &categoryxpmmask);
3417         stock_pixmap_gdk(window, STOCK_PIXMAP_LDAP, &ldapxpm, &ldapxpmmask);
3418
3419         _addressBookTypeHash_ = g_hash_table_new( g_int_hash, g_int_equal );
3420         _addressBookTypeList_ = NULL;
3421
3422         /* Interface */
3423         atci = g_new0( AddressTypeControlItem, 1 );
3424         atci->objectType = ADDR_INTERFACE;
3425         atci->interfaceType = ADDR_IF_NONE;
3426         atci->showInTree = TRUE;
3427         atci->treeExpand = TRUE;
3428         atci->treeLeaf = FALSE;
3429         atci->displayName = _( "Interface" );
3430         atci->iconXpm = folderxpm;
3431         atci->maskXpm = folderxpmmask;
3432         atci->iconXpmOpen = folderopenxpm;
3433         atci->maskXpmOpen = folderopenxpmmask;
3434         atci->menuCommand = NULL;
3435         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3436         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3437
3438         /* Address book */
3439         atci = g_new0( AddressTypeControlItem, 1 );
3440         atci->objectType = ADDR_BOOK;
3441         atci->interfaceType = ADDR_IF_BOOK;
3442         atci->showInTree = TRUE;
3443         atci->treeExpand = TRUE;
3444         atci->treeLeaf = FALSE;
3445         atci->displayName = _( "Address Book" );
3446         atci->iconXpm = bookxpm;
3447         atci->maskXpm = bookxpmmask;
3448         atci->iconXpmOpen = bookxpm;
3449         atci->maskXpmOpen = bookxpmmask;
3450         atci->menuCommand = "/File/New Book";
3451         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3452         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3453
3454         /* Item person */
3455         atci = g_new0( AddressTypeControlItem, 1 );
3456         atci->objectType = ADDR_ITEM_PERSON;
3457         atci->interfaceType = ADDR_IF_NONE;
3458         atci->showInTree = FALSE;
3459         atci->treeExpand = FALSE;
3460         atci->treeLeaf = FALSE;
3461         atci->displayName = _( "Person" );
3462         atci->iconXpm = NULL;
3463         atci->maskXpm = NULL;
3464         atci->iconXpmOpen = NULL;
3465         atci->maskXpmOpen = NULL;
3466         atci->menuCommand = NULL;
3467         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3468         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3469
3470         /* Item email */
3471         atci = g_new0( AddressTypeControlItem, 1 );
3472         atci->objectType = ADDR_ITEM_EMAIL;
3473         atci->interfaceType = ADDR_IF_NONE;
3474         atci->showInTree = FALSE;
3475         atci->treeExpand = FALSE;
3476         atci->treeLeaf = TRUE;
3477         atci->displayName = _( "EMail Address" );
3478         atci->iconXpm = addressxpm;
3479         atci->maskXpm = addressxpmmask;
3480         atci->iconXpmOpen = addressxpm;
3481         atci->maskXpmOpen = addressxpmmask;
3482         atci->menuCommand = NULL;
3483         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3484         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3485
3486         /* Item group */
3487         atci = g_new0( AddressTypeControlItem, 1 );
3488         atci->objectType = ADDR_ITEM_GROUP;
3489         atci->interfaceType = ADDR_IF_BOOK;
3490         atci->showInTree = TRUE;
3491         atci->treeExpand = FALSE;
3492         atci->treeLeaf = FALSE;
3493         atci->displayName = _( "Group" );
3494         atci->iconXpm = groupxpm;
3495         atci->maskXpm = groupxpmmask;
3496         atci->iconXpmOpen = groupxpm;
3497         atci->maskXpmOpen = groupxpmmask;
3498         atci->menuCommand = NULL;
3499         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3500         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3501
3502         /* Item folder */
3503         atci = g_new0( AddressTypeControlItem, 1 );
3504         atci->objectType = ADDR_ITEM_FOLDER;
3505         atci->interfaceType = ADDR_IF_BOOK;
3506         atci->showInTree = TRUE;
3507         atci->treeExpand = FALSE;
3508         atci->treeLeaf = FALSE;
3509         atci->displayName = _( "Folder" );
3510         atci->iconXpm = folderxpm;
3511         atci->maskXpm = folderxpmmask;
3512         atci->iconXpmOpen = folderopenxpm;
3513         atci->maskXpmOpen = folderopenxpmmask;
3514         atci->menuCommand = NULL;
3515         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3516         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3517
3518         /* vCard */
3519         atci = g_new0( AddressTypeControlItem, 1 );
3520         atci->objectType = ADDR_VCARD;
3521         atci->interfaceType = ADDR_IF_VCARD;
3522         atci->showInTree = TRUE;
3523         atci->treeExpand = TRUE;
3524         atci->treeLeaf = TRUE;
3525         atci->displayName = _( "vCard" );
3526         atci->iconXpm = vcardxpm;
3527         atci->maskXpm = vcardxpmmask;
3528         atci->iconXpmOpen = vcardxpm;
3529         atci->maskXpmOpen = vcardxpmmask;
3530         atci->menuCommand = "/File/New vCard";
3531         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3532         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3533
3534         /* J-Pilot */
3535         atci = g_new0( AddressTypeControlItem, 1 );
3536         atci->objectType = ADDR_JPILOT;
3537         atci->interfaceType = ADDR_IF_JPILOT;
3538         atci->showInTree = TRUE;
3539         atci->treeExpand = TRUE;
3540         atci->treeLeaf = FALSE;
3541         atci->displayName = _( "JPilot" );
3542         atci->iconXpm = jpilotxpm;
3543         atci->maskXpm = jpilotxpmmask;
3544         atci->iconXpmOpen = jpilotxpm;
3545         atci->maskXpmOpen = jpilotxpmmask;
3546         atci->menuCommand = "/File/New JPilot";
3547         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3548         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3549
3550         /* Category */
3551         atci = g_new0( AddressTypeControlItem, 1 );
3552         atci->objectType = ADDR_CATEGORY;
3553         atci->interfaceType = ADDR_IF_JPILOT;
3554         atci->showInTree = TRUE;
3555         atci->treeExpand = TRUE;
3556         atci->treeLeaf = TRUE;
3557         atci->displayName = _( "JPilot" );
3558         atci->iconXpm = categoryxpm;
3559         atci->maskXpm = categoryxpmmask;
3560         atci->iconXpmOpen = categoryxpm;
3561         atci->maskXpmOpen = categoryxpmmask;
3562         atci->menuCommand = NULL;
3563         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3564         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3565
3566         /* LDAP Server */
3567         atci = g_new0( AddressTypeControlItem, 1 );
3568         atci->objectType = ADDR_LDAP;
3569         atci->interfaceType = ADDR_IF_LDAP;
3570         atci->showInTree = TRUE;
3571         atci->treeExpand = TRUE;
3572         atci->treeLeaf = TRUE;
3573         atci->displayName = _( "LDAP Server" );
3574         atci->iconXpm = ldapxpm;
3575         atci->maskXpm = ldapxpmmask;
3576         atci->iconXpmOpen = ldapxpm;
3577         atci->maskXpmOpen = ldapxpmmask;
3578         atci->menuCommand = "/File/New Server";
3579         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
3580         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
3581
3582 }
3583
3584 /*
3585 * Search for specified object type.
3586 */
3587 AddressTypeControlItem *addrbookctl_lookup( gint ot ) {
3588         gint objType = ot;
3589         return ( AddressTypeControlItem * ) g_hash_table_lookup( _addressBookTypeHash_, &objType );
3590 }
3591
3592 /*
3593 * Search for specified interface type.
3594 */
3595 AddressTypeControlItem *addrbookctl_lookup_iface( AddressIfType ifType ) {
3596         GList *node = _addressBookTypeList_;
3597         while( node ) {
3598                 AddressTypeControlItem *atci = node->data;
3599                 if( atci->interfaceType == ifType ) return atci;
3600                 node = g_list_next( node );
3601         }
3602         return NULL;
3603 }
3604
3605 static void addrbookctl_free_address( AddressObject *obj ) {
3606         g_free( obj->name );
3607         obj->type = ADDR_NONE;
3608         obj->name = NULL;
3609 }
3610
3611 static void addrbookctl_free_interface( AdapterInterface *adapter ) {
3612         addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
3613         adapter->interface = NULL;
3614         adapter->interfaceType = ADDR_IF_NONE;
3615         adapter->atci = NULL;
3616         adapter->enabled = FALSE;
3617         adapter->haveLibrary = FALSE;
3618         adapter->treeNode = NULL;
3619         g_free( adapter );
3620 }
3621
3622 static void addrbookctl_free_datasource( AdapterDSource *adapter ) {
3623         addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
3624         adapter->dataSource = NULL;
3625         adapter->subType = ADDR_NONE;
3626         g_free( adapter );
3627 }
3628
3629 static void addrbookctl_free_folder( AdapterFolder *adapter ) {
3630         addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
3631         adapter->itemFolder = NULL;
3632         g_free( adapter );
3633 }
3634
3635 static void addrbookctl_free_group( AdapterGroup *adapter ) {
3636         addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
3637         adapter->itemGroup = NULL;
3638         g_free( adapter );
3639 }
3640
3641 /*
3642  * Build GUI interface list.
3643  */
3644 void addrbookctl_build_iflist() {
3645         AddressTypeControlItem *atci;
3646         AdapterInterface *adapter;
3647         GList *list = NULL;
3648
3649         if( _addressIndex_ == NULL ) {
3650                 _addressIndex_ = addrindex_create_index();
3651                 if( _clipBoard_ == NULL ) {
3652                         _clipBoard_ = addrclip_create();
3653                 }
3654                 addrclip_set_index( _clipBoard_, _addressIndex_ );
3655         }
3656         _addressInterfaceList_ = NULL;
3657         list = addrindex_get_interface_list( _addressIndex_ );
3658         while( list ) {
3659                 AddressInterface *interface = list->data;
3660                 atci = addrbookctl_lookup_iface( interface->type );
3661                 if( atci ) {
3662                         adapter = g_new0( AdapterInterface, 1 );
3663                         adapter->interfaceType = interface->type;
3664                         adapter->atci = atci;
3665                         adapter->interface = interface;
3666                         adapter->treeNode = NULL;
3667                         adapter->enabled = TRUE;
3668                         adapter->haveLibrary = interface->haveLibrary;
3669                         ADDRESS_OBJECT(adapter)->type = ADDR_INTERFACE;
3670                         ADDRESS_OBJECT_NAME(adapter) = g_strdup( atci->displayName );
3671                         _addressInterfaceList_ = g_list_append( _addressInterfaceList_, adapter );
3672                 }
3673                 list = g_list_next( list );
3674         }
3675 }
3676
3677 void addrbookctl_free_selection( GList *list ) {
3678         GList *node = list;
3679         while( node ) {
3680                 AdapterInterface *adapter = node->data;
3681                 adapter = NULL;
3682                 node = g_list_next( node );
3683         }
3684         g_list_free( list );
3685 }
3686
3687 /*
3688 * Find GUI interface type specified interface type.
3689 * Return: Interface item, or NULL if not found.
3690 */
3691 AdapterInterface *addrbookctl_find_interface( AddressIfType ifType ) {
3692         GList *node = _addressInterfaceList_;
3693         while( node ) {
3694                 AdapterInterface *adapter = node->data;
3695                 if( adapter->interfaceType == ifType ) return adapter;
3696                 node = g_list_next( node );
3697         }
3698         return NULL;
3699 }
3700
3701 /*
3702 * Build interface list selection.
3703 */
3704 void addrbookctl_build_ifselect() {
3705         GList *newList = NULL;
3706         gchar *selectStr;
3707         gchar **splitStr;
3708         gint ifType;
3709         gint i;
3710         gchar *endptr = NULL;
3711         gboolean enabled;
3712         AdapterInterface *adapter;
3713         /* GList *node; */
3714
3715         selectStr = g_strdup( ADDRESSBOOK_IFACE_SELECTION );
3716
3717         /* Parse string */
3718         splitStr = g_strsplit( selectStr, ",", -1 );
3719         for( i = 0; i < ADDRESSBOOK_MAX_IFACE; i++ ) {
3720                 if( splitStr[i] ) {
3721                         /* printf( "%d : %s\n", i, splitStr[i] ); */
3722                         ifType = strtol( splitStr[i], &endptr, 10 );
3723                         enabled = TRUE;
3724                         if( *endptr ) {
3725                                 if( strcmp( endptr, "/n" ) == 0 ) {
3726                                         enabled = FALSE;
3727                                 }
3728                         }
3729                         /* printf( "\t%d : %s\n", ifType, enabled ? "yes" : "no" ); */
3730                         adapter = addrbookctl_find_interface( ifType );
3731                         if( adapter ) {
3732                                 newList = g_list_append( newList, adapter );
3733                         }
3734                 }
3735                 else {
3736                         break;
3737                 }
3738         }
3739         /* printf( "i=%d\n", i ); */
3740         g_strfreev( splitStr );
3741         g_free( selectStr );
3742
3743         /* Replace existing list */
3744         mgu_clear_list( _addressIFaceSelection_ );
3745         g_list_free( _addressIFaceSelection_ );
3746         _addressIFaceSelection_ = newList;
3747         newList = NULL;
3748
3749 }
3750
3751 /* **********************************************************************
3752 * Add sender to address book.
3753 * ***********************************************************************
3754 */
3755
3756 /*
3757  * This function is used by the Add sender to address book function.
3758  */
3759 gboolean addressbook_add_contact( const gchar *name, const gchar *address, const gchar *remarks ) {
3760         debug_print( "addressbook_add_contact: name/address: %s - %s\n", name, address );
3761         if( addressadd_selection( _addressIndex_, name, address, remarks ) ) {
3762                 debug_print( "addressbook_add_contact - added\n" );
3763                 addressbook_refresh();
3764         }
3765         return TRUE;
3766 }
3767
3768 /* **********************************************************************
3769 * Address completion support.
3770 * ***********************************************************************
3771 */
3772
3773 /*
3774 * This function is used by the address completion function to load
3775 * addresses.
3776 * Enter: callBackFunc Function to be called when an address is
3777 *                     to be loaded.
3778 * Return: TRUE if data loaded, FALSE if address index not loaded.
3779 */
3780 gboolean addressbook_load_completion( gint (*callBackFunc) ( const gchar *, const gchar *, const gchar * ) ) {
3781         AddressDataSource *ds;
3782         GList *nodeIf, *nodeDS;
3783         GList *listP, *nodeP;
3784         GList *nodeM;
3785         gchar *sName, *sAddress, *sAlias, *sFriendly;
3786
3787         debug_print( "addressbook_load_completion\n" );
3788
3789         if( _addressIndex_ == NULL ) return FALSE;
3790
3791         nodeIf = addrindex_get_interface_list( _addressIndex_ );
3792         while( nodeIf ) {
3793                 AddressInterface *interface = nodeIf->data;
3794                 nodeDS = interface->listSource;
3795                 while( nodeDS ) {
3796                         ds = nodeDS->data;
3797
3798                         /* Read address book */
3799                         if( addrindex_ds_get_modify_flag( ds ) ) {
3800                                 addrindex_ds_read_data( ds );
3801                         }
3802
3803                         if( ! addrindex_ds_get_read_flag( ds ) ) {
3804                                 addrindex_ds_read_data( ds );
3805                         }
3806
3807                         /* Get all persons */
3808                         listP = addrindex_ds_get_all_persons( ds );
3809                         nodeP = listP;
3810                         while( nodeP ) {
3811                                 ItemPerson *person = nodeP->data;
3812                                 nodeM = person->listEMail;
3813
3814                                 /* Figure out name to use */
3815                                 sName = person->nickName;
3816                                 if( sName == NULL || *sName == '\0' ) {
3817                                         sName = ADDRITEM_NAME(person);
3818                                 }
3819
3820                                 /* Process each E-Mail address */
3821                                 while( nodeM ) {
3822                                         ItemEMail *email = nodeM->data;
3823                                         /* Have mail */
3824                                         sFriendly = sName;
3825                                         sAddress = email->address;
3826                                         if( sAddress || *sAddress != '\0' ) {
3827                                                 sAlias = ADDRITEM_NAME(email);
3828                                                 if( sAlias && *sAlias != '\0' ) {
3829                                                         sFriendly = sAlias;
3830                                                 }
3831                                                 ( callBackFunc ) ( sFriendly, sAddress, sName );
3832                                         }
3833
3834                                         nodeM = g_list_next( nodeM );
3835                                 }
3836                                 nodeP = g_list_next( nodeP );
3837                         }
3838                         /* Free up the list */
3839                         g_list_free( listP );
3840
3841                         nodeDS = g_list_next( nodeDS );
3842                 }
3843                 nodeIf = g_list_next( nodeIf );
3844         }
3845         debug_print( "addressbook_load_completion... done\n" );
3846
3847         return TRUE;
3848 }
3849
3850 /* **********************************************************************
3851 * Address Import.
3852 * ***********************************************************************
3853 */
3854
3855 /*
3856 * Import LDIF file.
3857 */
3858 static void addressbook_import_ldif_cb() {
3859         AddressDataSource *ds = NULL;
3860         AdapterDSource *ads = NULL;
3861         AddressBookFile *abf = NULL;
3862         AdapterInterface *adapter;
3863         GtkCTreeNode *newNode;
3864
3865         adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
3866         if( adapter ) {
3867                 if( adapter->treeNode ) {
3868                         abf = addressbook_imp_ldif( _addressIndex_ );
3869                         if( abf ) {
3870                                 ds = addrindex_index_add_datasource(
3871                                         _addressIndex_, ADDR_IF_BOOK, abf );
3872                                 ads = addressbook_create_ds_adapter(
3873                                         ds, ADDR_BOOK, NULL );
3874                                 addressbook_ads_set_name(
3875                                         ads, addrbook_get_name( abf ) );
3876                                 newNode = addressbook_add_object(
3877                                         adapter->treeNode,
3878                                         ADDRESS_OBJECT(ads) );
3879                                 if( newNode ) {
3880                                         gtk_ctree_select(
3881                                                 GTK_CTREE(addrbook.ctree),
3882                                                 newNode );
3883                                         addrbook.treeSelected = newNode;
3884                                 }
3885
3886                                 /* Notify address completion */
3887                                 invalidate_address_completion();
3888                         }
3889                 }
3890         }
3891 }
3892
3893 /*
3894 * Import MUTT file.
3895 */
3896 static void addressbook_import_mutt_cb() {
3897         AddressDataSource *ds = NULL;
3898         AdapterDSource *ads = NULL;
3899         AddressBookFile *abf = NULL;
3900         AdapterInterface *adapter;
3901         GtkCTreeNode *newNode;
3902
3903         adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
3904         if( adapter ) {
3905                 if( adapter->treeNode ) {
3906                         abf = addressbook_imp_mutt( _addressIndex_ );
3907                         if( abf ) {
3908                                 ds = addrindex_index_add_datasource(
3909                                         _addressIndex_, ADDR_IF_BOOK, abf );
3910                                 ads = addressbook_create_ds_adapter(
3911                                         ds, ADDR_BOOK, NULL );
3912                                 addressbook_ads_set_name(
3913                                         ads, addrbook_get_name( abf ) );
3914                                 newNode = addressbook_add_object(
3915                                         adapter->treeNode,
3916                                         ADDRESS_OBJECT(ads) );
3917                                 if( newNode ) {
3918                                         gtk_ctree_select(
3919                                                 GTK_CTREE(addrbook.ctree),
3920                                                 newNode );
3921                                         addrbook.treeSelected = newNode;
3922                                 }
3923
3924                                 /* Notify address completion */
3925                                 invalidate_address_completion();
3926                         }
3927                 }
3928         }
3929 }
3930
3931 /*
3932 * Import Pine file.
3933 */
3934 static void addressbook_import_pine_cb() {
3935         AddressDataSource *ds = NULL;
3936         AdapterDSource *ads = NULL;
3937         AddressBookFile *abf = NULL;
3938         AdapterInterface *adapter;
3939         GtkCTreeNode *newNode;
3940
3941         adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
3942         if( adapter ) {
3943                 if( adapter->treeNode ) {
3944                         abf = addressbook_imp_pine( _addressIndex_ );
3945                         if( abf ) {
3946                                 ds = addrindex_index_add_datasource(
3947                                         _addressIndex_, ADDR_IF_BOOK, abf );
3948                                 ads = addressbook_create_ds_adapter(
3949                                         ds, ADDR_BOOK, NULL );
3950                                 addressbook_ads_set_name(
3951                                         ads, addrbook_get_name( abf ) );
3952                                 newNode = addressbook_add_object(
3953                                         adapter->treeNode,
3954                                         ADDRESS_OBJECT(ads) );
3955                                 if( newNode ) {
3956                                         gtk_ctree_select(
3957                                                 GTK_CTREE(addrbook.ctree),
3958                                                 newNode );
3959                                         addrbook.treeSelected = newNode;
3960                                 }
3961
3962                                 /* Notify address completion */
3963                                 invalidate_address_completion();
3964                         }
3965                 }
3966         }
3967 }
3968
3969 /*
3970  * Harvest addresses.
3971  * Enter: folderItem Folder to import.
3972  *        sourceInd  Source indicator: FALSE - Folder, TRUE - Messages.
3973  *        msgList    List of message numbers, or NULL to process folder.
3974  */
3975 void addressbook_harvest(
3976         FolderItem *folderItem, gboolean sourceInd, GList *msgList )
3977 {
3978         AddressDataSource *ds = NULL;
3979         AdapterDSource *ads = NULL;
3980         AddressBookFile *abf = NULL;
3981         AdapterInterface *adapter;
3982         GtkCTreeNode *newNode;
3983
3984         abf = addrgather_dlg_execute(
3985                 folderItem, _addressIndex_, sourceInd, msgList );
3986         if( abf ) {
3987                 ds = addrindex_index_add_datasource(
3988                         _addressIndex_, ADDR_IF_BOOK, abf );
3989
3990                 adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
3991                 if( adapter ) {
3992                         if( adapter->treeNode ) {
3993                                 ads = addressbook_create_ds_adapter(
3994                                         ds, ADDR_BOOK, addrbook_get_name( abf ) );
3995                                 newNode = addressbook_add_object(
3996                                                 adapter->treeNode,
3997                                                 ADDRESS_OBJECT(ads) );
3998                         }
3999                 }
4000
4001                 /* Notify address completion */
4002                 invalidate_address_completion();
4003         }
4004 }
4005
4006 /*
4007 * End of Source.
4008 */
4009