fixed segfault
[claws.git] / src / addressbook.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999,2000 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 "xml.h"
56 #include "prefs.h"
57 #include "procmime.h"
58 #include "utils.h"
59 #include "gtkutils.h"
60 #include "codeconv.h"
61 #include "about.h"
62 #include "addr_compl.h"
63
64 #include "addressitem.h"
65 #include "vcard.h"
66 #include "editvcard.h"
67
68 #ifdef USE_JPILOT
69 #include "jpilot.h"
70 #include "editjpilot.h"
71 #endif
72
73 #ifdef USE_LDAP
74 #include <pthread.h>
75 #include "syldap.h"
76 #include "editldap.h"
77
78 // Interval to check for LDAP search results
79 // #define ADDRESSBOOK_LDAP_TIMER_INTERVAL      100
80 #define ADDRESSBOOK_LDAP_BUSYMSG        "Busy"
81
82 #endif
83
84 #include "pixmaps/dir-close.xpm"
85 #include "pixmaps/dir-open.xpm"
86 #include "pixmaps/group.xpm"
87 #include "pixmaps/vcard.xpm"
88 #ifdef USE_JPILOT
89 #include "pixmaps/jpilot.xpm"
90 #include "pixmaps/category.xpm"
91 #endif
92 #ifdef USE_LDAP
93 #include "pixmaps/ldap.xpm"
94 #endif
95
96 // XML tag names for top level folders
97 #define ADDRESS_TAG_COMMON    "common_address"
98 #define ADDRESS_TAG_PERSONAL  "personal_address"
99 #define ADDRESS_TAG_VCARD     "vcard_list"
100 #ifdef USE_JPILOT
101 #define ADDRESS_TAG_JPILOT    "jpilot_list"
102 #endif
103 #ifdef USE_LDAP
104 #define ADDRESS_TAG_LDAP      "ldap_list"
105 #endif
106
107 typedef enum
108 {
109         COL_NAME        = 0,
110         COL_ADDRESS     = 1,
111         COL_REMARKS     = 2
112 } AddressBookColumnPos;
113
114 #define N_COLS  3
115 #define COL_NAME_WIDTH          144
116 #define COL_ADDRESS_WIDTH       156
117
118 #define COL_FOLDER_WIDTH        170
119 #define ADDRESSBOOK_WIDTH       640
120 #define ADDRESSBOOK_HEIGHT      360
121
122 #define ADDRESSBOOK_MSGBUF_SIZE 2048
123
124 static GdkPixmap *folderxpm;
125 static GdkBitmap *folderxpmmask;
126 static GdkPixmap *folderopenxpm;
127 static GdkBitmap *folderopenxpmmask;
128 static GdkPixmap *groupxpm;
129 static GdkBitmap *groupxpmmask;
130 static GdkPixmap *vcardxpm;
131 static GdkBitmap *vcardxpmmask;
132 #ifdef USE_JPILOT
133 static GdkPixmap *jpilotxpm;
134 static GdkBitmap *jpilotxpmmask;
135 static GdkPixmap *categoryxpm;
136 static GdkBitmap *categoryxpmmask;
137 #endif
138 #ifdef USE_LDAP
139 static GdkPixmap *ldapxpm;
140 static GdkBitmap *ldapxpmmask;
141 #endif
142
143 // Pilot library indicator (set at run-time)
144 static _have_pilot_library_;
145
146 // LDAP library indicator (set at run-time)
147 static _have_ldap_library_;
148
149 // Message buffer
150 static gchar addressbook_msgbuf[ ADDRESSBOOK_MSGBUF_SIZE ];
151
152 static AddressBook addrbook;
153
154 static struct _AddressEdit
155 {
156         GtkWidget *window;
157         GtkWidget *name_entry;
158         GtkWidget *addr_entry;
159         GtkWidget *rem_entry;
160         GtkWidget *ok_btn;
161         GtkWidget *cancel_btn;
162 } addredit;
163
164 static void addressbook_create                  (gboolean show);
165 static gint addressbook_close                   (void);
166 static void addressbook_button_set_sensitive    (void);
167
168 /* callback functions */
169 static void addressbook_del_clicked             (GtkButton      *button,
170                                                  gpointer        data);
171 static void addressbook_reg_clicked             (GtkButton      *button,
172                                                  gpointer        data);
173 static void addressbook_to_clicked              (GtkButton      *button,
174                                                  gpointer        data);
175 static void addressbook_lup_clicked             (GtkButton      *button,
176                                                  gpointer       data);
177
178 static void addressbook_tree_selected           (GtkCTree       *ctree,
179                                                  GtkCTreeNode   *node,
180                                                  gint            column,
181                                                  gpointer        data);
182 static void addressbook_list_selected           (GtkCList       *clist,
183                                                  gint            row,
184                                                  gint            column,
185                                                  GdkEvent       *event,
186                                                  gpointer        data);
187 static void addressbook_entry_gotfocus          (GtkWidget      *widget);
188
189 #if 0
190 static void addressbook_entry_changed           (GtkWidget      *widget);
191 #endif
192
193 static void addressbook_list_button_pressed     (GtkWidget      *widget,
194                                                  GdkEventButton *event,
195                                                  gpointer        data);
196 static void addressbook_list_button_released    (GtkWidget      *widget,
197                                                  GdkEventButton *event,
198                                                  gpointer        data);
199 static void addressbook_tree_button_pressed     (GtkWidget      *ctree,
200                                                  GdkEventButton *event,
201                                                  gpointer        data);
202 static void addressbook_tree_button_released    (GtkWidget      *ctree,
203                                                  GdkEventButton *event,
204                                                  gpointer        data);
205 static void addressbook_popup_close             (GtkMenuShell   *menu_shell,
206                                                  gpointer        data);
207
208 static void addressbook_new_folder_cb           (gpointer        data,
209                                                  guint           action,
210                                                  GtkWidget      *widget);
211 static void addressbook_new_group_cb            (gpointer        data,
212                                                  guint           action,
213                                                  GtkWidget      *widget);
214 static void addressbook_edit_folder_cb          (gpointer        data,
215                                                  guint           action,
216                                                  GtkWidget      *widget);
217 static void addressbook_delete_folder_cb        (gpointer        data,
218                                                  guint           action,
219                                                  GtkWidget      *widget);
220
221 static void addressbook_change_node_name        (GtkCTreeNode   *node,
222                                                  const gchar    *name);
223 static void addressbook_edit_group              (GtkCTreeNode   *group_node);
224
225 static void addressbook_edit_address_create     (gboolean       *cancelled);
226 static void edit_address_ok                     (GtkWidget      *widget,
227                                                  gboolean       *cancelled);
228 static void edit_address_cancel                 (GtkWidget      *widget,
229                                                  gboolean       *cancelled);
230 static gint edit_address_delete_event           (GtkWidget      *widget,
231                                                  GdkEventAny    *event,
232                                                  gboolean       *cancelled);
233 static void edit_address_key_pressed            (GtkWidget      *widget,
234                                                  GdkEventKey    *event,
235                                                  gboolean       *cancelled);
236 static AddressItem *addressbook_edit_address    (AddressItem    *item);
237
238 static void addressbook_new_address_cb          (gpointer        data,
239                                                  guint           action,
240                                                  GtkWidget      *widget);
241 static void addressbook_edit_address_cb         (gpointer        data,
242                                                  guint           action,
243                                                  GtkWidget      *widget);
244 static void addressbook_delete_address_cb       (gpointer        data,
245                                                  guint           action,
246                                                  GtkWidget      *widget);
247
248 static void close_cb                            (gpointer        data,
249                                                  guint           action,
250                                                  GtkWidget      *widget);
251
252 // VCard edit stuff
253 static void addressbook_new_vcard_cb            ( gpointer      data,
254                                                   guint         action,
255                                                   GtkWidget     *widget );
256
257 #ifdef USE_JPILOT
258 // JPilot edit stuff
259 static void addressbook_new_jpilot_cb           ( gpointer      data,
260                                                   guint         action,
261                                                   GtkWidget     *widget );
262 #endif
263
264 #ifdef USE_LDAP
265 // LDAP edit stuff
266 static void addressbook_new_ldap_cb             ( gpointer      data,
267                                                   guint         action,
268                                                   GtkWidget     *widget );
269 #endif
270
271 static AddressItem *addressbook_parse_address   (const gchar    *str);
272 static void addressbook_append_to_compose_entry (AddressItem    *item,
273                                                  ComposeEntryType type);
274
275 static void addressbook_set_clist               (AddressObject  *obj);
276
277 static void addressbook_read_file               (void);
278 static void addressbook_get_tree                (XMLFile        *file,
279                                                  GtkCTreeNode   *node,
280                                                  const gchar    *folder_tag);
281 static void addressbook_add_objs                (XMLFile        *file,
282                                                  GtkCTreeNode   *node);
283
284 static GtkCTreeNode *addressbook_add_object     (GtkCTreeNode   *node,
285                                                  AddressObject  *obj);
286 static void addressbook_delete_object           (AddressObject  *obj);
287 static AddressObject *addressbook_find_object_by_name
288                                                 (GtkCTreeNode   *node,
289                                                  const gchar    *name);
290
291 static AddressItem *addressbook_parse_item      (XMLFile        *file);
292 static void addressbook_xml_recursive_write     (GtkCTreeNode   *node,
293                                                  FILE           *fp);
294 static void addressbook_node_write_begin        (GtkCTreeNode   *node,
295                                                  FILE           *fp);
296 static void addressbook_node_write_end          (GtkCTreeNode   *node,
297                                                  FILE           *fp);
298 static void addressbook_write_items             (FILE           *fp,
299                                                  GList          *items,
300                                                  guint           level);
301 static void tab_indent_out                      (FILE           *fp,
302                                                  guint           level);
303
304 static void key_pressed                         (GtkWidget      *widget,
305                                                  GdkEventKey    *event,
306                                                  gpointer        data);
307 static gint addressbook_list_compare_func       (GtkCList       *clist,
308                                                  gconstpointer   ptr1,
309                                                  gconstpointer   ptr2);
310 static gint addressbook_obj_name_compare        (gconstpointer   a,
311                                                  gconstpointer   b);
312
313 static AddressVCard *addressbook_parse_vcard    ( XMLFile       *file );
314 static void addressbook_write_vcard             ( FILE          *fp,
315                                                 AddressVCard    *vcard,
316                                                 guint           level );
317 static void addressbook_vcard_show_message      ( VCardFile *vcf );
318
319 #ifdef USE_JPILOT
320 static AddressJPilot *addressbook_parse_jpilot  ( XMLFile       *file );
321 static void addressbook_write_jpilot            ( FILE          *fp,
322                                                 AddressJPilot   *jpilot,
323                                                 guint           level );
324 static void addressbook_jpilot_show_message     ( JPilotFile *jpf );
325 #endif
326 #ifdef USE_LDAP
327 static AddressLDAP *addressbook_parse_ldap      ( XMLFile       *file );
328 static void addressbook_write_ldap              ( FILE          *fp,
329                                                 AddressLDAP     *ldapi,
330                                                 guint           level );
331 static void addressbook_ldap_show_message       ( SyldapServer *server );
332 #endif
333
334 static GtkItemFactoryEntry addressbook_entries[] =
335 {
336         {N_("/_File"),                  NULL, NULL, 0, "<Branch>"},
337         {N_("/_File/New _Address"),     "<alt>N", addressbook_new_address_cb, 0, NULL},
338         {N_("/_File/New _Group"),       "<alt>G", addressbook_new_group_cb,   0, NULL},
339         {N_("/_File/New _Folder"),      "<alt>R", addressbook_new_folder_cb,  0, NULL},
340         {N_("/_File/New _V-Card"),      "<alt>D", addressbook_new_vcard_cb,  0, NULL},
341 #ifdef USE_JPILOT
342         {N_("/_File/New _J-Pilot"),     "<alt>J", addressbook_new_jpilot_cb,  0, NULL},
343 #endif
344 #ifdef USE_LDAP
345         {N_("/_File/New _Server"),      "<alt>S", addressbook_new_ldap_cb,  0, NULL},
346 #endif
347         {N_("/_File/---"),              NULL, NULL, 0, "<Separator>"},
348         {N_("/_File/_Edit"),            "<alt>Return", addressbook_edit_address_cb, 0, NULL},
349         {N_("/_File/_Delete"),          NULL, addressbook_delete_address_cb, 0, NULL},
350         {N_("/_File/---"),              NULL, NULL, 0, "<Separator>"},
351         {N_("/_File/_Close"),           "<alt>W", close_cb, 0, NULL},
352         {N_("/_Help"),                  NULL, NULL, 0, "<LastBranch>"},
353         {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
354 };
355
356 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
357 {
358         {N_("/New _Address"),   NULL, addressbook_new_address_cb, 0, NULL},
359         {N_("/New _Group"),     NULL, addressbook_new_group_cb,   0, NULL},
360         {N_("/New _Folder"),    NULL, addressbook_new_folder_cb,  0, NULL},
361         {N_("/New _V-Card"),    NULL, addressbook_new_vcard_cb,   0, NULL},
362 #ifdef USE_JPILOT
363         {N_("/New _J-Pilot"),   NULL, addressbook_new_jpilot_cb,  0, NULL},
364 #endif
365 #ifdef USE_LDAP
366         {N_("/New _Server"),    NULL, addressbook_new_ldap_cb,  0, NULL},
367 #endif
368         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
369         {N_("/_Edit"),          NULL, addressbook_edit_folder_cb,   0, NULL},
370         {N_("/_Delete"),        NULL, addressbook_delete_folder_cb, 0, NULL}
371 };
372
373 static GtkItemFactoryEntry addressbook_list_popup_entries[] =
374 {
375         {N_("/New _Address"),   NULL, addressbook_new_address_cb,  0, NULL},
376         {N_("/New _Group"),     NULL, addressbook_new_group_cb,    0, NULL},
377         {N_("/New _Folder"),    NULL, addressbook_new_folder_cb,   0, NULL},
378         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
379         {N_("/_Edit"),          NULL, addressbook_edit_address_cb,   0, NULL},
380         {N_("/_Delete"),        NULL, addressbook_delete_address_cb, 0, NULL}
381 };
382
383 void addressbook_open(Compose *target)
384 {
385         if (!addrbook.window) {
386                 addressbook_create(TRUE);
387                 addressbook_read_file();
388                 addrbook.open_folder = TRUE;
389                 gtk_ctree_select(GTK_CTREE(addrbook.ctree),
390                                  GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
391         } else
392                 gtk_widget_hide(addrbook.window);
393
394         gtk_widget_show_all(addrbook.window);
395
396         addressbook_set_target_compose(target);
397 }
398
399 void addressbook_set_target_compose(Compose *target)
400 {
401         addrbook.target_compose = target;
402
403         addressbook_button_set_sensitive();
404 }
405
406 Compose *addressbook_get_target_compose(void)
407 {
408         return addrbook.target_compose;
409 }
410
411 static void addressbook_create(gboolean show)
412 {
413         GtkWidget *window;
414         GtkWidget *vbox;
415         GtkWidget *menubar;
416         GtkWidget *vbox2;
417         GtkWidget *ctree_swin;
418         GtkWidget *ctree;
419         GtkWidget *clist_vbox;
420         GtkWidget *clist_swin;
421         GtkWidget *clist;
422         GtkWidget *paned;
423         GtkWidget *hbox;
424         GtkWidget *label;
425         GtkWidget *entry;
426         GtkWidget *statusbar;
427         GtkWidget *hmbox;
428         GtkWidget *hbbox;
429         GtkWidget *hsbox;
430         GtkWidget *del_btn;
431         GtkWidget *reg_btn;
432         GtkWidget *lup_btn;
433         GtkWidget *to_btn;
434         GtkWidget *cc_btn;
435         GtkWidget *bcc_btn;
436         GtkWidget *tree_popup;
437         GtkWidget *list_popup;
438         GtkItemFactory *tree_factory;
439         GtkItemFactory *list_factory;
440         GtkItemFactory *menu_factory;
441         gint n_entries;
442
443         gchar *titles[N_COLS] = {_("Name"), _("E-Mail address"), _("Remarks")};
444         gchar *text;
445         gint i;
446
447         debug_print("Creating addressbook window...\n");
448
449         // Global flag if we have library installed (at run-time)
450         _have_pilot_library_ = FALSE;
451         _have_ldap_library_ = FALSE;
452
453         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
454         gtk_window_set_title(GTK_WINDOW(window), _("Address book"));
455         gtk_widget_set_usize(window, ADDRESSBOOK_WIDTH, ADDRESSBOOK_HEIGHT);
456         //gtk_container_set_border_width(GTK_CONTAINER(window), BORDER_WIDTH);
457         gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
458         gtk_widget_realize(window);
459
460         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
461                            GTK_SIGNAL_FUNC(addressbook_close), NULL);
462         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
463                            GTK_SIGNAL_FUNC(key_pressed), NULL);
464         gtk_signal_connect(GTK_OBJECT(window), "focus_in_event",
465                            GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
466         gtk_signal_connect(GTK_OBJECT(window), "focus_out_event",
467                            GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
468
469         vbox = gtk_vbox_new(FALSE, 0);
470         gtk_container_add(GTK_CONTAINER(window), vbox);
471
472         n_entries = sizeof(addressbook_entries) /
473                 sizeof(addressbook_entries[0]);
474         menubar = menubar_create(window, addressbook_entries, n_entries,
475                                  "<AddressBook>", NULL);
476         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
477         menu_factory = gtk_item_factory_from_widget(menubar);
478
479         vbox2 = gtk_vbox_new(FALSE, 4);
480         gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
481         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
482
483         ctree_swin = gtk_scrolled_window_new(NULL, NULL);
484         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ctree_swin),
485                                        GTK_POLICY_AUTOMATIC,
486                                        GTK_POLICY_ALWAYS);
487         gtk_widget_set_usize(ctree_swin, COL_FOLDER_WIDTH + 40, -1);
488
489         ctree = gtk_ctree_new(1, 0);
490         gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
491         gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
492         gtk_clist_set_column_width(GTK_CLIST(ctree), 0, COL_FOLDER_WIDTH);
493         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
494         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
495                                      GTK_CTREE_EXPANDER_SQUARE);
496         gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
497         gtk_clist_set_compare_func(GTK_CLIST(ctree),
498                                    addressbook_list_compare_func);
499
500         gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
501                            GTK_SIGNAL_FUNC(addressbook_tree_selected), NULL);
502         gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
503                            GTK_SIGNAL_FUNC(addressbook_tree_button_pressed),
504                            NULL);
505         gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
506                            GTK_SIGNAL_FUNC(addressbook_tree_button_released),
507                            NULL);
508
509         clist_vbox = gtk_vbox_new(FALSE, 4);
510
511         clist_swin = gtk_scrolled_window_new(NULL, NULL);
512         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
513                                        GTK_POLICY_AUTOMATIC,
514                                        GTK_POLICY_ALWAYS);
515         gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
516
517         clist = gtk_clist_new_with_titles(N_COLS, titles);
518         gtk_container_add(GTK_CONTAINER(clist_swin), clist);
519         gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_EXTENDED);
520         gtk_clist_set_column_width(GTK_CLIST(clist), COL_NAME,
521                                    COL_NAME_WIDTH);
522         gtk_clist_set_column_width(GTK_CLIST(clist), COL_ADDRESS,
523                                    COL_ADDRESS_WIDTH);
524         gtk_clist_set_compare_func(GTK_CLIST(clist),
525                                    addressbook_list_compare_func);
526
527         for (i = 0; i < N_COLS; i++)
528                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button,
529                                        GTK_CAN_FOCUS);
530
531         gtk_signal_connect(GTK_OBJECT(clist), "select_row",
532                            GTK_SIGNAL_FUNC(addressbook_list_selected), NULL);
533         gtk_signal_connect(GTK_OBJECT(clist), "button_press_event",
534                            GTK_SIGNAL_FUNC(addressbook_list_button_pressed),
535                            NULL);
536         gtk_signal_connect(GTK_OBJECT(clist), "button_release_event",
537                            GTK_SIGNAL_FUNC(addressbook_list_button_released),
538                            NULL);
539
540         hbox = gtk_hbox_new(FALSE, 4);
541         gtk_box_pack_start(GTK_BOX(clist_vbox), hbox, FALSE, FALSE, 0);
542
543         label = gtk_label_new(_("Name:"));
544         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
545
546         entry = gtk_entry_new();
547         gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
548
549         address_completion_register_entry(GTK_ENTRY(entry));
550         gtk_signal_connect(GTK_OBJECT(entry), "focus_in_event",
551                            GTK_SIGNAL_FUNC(addressbook_entry_gotfocus), NULL);
552
553 #if 0
554         gtk_signal_connect(GTK_OBJECT(entry), "changed",
555                            GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
556 #endif
557
558         paned = gtk_hpaned_new();
559         gtk_box_pack_start(GTK_BOX(vbox2), paned, TRUE, TRUE, 0);
560         gtk_paned_add1(GTK_PANED(paned), ctree_swin);
561         gtk_paned_add2(GTK_PANED(paned), clist_vbox);
562
563         // Status bar
564         hsbox = gtk_hbox_new(FALSE, 0);
565         gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
566         statusbar = gtk_statusbar_new();
567         gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
568
569         // Button panel
570         hbbox = gtk_hbutton_box_new();
571         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
572         gtk_button_box_set_spacing(GTK_BUTTON_BOX(hbbox), 2);
573         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
574
575         del_btn = gtk_button_new_with_label(_("Delete"));
576         GTK_WIDGET_SET_FLAGS(del_btn, GTK_CAN_DEFAULT);
577         gtk_box_pack_start(GTK_BOX(hbbox), del_btn, TRUE, TRUE, 0);
578         reg_btn = gtk_button_new_with_label(_("Add"));
579         GTK_WIDGET_SET_FLAGS(reg_btn, GTK_CAN_DEFAULT);
580         gtk_box_pack_start(GTK_BOX(hbbox), reg_btn, TRUE, TRUE, 0);
581         lup_btn = gtk_button_new_with_label(_("Lookup"));
582         GTK_WIDGET_SET_FLAGS(lup_btn, GTK_CAN_DEFAULT);
583         gtk_box_pack_start(GTK_BOX(hbbox), lup_btn, TRUE, TRUE, 0);
584
585         gtk_signal_connect(GTK_OBJECT(del_btn), "clicked",
586                            GTK_SIGNAL_FUNC(addressbook_del_clicked), NULL);
587         gtk_signal_connect(GTK_OBJECT(reg_btn), "clicked",
588                            GTK_SIGNAL_FUNC(addressbook_reg_clicked), NULL);
589         gtk_signal_connect(GTK_OBJECT(lup_btn), "clicked",
590                            GTK_SIGNAL_FUNC(addressbook_lup_clicked), NULL);
591
592         to_btn = gtk_button_new_with_label
593                 (prefs_common.trans_hdr ? _("To:") : "To:");
594         GTK_WIDGET_SET_FLAGS(to_btn, GTK_CAN_DEFAULT);
595         gtk_box_pack_start(GTK_BOX(hbbox), to_btn, TRUE, TRUE, 0);
596         cc_btn = gtk_button_new_with_label
597                 (prefs_common.trans_hdr ? _("Cc:") : "Cc:");
598         GTK_WIDGET_SET_FLAGS(cc_btn, GTK_CAN_DEFAULT);
599         gtk_box_pack_start(GTK_BOX(hbbox), cc_btn, TRUE, TRUE, 0);
600         bcc_btn = gtk_button_new_with_label
601                 (prefs_common.trans_hdr ? _("Bcc:") : "Bcc:");
602         GTK_WIDGET_SET_FLAGS(bcc_btn, GTK_CAN_DEFAULT);
603         gtk_box_pack_start(GTK_BOX(hbbox), bcc_btn, TRUE, TRUE, 0);
604
605         gtk_signal_connect(GTK_OBJECT(to_btn), "clicked",
606                            GTK_SIGNAL_FUNC(addressbook_to_clicked),
607                            GINT_TO_POINTER(COMPOSE_TO));
608         gtk_signal_connect(GTK_OBJECT(cc_btn), "clicked",
609                            GTK_SIGNAL_FUNC(addressbook_to_clicked),
610                            GINT_TO_POINTER(COMPOSE_CC));
611         gtk_signal_connect(GTK_OBJECT(bcc_btn), "clicked",
612                            GTK_SIGNAL_FUNC(addressbook_to_clicked),
613                            GINT_TO_POINTER(COMPOSE_BCC));
614
615         PIXMAP_CREATE(window, folderxpm, folderxpmmask, DIRECTORY_CLOSE_XPM);
616         PIXMAP_CREATE(window, folderopenxpm, folderopenxpmmask,
617                       DIRECTORY_OPEN_XPM);
618         PIXMAP_CREATE(window, groupxpm, groupxpmmask, group_xpm);
619         PIXMAP_CREATE(window, vcardxpm, vcardxpmmask, vcard_xpm);
620 #ifdef USE_JPILOT
621         PIXMAP_CREATE(window, jpilotxpm, jpilotxpmmask, jpilot_xpm);
622         PIXMAP_CREATE(window, categoryxpm, categoryxpmmask, category_xpm);
623 #endif
624 #ifdef USE_LDAP
625         PIXMAP_CREATE(window, ldapxpm, ldapxpmmask, ldap_xpm);
626 #endif
627
628         text = _("Common address");
629         addrbook.common =
630                 gtk_ctree_insert_node(GTK_CTREE(ctree),
631                                       NULL, NULL, &text, FOLDER_SPACING,
632                                       folderxpm, folderxpmmask,
633                                       folderopenxpm, folderopenxpmmask,
634                                       FALSE, FALSE);
635         text = _("Personal address");
636         addrbook.personal =
637                 gtk_ctree_insert_node(GTK_CTREE(ctree),
638                                       NULL, NULL, &text, FOLDER_SPACING,
639                                       folderxpm, folderxpmmask,
640                                       folderopenxpm, folderopenxpmmask,
641                                       FALSE, FALSE);
642
643         text = _("V-Card");
644         addrbook.vcard =
645                 gtk_ctree_insert_node(GTK_CTREE(ctree),
646                                       NULL, NULL, &text, FOLDER_SPACING,
647                                       folderxpm, folderxpmmask,
648                                       folderopenxpm, folderopenxpmmask,
649                                       FALSE, FALSE);
650
651 #ifdef USE_JPILOT
652         text = _("J-Pllot");
653         addrbook.jpilot =
654                 gtk_ctree_insert_node(GTK_CTREE(ctree),
655                                       NULL, NULL, &text, FOLDER_SPACING,
656                                       folderxpm, folderxpmmask,
657                                       folderopenxpm, folderopenxpmmask,
658                                       FALSE, FALSE);
659         if( jpilot_test_pilot_lib() ) {
660                 _have_pilot_library_ = TRUE;
661                 menu_set_sensitive( menu_factory, "/File/New J-Pilot", TRUE );
662         }
663         else {
664                 menu_set_sensitive( menu_factory, "/File/New J-Pilot", FALSE );
665         }
666 #endif
667
668 #ifdef USE_LDAP
669         text = _("Directory");
670         addrbook.ldap =
671                 gtk_ctree_insert_node(GTK_CTREE(ctree),
672                                       NULL, NULL, &text, FOLDER_SPACING,
673                                       folderxpm, folderxpmmask,
674                                       folderopenxpm, folderopenxpmmask,
675                                       FALSE, FALSE);
676         if( syldap_test_ldap_lib() ) {
677                 _have_ldap_library_ = TRUE;
678                 menu_set_sensitive( menu_factory, "/File/New Server", TRUE );
679         }
680         else {
681                 menu_set_sensitive( menu_factory, "/File/New Server", FALSE );
682         }
683 #endif
684
685         /* popup menu */
686         n_entries = sizeof(addressbook_tree_popup_entries) /
687                 sizeof(addressbook_tree_popup_entries[0]);
688         tree_popup = menu_create_items(addressbook_tree_popup_entries,
689                                        n_entries,
690                                        "<AddressBookTree>", &tree_factory,
691                                        NULL);
692         gtk_signal_connect(GTK_OBJECT(tree_popup), "selection_done",
693                            GTK_SIGNAL_FUNC(addressbook_popup_close), NULL);
694         n_entries = sizeof(addressbook_list_popup_entries) /
695                 sizeof(addressbook_list_popup_entries[0]);
696         list_popup = menu_create_items(addressbook_list_popup_entries,
697                                        n_entries,
698                                        "<AddressBookList>", &list_factory,
699                                        NULL);
700
701         addrbook.window  = window;
702         addrbook.menubar = menubar;
703         addrbook.ctree   = ctree;
704         addrbook.clist   = clist;
705         addrbook.entry   = entry;
706         addrbook.statusbar = statusbar;
707         addrbook.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Addressbook Window" );
708
709         addrbook.del_btn = del_btn;
710         addrbook.reg_btn = reg_btn;
711         addrbook.lup_btn = lup_btn;
712         addrbook.to_btn  = to_btn;
713         addrbook.cc_btn  = cc_btn;
714         addrbook.bcc_btn = bcc_btn;
715
716         addrbook.tree_popup   = tree_popup;
717         addrbook.list_popup   = list_popup;
718         addrbook.tree_factory = tree_factory;
719         addrbook.list_factory = list_factory;
720         addrbook.menu_factory = menu_factory;
721
722         address_completion_start(window);
723
724         if (show) 
725                 gtk_widget_show_all(window);
726 }
727
728 static gint addressbook_close(void)
729 {
730         gtk_widget_hide(addrbook.window);
731         addressbook_export_to_file();
732         /* tell addr_compl that there's a new addressbook file */
733         invalidate_address_completion();
734         return TRUE;
735 }
736
737 static void addressbook_status_show( gchar *msg ) {
738         if( addrbook.statusbar != NULL ) {
739                 gtk_statusbar_pop( GTK_STATUSBAR(addrbook.statusbar), addrbook.status_cid );
740                 if( msg ) {
741                         gtk_statusbar_push( GTK_STATUSBAR(addrbook.statusbar), addrbook.status_cid, msg );
742                 }
743         }
744 }
745
746 static void addressbook_button_set_sensitive(void)
747 {
748         gboolean to_sens  = FALSE;
749         gboolean cc_sens  = FALSE;
750         gboolean bcc_sens = FALSE;
751
752         if (!addrbook.window) return;
753
754         if (addrbook.target_compose) {
755                 to_sens = TRUE;
756                 cc_sens = TRUE;
757                 if (addrbook.target_compose->use_bcc)
758                         bcc_sens = TRUE;
759         }
760
761         gtk_widget_set_sensitive(addrbook.to_btn, to_sens);
762         gtk_widget_set_sensitive(addrbook.cc_btn, cc_sens);
763         gtk_widget_set_sensitive(addrbook.bcc_btn, bcc_sens);
764 }
765
766 static void addressbook_del_clicked(GtkButton *button, gpointer data)
767 {
768         GtkCList *clist = GTK_CLIST(addrbook.clist);
769         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
770         AddressObject *pobj, *obj;
771         GList *cur, *next;
772         gint row;
773         gboolean remFlag;
774
775         if (!clist->selection) {
776                 addressbook_delete_folder_cb(NULL, 0, NULL);
777                 return;
778         }
779
780         pobj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
781         g_return_if_fail(pobj != NULL);
782
783         if (alertpanel(_("Delete address(es)"),
784                        _("Really delete the address(es)?"),
785                        _("Yes"), _("No"), NULL) != G_ALERTDEFAULT)
786                 return;
787
788         for (cur = clist->selection; cur != NULL; cur = next) {
789                 next = cur->next;
790                 row = GPOINTER_TO_INT(cur->data);
791                 remFlag = FALSE;
792
793                 obj = gtk_clist_get_row_data(clist, row);
794                 if (!obj) continue;
795
796                 if (pobj->type == ADDR_GROUP) {
797                         AddressGroup *group = ADDRESS_GROUP(pobj);
798                         group->items = g_list_remove(group->items, obj);
799                 } else if (pobj->type == ADDR_FOLDER) {
800                         AddressFolder *folder = ADDRESS_FOLDER(pobj);
801
802                         folder->items = g_list_remove(folder->items, obj);
803                         if (obj->type == ADDR_GROUP) {
804                                 remFlag = TRUE;
805                         }
806                         else if (obj->type == ADDR_VCARD) {
807                                 remFlag = TRUE;
808                         }
809                         else if (obj->type == ADDR_JPILOT) {
810                                 remFlag = TRUE;
811                         }
812                         else if (obj->type == ADDR_LDAP) {
813                                 remFlag = TRUE;
814                         }
815
816                         if( remFlag ) {
817                                 GtkCTreeNode *node;
818                                 node = gtk_ctree_find_by_row_data
819                                         (ctree, addrbook.opened, obj);
820                                 if (node) gtk_ctree_remove_node(ctree, node);
821                         }
822                 } else
823                         continue;
824
825                 addressbook_delete_object(obj);
826
827                 gtk_clist_remove(clist, row);
828         }
829 }
830
831 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
832 {
833         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
834         GtkEntry *entry = GTK_ENTRY(addrbook.entry);
835         AddressObject *obj;
836         AddressItem *item;
837         gchar *str;
838
839         if (*gtk_entry_get_text(entry) == '\0') {
840                 addressbook_new_address_cb(NULL, 0, NULL);
841                 return;
842         }
843         if (!addrbook.opened) return;
844
845         obj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
846         if (!obj) return;
847
848         g_return_if_fail(obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER);
849
850         str = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
851
852         item = addressbook_parse_address(str);
853         g_free(str);
854         if (item) {
855                 if (addressbook_find_object_by_name
856                         (addrbook.opened, item->name) != NULL) {
857                         addressbook_delete_object(ADDRESS_OBJECT(item));
858                         item = NULL;
859                 } else if (addressbook_edit_address(item) == NULL) {
860                         addressbook_delete_object(ADDRESS_OBJECT(item));
861                         return;
862                 }
863         }
864
865         if (!item) {
866                 item = addressbook_edit_address(NULL);
867                 if (!item) return;
868         }
869
870         if (addressbook_find_object_by_name(addrbook.opened, item->name)) {
871                 addressbook_delete_object(ADDRESS_OBJECT(item));
872                 return;
873         }
874
875         addressbook_add_object(addrbook.opened, ADDRESS_OBJECT(item));
876         addrbook.open_folder = TRUE;
877         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
878 }
879
880 static AddressItem *addressbook_parse_address(const gchar *str)
881 {
882         gchar *name    = NULL;
883         gchar *address = NULL;
884         AddressItem *item;
885         gchar *buf;
886         gchar *start, *end;
887
888         Xalloca(buf, strlen(str) + 1, return NULL);
889
890         strcpy(buf, str);
891         g_strstrip(buf);
892         if (*buf == '\0') return NULL;
893
894         if ((start = strchr(buf, '<'))) {
895                 if (start > buf) {
896                         *start = '\0';
897                         g_strstrip(buf);
898                         if (*buf != '\0')
899                                 name = g_strdup(buf);
900                 }
901                 start++;
902                 if ((end = strchr(start, '>'))) {
903                         *end = '\0';
904                         g_strstrip(start);
905                         if (*start != '\0')
906                                 address = g_strdup(start);
907                 }
908         } else
909                 name = g_strdup(buf);
910
911         if (!name && !address) return NULL;
912
913         item = mgu_create_address();
914         ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
915         item->name    = name;
916         item->address = address;
917         item->remarks = NULL;
918
919         return item;
920 }
921
922 static void addressbook_to_clicked(GtkButton *button, gpointer data)
923 {
924         GtkCList *clist = GTK_CLIST(addrbook.clist);
925         GList *cur;
926
927         if (!addrbook.target_compose) return;
928
929         for (cur = clist->selection; cur != NULL; cur = cur->next) {
930                 AddressObject *obj;
931
932                 obj = gtk_clist_get_row_data(clist,
933                                              GPOINTER_TO_INT(cur->data));
934                 if (!obj) return;
935
936                 if (obj->type == ADDR_ITEM) {
937                         addressbook_append_to_compose_entry
938                                 (ADDRESS_ITEM(obj), (ComposeEntryType)data);
939                 } else if (obj->type == ADDR_GROUP) {
940                         AddressGroup *group;
941                         GList *cur_item;
942
943                         group = ADDRESS_GROUP(obj);
944                         for (cur_item = group->items; cur_item != NULL;
945                              cur_item = cur_item->next) {
946                                 if (ADDRESS_OBJECT(cur_item->data)->type
947                                     != ADDR_ITEM)
948                                         continue;
949                                 addressbook_append_to_compose_entry
950                                         (ADDRESS_ITEM(cur_item->data),
951                                          (ComposeEntryType)data);
952                         }
953                 }
954         }
955 }
956
957 static void addressbook_append_to_compose_entry(AddressItem *item,
958                                                 ComposeEntryType type)
959 {
960         Compose *compose = addrbook.target_compose;
961
962         if (item->name && item->address) {
963                 gchar *buf;
964
965                 buf = g_strdup_printf
966                         ("%s <%s>", item->name, item->address);
967                 compose_entry_append(compose, buf, type);
968                 g_free(buf);
969         } else if (item->address)
970                 compose_entry_append(compose, item->address, type);
971 }
972
973 static void addressbook_menubar_set_sensitive( gboolean sensitive ) {
974         menu_set_sensitive( addrbook.menu_factory, "/File/New Address", sensitive );
975         menu_set_sensitive( addrbook.menu_factory, "/File/New Group",   sensitive );
976         menu_set_sensitive( addrbook.menu_factory, "/File/New Folder",  sensitive );
977         menu_set_sensitive( addrbook.menu_factory, "/File/New V-Card",  sensitive );
978 #ifdef USE_JPILOT
979         menu_set_sensitive( addrbook.menu_factory, "/File/New J-Pilot", sensitive );
980 #endif
981 #ifdef USE_LDAP
982         menu_set_sensitive( addrbook.menu_factory, "/File/New Server",  sensitive );
983 #endif
984         gtk_widget_set_sensitive( addrbook.reg_btn, sensitive );
985         gtk_widget_set_sensitive( addrbook.del_btn, sensitive );
986 }
987
988 static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode *node ) {
989         gboolean canEdit = TRUE;
990         if( obj->type == ADDR_FOLDER ) {
991                 if( node == addrbook.common ) {
992                         canEdit = FALSE;
993                 }
994                 if( node == addrbook.personal ) {
995                         canEdit = FALSE;
996                 }
997                 if( node == addrbook.vcard ) {
998                         canEdit = FALSE;
999                         menu_set_sensitive( addrbook.menu_factory, "/File/New V-Card", TRUE );
1000                 }
1001 #ifdef USE_JPILOT
1002                 else if( node == addrbook.jpilot ) {
1003                         canEdit = FALSE;
1004                         if( _have_pilot_library_ ) {
1005                                 menu_set_sensitive( addrbook.menu_factory, "/File/New J-Pilot", TRUE );
1006                         }
1007                 }
1008 #endif
1009 #ifdef USE_LDAP
1010                 else if( node == addrbook.ldap ) {
1011                         canEdit = FALSE;
1012                         if( _have_ldap_library_ ) {
1013                                 menu_set_sensitive( addrbook.menu_factory, "/File/New Server", TRUE );
1014                         }
1015                 }
1016 #endif
1017                 else {
1018                         menu_set_sensitive( addrbook.menu_factory, "/File/New Address", TRUE );
1019                         menu_set_sensitive( addrbook.menu_factory, "/File/New Group",   TRUE );
1020                         menu_set_sensitive( addrbook.menu_factory, "/File/New Folder",  TRUE );
1021                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1022                         gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1023                 }
1024         }
1025         else if( obj->type == ADDR_GROUP ) {
1026                 menu_set_sensitive( addrbook.menu_factory, "/File/New Address", TRUE );
1027                 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1028                 gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1029         }
1030 #ifdef USE_JPILOT
1031         else if( obj->type == ADDR_JPILOT ) {
1032                 if( ! _have_pilot_library_ ) canEdit = FALSE;
1033         }
1034         else if( obj->type == ADDR_CATEGORY ) {
1035                 canEdit = FALSE;
1036         }
1037 #endif
1038 #ifdef USE_LDAP
1039         else if( obj->type == ADDR_LDAP ) {
1040                 if( ! _have_ldap_library_ ) canEdit = FALSE;
1041         }
1042 #endif
1043         menu_set_sensitive( addrbook.menu_factory, "/File/Edit",    canEdit );
1044         menu_set_sensitive( addrbook.menu_factory, "/File/Delete",  canEdit );
1045 }
1046
1047 static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
1048                                       gint column, gpointer data)
1049 {
1050         AddressObject *obj;
1051
1052         addrbook.selected = node;
1053         addrbook.open_folder = FALSE;
1054         addressbook_status_show( "" );
1055         if( addrbook.entry != NULL ) {
1056                 gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
1057         }
1058
1059         obj = gtk_ctree_node_get_row_data(ctree, node);
1060         if( obj == NULL ) return;
1061
1062         addrbook.opened = node;
1063
1064         if(     obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER ||
1065                 obj->type == ADDR_VCARD || obj->type == ADDR_JPILOT ||
1066                 obj->type == ADDR_CATEGORY || obj->type == ADDR_LDAP ) {
1067                 addressbook_set_clist(obj);
1068         }
1069
1070         if( obj->type == ADDR_VCARD ) {
1071                 // Read from file
1072                 VCardFile *vcf;
1073                 vcf = ADDRESS_VCARD(obj)->cardFile;
1074                 vcard_read_data( vcf );
1075                 addressbook_vcard_show_message( vcf );
1076                 ADDRESS_VCARD(obj)->items = vcard_get_address_list( vcf );
1077                 addressbook_set_clist( obj );
1078         }
1079 #ifdef USE_JPILOT
1080         else if( obj->type == ADDR_JPILOT ) {
1081                 if( _have_pilot_library_ ) {
1082                         // Read from file
1083                         JPilotFile *jpf;
1084                         GList *catList, *catNode;
1085                         AddressCategory *acat;
1086                         GtkCTreeNode *childNode, *nextNode;
1087                         GtkCTreeRow *currRow;
1088
1089                         jpf = ADDRESS_JPILOT(obj)->pilotFile;
1090                         addressbook_jpilot_show_message( jpf );
1091                         if( jpilot_get_modified( jpf ) ) {
1092                                 jpilot_read_data( jpf );
1093                                 catList = jpilot_get_category_items( jpf );
1094
1095                                 // Remove existing categories
1096                                 currRow = GTK_CTREE_ROW( node );
1097                                 if( currRow ) {
1098                                         while( nextNode = currRow->children ) {
1099                                                 gtk_ctree_remove_node( ctree, nextNode );
1100                                         }
1101                                 }
1102
1103                                 // Load new categories into the tree.
1104                                 catNode = catList;
1105                                 while( catNode ) {
1106                                         AddressItem *item = catNode->data;
1107                                         acat = g_new(AddressCategory, 1);
1108                                         ADDRESS_OBJECT_TYPE(acat) = ADDR_CATEGORY;
1109                                         acat->name = g_strdup( item->name );
1110                                         acat->items = NULL;
1111                                         acat->pilotFile = jpf;
1112                                         acat->category = item;
1113                                         catNode = g_list_next( catNode );
1114                                         addressbook_add_object(node, ADDRESS_OBJECT(acat));
1115                                 }
1116
1117                                 ADDRESS_JPILOT(obj)->items = catList;
1118                         }
1119                         addressbook_set_clist( obj );
1120                 }
1121         }
1122         else if( obj->type == ADDR_CATEGORY ) {
1123                 if( _have_pilot_library_ ) {
1124                         // Read from file
1125                         JPilotFile *jpf;
1126
1127                         jpf = ADDRESS_JPILOT(obj)->pilotFile;
1128                         if( jpilot_get_modified( jpf ) ) {
1129                                 // Force parent to be reloaded
1130                                 gtk_ctree_select( GTK_CTREE(addrbook.ctree), GTK_CTREE_ROW(node)->parent);
1131                                 gtk_ctree_expand( GTK_CTREE(addrbook.ctree), GTK_CTREE_ROW(node)->parent);
1132                         }
1133                         else {
1134                                 AddressItem *item = NULL;
1135                                 AddressCategory *acat = ADDRESS_CATEGORY(obj);
1136                                 if( acat ) item = acat->category;
1137                                 if( item ) {
1138                                         ADDRESS_CATEGORY(obj)->items =
1139                                                 jpilot_get_address_list_cat( jpf, item->categoryID );
1140                                 }
1141                                 addressbook_set_clist( obj );
1142                         }
1143                 }
1144         }
1145 #endif
1146 #ifdef USE_LDAP
1147         else if( obj->type == ADDR_LDAP ) {
1148                 if( _have_ldap_library_ ) {
1149                         // Read from cache
1150                         SyldapServer *server;
1151                         server = ADDRESS_LDAP(obj)->ldapServer;
1152                         addressbook_ldap_show_message( server );
1153                         if( ! server->busyFlag ) {
1154                                 ADDRESS_LDAP(obj)->items = syldap_get_address_list( server );
1155                                 addressbook_set_clist( obj );
1156                         }
1157                 }
1158         }
1159 #endif
1160
1161         // Setup main menu selections
1162         addressbook_menubar_set_sensitive( FALSE );
1163         addressbook_menuitem_set_sensitive( obj, node );
1164 }
1165
1166 static void addressbook_list_selected(GtkCList *clist, gint row, gint column,
1167                                       GdkEvent *event, gpointer data)
1168 {
1169         GtkEntry *entry = GTK_ENTRY(addrbook.entry);
1170         AddressObject *obj;
1171         GList *cur;
1172
1173         if (event && event->type == GDK_2BUTTON_PRESS) {
1174                 if (prefs_common.add_address_by_click &&
1175                     addrbook.target_compose)
1176                         addressbook_to_clicked(NULL, NULL);
1177                 else
1178                         addressbook_edit_address_cb(NULL, 0, NULL);
1179                 return;
1180         }
1181
1182 #if 0
1183         gtk_signal_handler_block_by_func
1184                 (GTK_OBJECT(entry),
1185                  GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
1186 #endif           
1187
1188         gtk_entry_set_text(entry, "");
1189
1190         for (cur = clist->selection; cur != NULL; cur = cur->next) {
1191                 obj = gtk_clist_get_row_data(clist,
1192                                              GPOINTER_TO_INT(cur->data));
1193                 g_return_if_fail(obj != NULL);
1194
1195                 if (obj->type == ADDR_ITEM) {
1196                         AddressItem *item;
1197
1198                         item = ADDRESS_ITEM(obj);
1199                         if (item->name && item->address) {
1200                                 gchar *buf;
1201
1202                                 buf = g_strdup_printf
1203                                         ("%s <%s>", item->name, item->address);
1204                                 if (*gtk_entry_get_text(entry) != '\0')
1205                                         gtk_entry_append_text(entry, ", ");
1206                                 gtk_entry_append_text(entry, buf);
1207                                 g_free(buf);
1208                         } else if (item->address) {
1209                                 if (*gtk_entry_get_text(entry) != '\0')
1210                                         gtk_entry_append_text(entry, ", ");
1211                                 gtk_entry_append_text(entry, item->address);
1212                         }
1213                 }
1214         }
1215
1216 #if 0
1217         gtk_signal_handler_unblock_by_func
1218                 (GTK_OBJECT(entry),
1219                  GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
1220 #endif           
1221 }
1222
1223 #if 0
1224 static void addressbook_entry_changed(GtkWidget *widget)
1225 {
1226         GtkCList *clist = GTK_CLIST(addrbook.clist);
1227         GtkEntry *entry = GTK_ENTRY(addrbook.entry);
1228         const gchar *str;
1229         gint len;
1230         gint row;
1231
1232         //if (clist->selection && clist->selection->next) return;
1233
1234         str = gtk_entry_get_text(entry);
1235         if (*str == '\0') {
1236                 gtk_clist_unselect_all(clist);
1237                 return;
1238         }
1239         len = strlen(str);
1240
1241         for (row = 0; row < clist->rows; row++) {
1242                 AddressObject *obj;
1243                 const gchar *name;
1244
1245                 obj = ADDRESS_OBJECT(gtk_clist_get_row_data(clist, row));
1246                 if (!obj) continue;
1247                 if (obj->type == ADDR_ITEM)
1248                         name = ADDRESS_ITEM(obj)->name;
1249                 else if (obj->type == ADDR_GROUP)
1250                         name = ADDRESS_GROUP(obj)->name;
1251                 else
1252                         continue;
1253
1254                 if (name && !strncasecmp(name, str, len)) {
1255                         gtk_clist_unselect_all(clist);
1256                         gtk_clist_select_row(clist, row, -1);
1257                         return;
1258                 }
1259         }
1260
1261         gtk_clist_unselect_all(clist);
1262 }
1263 #endif
1264
1265 static void addressbook_entry_gotfocus( GtkWidget *widget ) {
1266         gtk_editable_select_region( GTK_EDITABLE(addrbook.entry), 0, -1 );
1267 }
1268
1269 static void addressbook_list_button_pressed(GtkWidget *widget,
1270                                             GdkEventButton *event,
1271                                             gpointer data)
1272 {
1273         GtkCList *clist = GTK_CLIST(widget);
1274         gint row, column;
1275         gint tRow, tCol;
1276         AddressObject *obj, *pobj;
1277
1278         if (!event) return;
1279
1280         obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
1281                                           addrbook.opened);
1282         g_return_if_fail(obj != NULL);
1283
1284         pobj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), addrbook.selected);
1285         if( pobj ) {
1286                 if(     pobj->type == ADDR_VCARD ||
1287                         pobj->type == ADDR_JPILOT ||
1288                         pobj->type == ADDR_CATEGORY ||
1289                         pobj->type == ADDR_LDAP ) {
1290                         menu_set_sensitive(addrbook.menu_factory, "/File/Edit", FALSE);
1291                         menu_set_sensitive(addrbook.menu_factory, "/File/Delete", FALSE);
1292                 }
1293         }
1294
1295         if (event->button != 3) return;
1296         menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.list_popup));
1297
1298         if (gtk_clist_get_selection_info
1299                 (clist, event->x, event->y, &row, &column)) {
1300                 GtkCListRow *clist_row;
1301
1302                 clist_row = g_list_nth(clist->row_list, row)->data;
1303                 if (clist_row->state != GTK_STATE_SELECTED) {
1304                         gtk_clist_unselect_all(clist);
1305                         gtk_clist_select_row(clist, row, column);
1306                 }
1307                 gtkut_clist_set_focus_row(clist, row);
1308
1309                 if(     obj->type != ADDR_VCARD &&
1310                         obj->type != ADDR_JPILOT &&
1311                         obj->type != ADDR_CATEGORY &&
1312                         obj->type != ADDR_LDAP ) {
1313                         menu_set_sensitive(addrbook.list_factory, "/Edit", TRUE);
1314                         menu_set_sensitive(addrbook.list_factory, "/Delete", TRUE);
1315                 }
1316         }
1317
1318         if( !(  addrbook.opened == addrbook.vcard ||
1319                 addrbook.opened == addrbook.jpilot ||
1320                 addrbook.opened == addrbook.ldap ) ) {
1321
1322                 if( obj->type == ADDR_FOLDER || obj->type == ADDR_GROUP ) {
1323                         menu_set_sensitive(addrbook.list_factory, "/New Address", TRUE);
1324                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1325                         gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1326                 }
1327                 if (obj->type == ADDR_FOLDER) {
1328                         menu_set_sensitive(addrbook.list_factory, "/New Folder", TRUE);
1329                         menu_set_sensitive(addrbook.list_factory, "/New Group", TRUE);
1330                 }
1331         }
1332         gtk_menu_popup(GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
1333                        event->button, event->time);
1334 }
1335
1336 static void addressbook_list_button_released(GtkWidget *widget,
1337                                              GdkEventButton *event,
1338                                              gpointer data)
1339 {
1340 }
1341
1342 static void addressbook_tree_button_pressed(GtkWidget *ctree,
1343                                             GdkEventButton *event,
1344                                             gpointer data)
1345 {
1346         GtkCList *clist = GTK_CLIST(ctree);
1347         gint row, column;
1348         AddressObject *obj;
1349         GtkCTreeNode *node;
1350
1351         if (!event) return;
1352         if (event->button == 1) {
1353                 addrbook.open_folder = TRUE;
1354                 return;
1355         }
1356         if (event->button != 3) return;
1357
1358         if (!gtk_clist_get_selection_info
1359                 (clist, event->x, event->y, &row, &column)) return;
1360         gtk_clist_select_row(clist, row, column);
1361
1362         obj = gtk_clist_get_row_data(clist, row);
1363         g_return_if_fail(obj != NULL);
1364
1365         menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
1366
1367         if (obj->type == ADDR_FOLDER) {
1368                 node = gtk_ctree_node_nth(GTK_CTREE(ctree), row);
1369                 if( node == addrbook.vcard ) {
1370                         menu_set_sensitive(addrbook.tree_factory, "/New V-Card", TRUE);
1371                 }
1372 #ifdef USE_JPILOT
1373                 else if( node == addrbook.jpilot ) {
1374                         if( _have_pilot_library_ ) {
1375                                 menu_set_sensitive(addrbook.tree_factory, "/New J-Pilot", TRUE);
1376                         }
1377                 }
1378 #endif
1379 #ifdef USE_LDAP
1380                 else if( node == addrbook.ldap ) {
1381                         if( _have_ldap_library_ ) {
1382                                 menu_set_sensitive(addrbook.tree_factory, "/New Server", TRUE);
1383                         }
1384                 }
1385 #endif
1386                 else {
1387                         menu_set_sensitive(addrbook.tree_factory, "/New Address", TRUE);
1388                         menu_set_sensitive(addrbook.tree_factory, "/New Folder", TRUE);
1389                         menu_set_sensitive(addrbook.tree_factory, "/New Group", TRUE);
1390                         if (node && GTK_CTREE_ROW(node)->level >= 2) {
1391                                 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1392                                 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1393                         }
1394                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1395                         gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1396                 }
1397         }
1398         else if (obj->type == ADDR_GROUP) {
1399                 menu_set_sensitive(addrbook.tree_factory, "/New Address", TRUE);
1400                 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1401                 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1402                 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1403                 gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1404         }
1405         else if (obj->type == ADDR_VCARD) {
1406                 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1407                 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1408         }
1409 #ifdef USE_JPILOT
1410         else if (obj->type == ADDR_JPILOT) {
1411                 if( _have_pilot_library_ ) {
1412                         menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1413                         menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1414                 }
1415         }
1416         else if (obj->type == ADDR_CATEGORY) {
1417                 if( _have_pilot_library_ ) {
1418                         menu_set_sensitive(addrbook.tree_factory, "/Edit", FALSE);
1419                         menu_set_sensitive(addrbook.tree_factory, "/Delete", FALSE);
1420                 }
1421         }
1422 #endif
1423 #ifdef USE_LDAP
1424         else if (obj->type == ADDR_LDAP) {
1425                 if( _have_ldap_library_ ) {
1426                         menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1427                         menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1428                 }
1429         }
1430 #endif
1431         else {
1432                 return;
1433         }
1434         gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
1435                        event->button, event->time);
1436 }
1437
1438 static void addressbook_tree_button_released(GtkWidget *ctree,
1439                                              GdkEventButton *event,
1440                                              gpointer data)
1441 {
1442         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1443         gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree), addrbook.opened);
1444 }
1445
1446 static void addressbook_popup_close(GtkMenuShell *menu_shell, gpointer data)
1447 {
1448         if (!addrbook.opened) return;
1449
1450         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1451         gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree),
1452                                   addrbook.opened);
1453 }
1454
1455 static void addressbook_new_folder_cb(gpointer data, guint action,
1456                                       GtkWidget *widget)
1457 {
1458         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1459         AddressObject *obj;
1460         AddressFolder *folder;
1461         gchar *new_folder;
1462
1463         if (!addrbook.selected) return;
1464
1465         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1466         g_return_if_fail(obj != NULL);
1467         if (obj->type != ADDR_FOLDER) return;
1468
1469         new_folder = input_dialog(_("New folder"),
1470                                   _("Input the name of new folder:"),
1471                                   _("NewFolder"));
1472         if (!new_folder) return;
1473         g_strstrip(new_folder);
1474         if (*new_folder == '\0') {
1475                 g_free(new_folder);
1476                 return;
1477         }
1478
1479         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1480                                               new_folder,
1481                                               addressbook_obj_name_compare)) {
1482                 alertpanel_error(_("The name already exists."));
1483                 g_free(new_folder);
1484                 return;
1485         }
1486
1487         folder = g_new(AddressFolder, 1);
1488         ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1489         folder->name = g_strdup(new_folder);
1490         folder->items = NULL;
1491
1492         addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(folder));
1493
1494         g_free(new_folder);
1495
1496         if (addrbook.selected == addrbook.opened)
1497                 addressbook_set_clist(obj);
1498 }
1499
1500 static void addressbook_new_group_cb(gpointer data, guint action,
1501                                      GtkWidget *widget)
1502 {
1503         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1504         AddressObject *obj;
1505         AddressGroup *group;
1506         gchar *new_group;
1507
1508         if (!addrbook.selected) return;
1509
1510         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1511         g_return_if_fail(obj != NULL);
1512         if (obj->type != ADDR_FOLDER) return;
1513
1514         new_group = input_dialog(_("New group"),
1515                                  _("Input the name of new group:"),
1516                                   _("NewGroup"));
1517         if (!new_group) return;
1518         g_strstrip(new_group);
1519         if (*new_group == '\0') {
1520                 g_free(new_group);
1521                 return;
1522         }
1523
1524         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1525                                               new_group,
1526                                               addressbook_obj_name_compare)) {
1527                 alertpanel_error(_("The name already exists."));
1528                 g_free(new_group);
1529                 return;
1530         }
1531
1532         group = g_new(AddressGroup, 1);
1533         ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1534         group->name = g_strdup(new_group);
1535         group->items = NULL;
1536
1537         addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(group));
1538
1539         g_free(new_group);
1540
1541         if (addrbook.selected == addrbook.opened)
1542                 addressbook_set_clist(obj);
1543 }
1544
1545 static void addressbook_change_node_name(GtkCTreeNode *node, const gchar *name)
1546 {
1547         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1548         gchar *text[1];
1549         guint8 spacing;
1550         GdkPixmap *pix_cl, *pix_op;
1551         GdkBitmap *mask_cl, *mask_op;
1552         gboolean is_leaf, expanded;
1553
1554         gtk_ctree_get_node_info(ctree, node, text, &spacing,
1555                                 &pix_cl, &mask_cl, &pix_op, &mask_op,
1556                                 &is_leaf, &expanded);
1557         gtk_ctree_set_node_info(ctree, node, name, spacing,
1558                                 pix_cl, mask_cl, pix_op, mask_op,
1559                                 is_leaf, expanded);
1560 }
1561
1562 static void addressbook_edit_group(GtkCTreeNode *group_node)
1563 {
1564         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1565         GtkCList *clist = GTK_CLIST(addrbook.clist);
1566         AddressObject *obj;
1567         AddressGroup *group;
1568         gchar *new_name;
1569         GtkCTreeNode *node;
1570
1571         if (!group_node && clist->selection) {
1572                 obj = gtk_clist_get_row_data(clist,
1573                                              GPOINTER_TO_INT(clist->selection->data));
1574                 g_return_if_fail(obj != NULL);
1575                 if (obj->type != ADDR_GROUP) return;
1576                 node = gtk_ctree_find_by_row_data
1577                         (ctree, addrbook.selected, obj);
1578                 if (!node) return;
1579         } else {
1580                 if (group_node)
1581                         node = group_node;
1582                 else
1583                         node = addrbook.selected;
1584                 obj = gtk_ctree_node_get_row_data(ctree, node);
1585                 g_return_if_fail(obj != NULL);
1586                 if (obj->type != ADDR_GROUP) return;
1587         }
1588
1589         group = ADDRESS_GROUP(obj);
1590
1591         new_name = input_dialog(_("Edit group"),
1592                                 _("Input the new name of group:"),
1593                                 group->name);
1594         if (!new_name) return;
1595         g_strstrip(new_name);
1596         if (*new_name == '\0') {
1597                 g_free(new_name);
1598                 return;
1599         }
1600
1601         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1602                                               new_name,
1603                                               addressbook_obj_name_compare)) {
1604                 alertpanel_error(_("The name already exists."));
1605                 g_free(new_name);
1606                 return;
1607         }
1608
1609         g_free(group->name);
1610         group->name = g_strdup(new_name);
1611
1612         addressbook_change_node_name(node, new_name);
1613         gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(node)->parent);
1614
1615         g_free(new_name);
1616
1617         addrbook.open_folder = TRUE;
1618         gtk_ctree_select(ctree, addrbook.opened);
1619 }
1620
1621 static void addressbook_edit_folder_cb(gpointer data, guint action,
1622                                        GtkWidget *widget)
1623 {
1624         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1625         AddressObject *obj;
1626         AddressFolder *folder;
1627         gchar *new_name = NULL;
1628         GtkCTreeNode *node = NULL, *parentNode = NULL;
1629
1630         if (!addrbook.selected) return;
1631         if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1632
1633         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1634         g_return_if_fail(obj != NULL);
1635         g_return_if_fail(obj->type == ADDR_FOLDER || obj->type == ADDR_GROUP ||
1636                         obj->type == ADDR_VCARD || obj->type == ADDR_JPILOT ||
1637                         obj->type == ADDR_CATEGORY || obj->type == ADDR_LDAP );
1638
1639         if (obj->type == ADDR_GROUP) {
1640                 addressbook_edit_group(addrbook.selected);
1641                 return;
1642         }
1643
1644         if( obj->type == ADDR_VCARD ) {
1645                 AddressVCard *vcard = ADDRESS_VCARD(obj);
1646                 if( addressbook_edit_vcard( vcard ) == NULL ) return;
1647                 new_name = vcard->name;
1648                 parentNode = addrbook.vcard;
1649         }
1650 #ifdef USE_JPILOT
1651         else if( obj->type == ADDR_JPILOT ) {
1652                 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
1653                 if( ! _have_pilot_library_ ) return;
1654                 if( addressbook_edit_jpilot( jpilot ) == NULL ) return;
1655                 new_name = jpilot->name;
1656                 parentNode = addrbook.jpilot;
1657         }
1658 #endif
1659 #ifdef USE_LDAP
1660         else if( obj->type == ADDR_LDAP ) {
1661                 AddressLDAP *ldapi = ADDRESS_LDAP(obj);
1662                 if( ! _have_ldap_library_ ) return;
1663                 if( addressbook_edit_ldap( ldapi ) == NULL ) return;
1664                 new_name = ldapi->name;
1665                 parentNode = addrbook.ldap;
1666         }
1667 #endif
1668
1669         if( new_name && parentNode) {
1670                 // Update node in tree view
1671                 node = gtk_ctree_find_by_row_data( ctree, addrbook.selected, obj );
1672                 if( ! node ) return;
1673                 addressbook_change_node_name( node, new_name );
1674                 gtk_ctree_sort_node(ctree, parentNode);
1675                 addrbook.open_folder = TRUE;
1676                 gtk_ctree_select( GTK_CTREE(addrbook.ctree), node );
1677                 return;
1678         }
1679
1680         folder = ADDRESS_FOLDER(obj);
1681         new_name = input_dialog(_("Edit folder"),
1682                                 _("Input the new name of folder:"),
1683                                 folder->name);
1684
1685         if (!new_name) return;
1686         g_strstrip(new_name);
1687         if (*new_name == '\0') {
1688                 g_free(new_name);
1689                 return;
1690         }
1691
1692         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1693                                               new_name,
1694                                               addressbook_obj_name_compare)) {
1695                 alertpanel_error(_("The name already exists."));
1696                 g_free(new_name);
1697                 return;
1698         }
1699
1700         g_free(folder->name);
1701         folder->name = g_strdup(new_name);
1702
1703         addressbook_change_node_name(addrbook.selected, new_name);
1704         gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1705
1706         g_free(new_name);
1707 }
1708
1709 static void addressbook_delete_folder_cb(gpointer data, guint action,
1710                                          GtkWidget *widget)
1711 {
1712         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1713         AddressObject *obj, *pobj;
1714         gchar *name;
1715         gchar *message;
1716         AlertValue aval;
1717
1718         if (!addrbook.selected) return;
1719         if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1720
1721         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1722         g_return_if_fail(obj != NULL);
1723
1724         if (obj->type == ADDR_GROUP)
1725                 name = ADDRESS_GROUP(obj)->name;
1726         else if (obj->type == ADDR_FOLDER)
1727                 name = ADDRESS_FOLDER(obj)->name;
1728         else if (obj->type == ADDR_VCARD)
1729                 name = ADDRESS_VCARD(obj)->name;
1730 #ifdef USE_JPILOT
1731         else if (obj->type == ADDR_JPILOT) {
1732                 if( ! _have_pilot_library_ ) return;
1733                 name = ADDRESS_JPILOT(obj)->name;
1734         }
1735 #endif
1736 #ifdef USE_LDAP
1737         else if (obj->type == ADDR_LDAP) {
1738                 if( ! _have_ldap_library_ ) return;
1739                 name = ADDRESS_LDAP(obj)->name;
1740         }
1741 #endif
1742         else
1743                 return;
1744
1745         message = g_strdup_printf(_("Really delete `%s' ?"), name);
1746         aval = alertpanel(_("Delete"), message, _("Yes"), _("No"), NULL);
1747         g_free(message);
1748         if (aval != G_ALERTDEFAULT) return;
1749
1750         pobj = gtk_ctree_node_get_row_data
1751                 (ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1752         if (!pobj) return;
1753         g_return_if_fail(pobj->type == ADDR_FOLDER);
1754         ADDRESS_FOLDER(pobj)->items =
1755                 g_list_remove(ADDRESS_FOLDER(pobj)->items, obj);
1756
1757         addressbook_delete_object(obj);
1758         addrbook.open_folder = TRUE;
1759         gtk_ctree_remove_node(ctree, addrbook.selected);
1760         addrbook.open_folder = FALSE;
1761 }
1762
1763 #define SET_LABEL_AND_ENTRY(str, entry, top) \
1764 { \
1765         label = gtk_label_new(str); \
1766         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
1767                          GTK_FILL, 0, 0, 0); \
1768         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
1769  \
1770         entry = gtk_entry_new(); \
1771         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
1772                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
1773 }
1774
1775 static void addressbook_edit_address_create(gboolean *cancelled)
1776 {
1777         GtkWidget *window;
1778         GtkWidget *vbox;
1779         GtkWidget *table;
1780         GtkWidget *label;
1781         GtkWidget *name_entry;
1782         GtkWidget *addr_entry;
1783         GtkWidget *rem_entry;
1784         GtkWidget *hbbox;
1785         GtkWidget *ok_btn;
1786         GtkWidget *cancel_btn;
1787
1788         debug_print("Creating edit_address window...\n");
1789
1790         window = gtk_window_new(GTK_WINDOW_DIALOG);
1791         gtk_widget_set_usize(window, 400, -1);
1792         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
1793         gtk_window_set_title(GTK_WINDOW(window), _("Edit address"));
1794         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1795         gtk_window_set_modal(GTK_WINDOW(window), TRUE); 
1796         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
1797                            GTK_SIGNAL_FUNC(edit_address_delete_event),
1798                            cancelled);
1799         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
1800                            GTK_SIGNAL_FUNC(edit_address_key_pressed),
1801                            cancelled);
1802
1803         vbox = gtk_vbox_new(FALSE, 8);
1804         gtk_container_add(GTK_CONTAINER(window), vbox);
1805
1806         table = gtk_table_new(3, 2, FALSE);
1807         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
1808         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
1809         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
1810
1811         SET_LABEL_AND_ENTRY(_("Name"),    name_entry, 0);
1812         SET_LABEL_AND_ENTRY(_("Address"), addr_entry, 1);
1813         SET_LABEL_AND_ENTRY(_("Remarks"), rem_entry,  2);
1814
1815         gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
1816                                 &cancel_btn, _("Cancel"), NULL, NULL);
1817         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
1818         gtk_widget_grab_default(ok_btn);
1819
1820         gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
1821                            GTK_SIGNAL_FUNC(edit_address_ok), cancelled);
1822         gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
1823                            GTK_SIGNAL_FUNC(edit_address_cancel), cancelled);
1824
1825         gtk_widget_show_all(vbox);
1826
1827         addredit.window     = window;
1828         addredit.name_entry = name_entry;
1829         addredit.addr_entry = addr_entry;
1830         addredit.rem_entry  = rem_entry;
1831         addredit.ok_btn     = ok_btn;
1832         addredit.cancel_btn = cancel_btn;
1833 }
1834
1835 static void edit_address_ok(GtkWidget *widget, gboolean *cancelled)
1836 {
1837         *cancelled = FALSE;
1838         gtk_main_quit();
1839 }
1840
1841 static void edit_address_cancel(GtkWidget *widget, gboolean *cancelled)
1842 {
1843         *cancelled = TRUE;
1844         gtk_main_quit();
1845 }
1846
1847 static gint edit_address_delete_event(GtkWidget *widget, GdkEventAny *event,
1848                                       gboolean *cancelled)
1849 {
1850         *cancelled = TRUE;
1851         gtk_main_quit();
1852
1853         return TRUE;
1854 }
1855
1856 static void edit_address_key_pressed(GtkWidget *widget, GdkEventKey *event,
1857                                      gboolean *cancelled)
1858 {
1859         if (event && event->keyval == GDK_Escape) {
1860                 *cancelled = TRUE;
1861                 gtk_main_quit();
1862         }
1863 }
1864
1865 static AddressItem *addressbook_edit_address(AddressItem *item)
1866 {
1867         static gboolean cancelled;
1868         const gchar *str;
1869
1870         if (!addredit.window)
1871                 addressbook_edit_address_create(&cancelled);
1872         gtk_widget_grab_focus(addredit.ok_btn);
1873         gtk_widget_grab_focus(addredit.name_entry);
1874         gtk_widget_show(addredit.window);
1875         manage_window_set_transient(GTK_WINDOW(addredit.window));
1876
1877         gtk_entry_set_text(GTK_ENTRY(addredit.name_entry), "");
1878         gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry), "");
1879         gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry),  "");
1880
1881         if (item) {
1882                 if (item->name)
1883                         gtk_entry_set_text(GTK_ENTRY(addredit.name_entry),
1884                                            item->name);
1885                 if (item->address)
1886                         gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry),
1887                                            item->address);
1888                 if (item->remarks)
1889                         gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry),
1890                                            item->remarks);
1891         }
1892
1893         gtk_main();
1894         gtk_widget_hide(addredit.window);
1895         if (cancelled == TRUE) return NULL;
1896
1897         str = gtk_entry_get_text(GTK_ENTRY(addredit.name_entry));
1898         if (*str == '\0') return NULL;
1899
1900         if (!item) {
1901                 item = mgu_create_address();
1902                 ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
1903         }
1904
1905         g_free(item->name);
1906         item->name = g_strdup(str);
1907
1908         str = gtk_entry_get_text(GTK_ENTRY(addredit.addr_entry));
1909         g_free(item->address);
1910         if (*str == '\0')
1911                 item->address = NULL;
1912         else
1913                 item->address = g_strdup(str);
1914
1915         str = gtk_entry_get_text(GTK_ENTRY(addredit.rem_entry));
1916         g_free(item->remarks);
1917         if (*str == '\0')
1918                 item->remarks = NULL;
1919         else
1920                 item->remarks = g_strdup(str);
1921
1922         return item;
1923 }
1924
1925 static void addressbook_new_address_cb(gpointer data, guint action,
1926                                        GtkWidget *widget)
1927 {
1928         AddressItem *item;
1929
1930         item = addressbook_edit_address(NULL);
1931
1932         if (item) {
1933                 addressbook_add_object(addrbook.selected,
1934                                        ADDRESS_OBJECT(item));
1935                 if (addrbook.selected == addrbook.opened) {
1936                         addrbook.open_folder = TRUE;
1937                         gtk_ctree_select(GTK_CTREE(addrbook.ctree),
1938                                          addrbook.opened);
1939                 }
1940         }
1941 }
1942
1943 static void addressbook_edit_address_cb(gpointer data, guint action,
1944                                         GtkWidget *widget)
1945 {
1946         GtkCList *clist = GTK_CLIST(addrbook.clist);
1947         GtkCTree *ctree;
1948         AddressObject *obj, *pobj;
1949         GtkCTreeNode *node = NULL, *parentNode = NULL;
1950         gchar *nodeName;
1951
1952         if (!clist->selection) {
1953                 addressbook_edit_folder_cb(NULL, 0, NULL);
1954                 return;
1955         }
1956
1957         obj = gtk_clist_get_row_data(clist, GPOINTER_TO_INT(clist->selection->data));
1958         g_return_if_fail(obj != NULL);
1959
1960         pobj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), addrbook.selected);
1961
1962         if (obj->type == ADDR_ITEM) {
1963                 AddressItem *item = ADDRESS_ITEM(obj);
1964
1965                 if( pobj ) {
1966                         // Prevent edit of readonly items
1967                         if(     pobj->type == ADDR_VCARD ||
1968                                 pobj->type == ADDR_JPILOT ||
1969                                 pobj->type == ADDR_CATEGORY ||
1970                                 pobj->type == ADDR_LDAP ) return;
1971                 }
1972
1973                 if (addressbook_edit_address(item) == NULL) return;
1974
1975                 addrbook.open_folder = TRUE;
1976                 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1977                 return;
1978         }
1979         else if (obj->type == ADDR_GROUP) {
1980                 addressbook_edit_group(NULL);
1981                 return;
1982         }
1983         else if( obj->type == ADDR_VCARD ) {
1984                 AddressVCard *vcard = ADDRESS_VCARD(obj);
1985                 if( addressbook_edit_vcard( vcard ) == NULL ) return;
1986                 nodeName = vcard->name;
1987                 parentNode = addrbook.vcard;
1988         }
1989 #ifdef USE_JPILOT
1990         else if( obj->type == ADDR_JPILOT ) {
1991                 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
1992                 if( addressbook_edit_jpilot( jpilot ) == NULL ) return;
1993                 nodeName = jpilot->name;
1994                 parentNode = addrbook.jpilot;
1995         }
1996 #endif
1997 #ifdef USE_LDAP
1998         else if( obj->type == ADDR_LDAP ) {
1999                 AddressLDAP *ldapi = ADDRESS_LDAP(obj);
2000                 if( addressbook_edit_ldap( ldapi ) == NULL ) return;
2001                 nodeName = ldapi->name;
2002                 parentNode = addrbook.ldap;
2003         }
2004 #endif
2005         else {
2006                 return;
2007         }
2008
2009         // Update tree node with node name
2010         ctree = GTK_CTREE( addrbook.ctree );
2011         node = gtk_ctree_find_by_row_data( ctree, addrbook.selected, obj );
2012         if( ! node ) return;
2013         addressbook_change_node_name( node, nodeName );
2014         gtk_ctree_sort_node(ctree, parentNode );
2015         addrbook.open_folder = TRUE;
2016         gtk_ctree_select( ctree, addrbook.opened );
2017 }
2018
2019 static void addressbook_delete_address_cb(gpointer data, guint action,
2020                                           GtkWidget *widget)
2021 {
2022         addressbook_del_clicked(NULL, NULL);
2023 }
2024
2025 static void close_cb(gpointer data, guint action, GtkWidget *widget)
2026 {
2027         addressbook_close();
2028 }
2029
2030 static void addressbook_set_clist(AddressObject *obj)
2031 {
2032         GtkCList *clist = GTK_CLIST(addrbook.clist);
2033         GList *items;
2034         gchar *text[N_COLS];
2035
2036         if (!obj) {
2037                 gtk_clist_clear(clist);
2038                 return;
2039         }
2040
2041         gtk_clist_freeze(clist);
2042         gtk_clist_clear(clist);
2043
2044         if (obj->type == ADDR_GROUP)
2045                 items = ADDRESS_GROUP(obj)->items;
2046         else if (obj->type == ADDR_FOLDER) {
2047                 items = ADDRESS_FOLDER(obj)->items;
2048         }
2049         else if (obj->type == ADDR_VCARD) {
2050                 items = ADDRESS_VCARD(obj)->items;
2051         }
2052 #ifdef USE_JPILOT
2053         else if (obj->type == ADDR_JPILOT) {
2054                 items = ADDRESS_JPILOT(obj)->items;
2055         }
2056         else if (obj->type == ADDR_CATEGORY) {
2057                 items = ADDRESS_CATEGORY(obj)->items;
2058         }
2059 #endif
2060 #ifdef USE_LDAP
2061         else if (obj->type == ADDR_LDAP) {
2062                 items = ADDRESS_LDAP(obj)->items;
2063         }
2064 #endif
2065         else {
2066                 gtk_clist_thaw(clist);
2067                 return;
2068         }
2069
2070         for (; items != NULL; items = items->next) {
2071                 AddressObject *iobj;
2072                 gint row;
2073                 iobj = ADDRESS_OBJECT(items->data);
2074                 if( iobj == NULL ) continue;
2075
2076                 if (iobj->type == ADDR_GROUP) {
2077                         AddressGroup *group;
2078
2079                         group = ADDRESS_GROUP(iobj);
2080                         text[COL_NAME]    = group->name;
2081                         text[COL_ADDRESS] = NULL;
2082                         text[COL_REMARKS] = NULL;
2083                         row = gtk_clist_append(clist, text);
2084                         gtk_clist_set_pixtext(clist, row, COL_NAME,
2085                                               group->name, 4,
2086                                               groupxpm, groupxpmmask);
2087                         gtk_clist_set_row_data(clist, row, iobj);
2088                 } if (iobj->type == ADDR_VCARD) {
2089                         AddressVCard *vcard;
2090
2091                         vcard = ADDRESS_VCARD(iobj);
2092                         text[COL_NAME]    = vcard->name;
2093                         text[COL_ADDRESS] = NULL;
2094                         text[COL_REMARKS] = NULL;
2095                         row = gtk_clist_append(clist, text);
2096                         gtk_clist_set_pixtext(clist, row, COL_NAME,
2097                                               vcard->name, 4,
2098                                               vcardxpm, vcardxpmmask);
2099                         gtk_clist_set_row_data(clist, row, iobj);
2100 #ifdef USE_JPILOT
2101                 } if (iobj->type == ADDR_JPILOT) {
2102                         AddressJPilot *jpilot;
2103
2104                         jpilot = ADDRESS_JPILOT(iobj);
2105                         text[COL_NAME]    = jpilot->name;
2106                         text[COL_ADDRESS] = NULL;
2107                         text[COL_REMARKS] = NULL;
2108                         row = gtk_clist_append(clist, text);
2109                         gtk_clist_set_pixtext(clist, row, COL_NAME,
2110                                               jpilot->name, 4,
2111                                               jpilotxpm, jpilotxpmmask);
2112                         gtk_clist_set_row_data(clist, row, iobj);
2113                 } if (iobj->type == ADDR_CATEGORY) {
2114                         AddressCategory *category;
2115
2116                         category = ADDRESS_CATEGORY(iobj);
2117                         text[COL_NAME]    = category->name;
2118                         text[COL_ADDRESS] = NULL;
2119                         text[COL_REMARKS] = NULL;
2120                         row = gtk_clist_append(clist, text);
2121                         gtk_clist_set_pixtext(clist, row, COL_NAME,
2122                                               category->name, 4,
2123                                               categoryxpm, categoryxpmmask);
2124                         gtk_clist_set_row_data(clist, row, iobj);
2125 #endif
2126 #ifdef USE_LDAP
2127                 } if (iobj->type == ADDR_LDAP) {
2128                         AddressLDAP *ldapi;
2129
2130                         ldapi = ADDRESS_LDAP(iobj);
2131                         text[COL_NAME]    = ldapi->name;
2132                         text[COL_ADDRESS] = NULL;
2133                         text[COL_REMARKS] = NULL;
2134                         row = gtk_clist_append(clist, text);
2135                         gtk_clist_set_pixtext(clist, row, COL_NAME,
2136                                               ldapi->name, 4,
2137                                               ldapxpm, ldapxpmmask);
2138                         gtk_clist_set_row_data(clist, row, iobj);
2139 #endif
2140                 } else if (iobj->type == ADDR_ITEM) {
2141                         AddressItem *item;
2142
2143                         item = ADDRESS_ITEM(iobj);
2144                         text[COL_NAME]    = item->name;
2145                         text[COL_ADDRESS] = item->address;
2146                         text[COL_REMARKS] = item->remarks;
2147                         row = gtk_clist_append(clist, text);
2148                         gtk_clist_set_row_data(clist, row, iobj);
2149                 }
2150         }
2151
2152         gtk_clist_sort(clist);
2153         gtk_clist_thaw(clist);
2154 }
2155
2156 static void addressbook_read_file(void)
2157 {
2158         XMLFile *file;
2159         gchar *path;
2160
2161         debug_print(_("Reading addressbook file..."));
2162
2163         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
2164         if ((file = xml_open_file(path)) == NULL) {
2165                 debug_print(_("%s doesn't exist.\n"), path);
2166                 g_free(path);
2167                 addressbook_get_tree(NULL, addrbook.common, ADDRESS_TAG_COMMON);
2168                 addressbook_get_tree(NULL, addrbook.personal, ADDRESS_TAG_PERSONAL);
2169                 addressbook_get_tree(NULL, addrbook.vcard, ADDRESS_TAG_VCARD);
2170 #ifdef USE_JPILOT
2171                 addressbook_get_tree(NULL, addrbook.jpilot, ADDRESS_TAG_JPILOT);
2172 #endif
2173 #ifdef USE_LDAP
2174                 addressbook_get_tree(NULL, addrbook.ldap, ADDRESS_TAG_LDAP);
2175 #endif
2176                 return;
2177         }
2178         g_free(path);
2179
2180         xml_get_dtd(file);
2181
2182         if (xml_parse_next_tag(file) < 0 ||
2183             xml_compare_tag(file, "addressbook") == FALSE) {
2184                 g_warning("Invalid addressbook data\n");
2185                 xml_close_file(file);
2186                 return;
2187         }
2188
2189         addressbook_get_tree(file, addrbook.common, ADDRESS_TAG_COMMON);
2190         addressbook_get_tree(file, addrbook.personal, ADDRESS_TAG_PERSONAL);
2191         addressbook_get_tree(file, addrbook.vcard, ADDRESS_TAG_VCARD);
2192 #ifdef USE_JPILOT
2193         addressbook_get_tree(file, addrbook.jpilot, ADDRESS_TAG_JPILOT);
2194 #endif
2195 #ifdef USE_LDAP
2196         addressbook_get_tree(file, addrbook.ldap, ADDRESS_TAG_LDAP);
2197 #endif
2198
2199         xml_close_file(file);
2200
2201         debug_print(_("done.\n"));
2202 }
2203
2204 static void addressbook_get_tree(XMLFile *file, GtkCTreeNode *node,
2205                                  const gchar *folder_tag)
2206 {
2207         AddressFolder *folder;
2208
2209         g_return_if_fail(node != NULL);
2210
2211         folder = g_new(AddressFolder, 1);
2212         ADDRESS_OBJECT(folder)->type = ADDR_FOLDER;
2213         folder->name = g_strdup(folder_tag);
2214         folder->items = NULL;
2215         gtk_ctree_node_set_row_data(GTK_CTREE(addrbook.ctree), node, folder);
2216
2217         if (file) {
2218                 if (xml_parse_next_tag(file) < 0 ||
2219                     xml_compare_tag(file, folder_tag) == FALSE) {
2220                         g_warning("Invalid addressbook data\n");
2221                         return;
2222                 }
2223         }
2224
2225         if (file) addressbook_add_objs(file, node);
2226 }
2227
2228 static void addressbook_add_objs(XMLFile *file, GtkCTreeNode *node)
2229 {
2230         GList *attr;
2231         guint prev_level;
2232         GtkCTreeNode *new_node;
2233
2234         for (;;) {
2235                 prev_level = file->level;
2236                 if (xml_parse_next_tag(file) < 0) return;
2237                 if (file->level < prev_level) return;
2238
2239                 if (xml_compare_tag(file, "group")) {
2240                         AddressGroup *group;
2241
2242                         group = g_new(AddressGroup, 1);
2243                         ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
2244                         attr = xml_get_current_tag_attr(file);
2245                         if (attr)
2246                                 group->name = g_strdup(((XMLAttr *)attr->data)->value);
2247                         else
2248                                 group->name = NULL;
2249                         group->items = NULL;
2250
2251                         new_node = addressbook_add_object
2252                                 (node, ADDRESS_OBJECT(group));
2253
2254                         addressbook_add_objs(file, new_node);
2255                 } else if (xml_compare_tag(file, "folder")) {
2256                         AddressFolder *folder;
2257
2258                         folder = g_new(AddressFolder, 1);
2259                         ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
2260                         attr = xml_get_current_tag_attr(file);
2261                         if (attr)
2262                                 folder->name = g_strdup(((XMLAttr *)attr->data)->value);
2263                         else
2264                                 folder->name = NULL;
2265                         folder->items = NULL;
2266
2267                         new_node = addressbook_add_object
2268                                 (node, ADDRESS_OBJECT(folder));
2269
2270                         addressbook_add_objs(file, new_node);
2271                 }
2272                 else if( xml_compare_tag( file, "vcard" ) ) {
2273                         AddressVCard *vcard;
2274                         vcard = addressbook_parse_vcard( file );
2275                         if( ! vcard ) return;
2276                         new_node = addressbook_add_object
2277                                 (node, ADDRESS_OBJECT(vcard));
2278                 }
2279 #ifdef USE_JPILOT
2280                 else if( xml_compare_tag( file, "jpilot" ) ) {
2281                         AddressJPilot *jpilot;
2282                         jpilot = addressbook_parse_jpilot( file );
2283                         if( ! jpilot ) return;
2284                         new_node = addressbook_add_object
2285                                 (node, ADDRESS_OBJECT(jpilot));
2286                 }
2287 #endif
2288 #ifdef USE_LDAP
2289                 else if( xml_compare_tag( file, "server" ) ) {
2290                         AddressLDAP *ldapi;
2291                         ldapi = addressbook_parse_ldap( file );
2292                         if( ! ldapi ) return;
2293                         new_node = addressbook_add_object
2294                                 (node, ADDRESS_OBJECT(ldapi));
2295                 }
2296 #endif
2297                 else if (xml_compare_tag(file, "item")) {
2298                         AddressItem *item;
2299
2300                         item = addressbook_parse_item(file);
2301                         if (!item) return;
2302                         new_node = addressbook_add_object
2303                                 (node, ADDRESS_OBJECT(item));
2304                 } else {
2305                         g_warning("Invalid tag\n");
2306                         return;
2307                 }
2308         }
2309 }
2310
2311 static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
2312                                             AddressObject *obj)
2313 {
2314         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2315         GtkCTreeNode *added;
2316         AddressObject *pobj;
2317
2318         g_return_val_if_fail(node != NULL, NULL);
2319         g_return_val_if_fail(obj  != NULL, NULL);
2320
2321         pobj = gtk_ctree_node_get_row_data(ctree, node);
2322         g_return_val_if_fail(pobj != NULL, NULL);
2323
2324         if (pobj->type == ADDR_ITEM) {
2325                 g_warning("Parent object mustn't be an item.\n");
2326                 return NULL;
2327         }
2328         if (pobj->type == ADDR_FOLDER &&
2329             (obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER))
2330                 gtk_ctree_expand(ctree, node);
2331
2332         if (pobj->type == ADDR_FOLDER && obj->type == ADDR_VCARD )
2333                 gtk_ctree_expand(ctree, node);
2334
2335         if (pobj->type == ADDR_FOLDER && obj->type == ADDR_JPILOT )
2336                 gtk_ctree_expand(ctree, node);
2337
2338         if (pobj->type == ADDR_FOLDER && obj->type == ADDR_LDAP )
2339                 gtk_ctree_expand(ctree, node);
2340
2341         if (obj->type == ADDR_GROUP) {
2342                 AddressGroup *group = ADDRESS_GROUP(obj);
2343
2344                 if (pobj->type != ADDR_FOLDER) {
2345                         g_warning("Group can't be added in another group.\n");
2346                         return NULL;
2347                 }
2348
2349                 added = gtk_ctree_insert_node(ctree, node, NULL,
2350                                               &group->name, FOLDER_SPACING,
2351                                               groupxpm, groupxpmmask,
2352                                               groupxpm, groupxpmmask,
2353                                               TRUE, FALSE);
2354                 gtk_ctree_node_set_row_data(ctree, added, obj);
2355         } else if (obj->type == ADDR_FOLDER) {
2356                 AddressFolder *folder = ADDRESS_FOLDER(obj);
2357
2358                 if (pobj->type != ADDR_FOLDER) {
2359                         g_warning("Group can't contain folder.\n");
2360                         return NULL;
2361                 }
2362
2363                 added = gtk_ctree_insert_node(ctree, node, NULL,
2364                                               &folder->name, FOLDER_SPACING,
2365                                               folderxpm, folderxpmmask,
2366                                               folderopenxpm, folderopenxpmmask,
2367                                               FALSE, FALSE);
2368                 gtk_ctree_node_set_row_data(ctree, added, obj);
2369
2370         }
2371         else if (obj->type == ADDR_VCARD) {
2372                 AddressVCard *vcard = ADDRESS_VCARD(obj);
2373                 added = gtk_ctree_insert_node(ctree, node, NULL,
2374                                               &vcard->name, FOLDER_SPACING,
2375                                               vcardxpm, vcardxpmmask,
2376                                               vcardxpm, vcardxpmmask,
2377                                               TRUE, FALSE);
2378                 gtk_ctree_node_set_row_data(ctree, added, obj);
2379         }
2380 #ifdef USE_JPILOT
2381         else if (obj->type == ADDR_JPILOT) {
2382                 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
2383                 added = gtk_ctree_insert_node(ctree, node, NULL,
2384                                               &jpilot->name, FOLDER_SPACING,
2385                                               jpilotxpm, jpilotxpmmask,
2386                                               jpilotxpm, jpilotxpmmask,
2387                                               FALSE, FALSE);
2388                 gtk_ctree_node_set_row_data(ctree, added, obj);
2389         }
2390         else if (obj->type == ADDR_CATEGORY) {
2391                 AddressCategory *category = ADDRESS_CATEGORY(obj);
2392                 added = gtk_ctree_insert_node(ctree, node, NULL,
2393                                               &category->name, FOLDER_SPACING,
2394                                               categoryxpm, categoryxpmmask,
2395                                               categoryxpm, categoryxpmmask,
2396                                               TRUE, FALSE);
2397                 gtk_ctree_node_set_row_data(ctree, added, obj);
2398         }
2399 #endif
2400 #ifdef USE_LDAP
2401         else if (obj->type == ADDR_LDAP) {
2402                 AddressLDAP *server = ADDRESS_LDAP(obj);
2403                 added = gtk_ctree_insert_node(ctree, node, NULL,
2404                                               &server->name, FOLDER_SPACING,
2405                                               ldapxpm, ldapxpmmask,
2406                                               ldapxpm, ldapxpmmask,
2407                                               TRUE, FALSE);
2408                 gtk_ctree_node_set_row_data(ctree, added, obj);
2409         }
2410 #endif
2411         else {
2412                 added = node;
2413         }
2414
2415         if (obj->type == ADDR_GROUP || obj->type == ADDR_ITEM) {
2416                 if (pobj->type == ADDR_GROUP) {
2417                         AddressGroup *group = ADDRESS_GROUP(pobj);
2418
2419                         group->items = g_list_append(group->items, obj);
2420                 } else if (pobj->type == ADDR_FOLDER) {
2421                         AddressFolder *folder = ADDRESS_FOLDER(pobj);
2422
2423                         folder->items = g_list_append(folder->items, obj);
2424                 }
2425         }
2426
2427         if (pobj->type == ADDR_FOLDER) {
2428                 if (obj->type == ADDR_VCARD || obj->type == ADDR_JPILOT || obj->type == ADDR_LDAP) {
2429                         AddressFolder *folder = ADDRESS_FOLDER(pobj);
2430                         folder->items = g_list_append(folder->items, obj);
2431                 }
2432         }
2433
2434         gtk_ctree_sort_node(ctree, node);
2435
2436         return added;
2437 }
2438
2439 static void addressbook_delete_object(AddressObject *obj)
2440 {
2441         if (!obj) return;
2442
2443         if (obj->type == ADDR_ITEM) {
2444                 AddressItem *item = ADDRESS_ITEM(obj);
2445
2446                 mgu_free_address( item );
2447         } else if (obj->type == ADDR_GROUP) {
2448                 AddressGroup *group = ADDRESS_GROUP(obj);
2449
2450                 g_free(group->name);
2451                 while (group->items != NULL) {
2452                         addressbook_delete_object
2453                                 (ADDRESS_OBJECT(group->items->data));
2454                         group->items = g_list_remove(group->items,
2455                                                      group->items->data);
2456                 }
2457                 g_free(group);
2458         } else if (obj->type == ADDR_FOLDER) {
2459                 AddressFolder *folder = ADDRESS_FOLDER(obj);
2460
2461                 g_free(folder->name);
2462                 while (folder->items != NULL) {
2463                         addressbook_delete_object
2464                                 (ADDRESS_OBJECT(folder->items->data));
2465                         folder->items = g_list_remove(folder->items,
2466                                                       folder->items->data);
2467                 }
2468                 g_free(folder);
2469         }
2470         else if( obj->type == ADDR_VCARD ) {
2471                 AddressVCard *vcard = ADDRESS_VCARD(obj);
2472                 g_free( vcard->name );
2473                 vcard_free( vcard->cardFile );
2474                 vcard->cardFile = NULL;
2475                 vcard->items = NULL;
2476                 g_free( vcard );
2477         }
2478 #ifdef USE_JPILOT
2479         else if( obj->type == ADDR_JPILOT ) {
2480                 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
2481                 g_free( jpilot->name );
2482                 jpilot_free( jpilot->pilotFile );
2483                 jpilot->pilotFile = NULL;
2484                 jpilot->items = NULL;
2485                 g_free( jpilot );
2486         }
2487 #endif
2488 #ifdef USE_LDAP
2489         else if( obj->type == ADDR_LDAP ) {
2490                 AddressLDAP *ldapi = ADDRESS_LDAP(obj);
2491                 g_free( ldapi->name );
2492                 syldap_free( ldapi->ldapServer );
2493                 ldapi->ldapServer = NULL;
2494                 ldapi->items = NULL;
2495                 g_free( ldapi );
2496         }
2497 #endif
2498 }
2499
2500 static AddressObject *addressbook_find_object_by_name(GtkCTreeNode *node,
2501                                                       const gchar *name)
2502 {
2503         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2504         AddressObject *obj;
2505         GList *found;
2506
2507         g_return_val_if_fail(node != NULL, NULL);
2508
2509         obj = gtk_ctree_node_get_row_data(ctree, node);
2510         g_return_val_if_fail(obj != NULL, NULL);
2511
2512         if (obj->type == ADDR_GROUP) {
2513                 AddressGroup *group = ADDRESS_GROUP(obj);
2514
2515                 found = g_list_find_custom(group->items, (gpointer)name,
2516                                            addressbook_obj_name_compare);
2517                 if (found) return ADDRESS_OBJECT(found->data);
2518         } else if (obj->type == ADDR_FOLDER) {
2519                 AddressFolder *folder = ADDRESS_FOLDER(obj);
2520
2521                 found = g_list_find_custom(folder->items, (gpointer)name,
2522                                            addressbook_obj_name_compare);
2523                 if (found) return ADDRESS_OBJECT(found->data);
2524         } else if (obj->type == ADDR_ITEM) {
2525                 if (!addressbook_obj_name_compare(obj, name)) return obj;
2526         }
2527
2528         return NULL;
2529 }
2530
2531 static AddressItem *addressbook_parse_item(XMLFile *file)
2532 {
2533         gchar *element;
2534         AddressItem *item;
2535         guint level;
2536
2537         item = mgu_create_address();
2538         ADDRESS_OBJECT(item)->type = ADDR_ITEM;
2539
2540         level = file->level;
2541
2542         while (xml_parse_next_tag(file) == 0) {
2543                 if (file->level < level) return item;
2544                 if (file->level == level) break;
2545
2546                 element = xml_get_element(file);
2547
2548                 if (xml_compare_tag(file, "name")) {
2549                         item->name = element;
2550                 } else if (xml_compare_tag(file, "address")) {
2551                         item->address = element;
2552                 } else if (xml_compare_tag(file, "remarks")) {
2553                         item->remarks = element;
2554                 }
2555
2556                 if (xml_parse_next_tag(file) < 0) break;
2557                 if (file->level != level) break;
2558         }
2559
2560         g_warning("addressbook_parse_item(): Parse error\n");
2561         mgu_free_address( item );
2562 }
2563
2564 void addressbook_export_to_file(void)
2565 {
2566         PrefFile *pfile;
2567         gchar *path;
2568
2569         if (!addrbook.ctree) return;
2570
2571         debug_print(_("Exporting addressbook to file..."));
2572
2573         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
2574         if ((pfile = prefs_write_open(path)) == NULL) {
2575                 g_free(path);
2576                 return;
2577         }
2578         g_free(path);
2579
2580         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
2581                 conv_get_current_charset_str());
2582         fputs("<addressbook>\n\n", pfile->fp);
2583
2584         addressbook_xml_recursive_write(NULL, pfile->fp);
2585
2586         fputs("</addressbook>\n", pfile->fp);
2587
2588         if (prefs_write_close(pfile) < 0) {
2589                 g_warning(_("failed to write addressbook data.\n"));
2590                 return;
2591         }
2592
2593         debug_print(_("done.\n"));
2594 }
2595
2596 /* Most part of this function was taken from gtk_ctree_pre_recursive() and
2597    gtk_ctree_post_recursive(). */
2598 static void addressbook_xml_recursive_write(GtkCTreeNode *node, FILE *fp)
2599 {
2600         GtkCTreeNode *work;
2601         GtkCTreeNode *tmp;
2602
2603         if (node) {
2604                 work = GTK_CTREE_ROW(node)->children;
2605                 addressbook_node_write_begin(node, fp);
2606         } else
2607                 work = GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list);
2608
2609         while (work) {
2610                 tmp = GTK_CTREE_ROW(work)->sibling;
2611                 addressbook_xml_recursive_write(work, fp);
2612                 work = tmp;
2613         }
2614
2615         if (node)
2616                 addressbook_node_write_end(node, fp);
2617 }
2618
2619 static void addressbook_node_write_begin(GtkCTreeNode *node, FILE *fp)
2620 {
2621         AddressObject *obj;
2622
2623         obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
2624         g_return_if_fail(obj != NULL);
2625
2626         if (obj->type == ADDR_FOLDER) {
2627                 AddressFolder *folder = ADDRESS_FOLDER(obj);
2628
2629                 if (GTK_CTREE_ROW(node)->level == 1) {
2630                         fprintf(fp, "<%s>\n", folder->name);
2631                 } else {
2632                         tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
2633                         fputs("<folder name=\"", fp);
2634                         xml_file_put_escape_str(fp, folder->name);
2635                         fputs("\">\n", fp);
2636                 }
2637         } else if (obj->type == ADDR_GROUP) {
2638                 AddressGroup *group = ADDRESS_GROUP(obj);
2639
2640                 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
2641                 fputs("<group name=\"", fp);
2642                 xml_file_put_escape_str(fp, group->name);
2643                 fputs("\">\n", fp);
2644         }
2645 }
2646
2647 static void addressbook_node_write_end(GtkCTreeNode *node, FILE *fp)
2648 {
2649         AddressObject *obj;
2650
2651         obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
2652         g_return_if_fail(obj != NULL);
2653
2654         if (obj->type == ADDR_FOLDER) {
2655                 AddressFolder *folder = ADDRESS_FOLDER(obj);
2656
2657                 addressbook_write_items(fp, folder->items,
2658                                         GTK_CTREE_ROW(node)->level);
2659
2660                 if (GTK_CTREE_ROW(node)->level == 1) {
2661                         fprintf(fp, "</%s>\n\n", folder->name);
2662                 } else {
2663                         tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
2664                         fputs("</folder>\n", fp);
2665                 }
2666         } else if (obj->type == ADDR_GROUP) {
2667                 AddressGroup *group = ADDRESS_GROUP(obj);
2668
2669                 addressbook_write_items(fp, group->items,
2670                                         GTK_CTREE_ROW(node)->level);
2671
2672                 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
2673                 fputs("</group>\n", fp);
2674         }
2675         else if (obj->type == ADDR_VCARD) {
2676                 AddressVCard *vcard = ADDRESS_VCARD(obj);
2677                 addressbook_write_vcard( fp, vcard, GTK_CTREE_ROW(node)->level);
2678         }
2679 #ifdef USE_JPILOT
2680         else if (obj->type == ADDR_JPILOT) {
2681                 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
2682                 addressbook_write_jpilot( fp, jpilot, GTK_CTREE_ROW(node)->level);
2683         }
2684 #endif
2685 #ifdef USE_LDAP
2686         else if (obj->type == ADDR_LDAP) {
2687                 AddressLDAP *ldap = ADDRESS_LDAP(obj);
2688                 addressbook_write_ldap( fp, ldap, GTK_CTREE_ROW(node)->level);
2689         }
2690 #endif
2691 }
2692
2693 static void addressbook_write_items(FILE *fp, GList *items, guint level)
2694 {
2695         AddressItem *item;
2696
2697         for (; items != NULL; items = items->next) {
2698                 if (ADDRESS_OBJECT_TYPE(items->data) == ADDR_ITEM) {
2699                         item = ADDRESS_ITEM(items->data);
2700
2701                         tab_indent_out(fp, level);
2702                         fputs("<item>\n", fp);
2703
2704                         tab_indent_out(fp, level + 1);
2705                         fputs("<name>", fp);
2706                         xml_file_put_escape_str(fp, item->name);
2707                         fputs("</name>\n", fp);
2708
2709                         tab_indent_out(fp, level + 1);
2710                         fputs("<address>", fp);
2711                         xml_file_put_escape_str(fp, item->address);
2712                         fputs("</address>\n", fp);
2713
2714                         tab_indent_out(fp, level + 1);
2715                         fputs("<remarks>", fp);
2716                         xml_file_put_escape_str(fp, item->remarks);
2717                         fputs("</remarks>\n", fp);
2718
2719                         tab_indent_out(fp, level);
2720                         fputs("</item>\n", fp);
2721                 }
2722         }
2723 }
2724
2725 static void tab_indent_out(FILE *fp, guint level)
2726 {
2727         gint i;
2728
2729         for (i = 0; i < level; i++)
2730                 fputs("    ", fp);
2731 }
2732
2733 static void key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
2734 {
2735         if (event && event->keyval == GDK_Escape)
2736                 addressbook_close();
2737 }
2738
2739 static gint addressbook_list_compare_func(GtkCList *clist,
2740                                           gconstpointer ptr1,
2741                                           gconstpointer ptr2)
2742 {
2743         AddressObject *obj1 = ((GtkCListRow *)ptr1)->data;
2744         AddressObject *obj2 = ((GtkCListRow *)ptr2)->data;
2745         gchar *name1, *name2;
2746
2747         if (obj1) {
2748                 if (obj1->type == ADDR_ITEM)
2749                         name1 = ADDRESS_ITEM(obj1)->name;
2750                 else if (obj1->type == ADDR_GROUP)
2751                         name1 = ADDRESS_GROUP(obj1)->name;
2752                 else if (obj1->type == ADDR_FOLDER)
2753                         name1 = ADDRESS_FOLDER(obj1)->name;
2754                 else if (obj1->type == ADDR_VCARD)
2755                         name1 = ADDRESS_VCARD(obj1)->name;
2756 #ifdef USE_JPILOT
2757                 else if (obj1->type == ADDR_JPILOT)
2758                         name1 = ADDRESS_JPILOT(obj1)->name;
2759                 else if (obj1->type == ADDR_CATEGORY)
2760                         name1 = ADDRESS_CATEGORY(obj1)->name;
2761 #endif
2762 #ifdef USE_LDAP
2763                 else if (obj1->type == ADDR_LDAP)
2764                         name1 = ADDRESS_LDAP(obj1)->name;
2765 #endif
2766                 else
2767                         name1 = NULL;
2768         } else
2769                 name1 = NULL;
2770
2771         if (obj2) {
2772                 if (obj2->type == ADDR_ITEM)
2773                         name2 = ADDRESS_ITEM(obj2)->name;
2774                 else if (obj2->type == ADDR_GROUP)
2775                         name2 = ADDRESS_GROUP(obj2)->name;
2776                 else if (obj2->type == ADDR_FOLDER)
2777                         name2 = ADDRESS_FOLDER(obj2)->name;
2778                 else if (obj2->type == ADDR_VCARD)
2779                         name2 = ADDRESS_VCARD(obj2)->name;
2780 #ifdef USE_JPILOT
2781                 else if (obj2->type == ADDR_JPILOT)
2782                         name2 = ADDRESS_JPILOT(obj2)->name;
2783                 else if (obj2->type == ADDR_CATEGORY)
2784                         name2 = ADDRESS_CATEGORY(obj2)->name;
2785 #endif
2786 #ifdef USE_LDAP
2787                 else if (obj2->type == ADDR_LDAP)
2788                         name2 = ADDRESS_LDAP(obj2)->name;
2789 #endif
2790                 else
2791                         name2 = NULL;
2792         } else
2793                 name2 = NULL;
2794
2795         if (!name1)
2796                 return (name2 != NULL);
2797         if (!name2)
2798                 return -1;
2799
2800         return strcasecmp(name1, name2);
2801 }
2802
2803 static gint addressbook_obj_name_compare(gconstpointer a, gconstpointer b)
2804 {
2805         const AddressObject *obj = a;
2806         const gchar *name = b;
2807
2808         if (!obj || !name) return -1;
2809
2810         if (obj->type == ADDR_GROUP) {
2811                 AddressGroup *group = ADDRESS_GROUP(obj);
2812                 if (!group->name)
2813                         return -1;
2814                 else
2815                         return strcasecmp(group->name, name);
2816         } else if (obj->type == ADDR_FOLDER) {
2817                 AddressFolder *folder = ADDRESS_FOLDER(obj);
2818                 if (!folder->name)
2819                         return -1;
2820                 else
2821                         return strcasecmp(folder->name, name);
2822         }
2823         else if (obj->type == ADDR_VCARD) {
2824                 AddressVCard *vcard = ADDRESS_VCARD(obj);
2825                 if (!vcard->name)
2826                         return -1;
2827                 else
2828                         return strcasecmp(vcard->name, name);
2829         }
2830 #ifdef USE_JPILOT
2831         else if (obj->type == ADDR_JPILOT) {
2832                 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
2833                 if (!jpilot->name)
2834                         return -1;
2835                 else
2836                         return strcasecmp(jpilot->name, name);
2837         }
2838         else if (obj->type == ADDR_CATEGORY) {
2839                 AddressCategory *category = ADDRESS_CATEGORY(obj);
2840                 if (!category->name)
2841                         return -1;
2842                 else
2843                         return strcasecmp(category->name, name);
2844         }
2845 #endif
2846 #ifdef USE_LDAP
2847         else if (obj->type == ADDR_LDAP) {
2848                 AddressLDAP *server = ADDRESS_LDAP(obj);
2849                 if (!server->name)
2850                         return -1;
2851                 else
2852                         return strcasecmp(server->name, name);
2853         }
2854 #endif
2855         else if (obj->type == ADDR_ITEM) {
2856                 AddressItem *item = ADDRESS_ITEM(obj);
2857                 if (!item->name)
2858                         return -1;
2859                 else
2860                         return strcasecmp(item->name, name);
2861         } else
2862                 return -1;
2863 }
2864
2865 static AddressVCard *addressbook_parse_vcard(XMLFile *file) {
2866         AddressVCard *item = NULL;
2867         VCardFile *vcf;
2868         GList *attr;
2869         gchar *name, *value;
2870
2871         vcf = vcard_create();
2872         attr = xml_get_current_tag_attr( file );
2873         while( attr ) {
2874                 name = ((XMLAttr *)attr->data)->name;
2875                 value = ((XMLAttr *)attr->data)->value;
2876                 if( strcmp( name, "name" ) == 0 ) {
2877                         vcard_set_name( vcf, value );
2878                 }
2879                 else if( strcmp( name, "file" ) == 0) {
2880                         vcard_set_file( vcf, value );
2881                 }
2882                 attr = g_list_next( attr );
2883         }
2884
2885         // Move to next tag
2886         if( xml_parse_next_tag( file ) >= 0 ) {
2887                 if( vcard_validate( vcf ) ) {
2888                         item = g_new( AddressVCard, 1 );
2889                         ADDRESS_OBJECT(item)->type = ADDR_VCARD;
2890                         item->name = g_strdup( vcf->name );
2891                         item->cardFile = vcf;
2892                         item->items = NULL;
2893                         return item;
2894                 }
2895         }
2896
2897         // Must be an invalid tag or data.
2898         g_warning( "addressbook_parse_vcard(): Parse error\n");
2899         vcard_free( vcf );
2900         vcf = NULL;
2901         item = NULL;
2902         return NULL;
2903 }
2904
2905 static void addressbook_write_vcard( FILE *fp, AddressVCard *vcard, guint level ) {
2906         VCardFile *cardFile = vcard->cardFile;
2907         if( cardFile ) {
2908                 tab_indent_out(fp, 1);
2909                 fputs("<vcard ", fp);
2910                 fputs("name=\"", fp);
2911                 xml_file_put_escape_str(fp, cardFile->name);
2912                 fputs("\"", fp);
2913                 fputs(" file=\"", fp);
2914                 xml_file_put_escape_str(fp, cardFile->path);
2915                 fputs("\"", fp);
2916                 fputs(" />\n", fp);
2917         }
2918 }
2919
2920 #ifdef USE_JPILOT
2921 static AddressJPilot *addressbook_parse_jpilot(XMLFile *file) {
2922         AddressJPilot *item = NULL;
2923         JPilotFile *jpf;
2924         GList *attr;
2925         gchar *name, *value;
2926
2927         jpf = jpilot_create();
2928         attr = xml_get_current_tag_attr( file );
2929         while( attr ) {
2930                 name = ((XMLAttr *)attr->data)->name;
2931                 value = ((XMLAttr *)attr->data)->value;
2932                 if( strcmp( name, "name" ) == 0 ) {
2933                         jpilot_set_name( jpf, value );
2934                 }
2935                 else if( strcmp( name, "file" ) == 0 ) {
2936                         jpilot_set_file( jpf, value );
2937                 }
2938                 else if( strcmp( name, "custom-1" ) == 0 ) {
2939                         jpilot_add_custom_label( jpf, value );
2940                 }
2941                 else if( strcmp( name, "custom-2" ) == 0 ) {
2942                         jpilot_add_custom_label( jpf, value );
2943                 }
2944                 else if( strcmp( name, "custom-3" ) == 0 ) {
2945                         jpilot_add_custom_label( jpf, value );
2946                 }
2947                 else if( strcmp( name, "custom-4" ) == 0 ) {
2948                         jpilot_add_custom_label( jpf, value );
2949                 }
2950                 attr = g_list_next( attr );
2951         }
2952
2953         // Move to next tag
2954         if( xml_parse_next_tag( file ) >= 0 ) {
2955                 if( jpilot_validate( jpf ) ) {
2956                         item = g_new( AddressJPilot, 1 );
2957                         ADDRESS_OBJECT(item)->type = ADDR_JPILOT;
2958                         item->name = g_strdup( jpf->name );
2959                         item->pilotFile = jpf;
2960                         item->items = NULL;
2961                         return item;
2962                 }
2963         }
2964
2965         // Must be an invalid tag or data.
2966         g_warning( "addressbook_parse_jpilot(): Parse error\n");
2967         jpilot_free( jpf );
2968         jpf = NULL;
2969         item = NULL;
2970         return NULL;
2971 }
2972
2973 static void addressbook_write_jpilot( FILE *fp, AddressJPilot *jpilot, guint level ) {
2974         JPilotFile *pilotFile = jpilot->pilotFile;
2975         if( pilotFile ) {
2976                 gint ind;
2977                 GList *node;
2978                 GList *customLbl = jpilot_get_custom_labels( pilotFile );
2979                 tab_indent_out(fp, 1);
2980                 fputs("<jpilot ", fp);
2981                 fputs("name=\"", fp);
2982                 xml_file_put_escape_str(fp, pilotFile->name);
2983                 fputs("\" file=\"", fp);
2984                 xml_file_put_escape_str(fp, pilotFile->path);
2985
2986                 fputs( "\" ", fp );
2987                 node = customLbl;
2988                 ind = 1;
2989                 while( node ) {
2990                         fprintf( fp, "custom-%d=\"", ind );
2991                         xml_file_put_escape_str( fp, node->data );
2992                         fputs( "\" ", fp );
2993                         ind++;
2994                         node = g_list_next( node );
2995                 }
2996                 fputs("/>\n", fp);
2997         }
2998 }
2999 #endif
3000
3001 static void addressbook_new_vcard_cb( gpointer data, guint action, GtkWidget *widget ) {
3002         AddressVCard *vcard;
3003
3004         if( addrbook.selected != addrbook.vcard ) return;
3005         vcard = addressbook_edit_vcard( NULL );
3006         if( vcard ) {
3007                 addressbook_add_object( addrbook.selected, ADDRESS_OBJECT(vcard) );
3008                 if( addrbook.selected == addrbook.opened ) {
3009                         addrbook.open_folder = TRUE;
3010                         gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
3011                 }
3012         }
3013 }
3014
3015 static void addressbook_vcard_show_message( VCardFile *vcf ) {
3016         *addressbook_msgbuf = '\0';
3017         if( vcf ) {
3018                 if( vcf->retVal == MGU_SUCCESS ) {
3019                         sprintf( addressbook_msgbuf, "%s", vcf->name );
3020                 }
3021                 else {
3022                         sprintf( addressbook_msgbuf, "%s: %s", vcf->name, mgu_error2string( vcf->retVal ) );
3023                 }
3024         }
3025         addressbook_status_show( addressbook_msgbuf );
3026 }
3027
3028 #ifdef USE_JPILOT
3029 static void addressbook_new_jpilot_cb( gpointer data, guint action, GtkWidget *widget ) {
3030         AddressJPilot *jpilot;
3031
3032         if( addrbook.selected != addrbook.jpilot ) return;
3033         if( ! _have_pilot_library_ ) return;
3034         jpilot = addressbook_edit_jpilot( NULL );
3035         if( jpilot ) {
3036                 addressbook_add_object( addrbook.selected, ADDRESS_OBJECT(jpilot) );
3037                 if( addrbook.selected == addrbook.opened ) {
3038                         addrbook.open_folder = TRUE;
3039                         gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
3040                 }
3041         }
3042 }
3043
3044 static void addressbook_jpilot_show_message( JPilotFile *jpf ) {
3045         *addressbook_msgbuf = '\0';
3046         if( jpf ) {
3047                 if( jpf->retVal == MGU_SUCCESS ) {
3048                         sprintf( addressbook_msgbuf, "%s", jpf->name );
3049                 }
3050                 else {
3051                         sprintf( addressbook_msgbuf, "%s: %s", jpf->name, mgu_error2string( jpf->retVal ) );
3052                 }
3053         }
3054         addressbook_status_show( addressbook_msgbuf );
3055 }
3056
3057 #endif
3058
3059 #ifdef USE_LDAP
3060 static void addressbook_new_ldap_cb( gpointer data, guint action, GtkWidget *widget ) {
3061         AddressLDAP *ldapi;
3062
3063         if( addrbook.selected != addrbook.ldap ) return;
3064         if( ! _have_ldap_library_ ) return;
3065         ldapi = addressbook_edit_ldap( NULL );
3066         if( ldapi ) {
3067                 addressbook_add_object( addrbook.selected, ADDRESS_OBJECT(ldapi) );
3068                 if( addrbook.selected == addrbook.opened ) {
3069                         addrbook.open_folder = TRUE;
3070                         gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
3071                 }
3072         }
3073 }
3074
3075 static AddressLDAP *addressbook_parse_ldap(XMLFile *file) {
3076         AddressLDAP *item = NULL;
3077         SyldapServer *server;
3078         GList *attr;
3079         gchar *name, *value;
3080         gint ivalue;
3081
3082         server = syldap_create();
3083         attr = xml_get_current_tag_attr( file );
3084         while( attr ) {
3085                 name = ((XMLAttr *)attr->data)->name;
3086                 value = ((XMLAttr *)attr->data)->value;
3087                 ivalue = atoi( value );
3088                 if( strcmp( name, "name" ) == 0 ) {
3089                         syldap_set_name( server, value );
3090                 }
3091                 else if( strcmp( name, "host" ) == 0 ) {
3092                         syldap_set_host( server, value );
3093                 }
3094                 else if( strcmp( name, "port" ) == 0 ) {
3095                         syldap_set_port( server, ivalue );
3096                 }
3097                 else if( strcmp( name, "base-dn" ) == 0 ) {
3098                         syldap_set_base_dn( server, value );
3099                 }
3100                 else if( strcmp( name, "bind-dn" ) == 0 ) {
3101                         syldap_set_bind_dn( server, value );
3102                 }
3103                 else if( strcmp( name, "bind-pass" ) == 0 ) {
3104                         syldap_set_bind_password( server, value );
3105                 }
3106                 else if( strcmp( name, "criteria" ) == 0 ) {
3107                         syldap_set_search_criteria( server, value );
3108                 }
3109                 else if( strcmp( name, "max-entry" ) == 0 ) {
3110                         syldap_set_max_entries( server, ivalue );
3111                 }
3112                 else if( strcmp( name, "timeout" ) == 0 ) {
3113                         syldap_set_timeout( server, ivalue );
3114                 }
3115                 attr = g_list_next( attr );
3116         }
3117
3118         // Move to next tag
3119         if( xml_parse_next_tag( file ) >= 0 ) {
3120                 item = g_new( AddressLDAP, 1 );
3121                 ADDRESS_OBJECT(item)->type = ADDR_LDAP;
3122                 item->name = g_strdup( server->name );
3123                 item->ldapServer = server;
3124                 item->items = NULL;
3125                 return item;
3126         }
3127
3128         // Must be an invalid tag or data.
3129         g_warning( "addressbook_parse_ldap(): Parse error\n");
3130         syldap_free( server );
3131         server = NULL;
3132         item = NULL;
3133         return NULL;
3134 }
3135
3136 static void addressbook_write_ldap( FILE *fp, AddressLDAP *ldapi, guint level ) {
3137         SyldapServer *server = ldapi->ldapServer;
3138         if( server ) {
3139                 tab_indent_out(fp, 1);
3140                 fputs("<server ", fp);
3141                 fputs("name=\"", fp);
3142                 xml_file_put_escape_str(fp, server->name);
3143                 fputs("\" host=\"", fp);
3144                 xml_file_put_escape_str(fp, server->hostName);
3145                 fprintf( fp, "\" port=\"%d", server->port);
3146                 fputs("\" base-dn=\"", fp);
3147                 xml_file_put_escape_str(fp, server->baseDN);
3148                 fputs("\" bind-dn=\"", fp);
3149                 xml_file_put_escape_str(fp, server->bindDN);
3150                 fputs("\" bind-pass=\"", fp);
3151                 xml_file_put_escape_str(fp, server->bindPass);
3152                 fputs("\" criteria=\"", fp);
3153                 xml_file_put_escape_str(fp, server->searchCriteria);
3154                 fprintf( fp, "\" max-entry=\"%d", server->maxEntries);
3155                 fprintf( fp, "\" timeout=\"%d", server->timeOut);
3156                 fputs("\" />\n", fp);
3157         }
3158 }
3159
3160 static void addressbook_ldap_show_message( SyldapServer *svr ) {
3161         *addressbook_msgbuf = '\0';
3162         if( svr ) {
3163                 if( svr->busyFlag ) {
3164                         sprintf( addressbook_msgbuf, "%s: %s", svr->name, ADDRESSBOOK_LDAP_BUSYMSG );
3165                 }
3166                 else {
3167                         if( svr->retVal == MGU_SUCCESS ) {
3168                                 sprintf( addressbook_msgbuf, "%s", svr->name );
3169                         }
3170                         else {
3171                                 sprintf( addressbook_msgbuf, "%s: %s", svr->name, mgu_error2string( svr->retVal ) );
3172                         }
3173                 }
3174         }
3175         addressbook_status_show( addressbook_msgbuf );
3176 }
3177
3178 static gint ldapsearch_callback( SyldapServer *sls ) {
3179         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
3180         AddressObject *obj;
3181
3182         if( sls == NULL ) return;
3183         if( ! addrbook.selected ) return;
3184         if( GTK_CTREE_ROW( addrbook.selected )->level == 1 ) return;
3185
3186         obj = gtk_ctree_node_get_row_data( ctree, addrbook.selected );
3187         if( obj == NULL ) return;
3188         if( obj->type == ADDR_LDAP ) {
3189                 AddressLDAP *ldapi = ADDRESS_LDAP(obj);
3190                 SyldapServer *server = ldapi->ldapServer;
3191                 if( server == sls ) {
3192                         if( ! _have_ldap_library_ ) return;
3193                         // Read from cache
3194                         gtk_widget_show_all(addrbook.window);
3195                         ADDRESS_LDAP(obj)->items = syldap_get_address_list( sls );
3196                         addressbook_set_clist( obj );
3197                         addressbook_ldap_show_message( sls );
3198                         gtk_widget_show_all(addrbook.window);
3199                 }
3200         }
3201 }
3202 #endif
3203
3204 /*
3205  * Lookup button handler.
3206  */
3207 static void addressbook_lup_clicked( GtkButton *button, gpointer data ) {
3208         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
3209         AddressObject *obj;
3210         gchar *sLookup;
3211 #ifdef USE_LDAP
3212         AddressLDAP *ldapi;
3213         SyldapServer *server;
3214 #endif
3215
3216         sLookup = gtk_editable_get_chars( GTK_EDITABLE(addrbook.entry), 0, -1 );
3217         g_strchomp( sLookup );
3218
3219         if( ! addrbook.selected ) return;
3220         if( GTK_CTREE_ROW( addrbook.selected )->level == 1 ) return;
3221
3222         obj = gtk_ctree_node_get_row_data( ctree, addrbook.selected );
3223         if( obj == NULL ) return;
3224
3225 #ifdef USE_LDAP
3226         if( obj->type == ADDR_LDAP ) {
3227                 ldapi = ADDRESS_LDAP(obj);
3228                 server = ldapi->ldapServer;
3229                 if( server ) {
3230                         if( ! _have_ldap_library_ ) return;
3231                         syldap_cancel_read( server );
3232                         if( *sLookup == '\0' || strlen( sLookup ) < 1 ) return;
3233                         syldap_set_search_value( server, sLookup );
3234                         syldap_set_callback( server, ldapsearch_callback );
3235                         syldap_read_data_th( server );
3236                         addressbook_ldap_show_message( server );
3237                 }
3238         }
3239 #endif
3240
3241 }
3242
3243 /***/
3244
3245 typedef struct {
3246         gboolean                init;                   /* if FALSE should init jump buffer */
3247         GtkCTreeNode   *node_found;             /* match (can be used to backtrack folders)  */
3248         AddressObject  *addr_found;             /* match */
3249         int                             level;                  /* current recursion level (0 is root level) */
3250         jmp_buf                 jumper;                 /* jump buffer */
3251 } FindObject;
3252
3253 typedef struct {
3254         FindObject              ancestor;
3255         const gchar        *groupname;
3256 } FindGroup;
3257
3258 typedef struct {
3259         FindObject              ancestor;
3260         const gchar    *name;
3261         const gchar    *address;
3262 } FindAddress;
3263
3264 typedef struct {
3265         FindObject              ancestor;
3266         GList              *grouplist;
3267 } FindAllGroups;
3268
3269 typedef gboolean (*ADDRESSBOOK_TRAVERSE_FUNC)(AddressObject *node, gpointer data);
3270
3271 /***/
3272
3273 static gboolean traverse_find_group_by_name(AddressObject *ao, FindGroup *find)
3274 {
3275         AddressFolder *folder;
3276         AddressGroup  *group;
3277
3278         /* a group or folder: both are groups */
3279         if (ADDRESS_OBJECT_TYPE(ao) == ADDR_GROUP) {
3280                 group = ADDRESS_GROUP(ao);
3281                 if (0 == g_strcasecmp(group->name, find->groupname)) {
3282                         return TRUE;
3283                 }
3284         }
3285         else if (ADDRESS_OBJECT_TYPE(ao) == ADDR_FOLDER) {
3286                 folder = ADDRESS_FOLDER(ao);
3287                 if (0 == g_strcasecmp(folder->name, find->groupname)) {
3288                         return TRUE;
3289                 }
3290         }
3291         return FALSE;
3292 }
3293
3294 static gboolean traverse_find_name_email(AddressObject *ao, FindAddress *find)
3295 {
3296         AddressItem *item;
3297         if (ADDRESS_OBJECT_TYPE(ao) == ADDR_ITEM) {
3298                 gboolean nmatch = FALSE, amatch = FALSE;
3299                 item = ADDRESS_ITEM(ao);
3300                 /* conditions:
3301                  * o only match at the first characters in item strings 
3302                  * o match either name or address */
3303                 if (find->name && item->name) {
3304                         nmatch = item->name == strcasestr(item->name, find->name);
3305                 }
3306                 if (find->address && item->address) {
3307                         amatch = item->address == strcasestr(item->address, find->address);
3308                 }
3309                 return nmatch || amatch;
3310         }
3311         return FALSE;
3312 }
3313
3314 static gboolean traverse_find_all_groups(AddressObject *ao, FindAllGroups *find)
3315 {
3316         /* NOTE: added strings come from the address book. should perhaps 
3317          * strdup() them, especially if the address book is invalidated */
3318         if (ADDRESS_OBJECT_TYPE(ao) == ADDR_FOLDER) {
3319                 AddressFolder *folder = ADDRESS_FOLDER(ao);
3320                 find->grouplist = g_list_insert_sorted(find->grouplist, (gpointer) folder->name, (GCompareFunc) g_strcasecmp);
3321         }
3322         else if (ADDRESS_OBJECT_TYPE(ao) == ADDR_GROUP) {
3323                 AddressGroup *group = ADDRESS_GROUP(ao);
3324                 find->grouplist = g_list_insert_sorted(find->grouplist, (gpointer) group->name, (GCompareFunc) g_strcasecmp);
3325         }
3326         return FALSE;
3327 }
3328
3329 /* addressbook_traverse() - traverses all address objects stored in the address book. 
3330  * for some reason gtkctree's recursive tree functions don't allow a premature return, 
3331  * which is what we need if we need to enumerate the tree and check for a condition 
3332  * and then skipping other nodes. */ 
3333 static AddressObject *addressbook_traverse(GtkCTreeNode *node, ADDRESSBOOK_TRAVERSE_FUNC func, FindObject *data, int level)
3334 {
3335         GtkCTreeNode  *current, *tmp;
3336         AddressObject *ao;
3337
3338         if (data->init == FALSE) {
3339                 /* initialize non-local exit */
3340                 data->init  = TRUE;
3341                 data->level = 0;
3342                 /* HANDLE NON-LOCAL EXIT */
3343                 if (setjmp(data->jumper)) {
3344                         return data->addr_found;
3345                 }
3346         }
3347
3348         /* actual recursive code */
3349         if (!node) {
3350                 current = GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list);
3351         }
3352         else {
3353                 current = node;
3354         }
3355
3356         while (current) {
3357                 tmp = GTK_CTREE_ROW(current)->sibling;
3358                 ao = (AddressObject *) gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), current);
3359                 if (ao) {
3360                         GList *next;
3361
3362                         next = (ADDRESS_OBJECT_TYPE(ao) == ADDR_FOLDER) ? 
3363                                    g_list_first(((ADDRESS_FOLDER(ao))->items)) :
3364                                    (ADDRESS_OBJECT_TYPE(ao)  == ADDR_GROUP) ?
3365                                    g_list_first(((ADDRESS_GROUP(ao))->items))  : NULL;
3366
3367                         while (ao) {
3368                                 /* NOTE: first iteration of the root calls callback for the tree 
3369                                  * node, other iterations call callback for the address book items */
3370                                 if (func(ao, data)) {
3371                                         /* unwind */
3372                                         data->node_found = current;
3373                                         data->addr_found = ao;
3374                                         longjmp(data->jumper, 1);
3375                                 }
3376                                 /* ctree node only stores folders and groups. now descend into
3377                                  * address object data, searching for address items. */
3378                                 for ( ; next && ADDRESS_OBJECT_TYPE((next->data)) != ADDR_ITEM
3379                                           ; next = g_list_next(next))
3380                                         ;                       
3381                                 ao   = next ? (AddressObject *) next->data : NULL;
3382                                 next = next ? g_list_next(next) : NULL;
3383                         }                               
3384                 }
3385                 /* check the children (if level permits) */
3386                 if (level == -1 || data->level < level) {
3387                         current = GTK_CTREE_ROW(current)->children;
3388                         if (current) {
3389                                 data->level++;
3390                                 addressbook_traverse(current, func, data, level);
3391                                 data->level--;
3392                         }                       
3393                 }                       
3394                 /* check the siblings */
3395                 current = tmp;
3396         }
3397         return NULL;
3398 }
3399
3400 static GtkCTreeNode *addressbook_get_group_node(const gchar *name)
3401 {
3402         FindGroup fg = { { FALSE, NULL, NULL }, NULL };
3403         fg.groupname = name;
3404         addressbook_traverse(NULL, (void *)traverse_find_group_by_name, (FindObject *)&fg, -1);
3405         return fg.ancestor.node_found;
3406 }
3407
3408 static void addressbook_free_item(AddressItem *item)
3409 {
3410         if (item) {
3411                 if (item->name) g_free(item->name);
3412                 if (item->address) g_free(item->address);
3413                 if (item->remarks) g_free(item->remarks);
3414                 g_free(item);
3415         }
3416 }
3417
3418 static AddressItem *addressbook_alloc_item(const gchar *name, const gchar *address, const gchar *remarks)
3419 {
3420         AddressItem *item = g_new0(AddressItem, 1);
3421         
3422         if (item) {
3423                 item->obj.type = ADDR_ITEM;
3424                 if (item->name = g_strdup(name))
3425                         if (item->address = g_strdup(address)) {
3426                                 if (remarks) {
3427                                         item->remarks = g_strdup(remarks);
3428                                 }
3429                                 return item;
3430                         }
3431         }
3432         addressbook_free_item(item);
3433         return NULL;
3434 }
3435
3436 /***/
3437
3438 /* public provisional API */
3439
3440 /* addressbook_access() - should be called before using any of the following apis. it
3441  * reloads the address book. */
3442 void addressbook_access(void)
3443 {
3444         log_message("accessing address book\n");
3445         if (!addrbook.window) {
3446                 addressbook_create(FALSE);
3447                 addressbook_read_file();
3448                 addrbook.open_folder = TRUE;
3449                 gtk_ctree_select(GTK_CTREE(addrbook.ctree), GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
3450         } 
3451 }
3452
3453 /* addressbook_unaccess() - should only be called after changing the address book's
3454  * contents */
3455 void addressbook_unaccess(void)
3456 {
3457         log_message("unaccessing address book\n");
3458         addressbook_export_to_file();
3459         invalidate_address_completion();
3460 }
3461
3462 const gchar *addressbook_get_personal_folder_name(void)
3463 {
3464         return _("Personal addresses"); /* human readable */
3465 }
3466
3467 const gchar *addressbook_get_common_folder_name(void)
3468 {
3469         return _("Common addresses"); /* human readable */
3470 }
3471
3472 /* addressbook_find_group_by_name() - finds a group (folder or group) by
3473  * its name */
3474 AddressObject *addressbook_find_group_by_name(const gchar *name)
3475 {
3476         FindGroup          fg = { { FALSE, NULL, NULL } };
3477         AddressObject *ao;
3478
3479         /* initialize obj members */
3480         fg.groupname = name;
3481         ao = addressbook_traverse(NULL, 
3482                                                           (ADDRESSBOOK_TRAVERSE_FUNC)traverse_find_group_by_name, 
3483                                                           (FindObject *)&fg, -1);
3484         return ao;
3485 }
3486
3487 /* addressbook_find_contact() - finds an address item by either name or address
3488  * or both. the comparison is done on the first few characters of the strings */
3489 AddressObject *addressbook_find_contact(const gchar *name, const gchar *address)
3490 {
3491         FindAddress   fa = { { FALSE, NULL, NULL } };
3492         AddressObject *ao;
3493
3494         fa.name = name;
3495         fa.address = address;
3496         ao = addressbook_traverse(NULL, (ADDRESSBOOK_TRAVERSE_FUNC)traverse_find_name_email,
3497                                                           (FindObject *)&fa, -1);
3498         return ao;                                                        
3499 }
3500
3501 /* addressbook_get_group_list() - returns a list of strings with group names (both
3502  * groups and folders). free the list using g_list_free(). note that another
3503  * call may invalidate the returned list */
3504 GList *addressbook_get_group_list(void)
3505 {
3506         FindAllGroups fag = { { FALSE, NULL, NULL }, NULL };
3507         addressbook_traverse(NULL, (ADDRESSBOOK_TRAVERSE_FUNC)traverse_find_all_groups,
3508                                                  (FindObject *)&fag, -1);
3509         return fag.grouplist;
3510 }
3511
3512 /* addressbook_add_contact() - adds a contact to the address book. returns 1
3513  * if succesful else error */
3514 gint addressbook_add_contact(const gchar *group, const gchar *name, const gchar *address,
3515                                                          const gchar *remarks) 
3516 {
3517         GtkCTreeNode *node;
3518         AddressItem *item;
3519         FindAddress  fa = { { FALSE, NULL, NULL } };
3520
3521         /* a healthy mix of hiro's and my code */
3522         if (name == NULL || strlen(name) == 0
3523         ||  address == NULL || strlen(address) == 0
3524         ||  group == NULL || strlen(group) == 0) {
3525                 return __LINE__;
3526         }
3527         node = addressbook_get_group_node(group);
3528         if (!node) {
3529                 return __LINE__;
3530         }
3531
3532         /* check if it's already in this group */
3533         fa.name = name;
3534         fa.address = address;
3535
3536         if (addressbook_traverse(node, (gpointer)traverse_find_name_email, (gpointer)&fa, 0)) {
3537                 log_message("address <%s> already in %s\n", address, group);
3538                 return __LINE__;
3539         }
3540
3541         item = addressbook_alloc_item(name, address, remarks);
3542         if (!item) {
3543                 return __LINE__;
3544         }
3545
3546         if (!addressbook_add_object(node, (AddressObject *)item)) {
3547                 addressbook_free_item(item);
3548                 return __LINE__;
3549         }
3550
3551         /* make sure it's updated if selected */
3552         log_message("updating addressbook widgets\n");
3553         addrbook.open_folder = TRUE;
3554         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
3555
3556         /* not saved yet. only after unaccessing the address book */
3557         return 0;
3558 }
3559
3560 static void group_object_data_destroy(gchar *group)
3561 {
3562         if (group) {
3563                 g_free(group);
3564         }               
3565 }
3566
3567 /***/
3568
3569 typedef struct {
3570         gchar *name;
3571         gchar *address;
3572         gchar *remarks;
3573 } ContactInfo;
3574
3575 static void addressbook_destroy_contact(ContactInfo *ci)
3576 {
3577         g_return_if_fail(ci != NULL);
3578         if (ci->name) g_free(ci->name);
3579         if (ci->address) g_free(ci->address);
3580         if (ci->remarks) g_free(ci->remarks);
3581         g_free(ci);
3582 }
3583
3584 static ContactInfo *addressbook_new_contact(const gchar *name, const gchar *address, const gchar *remarks)
3585 {
3586         ContactInfo *ci = g_new0(ContactInfo, 1);
3587         
3588         g_return_val_if_fail(ci != NULL, NULL);
3589         g_return_val_if_fail(address != NULL, NULL); /* address should be valid */
3590         ci->name    = name ? g_strdup(name) : NULL;
3591         ci->address = g_strdup(address);
3592         ci->remarks = remarks ? g_strdup(remarks) : NULL;
3593         if (NULL == ci->address) {
3594                 addressbook_destroy_contact(ci);
3595                 ci = NULL;
3596         }
3597         return ci;
3598 }
3599
3600 static void addressbook_group_menu_selected(GtkMenuItem *menuitem,
3601                                                                                         ContactInfo *data)
3602 {
3603         const gchar *group_name = (const gchar *) gtk_object_get_data(GTK_OBJECT(menuitem),
3604                                                                                                                                   "group_name");
3605                                                                                                            
3606         if (!group_name) {
3607                 g_warning("%s(%d) - invalid group name\n", __FILE__, __LINE__);
3608                 return ;
3609         }
3610         g_return_if_fail(group_name != NULL); 
3611
3612         g_message("selected group %s from menu\n", group_name);
3613         g_message("selected %s <%s>\n", data->name ? data->name : data->address, data->address);
3614
3615         addressbook_access();
3616         addressbook_add_contact(group_name, data->name ? data->name : data->address, 
3617                                                         data->address, data->remarks ? data->remarks : data->address);
3618         addressbook_unaccess();
3619
3620         g_free(data);
3621 }
3622
3623 /* addressbook_add_contact_by_meny() - launches menu with group items. submenu may be
3624  * the menu item in the parent menu, or NULL for a normal right-click context menu */
3625 gboolean addressbook_add_contact_by_menu(GtkWidget   *submenu,
3626                                                                                  const gchar *name, 
3627                                                                                  const gchar *address, 
3628                                                                                  const gchar *remarks)
3629 {
3630         GtkWidget       *menu, *menuitem;
3631         GList           *groups, *tmp;
3632         ContactInfo *ci;
3633
3634         ci = addressbook_new_contact(name, address, remarks);
3635         g_return_val_if_fail(ci != NULL, FALSE);
3636
3637         addressbook_access();
3638         groups = addressbook_get_group_list();
3639         g_return_val_if_fail(groups != NULL, (addressbook_destroy_contact(ci), FALSE));
3640         
3641         menu = gtk_menu_new();
3642         g_return_val_if_fail(menu != NULL, (g_list_free(groups), addressbook_destroy_contact(ci), FALSE));
3643
3644         /* add groups to menu */
3645         for (tmp = g_list_first(groups); tmp != NULL; tmp = g_list_next(tmp)) {
3646                 const gchar *display_name;
3647                 gchar *original_name = (gchar *) tmp->data;
3648                 gboolean addItem = TRUE;
3649
3650                 if (!g_strcasecmp(original_name, ADDRESS_TAG_PERSONAL)) {
3651                         display_name = addressbook_get_personal_folder_name();
3652                 }
3653                 else if (!g_strcasecmp(original_name, ADDRESS_TAG_COMMON)) {
3654                         display_name = addressbook_get_common_folder_name();
3655                 }
3656                 else if( ! g_strcasecmp( original_name, ADDRESS_TAG_VCARD ) ) {
3657                         addItem = FALSE;
3658                 }
3659 #ifdef USE_JPILOT
3660                 else if( ! g_strcasecmp( original_name, ADDRESS_TAG_JPILOT ) ) {
3661                         addItem = FALSE;
3662                 }
3663 #endif
3664 #ifdef USE_LDAP
3665                 else if( ! g_strcasecmp( original_name, ADDRESS_TAG_LDAP ) ) {
3666                         addItem = FALSE;
3667                 }
3668 #endif
3669                 else {
3670                         display_name = original_name;
3671                 }
3672
3673                 if( addItem ) {
3674                         original_name = g_strdup(original_name);
3675                         menuitem = gtk_menu_item_new_with_label(display_name);
3676                         /* register the duplicated string pointer as object data,
3677                          * so we get the opportunity to free it */
3678                         gtk_object_set_data_full(GTK_OBJECT(menuitem), "group_name", 
3679                                                                  original_name, 
3680                                                                  (GtkDestroyNotify) group_object_data_destroy);
3681                         gtk_signal_connect(GTK_OBJECT(menuitem), "activate", 
3682                                                    GTK_SIGNAL_FUNC(addressbook_group_menu_selected),
3683                                                    (gpointer)(ci));
3684                         gtk_menu_append(GTK_MENU(menu), menuitem);
3685                         gtk_widget_show(menuitem);
3686                 }
3687         }
3688
3689         gtk_widget_show(menu);
3690
3691         if (submenu) {
3692                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu), menu);
3693                 gtk_widget_set_sensitive(GTK_WIDGET(submenu), TRUE);
3694         } 
3695         else {
3696                 gtk_widget_grab_focus(GTK_WIDGET(menu));
3697                 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME);
3698         }
3699
3700         if (groups) g_list_free(groups);
3701         return TRUE;
3702 }
3703
3704 /*
3705 * End of Source.
3706 */
3707