40ecc846cfdaa0f51c85bc2eaefd37744211b76d
[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
46 #include "intl.h"
47 #include "main.h"
48 #include "addressbook.h"
49 #include "manage_window.h"
50 #include "prefs_common.h"
51 #include "alertpanel.h"
52 #include "inputdialog.h"
53 #include "menu.h"
54 #include "xml.h"
55 #include "prefs.h"
56 #include "procmime.h"
57 #include "utils.h"
58 #include "gtkutils.h"
59 #include "codeconv.h"
60 #include "about.h"
61 #include "addr_compl.h"
62
63 #include "pixmaps/dir-close.xpm"
64 #include "pixmaps/dir-open.xpm"
65 #include "pixmaps/group.xpm"
66
67 typedef enum
68 {
69         COL_NAME        = 0,
70         COL_ADDRESS     = 1,
71         COL_REMARKS     = 2
72 } AddressBookColumnPos;
73
74 #define N_COLS  3
75 #define COL_NAME_WIDTH          144
76 #define COL_ADDRESS_WIDTH       144
77
78 #define COL_FOLDER_WIDTH        170
79 #define ADDRESSBOOK_WIDTH       640
80 #define ADDRESSBOOK_HEIGHT      340
81
82 static GdkPixmap *folderxpm;
83 static GdkBitmap *folderxpmmask;
84 static GdkPixmap *folderopenxpm;
85 static GdkBitmap *folderopenxpmmask;
86 static GdkPixmap *groupxpm;
87 static GdkBitmap *groupxpmmask;
88
89 static AddressBook addrbook;
90
91 static struct _AddressEdit
92 {
93         GtkWidget *window;
94         GtkWidget *name_entry;
95         GtkWidget *addr_entry;
96         GtkWidget *rem_entry;
97         GtkWidget *ok_btn;
98         GtkWidget *cancel_btn;
99 } addredit;
100
101 static void addressbook_create                  (void);
102 static gint addressbook_close                   (void);
103 static void addressbook_button_set_sensitive    (void);
104
105 /* callback functions */
106 static void addressbook_del_clicked             (GtkButton      *button,
107                                                  gpointer        data);
108 static void addressbook_reg_clicked             (GtkButton      *button,
109                                                  gpointer        data);
110 static void addressbook_to_clicked              (GtkButton      *button,
111                                                  gpointer        data);
112
113 static void addressbook_tree_selected           (GtkCTree       *ctree,
114                                                  GtkCTreeNode   *node,
115                                                  gint            column,
116                                                  gpointer        data);
117 static void addressbook_list_selected           (GtkCList       *clist,
118                                                  gint            row,
119                                                  gint            column,
120                                                  GdkEvent       *event,
121                                                  gpointer        data);
122 #if 0
123 static void addressbook_entry_changed           (GtkWidget      *widget);
124 #endif
125
126 static void addressbook_list_button_pressed     (GtkWidget      *widget,
127                                                  GdkEventButton *event,
128                                                  gpointer        data);
129 static void addressbook_list_button_released    (GtkWidget      *widget,
130                                                  GdkEventButton *event,
131                                                  gpointer        data);
132 static void addressbook_tree_button_pressed     (GtkWidget      *ctree,
133                                                  GdkEventButton *event,
134                                                  gpointer        data);
135 static void addressbook_tree_button_released    (GtkWidget      *ctree,
136                                                  GdkEventButton *event,
137                                                  gpointer        data);
138 static void addressbook_popup_close             (GtkMenuShell   *menu_shell,
139                                                  gpointer        data);
140
141 static void addressbook_new_folder_cb           (gpointer        data,
142                                                  guint           action,
143                                                  GtkWidget      *widget);
144 static void addressbook_new_group_cb            (gpointer        data,
145                                                  guint           action,
146                                                  GtkWidget      *widget);
147 static void addressbook_edit_folder_cb          (gpointer        data,
148                                                  guint           action,
149                                                  GtkWidget      *widget);
150 static void addressbook_delete_folder_cb        (gpointer        data,
151                                                  guint           action,
152                                                  GtkWidget      *widget);
153
154 static void addressbook_change_node_name        (GtkCTreeNode   *node,
155                                                  const gchar    *name);
156 static void addressbook_edit_group              (GtkCTreeNode   *group_node);
157
158 static void addressbook_edit_address_create     (gboolean       *cancelled);
159 static void edit_address_ok                     (GtkWidget      *widget,
160                                                  gboolean       *cancelled);
161 static void edit_address_cancel                 (GtkWidget      *widget,
162                                                  gboolean       *cancelled);
163 static gint edit_address_delete_event           (GtkWidget      *widget,
164                                                  GdkEventAny    *event,
165                                                  gboolean *cancelled);
166 static void edit_address_key_pressed            (GtkWidget      *widget,
167                                                  GdkEventKey    *event,
168                                                  gboolean       *cancelled);
169 static AddressItem *addressbook_edit_address    (AddressItem    *item);
170
171 static void addressbook_new_address_cb          (gpointer        data,
172                                                  guint           action,
173                                                  GtkWidget      *widget);
174 static void addressbook_edit_address_cb         (gpointer        data,
175                                                  guint           action,
176                                                  GtkWidget      *widget);
177 static void addressbook_delete_address_cb       (gpointer        data,
178                                                  guint           action,
179                                                  GtkWidget      *widget);
180
181 static void close_cb                            (gpointer        data,
182                                                  guint           action,
183                                                  GtkWidget      *widget);
184
185 static AddressItem *addressbook_parse_address   (const gchar    *str);
186 static void addressbook_append_to_compose_entry (AddressItem    *item,
187                                                  ComposeEntryType type);
188
189 static void addressbook_set_clist               (AddressObject  *obj);
190
191 static void addressbook_read_file               (void);
192 static void addressbook_get_tree                (XMLFile        *file,
193                                                  GtkCTreeNode   *node,
194                                                  const gchar    *folder_tag);
195 static void addressbook_add_objs                (XMLFile        *file,
196                                                  GtkCTreeNode   *node);
197
198 static GtkCTreeNode *addressbook_add_object     (GtkCTreeNode   *node,
199                                                  AddressObject  *obj);
200 static void addressbook_delete_object           (AddressObject  *obj);
201 static AddressObject *addressbook_find_object_by_name
202                                                 (GtkCTreeNode   *node,
203                                                  const gchar    *name);
204
205 static AddressItem *addressbook_parse_item      (XMLFile        *file);
206 static void addressbook_xml_recursive_write     (GtkCTreeNode   *node,
207                                                  FILE           *fp);
208 static void addressbook_node_write_begin        (GtkCTreeNode   *node,
209                                                  FILE           *fp);
210 static void addressbook_node_write_end          (GtkCTreeNode   *node,
211                                                  FILE           *fp);
212 static void addressbook_write_items             (FILE           *fp,
213                                                  GList          *items,
214                                                  guint           level);
215 static void tab_indent_out                      (FILE           *fp,
216                                                  guint           level);
217
218 static void key_pressed                         (GtkWidget      *widget,
219                                                  GdkEventKey    *event,
220                                                  gpointer        data);
221 static gint addressbook_list_compare_func       (GtkCList       *clist,
222                                                  gconstpointer   ptr1,
223                                                  gconstpointer   ptr2);
224 static gint addressbook_obj_name_compare        (gconstpointer   a,
225                                                  gconstpointer   b);
226
227 static GtkItemFactoryEntry addressbook_entries[] =
228 {
229         {N_("/_File"),                  NULL, NULL, 0, "<Branch>"},
230         {N_("/_File/New _address"),     "<alt>N", addressbook_new_address_cb, 0, NULL},
231         {N_("/_File/New _group"),       "<alt>G", addressbook_new_group_cb,   0, NULL},
232         {N_("/_File/New _folder"),      "<alt>R", addressbook_new_folder_cb,  0, NULL},
233         {N_("/_File/---"),              NULL, NULL, 0, "<Separator>"},
234         {N_("/_File/_Edit"),            "<alt>Return", addressbook_edit_address_cb, 0, NULL},
235         {N_("/_File/_Delete"),          NULL, addressbook_delete_address_cb, 0, NULL},
236         {N_("/_File/---"),              NULL, NULL, 0, "<Separator>"},
237         {N_("/_File/_Close"),           "<alt>W", close_cb, 0, NULL},
238         {N_("/_Help"),                  NULL, NULL, 0, "<LastBranch>"},
239         {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
240 };
241
242 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
243 {
244         {N_("/New _address"),   NULL, addressbook_new_address_cb, 0, NULL},
245         {N_("/New _group"),     NULL, addressbook_new_group_cb,   0, NULL},
246         {N_("/New _folder"),    NULL, addressbook_new_folder_cb,  0, NULL},
247         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
248         {N_("/_Edit"),          NULL, addressbook_edit_folder_cb,   0, NULL},
249         {N_("/_Delete"),        NULL, addressbook_delete_folder_cb, 0, NULL}
250 };
251
252 static GtkItemFactoryEntry addressbook_list_popup_entries[] =
253 {
254         {N_("/New _address"),   NULL, addressbook_new_address_cb,  0, NULL},
255         {N_("/New _group"),     NULL, addressbook_new_group_cb,    0, NULL},
256         {N_("/New _folder"),    NULL, addressbook_new_folder_cb,   0, NULL},
257         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
258         {N_("/_Edit"),          NULL, addressbook_edit_address_cb,   0, NULL},
259         {N_("/_Delete"),        NULL, addressbook_delete_address_cb, 0, NULL}
260 };
261
262 void addressbook_open(Compose *target)
263 {
264         if (!addrbook.window) {
265                 addressbook_create();
266                 addressbook_read_file();
267                 addrbook.open_folder = TRUE;
268                 gtk_ctree_select(GTK_CTREE(addrbook.ctree),
269                                  GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
270         } else
271                 gtk_widget_hide(addrbook.window);
272
273         gtk_widget_show(addrbook.window);
274
275         addressbook_set_target_compose(target);
276 }
277
278 void addressbook_set_target_compose(Compose *target)
279 {
280         addrbook.target_compose = target;
281
282         addressbook_button_set_sensitive();
283 }
284
285 Compose *addressbook_get_target_compose(void)
286 {
287         return addrbook.target_compose;
288 }
289
290 static void addressbook_create(void)
291 {
292         GtkWidget *window;
293         GtkWidget *vbox;
294         GtkWidget *menubar;
295         GtkWidget *vbox2;
296         GtkWidget *ctree_swin;
297         GtkWidget *ctree;
298         GtkWidget *clist_vbox;
299         GtkWidget *clist_swin;
300         GtkWidget *clist;
301         GtkWidget *paned;
302         GtkWidget *hbox;
303         GtkWidget *label;
304         GtkWidget *entry;
305         GtkWidget *hbbox;
306         GtkWidget *del_btn;
307         GtkWidget *reg_btn;
308         GtkWidget *lup_btn;
309         GtkWidget *to_btn;
310         GtkWidget *cc_btn;
311         GtkWidget *bcc_btn;
312         GtkWidget *tree_popup;
313         GtkWidget *list_popup;
314         GtkItemFactory *tree_factory;
315         GtkItemFactory *list_factory;
316         gint n_entries;
317
318         gchar *titles[N_COLS] = {_("Name"), _("E-Mail address"), _("Remarks")};
319         gchar *text;
320         gint i;
321
322         debug_print("Creating addressbook window...\n");
323
324         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
325         gtk_window_set_title(GTK_WINDOW(window), _("Address book"));
326         gtk_widget_set_usize(window, ADDRESSBOOK_WIDTH, ADDRESSBOOK_HEIGHT);
327         //gtk_container_set_border_width(GTK_CONTAINER(window), BORDER_WIDTH);
328         gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
329         gtk_widget_realize(window);
330
331         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
332                            GTK_SIGNAL_FUNC(addressbook_close), NULL);
333         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
334                            GTK_SIGNAL_FUNC(key_pressed), NULL);
335         gtk_signal_connect(GTK_OBJECT(window), "focus_in_event",
336                            GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
337         gtk_signal_connect(GTK_OBJECT(window), "focus_out_event",
338                            GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
339
340         vbox = gtk_vbox_new(FALSE, 0);
341         gtk_container_add(GTK_CONTAINER(window), vbox);
342
343         n_entries = sizeof(addressbook_entries) /
344                 sizeof(addressbook_entries[0]);
345         menubar = menubar_create(window, addressbook_entries, n_entries,
346                                  "<AddressBook>", NULL);
347         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
348
349         vbox2 = gtk_vbox_new(FALSE, 4);
350         gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
351         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
352
353         ctree_swin = gtk_scrolled_window_new(NULL, NULL);
354         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ctree_swin),
355                                        GTK_POLICY_AUTOMATIC,
356                                        GTK_POLICY_ALWAYS);
357         gtk_widget_set_usize(ctree_swin, COL_FOLDER_WIDTH + 40, -1);
358
359         ctree = gtk_ctree_new(1, 0);
360         gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
361         gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
362         gtk_clist_set_column_width(GTK_CLIST(ctree), 0, COL_FOLDER_WIDTH);
363         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
364         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
365                                      GTK_CTREE_EXPANDER_SQUARE);
366         gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
367         gtk_clist_set_compare_func(GTK_CLIST(ctree),
368                                    addressbook_list_compare_func);
369
370         gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
371                            GTK_SIGNAL_FUNC(addressbook_tree_selected), NULL);
372         gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
373                            GTK_SIGNAL_FUNC(addressbook_tree_button_pressed),
374                            NULL);
375         gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
376                            GTK_SIGNAL_FUNC(addressbook_tree_button_released),
377                            NULL);
378
379         clist_vbox = gtk_vbox_new(FALSE, 4);
380
381         clist_swin = gtk_scrolled_window_new(NULL, NULL);
382         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
383                                        GTK_POLICY_AUTOMATIC,
384                                        GTK_POLICY_ALWAYS);
385         gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
386
387         clist = gtk_clist_new_with_titles(N_COLS, titles);
388         gtk_container_add(GTK_CONTAINER(clist_swin), clist);
389         gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_EXTENDED);
390         gtk_clist_set_column_width(GTK_CLIST(clist), COL_NAME,
391                                    COL_NAME_WIDTH);
392         gtk_clist_set_column_width(GTK_CLIST(clist), COL_ADDRESS,
393                                    COL_ADDRESS_WIDTH);
394         gtk_clist_set_compare_func(GTK_CLIST(clist),
395                                    addressbook_list_compare_func);
396
397         for (i = 0; i < N_COLS; i++)
398                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button,
399                                        GTK_CAN_FOCUS);
400
401         gtk_signal_connect(GTK_OBJECT(clist), "select_row",
402                            GTK_SIGNAL_FUNC(addressbook_list_selected), NULL);
403         gtk_signal_connect(GTK_OBJECT(clist), "button_press_event",
404                            GTK_SIGNAL_FUNC(addressbook_list_button_pressed),
405                            NULL);
406         gtk_signal_connect(GTK_OBJECT(clist), "button_release_event",
407                            GTK_SIGNAL_FUNC(addressbook_list_button_released),
408                            NULL);
409
410         hbox = gtk_hbox_new(FALSE, 4);
411         gtk_box_pack_start(GTK_BOX(clist_vbox), hbox, FALSE, FALSE, 0);
412
413         label = gtk_label_new(_("Name:"));
414         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
415
416         entry = gtk_entry_new();
417         gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
418
419         address_completion_register_entry(GTK_ENTRY(entry));
420
421 #if 0
422         gtk_signal_connect(GTK_OBJECT(entry), "changed",
423                            GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
424 #endif
425
426         paned = gtk_hpaned_new();
427         gtk_box_pack_start(GTK_BOX(vbox2), paned, TRUE, TRUE, 0);
428         gtk_paned_add1(GTK_PANED(paned), ctree_swin);
429         gtk_paned_add2(GTK_PANED(paned), clist_vbox);
430
431         hbbox = gtk_hbutton_box_new();
432         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
433         gtk_button_box_set_spacing(GTK_BUTTON_BOX(hbbox), 2);
434         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
435
436         del_btn = gtk_button_new_with_label(_("Delete"));
437         GTK_WIDGET_SET_FLAGS(del_btn, GTK_CAN_DEFAULT);
438         gtk_box_pack_start(GTK_BOX(hbbox), del_btn, TRUE, TRUE, 0);
439         reg_btn = gtk_button_new_with_label(_("Add"));
440         GTK_WIDGET_SET_FLAGS(reg_btn, GTK_CAN_DEFAULT);
441         gtk_box_pack_start(GTK_BOX(hbbox), reg_btn, TRUE, TRUE, 0);
442         lup_btn = gtk_button_new_with_label(_("Lookup"));
443         GTK_WIDGET_SET_FLAGS(lup_btn, GTK_CAN_DEFAULT);
444         gtk_box_pack_start(GTK_BOX(hbbox), lup_btn, TRUE, TRUE, 0);
445
446         gtk_signal_connect(GTK_OBJECT(del_btn), "clicked",
447                            GTK_SIGNAL_FUNC(addressbook_del_clicked), NULL);
448         gtk_signal_connect(GTK_OBJECT(reg_btn), "clicked",
449                            GTK_SIGNAL_FUNC(addressbook_reg_clicked), NULL);
450
451         to_btn = gtk_button_new_with_label
452                 (prefs_common.trans_hdr ? _("To:") : "To:");
453         GTK_WIDGET_SET_FLAGS(to_btn, GTK_CAN_DEFAULT);
454         gtk_box_pack_start(GTK_BOX(hbbox), to_btn, TRUE, TRUE, 0);
455         cc_btn = gtk_button_new_with_label
456                 (prefs_common.trans_hdr ? _("Cc:") : "Cc:");
457         GTK_WIDGET_SET_FLAGS(cc_btn, GTK_CAN_DEFAULT);
458         gtk_box_pack_start(GTK_BOX(hbbox), cc_btn, TRUE, TRUE, 0);
459         bcc_btn = gtk_button_new_with_label
460                 (prefs_common.trans_hdr ? _("Bcc:") : "Bcc:");
461         GTK_WIDGET_SET_FLAGS(bcc_btn, GTK_CAN_DEFAULT);
462         gtk_box_pack_start(GTK_BOX(hbbox), bcc_btn, TRUE, TRUE, 0);
463
464         gtk_signal_connect(GTK_OBJECT(to_btn), "clicked",
465                            GTK_SIGNAL_FUNC(addressbook_to_clicked),
466                            GINT_TO_POINTER(COMPOSE_TO));
467         gtk_signal_connect(GTK_OBJECT(cc_btn), "clicked",
468                            GTK_SIGNAL_FUNC(addressbook_to_clicked),
469                            GINT_TO_POINTER(COMPOSE_CC));
470         gtk_signal_connect(GTK_OBJECT(bcc_btn), "clicked",
471                            GTK_SIGNAL_FUNC(addressbook_to_clicked),
472                            GINT_TO_POINTER(COMPOSE_BCC));
473
474         PIXMAP_CREATE(window, folderxpm, folderxpmmask, DIRECTORY_CLOSE_XPM);
475         PIXMAP_CREATE(window, folderopenxpm, folderopenxpmmask,
476                       DIRECTORY_OPEN_XPM);
477         PIXMAP_CREATE(window, groupxpm, groupxpmmask, group_xpm);
478
479         text = _("Common address");
480         addrbook.common =
481                 gtk_ctree_insert_node(GTK_CTREE(ctree),
482                                       NULL, NULL, &text, FOLDER_SPACING,
483                                       folderxpm, folderxpmmask,
484                                       folderopenxpm, folderopenxpmmask,
485                                       FALSE, FALSE);
486         text = _("Personal address");
487         addrbook.personal =
488                 gtk_ctree_insert_node(GTK_CTREE(ctree),
489                                       NULL, NULL, &text, FOLDER_SPACING,
490                                       folderxpm, folderxpmmask,
491                                       folderopenxpm, folderopenxpmmask,
492                                       FALSE, FALSE);
493
494         /* popup menu */
495         n_entries = sizeof(addressbook_tree_popup_entries) /
496                 sizeof(addressbook_tree_popup_entries[0]);
497         tree_popup = menu_create_items(addressbook_tree_popup_entries,
498                                        n_entries,
499                                        "<AddressBookTree>", &tree_factory,
500                                        NULL);
501         gtk_signal_connect(GTK_OBJECT(tree_popup), "selection_done",
502                            GTK_SIGNAL_FUNC(addressbook_popup_close), NULL);
503         n_entries = sizeof(addressbook_list_popup_entries) /
504                 sizeof(addressbook_list_popup_entries[0]);
505         list_popup = menu_create_items(addressbook_list_popup_entries,
506                                        n_entries,
507                                        "<AddressBookList>", &list_factory,
508                                        NULL);
509
510         addrbook.window = window;
511         addrbook.ctree  = ctree;
512         addrbook.clist  = clist;
513         addrbook.entry  = entry;
514
515         addrbook.del_btn = del_btn;
516         addrbook.reg_btn = reg_btn;
517         addrbook.lup_btn = lup_btn;
518         addrbook.to_btn  = to_btn;
519         addrbook.cc_btn  = cc_btn;
520         addrbook.bcc_btn = bcc_btn;
521
522         addrbook.tree_popup   = tree_popup;
523         addrbook.list_popup   = list_popup;
524         addrbook.tree_factory = tree_factory;
525         addrbook.list_factory = list_factory;
526
527         address_completion_start(window);
528
529         gtk_widget_show_all(window);
530 }
531
532 static gint addressbook_close(void)
533 {
534         gtk_widget_hide(addrbook.window);
535         addressbook_export_to_file();
536         /* tell addr_compl that there's a new addressbook file */
537         invalidate_address_completion();
538         return TRUE;
539 }
540
541 static void addressbook_button_set_sensitive(void)
542 {
543         gboolean to_sens  = FALSE;
544         gboolean cc_sens  = FALSE;
545         gboolean bcc_sens = FALSE;
546
547         if (!addrbook.window) return;
548
549         if (addrbook.target_compose) {
550                 to_sens = TRUE;
551                 cc_sens = TRUE;
552                 if (addrbook.target_compose->use_bcc)
553                         bcc_sens = TRUE;
554         }
555
556         gtk_widget_set_sensitive(addrbook.to_btn, to_sens);
557         gtk_widget_set_sensitive(addrbook.cc_btn, cc_sens);
558         gtk_widget_set_sensitive(addrbook.bcc_btn, bcc_sens);
559 }
560
561 static void addressbook_del_clicked(GtkButton *button, gpointer data)
562 {
563         GtkCList *clist = GTK_CLIST(addrbook.clist);
564         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
565         AddressObject *pobj, *obj;
566         GList *cur, *next;
567         gint row;
568
569         if (!clist->selection) {
570                 addressbook_delete_folder_cb(NULL, 0, NULL);
571                 return;
572         }
573
574         pobj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
575         g_return_if_fail(pobj != NULL);
576
577         if (alertpanel(_("Delete address(es)"),
578                        _("Really delete the address(es)?"),
579                        _("Yes"), _("No"), NULL) != G_ALERTDEFAULT)
580                 return;
581
582         for (cur = clist->selection; cur != NULL; cur = next) {
583                 next = cur->next;
584                 row = GPOINTER_TO_INT(cur->data);
585
586                 obj = gtk_clist_get_row_data(clist, row);
587                 if (!obj) continue;
588
589                 if (pobj->type == ADDR_GROUP) {
590                         AddressGroup *group = ADDRESS_GROUP(pobj);
591                         group->items = g_list_remove(group->items, obj);
592                 } else if (pobj->type == ADDR_FOLDER) {
593                         AddressFolder *folder = ADDRESS_FOLDER(pobj);
594
595                         folder->items = g_list_remove(folder->items, obj);
596                         if (obj->type == ADDR_GROUP) {
597                                 GtkCTreeNode *node;
598
599                                 node = gtk_ctree_find_by_row_data
600                                         (ctree, addrbook.opened, obj);
601                                 if (node) gtk_ctree_remove_node(ctree, node);
602                         }
603                 } else
604                         continue;
605
606                 addressbook_delete_object(obj);
607
608                 gtk_clist_remove(clist, row);
609         }
610 }
611
612 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
613 {
614         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
615         GtkEntry *entry = GTK_ENTRY(addrbook.entry);
616         AddressObject *obj;
617         AddressItem *item;
618         gchar *str;
619
620         if (*gtk_entry_get_text(entry) == '\0') {
621                 addressbook_new_address_cb(NULL, 0, NULL);
622                 return;
623         }
624         if (!addrbook.opened) return;
625
626         obj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
627         if (!obj) return;
628
629         g_return_if_fail(obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER);
630
631         str = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
632
633         item = addressbook_parse_address(str);
634         g_free(str);
635         if (item) {
636                 if (addressbook_find_object_by_name
637                         (addrbook.opened, item->name) != NULL) {
638                         addressbook_delete_object(ADDRESS_OBJECT(item));
639                         item = NULL;
640                 } else if (addressbook_edit_address(item) == NULL) {
641                         addressbook_delete_object(ADDRESS_OBJECT(item));
642                         return;
643                 }
644         }
645
646         if (!item) {
647                 item = addressbook_edit_address(NULL);
648                 if (!item) return;
649         }
650
651         if (addressbook_find_object_by_name(addrbook.opened, item->name)) {
652                 addressbook_delete_object(ADDRESS_OBJECT(item));
653                 return;
654         }
655
656         addressbook_add_object(addrbook.opened, ADDRESS_OBJECT(item));
657         addrbook.open_folder = TRUE;
658         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
659 }
660
661 static AddressItem *addressbook_parse_address(const gchar *str)
662 {
663         gchar *name    = NULL;
664         gchar *address = NULL;
665         AddressItem *item;
666         gchar *buf;
667         gchar *start, *end;
668
669         Xalloca(buf, strlen(str) + 1, return NULL);
670
671         strcpy(buf, str);
672         g_strstrip(buf);
673         if (*buf == '\0') return NULL;
674
675         if ((start = strchr(buf, '<'))) {
676                 if (start > buf) {
677                         *start = '\0';
678                         g_strstrip(buf);
679                         if (*buf != '\0')
680                                 name = g_strdup(buf);
681                 }
682                 start++;
683                 if ((end = strchr(start, '>'))) {
684                         *end = '\0';
685                         g_strstrip(start);
686                         if (*start != '\0')
687                                 address = g_strdup(start);
688                 }
689         } else
690                 name = g_strdup(buf);
691
692         if (!name && !address) return NULL;
693
694         item = g_new(AddressItem, 1);
695         ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
696         item->name    = name;
697         item->address = address;
698         item->remarks = NULL;
699
700         return item;
701 }
702
703 static void addressbook_to_clicked(GtkButton *button, gpointer data)
704 {
705         GtkCList *clist = GTK_CLIST(addrbook.clist);
706         GList *cur;
707
708         if (!addrbook.target_compose) return;
709
710         for (cur = clist->selection; cur != NULL; cur = cur->next) {
711                 AddressObject *obj;
712
713                 obj = gtk_clist_get_row_data(clist,
714                                              GPOINTER_TO_INT(cur->data));
715                 if (!obj) return;
716
717                 if (obj->type == ADDR_ITEM) {
718                         addressbook_append_to_compose_entry
719                                 (ADDRESS_ITEM(obj), (ComposeEntryType)data);
720                 } else if (obj->type == ADDR_GROUP) {
721                         AddressGroup *group;
722                         GList *cur_item;
723
724                         group = ADDRESS_GROUP(obj);
725                         for (cur_item = group->items; cur_item != NULL;
726                              cur_item = cur_item->next) {
727                                 if (ADDRESS_OBJECT(cur_item->data)->type
728                                     != ADDR_ITEM)
729                                         continue;
730                                 addressbook_append_to_compose_entry
731                                         (ADDRESS_ITEM(cur_item->data),
732                                          (ComposeEntryType)data);
733                         }
734                 }
735         }
736 }
737
738 static void addressbook_append_to_compose_entry(AddressItem *item,
739                                                 ComposeEntryType type)
740 {
741         Compose *compose = addrbook.target_compose;
742
743         if (item->name && item->address) {
744                 gchar *buf;
745
746                 buf = g_strdup_printf
747                         ("%s <%s>", item->name, item->address);
748                 compose_entry_append(compose, buf, type);
749                 g_free(buf);
750         } else if (item->address)
751                 compose_entry_append(compose, item->address, type);
752 }
753
754 static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
755                                       gint column, gpointer data)
756 {
757         AddressObject *obj;
758
759         addrbook.selected = node;
760
761         if (!addrbook.open_folder) return;
762         addrbook.open_folder = FALSE;
763
764         gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
765
766         obj = gtk_ctree_node_get_row_data(ctree, node);
767         g_return_if_fail(obj != NULL);
768
769         addrbook.opened = node;
770
771         if (obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER)
772                 addressbook_set_clist(obj);
773 }
774
775 static void addressbook_list_selected(GtkCList *clist, gint row, gint column,
776                                       GdkEvent *event, gpointer data)
777 {
778         GtkEntry *entry = GTK_ENTRY(addrbook.entry);
779         AddressObject *obj;
780         GList *cur;
781
782         if (event && event->type == GDK_2BUTTON_PRESS) {
783                 addressbook_edit_address_cb(NULL, 0, NULL);
784                 return;
785         }
786
787 #if 0
788         gtk_signal_handler_block_by_func
789                 (GTK_OBJECT(entry),
790                  GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
791 #endif           
792
793         gtk_entry_set_text(entry, "");
794
795         for (cur = clist->selection; cur != NULL; cur = cur->next) {
796                 obj = gtk_clist_get_row_data(clist,
797                                              GPOINTER_TO_INT(cur->data));
798                 g_return_if_fail(obj != NULL);
799
800                 if (obj->type == ADDR_ITEM) {
801                         AddressItem *item;
802
803                         item = ADDRESS_ITEM(obj);
804                         if (item->name && item->address) {
805                                 gchar *buf;
806
807                                 buf = g_strdup_printf
808                                         ("%s <%s>", item->name, item->address);
809                                 if (*gtk_entry_get_text(entry) != '\0')
810                                         gtk_entry_append_text(entry, ", ");
811                                 gtk_entry_append_text(entry, buf);
812                                 g_free(buf);
813                         } else if (item->address) {
814                                 if (*gtk_entry_get_text(entry) != '\0')
815                                         gtk_entry_append_text(entry, ", ");
816                                 gtk_entry_append_text(entry, item->address);
817                         }
818                 }
819         }
820
821 #if 0
822         gtk_signal_handler_unblock_by_func
823                 (GTK_OBJECT(entry),
824                  GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
825 #endif           
826 }
827
828 #if 0
829 static void addressbook_entry_changed(GtkWidget *widget)
830 {
831         GtkCList *clist = GTK_CLIST(addrbook.clist);
832         GtkEntry *entry = GTK_ENTRY(addrbook.entry);
833         const gchar *str;
834         gint len;
835         gint row;
836
837         //if (clist->selection && clist->selection->next) return;
838
839         str = gtk_entry_get_text(entry);
840         if (*str == '\0') {
841                 gtk_clist_unselect_all(clist);
842                 return;
843         }
844         len = strlen(str);
845
846         for (row = 0; row < clist->rows; row++) {
847                 AddressObject *obj;
848                 const gchar *name;
849
850                 obj = ADDRESS_OBJECT(gtk_clist_get_row_data(clist, row));
851                 if (!obj) continue;
852                 if (obj->type == ADDR_ITEM)
853                         name = ADDRESS_ITEM(obj)->name;
854                 else if (obj->type == ADDR_GROUP)
855                         name = ADDRESS_GROUP(obj)->name;
856                 else
857                         continue;
858
859                 if (name && !strncasecmp(name, str, len)) {
860                         gtk_clist_unselect_all(clist);
861                         gtk_clist_select_row(clist, row, -1);
862                         return;
863                 }
864         }
865
866         gtk_clist_unselect_all(clist);
867 }
868 #endif
869
870 static void addressbook_list_button_pressed(GtkWidget *widget,
871                                             GdkEventButton *event,
872                                             gpointer data)
873 {
874         GtkCList *clist = GTK_CLIST(widget);
875         gint row, column;
876         AddressObject *obj;
877
878         if (!event) return;
879         if (event->button != 3) return;
880
881         obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
882                                           addrbook.opened);
883         g_return_if_fail(obj != NULL);
884
885         menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.list_popup));
886
887         if (gtk_clist_get_selection_info
888                 (clist, event->x, event->y, &row, &column)) {
889                 GtkCListRow *clist_row;
890
891                 clist_row = g_list_nth(clist->row_list, row)->data;
892                 if (clist_row->state != GTK_STATE_SELECTED) {
893                         gtk_clist_unselect_all(clist);
894                         gtk_clist_select_row(clist, row, column);
895                 }
896                 gtkut_clist_set_focus_row(clist, row);
897
898                 menu_set_sensitive(addrbook.list_factory, "/Edit", TRUE);
899                 menu_set_sensitive(addrbook.list_factory, "/Delete", TRUE);
900         }
901
902         menu_set_sensitive(addrbook.list_factory, "/New address", TRUE);
903         if (obj->type == ADDR_FOLDER) {
904                 menu_set_sensitive(addrbook.list_factory, "/New folder", TRUE);
905                 menu_set_sensitive(addrbook.list_factory, "/New group", TRUE);
906         }
907
908         gtk_menu_popup(GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
909                        event->button, event->time);
910 }
911
912 static void addressbook_list_button_released(GtkWidget *widget,
913                                              GdkEventButton *event,
914                                              gpointer data)
915 {
916 }
917
918 static void addressbook_tree_button_pressed(GtkWidget *ctree,
919                                             GdkEventButton *event,
920                                             gpointer data)
921 {
922         GtkCList *clist = GTK_CLIST(ctree);
923         gint row, column;
924         AddressObject *obj;
925         GtkCTreeNode *node;
926
927         if (!event) return;
928         if (event->button == 1) {
929                 addrbook.open_folder = TRUE;
930                 return;
931         }
932         if (event->button != 3) return;
933
934         if (!gtk_clist_get_selection_info
935                 (clist, event->x, event->y, &row, &column)) return;
936         gtk_clist_select_row(clist, row, column);
937
938         obj = gtk_clist_get_row_data(clist, row);
939         g_return_if_fail(obj != NULL);
940
941         menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
942
943         if (obj->type == ADDR_FOLDER) {
944                 node = gtk_ctree_node_nth(GTK_CTREE(ctree), row);
945                 menu_set_sensitive(addrbook.tree_factory, "/New folder", TRUE);
946                 menu_set_sensitive(addrbook.tree_factory, "/New group", TRUE);
947                 if (node && GTK_CTREE_ROW(node)->level >= 2) {
948                         menu_set_sensitive(addrbook.tree_factory,
949                                            "/Edit", TRUE);
950                         menu_set_sensitive(addrbook.tree_factory,
951                                            "/Delete", TRUE);
952                 }
953         } else if (obj->type == ADDR_GROUP) {
954                 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
955                 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
956         } else
957                 return;
958
959         menu_set_sensitive(addrbook.tree_factory, "/New address", TRUE);
960
961         gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
962                        event->button, event->time);
963 }
964
965 static void addressbook_tree_button_released(GtkWidget *ctree,
966                                              GdkEventButton *event,
967                                              gpointer data)
968 {
969         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
970         gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree), addrbook.opened);
971 }
972
973 static void addressbook_popup_close(GtkMenuShell *menu_shell, gpointer data)
974 {
975         if (!addrbook.opened) return;
976
977         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
978         gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree),
979                                   addrbook.opened);
980 }
981
982 static void addressbook_new_folder_cb(gpointer data, guint action,
983                                       GtkWidget *widget)
984 {
985         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
986         AddressObject *obj;
987         AddressFolder *folder;
988         gchar *new_folder;
989
990         if (!addrbook.selected) return;
991
992         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
993         g_return_if_fail(obj != NULL);
994         if (obj->type != ADDR_FOLDER) return;
995
996         new_folder = input_dialog(_("New folder"),
997                                   _("Input the name of new folder:"),
998                                   _("NewFolder"));
999         if (!new_folder) return;
1000         g_strstrip(new_folder);
1001         if (*new_folder == '\0') {
1002                 g_free(new_folder);
1003                 return;
1004         }
1005
1006         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1007                                               new_folder,
1008                                               addressbook_obj_name_compare)) {
1009                 alertpanel_error(_("The name already exists."));
1010                 g_free(new_folder);
1011                 return;
1012         }
1013
1014         folder = g_new(AddressFolder, 1);
1015         ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1016         folder->name = g_strdup(new_folder);
1017         folder->items = NULL;
1018
1019         addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(folder));
1020
1021         g_free(new_folder);
1022
1023         if (addrbook.selected == addrbook.opened)
1024                 addressbook_set_clist(obj);
1025 }
1026
1027 static void addressbook_new_group_cb(gpointer data, guint action,
1028                                      GtkWidget *widget)
1029 {
1030         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1031         AddressObject *obj;
1032         AddressGroup *group;
1033         gchar *new_group;
1034
1035         if (!addrbook.selected) return;
1036
1037         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1038         g_return_if_fail(obj != NULL);
1039         if (obj->type != ADDR_FOLDER) return;
1040
1041         new_group = input_dialog(_("New group"),
1042                                  _("Input the name of new group:"),
1043                                   _("NewGroup"));
1044         if (!new_group) return;
1045         g_strstrip(new_group);
1046         if (*new_group == '\0') {
1047                 g_free(new_group);
1048                 return;
1049         }
1050
1051         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1052                                               new_group,
1053                                               addressbook_obj_name_compare)) {
1054                 alertpanel_error(_("The name already exists."));
1055                 g_free(new_group);
1056                 return;
1057         }
1058
1059         group = g_new(AddressGroup, 1);
1060         ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1061         group->name = g_strdup(new_group);
1062         group->items = NULL;
1063
1064         addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(group));
1065
1066         g_free(new_group);
1067
1068         if (addrbook.selected == addrbook.opened)
1069                 addressbook_set_clist(obj);
1070 }
1071
1072 static void addressbook_change_node_name(GtkCTreeNode *node, const gchar *name)
1073 {
1074         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1075         gchar *text[1];
1076         guint8 spacing;
1077         GdkPixmap *pix_cl, *pix_op;
1078         GdkBitmap *mask_cl, *mask_op;
1079         gboolean is_leaf, expanded;
1080
1081         gtk_ctree_get_node_info(ctree, node, text, &spacing,
1082                                 &pix_cl, &mask_cl, &pix_op, &mask_op,
1083                                 &is_leaf, &expanded);
1084         gtk_ctree_set_node_info(ctree, node, name, spacing,
1085                                 pix_cl, mask_cl, pix_op, mask_op,
1086                                 is_leaf, expanded);
1087 }
1088
1089 static void addressbook_edit_group(GtkCTreeNode *group_node)
1090 {
1091         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1092         GtkCList *clist = GTK_CLIST(addrbook.clist);
1093         AddressObject *obj;
1094         AddressGroup *group;
1095         gchar *new_name;
1096         GtkCTreeNode *node;
1097
1098         if (!group_node && clist->selection) {
1099                 obj = gtk_clist_get_row_data(clist,
1100                                              GPOINTER_TO_INT(clist->selection->data));
1101                 g_return_if_fail(obj != NULL);
1102                 if (obj->type != ADDR_GROUP) return;
1103                 node = gtk_ctree_find_by_row_data
1104                         (ctree, addrbook.selected, obj);
1105                 if (!node) return;
1106         } else {
1107                 if (group_node)
1108                         node = group_node;
1109                 else
1110                         node = addrbook.selected;
1111                 obj = gtk_ctree_node_get_row_data(ctree, node);
1112                 g_return_if_fail(obj != NULL);
1113                 if (obj->type != ADDR_GROUP) return;
1114         }
1115
1116         group = ADDRESS_GROUP(obj);
1117
1118         new_name = input_dialog(_("Edit group"),
1119                                 _("Input the new name of group:"),
1120                                 group->name);
1121         if (!new_name) return;
1122         g_strstrip(new_name);
1123         if (*new_name == '\0') {
1124                 g_free(new_name);
1125                 return;
1126         }
1127
1128         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1129                                               new_name,
1130                                               addressbook_obj_name_compare)) {
1131                 alertpanel_error(_("The name already exists."));
1132                 g_free(new_name);
1133                 return;
1134         }
1135
1136         g_free(group->name);
1137         group->name = g_strdup(new_name);
1138
1139         addressbook_change_node_name(node, new_name);
1140         gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(node)->parent);
1141
1142         g_free(new_name);
1143
1144         addrbook.open_folder = TRUE;
1145         gtk_ctree_select(ctree, addrbook.opened);
1146 }
1147
1148 static void addressbook_edit_folder_cb(gpointer data, guint action,
1149                                        GtkWidget *widget)
1150 {
1151         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1152         AddressObject *obj;
1153         AddressFolder *folder;
1154         gchar *new_name;
1155
1156         if (!addrbook.selected) return;
1157         if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1158
1159         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1160         g_return_if_fail(obj != NULL);
1161         g_return_if_fail(obj->type == ADDR_FOLDER || obj->type == ADDR_GROUP);
1162
1163         if (obj->type == ADDR_GROUP) {
1164                 addressbook_edit_group(addrbook.selected);
1165                 return;
1166         }
1167
1168         folder = ADDRESS_FOLDER(obj);
1169         new_name = input_dialog(_("Edit folder"),
1170                                 _("Input the new name of folder:"),
1171                                 folder->name);
1172
1173         if (!new_name) return;
1174         g_strstrip(new_name);
1175         if (*new_name == '\0') {
1176                 g_free(new_name);
1177                 return;
1178         }
1179
1180         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1181                                               new_name,
1182                                               addressbook_obj_name_compare)) {
1183                 alertpanel_error(_("The name already exists."));
1184                 g_free(new_name);
1185                 return;
1186         }
1187
1188         g_free(folder->name);
1189         folder->name = g_strdup(new_name);
1190
1191         addressbook_change_node_name(addrbook.selected, new_name);
1192         gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1193
1194         g_free(new_name);
1195 }
1196
1197 static void addressbook_delete_folder_cb(gpointer data, guint action,
1198                                          GtkWidget *widget)
1199 {
1200         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1201         AddressObject *obj, *pobj;
1202         gchar *name;
1203         gchar *message;
1204         AlertValue aval;
1205
1206         if (!addrbook.selected) return;
1207         if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1208
1209         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1210         g_return_if_fail(obj != NULL);
1211
1212         if (obj->type == ADDR_GROUP)
1213                 name = ADDRESS_GROUP(obj)->name;
1214         else if (obj->type == ADDR_FOLDER)
1215                 name = ADDRESS_FOLDER(obj)->name;
1216         else
1217                 return;
1218
1219         message = g_strdup_printf(_("Really delete `%s' ?"), name);
1220         aval = alertpanel(_("Delete"), message, _("Yes"), _("No"), NULL);
1221         g_free(message);
1222         if (aval != G_ALERTDEFAULT) return;
1223
1224         pobj = gtk_ctree_node_get_row_data
1225                 (ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1226         if (!pobj) return;
1227         g_return_if_fail(pobj->type == ADDR_FOLDER);
1228         ADDRESS_FOLDER(pobj)->items =
1229                 g_list_remove(ADDRESS_FOLDER(pobj)->items, obj);
1230
1231         addressbook_delete_object(obj);
1232         addrbook.open_folder = TRUE;
1233         gtk_ctree_remove_node(ctree, addrbook.selected);
1234         addrbook.open_folder = FALSE;
1235 }
1236
1237 #define SET_LABEL_AND_ENTRY(str, entry, top) \
1238 { \
1239         label = gtk_label_new(str); \
1240         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
1241                          GTK_FILL, 0, 0, 0); \
1242         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
1243  \
1244         entry = gtk_entry_new(); \
1245         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
1246                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
1247 }
1248
1249 static void addressbook_edit_address_create(gboolean *cancelled)
1250 {
1251         GtkWidget *window;
1252         GtkWidget *vbox;
1253         GtkWidget *table;
1254         GtkWidget *label;
1255         GtkWidget *name_entry;
1256         GtkWidget *addr_entry;
1257         GtkWidget *rem_entry;
1258         GtkWidget *hbbox;
1259         GtkWidget *ok_btn;
1260         GtkWidget *cancel_btn;
1261
1262         debug_print("Creating edit_address window...\n");
1263
1264         window = gtk_window_new(GTK_WINDOW_DIALOG);
1265         gtk_widget_set_usize(window, 400, -1);
1266         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
1267         gtk_window_set_title(GTK_WINDOW(window), _("Edit address"));
1268         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1269         gtk_window_set_modal(GTK_WINDOW(window), TRUE); 
1270         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
1271                            GTK_SIGNAL_FUNC(edit_address_delete_event),
1272                            cancelled);
1273         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
1274                            GTK_SIGNAL_FUNC(edit_address_key_pressed),
1275                            cancelled);
1276
1277         vbox = gtk_vbox_new(FALSE, 8);
1278         gtk_container_add(GTK_CONTAINER(window), vbox);
1279
1280         table = gtk_table_new(3, 2, FALSE);
1281         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
1282         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
1283         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
1284
1285         SET_LABEL_AND_ENTRY(_("Name"),    name_entry, 0);
1286         SET_LABEL_AND_ENTRY(_("Address"), addr_entry, 1);
1287         SET_LABEL_AND_ENTRY(_("Remarks"), rem_entry,  2);
1288
1289         gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
1290                                 &cancel_btn, _("Cancel"), NULL, NULL);
1291         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
1292         gtk_widget_grab_default(ok_btn);
1293
1294         gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
1295                            GTK_SIGNAL_FUNC(edit_address_ok), cancelled);
1296         gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
1297                            GTK_SIGNAL_FUNC(edit_address_cancel), cancelled);
1298
1299         gtk_widget_show_all(vbox);
1300
1301         addredit.window     = window;
1302         addredit.name_entry = name_entry;
1303         addredit.addr_entry = addr_entry;
1304         addredit.rem_entry  = rem_entry;
1305         addredit.ok_btn     = ok_btn;
1306         addredit.cancel_btn = cancel_btn;
1307 }
1308
1309 static void edit_address_ok(GtkWidget *widget, gboolean *cancelled)
1310 {
1311         *cancelled = FALSE;
1312         gtk_main_quit();
1313 }
1314
1315 static void edit_address_cancel(GtkWidget *widget, gboolean *cancelled)
1316 {
1317         *cancelled = TRUE;
1318         gtk_main_quit();
1319 }
1320
1321 static gint edit_address_delete_event(GtkWidget *widget, GdkEventAny *event,
1322                                       gboolean *cancelled)
1323 {
1324         *cancelled = TRUE;
1325         gtk_main_quit();
1326
1327         return TRUE;
1328 }
1329
1330 static void edit_address_key_pressed(GtkWidget *widget, GdkEventKey *event,
1331                                      gboolean *cancelled)
1332 {
1333         if (event && event->keyval == GDK_Escape) {
1334                 *cancelled = TRUE;
1335                 gtk_main_quit();
1336         }
1337 }
1338
1339 static AddressItem *addressbook_edit_address(AddressItem *item)
1340 {
1341         static gboolean cancelled;
1342         const gchar *str;
1343
1344         if (!addredit.window)
1345                 addressbook_edit_address_create(&cancelled);
1346         gtk_widget_grab_focus(addredit.ok_btn);
1347         gtk_widget_grab_focus(addredit.name_entry);
1348         gtk_widget_show(addredit.window);
1349         manage_window_set_transient(GTK_WINDOW(addredit.window));
1350
1351         gtk_entry_set_text(GTK_ENTRY(addredit.name_entry), "");
1352         gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry), "");
1353         gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry),  "");
1354
1355         if (item) {
1356                 if (item->name)
1357                         gtk_entry_set_text(GTK_ENTRY(addredit.name_entry),
1358                                            item->name);
1359                 if (item->address)
1360                         gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry),
1361                                            item->address);
1362                 if (item->remarks)
1363                         gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry),
1364                                            item->remarks);
1365         }
1366
1367         gtk_main();
1368         gtk_widget_hide(addredit.window);
1369         if (cancelled == TRUE) return NULL;
1370
1371         str = gtk_entry_get_text(GTK_ENTRY(addredit.name_entry));
1372         if (*str == '\0') return NULL;
1373
1374         if (!item) {
1375                 item = g_new0(AddressItem, 1);
1376                 ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
1377         }
1378
1379         g_free(item->name);
1380         item->name = g_strdup(str);
1381
1382         str = gtk_entry_get_text(GTK_ENTRY(addredit.addr_entry));
1383         g_free(item->address);
1384         if (*str == '\0')
1385                 item->address = NULL;
1386         else
1387                 item->address = g_strdup(str);
1388
1389         str = gtk_entry_get_text(GTK_ENTRY(addredit.rem_entry));
1390         g_free(item->remarks);
1391         if (*str == '\0')
1392                 item->remarks = NULL;
1393         else
1394                 item->remarks = g_strdup(str);
1395
1396         return item;
1397 }
1398
1399 static void addressbook_new_address_cb(gpointer data, guint action,
1400                                        GtkWidget *widget)
1401 {
1402         AddressItem *item;
1403
1404         item = addressbook_edit_address(NULL);
1405
1406         if (item) {
1407                 addressbook_add_object(addrbook.selected,
1408                                        ADDRESS_OBJECT(item));
1409                 if (addrbook.selected == addrbook.opened) {
1410                         addrbook.open_folder = TRUE;
1411                         gtk_ctree_select(GTK_CTREE(addrbook.ctree),
1412                                          addrbook.opened);
1413                 }
1414         }
1415 }
1416
1417 static void addressbook_edit_address_cb(gpointer data, guint action,
1418                                         GtkWidget *widget)
1419 {
1420         GtkCList *clist = GTK_CLIST(addrbook.clist);
1421         AddressObject *obj;
1422
1423         if (!clist->selection) {
1424                 addressbook_edit_folder_cb(NULL, 0, NULL);
1425                 return;
1426         }
1427
1428         obj = gtk_clist_get_row_data(clist,
1429                                      GPOINTER_TO_INT(clist->selection->data));
1430         g_return_if_fail(obj != NULL);
1431
1432         if (obj->type == ADDR_ITEM) {
1433                 AddressItem *item = ADDRESS_ITEM(obj);
1434
1435                 if (addressbook_edit_address(item) == NULL) return;
1436
1437                 addrbook.open_folder = TRUE;
1438                 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1439
1440                 return;
1441         } else if (obj->type == ADDR_GROUP) {
1442                 addressbook_edit_group(NULL);
1443         }
1444 }
1445
1446 static void addressbook_delete_address_cb(gpointer data, guint action,
1447                                           GtkWidget *widget)
1448 {
1449         addressbook_del_clicked(NULL, NULL);
1450 }
1451
1452 static void close_cb(gpointer data, guint action, GtkWidget *widget)
1453 {
1454         addressbook_close();
1455 }
1456
1457 static void addressbook_set_clist(AddressObject *obj)
1458 {
1459         GtkCList *clist = GTK_CLIST(addrbook.clist);
1460         GList *items;
1461         gchar *text[N_COLS];
1462
1463         if (!obj) {
1464                 gtk_clist_clear(clist);
1465                 return;
1466         }
1467
1468         gtk_clist_freeze(clist);
1469
1470         gtk_clist_clear(clist);
1471
1472         if (obj->type == ADDR_GROUP)
1473                 items = ADDRESS_GROUP(obj)->items;
1474         else if (obj->type == ADDR_FOLDER)
1475                 items = ADDRESS_FOLDER(obj)->items;
1476         else {
1477                 gtk_clist_thaw(clist);
1478                 return;
1479         }
1480
1481         for (; items != NULL; items = items->next) {
1482                 AddressObject *iobj;
1483                 gint row;
1484
1485                 iobj = ADDRESS_OBJECT(items->data);
1486
1487                 if (iobj->type == ADDR_GROUP) {
1488                         AddressGroup *group;
1489
1490                         group = ADDRESS_GROUP(iobj);
1491                         text[COL_NAME]    = group->name;
1492                         text[COL_ADDRESS] = NULL;
1493                         text[COL_REMARKS] = NULL;
1494                         row = gtk_clist_append(clist, text);
1495                         gtk_clist_set_pixtext(clist, row, COL_NAME,
1496                                               group->name, 4,
1497                                               groupxpm, groupxpmmask);
1498                         gtk_clist_set_row_data(clist, row, iobj);
1499                 } else if (iobj->type == ADDR_ITEM) {
1500                         AddressItem *item;
1501
1502                         item = ADDRESS_ITEM(iobj);
1503                         text[COL_NAME]    = item->name;
1504                         text[COL_ADDRESS] = item->address;
1505                         text[COL_REMARKS] = item->remarks;
1506                         row = gtk_clist_append(clist, text);
1507                         gtk_clist_set_row_data(clist, row, iobj);
1508                 }
1509         }
1510
1511         gtk_clist_sort(clist);
1512         gtk_clist_thaw(clist);
1513 }
1514
1515 static void addressbook_read_file(void)
1516 {
1517         XMLFile *file;
1518         gchar *path;
1519
1520         debug_print(_("Reading addressbook file..."));
1521
1522         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
1523         if ((file = xml_open_file(path)) == NULL) {
1524                 debug_print(_("%s doesn't exist.\n"), path);
1525                 g_free(path);
1526                 addressbook_get_tree(NULL, addrbook.common, "common_address");
1527                 addressbook_get_tree(NULL, addrbook.personal, "personal_address");
1528                 return;
1529         }
1530         g_free(path);
1531
1532         xml_get_dtd(file);
1533
1534         if (xml_parse_next_tag(file) < 0 ||
1535             xml_compare_tag(file, "addressbook") == FALSE) {
1536                 g_warning("Invalid addressbook data\n");
1537                 xml_close_file(file);
1538                 return;
1539         }
1540
1541         addressbook_get_tree(file, addrbook.common, "common_address");
1542         addressbook_get_tree(file, addrbook.personal, "personal_address");
1543
1544         xml_close_file(file);
1545
1546         debug_print(_("done.\n"));
1547 }
1548
1549 static void addressbook_get_tree(XMLFile *file, GtkCTreeNode *node,
1550                                  const gchar *folder_tag)
1551 {
1552         AddressFolder *folder;
1553
1554         g_return_if_fail(node != NULL);
1555
1556         folder = g_new(AddressFolder, 1);
1557         ADDRESS_OBJECT(folder)->type = ADDR_FOLDER;
1558         folder->name = g_strdup(folder_tag);
1559         folder->items = NULL;
1560         gtk_ctree_node_set_row_data(GTK_CTREE(addrbook.ctree), node, folder);
1561
1562         if (file) {
1563                 if (xml_parse_next_tag(file) < 0 ||
1564                     xml_compare_tag(file, folder_tag) == FALSE) {
1565                         g_warning("Invalid addressbook data\n");
1566                         return;
1567                 }
1568         }
1569
1570         if (file) addressbook_add_objs(file, node);
1571 }
1572
1573 static void addressbook_add_objs(XMLFile *file, GtkCTreeNode *node)
1574 {
1575         GList *attr;
1576         guint prev_level;
1577         GtkCTreeNode *new_node;
1578
1579         for (;;) {
1580                 prev_level = file->level;
1581                 if (xml_parse_next_tag(file) < 0) return;
1582                 if (file->level < prev_level) return;
1583
1584                 if (xml_compare_tag(file, "group")) {
1585                         AddressGroup *group;
1586
1587                         group = g_new(AddressGroup, 1);
1588                         ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1589                         attr = xml_get_current_tag_attr(file);
1590                         if (attr)
1591                                 group->name = g_strdup(((XMLAttr *)attr->data)->value);
1592                         else
1593                                 group->name = NULL;
1594                         group->items = NULL;
1595
1596                         new_node = addressbook_add_object
1597                                 (node, ADDRESS_OBJECT(group));
1598
1599                         addressbook_add_objs(file, new_node);
1600                 } else if (xml_compare_tag(file, "folder")) {
1601                         AddressFolder *folder;
1602
1603                         folder = g_new(AddressFolder, 1);
1604                         ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1605                         attr = xml_get_current_tag_attr(file);
1606                         if (attr)
1607                                 folder->name = g_strdup(((XMLAttr *)attr->data)->value);
1608                         else
1609                                 folder->name = NULL;
1610                         folder->items = NULL;
1611
1612                         new_node = addressbook_add_object
1613                                 (node, ADDRESS_OBJECT(folder));
1614
1615                         addressbook_add_objs(file, new_node);
1616                 } else if (xml_compare_tag(file, "item")) {
1617                         AddressItem *item;
1618
1619                         item = addressbook_parse_item(file);
1620                         if (!item) return;
1621                         new_node = addressbook_add_object
1622                                 (node, ADDRESS_OBJECT(item));
1623                 } else {
1624                         g_warning("Invalid tag\n");
1625                         return;
1626                 }
1627
1628                 if (!new_node) return;
1629         }
1630 }
1631
1632 static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
1633                                             AddressObject *obj)
1634 {
1635         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1636         GtkCTreeNode *added;
1637         AddressObject *pobj;
1638
1639         g_return_val_if_fail(node != NULL, NULL);
1640         g_return_val_if_fail(obj  != NULL, NULL);
1641
1642         pobj = gtk_ctree_node_get_row_data(ctree, node);
1643         g_return_val_if_fail(pobj != NULL, NULL);
1644         if (pobj->type == ADDR_ITEM) {
1645                 g_warning("Parent object mustn't be an item.\n");
1646                 return NULL;
1647         }
1648         if (pobj->type == ADDR_FOLDER &&
1649             (obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER))
1650                 gtk_ctree_expand(ctree, node);
1651
1652         if (obj->type == ADDR_GROUP) {
1653                 AddressGroup *group = ADDRESS_GROUP(obj);
1654
1655                 if (pobj->type != ADDR_FOLDER) {
1656                         g_warning("Group can't be added in another group.\n");
1657                         return NULL;
1658                 }
1659
1660                 added = gtk_ctree_insert_node(ctree, node, NULL,
1661                                               &group->name, FOLDER_SPACING,
1662                                               groupxpm, groupxpmmask,
1663                                               groupxpm, groupxpmmask,
1664                                               TRUE, FALSE);
1665                 gtk_ctree_node_set_row_data(ctree, added, obj);
1666         } else if (obj->type == ADDR_FOLDER) {
1667                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1668
1669                 if (pobj->type != ADDR_FOLDER) {
1670                         g_warning("Group can't contain folder.\n");
1671                         return NULL;
1672                 }
1673
1674                 added = gtk_ctree_insert_node(ctree, node, NULL,
1675                                               &folder->name, FOLDER_SPACING,
1676                                               folderxpm, folderxpmmask,
1677                                               folderopenxpm, folderopenxpmmask,
1678                                               FALSE, FALSE);
1679                 gtk_ctree_node_set_row_data(ctree, added, obj);
1680         } else {
1681                 added = node;
1682         }
1683
1684         if (obj->type == ADDR_GROUP || obj->type == ADDR_ITEM) {
1685                 if (pobj->type == ADDR_GROUP) {
1686                         AddressGroup *group = ADDRESS_GROUP(pobj);
1687
1688                         group->items = g_list_append(group->items, obj);
1689                 } else if (pobj->type == ADDR_FOLDER) {
1690                         AddressFolder *folder = ADDRESS_FOLDER(pobj);
1691
1692                         folder->items = g_list_append(folder->items, obj);
1693                 }
1694         }
1695
1696         gtk_ctree_sort_node(ctree, node);
1697
1698         return added;
1699 }
1700
1701 static void addressbook_delete_object(AddressObject *obj)
1702 {
1703         if (!obj) return;
1704
1705         if (obj->type == ADDR_ITEM) {
1706                 AddressItem *item = ADDRESS_ITEM(obj);
1707
1708                 g_free(item->name);
1709                 g_free(item->address);
1710                 g_free(item->remarks);
1711                 g_free(item);
1712         } else if (obj->type == ADDR_GROUP) {
1713                 AddressGroup *group = ADDRESS_GROUP(obj);
1714
1715                 g_free(group->name);
1716                 while (group->items != NULL) {
1717                         addressbook_delete_object
1718                                 (ADDRESS_OBJECT(group->items->data));
1719                         group->items = g_list_remove(group->items,
1720                                                      group->items->data);
1721                 }
1722                 g_free(group);
1723         } else if (obj->type == ADDR_FOLDER) {
1724                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1725
1726                 g_free(folder->name);
1727                 while (folder->items != NULL) {
1728                         addressbook_delete_object
1729                                 (ADDRESS_OBJECT(folder->items->data));
1730                         folder->items = g_list_remove(folder->items,
1731                                                       folder->items->data);
1732                 }
1733                 g_free(folder);
1734         }
1735 }
1736
1737 static AddressObject *addressbook_find_object_by_name(GtkCTreeNode *node,
1738                                                       const gchar *name)
1739 {
1740         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1741         AddressObject *obj;
1742         GList *found;
1743
1744         g_return_val_if_fail(node != NULL, NULL);
1745
1746         obj = gtk_ctree_node_get_row_data(ctree, node);
1747         g_return_val_if_fail(obj != NULL, NULL);
1748
1749         if (obj->type == ADDR_GROUP) {
1750                 AddressGroup *group = ADDRESS_GROUP(obj);
1751
1752                 found = g_list_find_custom(group->items, (gpointer)name,
1753                                            addressbook_obj_name_compare);
1754                 if (found) return ADDRESS_OBJECT(found->data);
1755         } else if (obj->type == ADDR_FOLDER) {
1756                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1757
1758                 found = g_list_find_custom(folder->items, (gpointer)name,
1759                                            addressbook_obj_name_compare);
1760                 if (found) return ADDRESS_OBJECT(found->data);
1761         } else if (obj->type == ADDR_ITEM) {
1762                 if (!addressbook_obj_name_compare(obj, name)) return obj;
1763         }
1764
1765         return NULL;
1766 }
1767
1768 #define PARSE_ITEM_ERROR() \
1769 { \
1770         g_warning("addressbook_parse_item(): Parse error\n"); \
1771         g_free(item->name); \
1772         g_free(item->address); \
1773         g_free(item->remarks); \
1774         g_free(item); \
1775         return NULL; \
1776 }
1777
1778 static AddressItem *addressbook_parse_item(XMLFile *file)
1779 {
1780         gchar *element;
1781         AddressItem *item;
1782         guint level;
1783
1784         item = g_new0(AddressItem, 1);
1785         ADDRESS_OBJECT(item)->type = ADDR_ITEM;
1786
1787         level = file->level;
1788
1789         while (xml_parse_next_tag(file) == 0) {
1790                 if (file->level < level) return item;
1791                 if (file->level == level) break;
1792
1793                 element = xml_get_element(file);
1794
1795                 if (xml_compare_tag(file, "name")) {
1796                         item->name = element;
1797                 } else if (xml_compare_tag(file, "address")) {
1798                         item->address = element;
1799                 } else if (xml_compare_tag(file, "remarks")) {
1800                         item->remarks = element;
1801                 }
1802
1803                 if (xml_parse_next_tag(file) < 0) break;
1804                 if (file->level != level) break;
1805         }
1806
1807         PARSE_ITEM_ERROR();
1808 }
1809
1810 void addressbook_export_to_file(void)
1811 {
1812         PrefFile *pfile;
1813         gchar *path;
1814
1815         if (!addrbook.ctree) return;
1816
1817         debug_print(_("Exporting addressbook to file..."));
1818
1819         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
1820         if ((pfile = prefs_write_open(path)) == NULL) {
1821                 g_free(path);
1822                 return;
1823         }
1824         g_free(path);
1825
1826         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
1827                 conv_get_current_charset_str());
1828         fputs("<addressbook>\n\n", pfile->fp);
1829
1830         addressbook_xml_recursive_write(NULL, pfile->fp);
1831
1832         fputs("</addressbook>\n", pfile->fp);
1833
1834         if (prefs_write_close(pfile) < 0) {
1835                 g_warning(_("failed to write addressbook data.\n"));
1836                 return;
1837         }
1838
1839         debug_print(_("done.\n"));
1840 }
1841
1842 /* Most part of this function was taken from gtk_ctree_pre_recursive() and
1843    gtk_ctree_post_recursive(). */
1844 static void addressbook_xml_recursive_write(GtkCTreeNode *node, FILE *fp)
1845 {
1846         GtkCTreeNode *work;
1847         GtkCTreeNode *tmp;
1848
1849         if (node) {
1850                 work = GTK_CTREE_ROW(node)->children;
1851                 addressbook_node_write_begin(node, fp);
1852         } else
1853                 work = GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list);
1854
1855         while (work) {
1856                 tmp = GTK_CTREE_ROW(work)->sibling;
1857                 addressbook_xml_recursive_write(work, fp);
1858                 work = tmp;
1859         }
1860
1861         if (node)
1862                 addressbook_node_write_end(node, fp);
1863 }
1864
1865 static void addressbook_node_write_begin(GtkCTreeNode *node, FILE *fp)
1866 {
1867         AddressObject *obj;
1868
1869         obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
1870         g_return_if_fail(obj != NULL);
1871
1872         if (obj->type == ADDR_FOLDER) {
1873                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1874
1875                 if (GTK_CTREE_ROW(node)->level == 1) {
1876                         fprintf(fp, "<%s>\n", folder->name);
1877                 } else {
1878                         tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1879                         fputs("<folder name=\"", fp);
1880                         xml_file_put_escape_str(fp, folder->name);
1881                         fputs("\">\n", fp);
1882                 }
1883         } else if (obj->type == ADDR_GROUP) {
1884                 AddressGroup *group = ADDRESS_GROUP(obj);
1885
1886                 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1887                 fputs("<group name=\"", fp);
1888                 xml_file_put_escape_str(fp, group->name);
1889                 fputs("\">\n", fp);
1890         }
1891 }
1892
1893 static void addressbook_node_write_end(GtkCTreeNode *node, FILE *fp)
1894 {
1895         AddressObject *obj;
1896
1897         obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
1898         g_return_if_fail(obj != NULL);
1899
1900         if (obj->type == ADDR_FOLDER) {
1901                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1902
1903                 addressbook_write_items(fp, folder->items,
1904                                         GTK_CTREE_ROW(node)->level);
1905
1906                 if (GTK_CTREE_ROW(node)->level == 1) {
1907                         fprintf(fp, "</%s>\n\n", folder->name);
1908                 } else {
1909                         tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1910                         fputs("</folder>\n", fp);
1911                 }
1912         } else if (obj->type == ADDR_GROUP) {
1913                 AddressGroup *group = ADDRESS_GROUP(obj);
1914
1915                 addressbook_write_items(fp, group->items,
1916                                         GTK_CTREE_ROW(node)->level);
1917
1918                 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1919                 fputs("</group>\n", fp);
1920         }
1921 }
1922
1923 static void addressbook_write_items(FILE *fp, GList *items, guint level)
1924 {
1925         AddressItem *item;
1926
1927         for (; items != NULL; items = items->next) {
1928                 if (ADDRESS_OBJECT_TYPE(items->data) == ADDR_ITEM) {
1929                         item = ADDRESS_ITEM(items->data);
1930
1931                         tab_indent_out(fp, level);
1932                         fputs("<item>\n", fp);
1933
1934                         tab_indent_out(fp, level + 1);
1935                         fputs("<name>", fp);
1936                         xml_file_put_escape_str(fp, item->name);
1937                         fputs("</name>\n", fp);
1938
1939                         tab_indent_out(fp, level + 1);
1940                         fputs("<address>", fp);
1941                         xml_file_put_escape_str(fp, item->address);
1942                         fputs("</address>\n", fp);
1943
1944                         tab_indent_out(fp, level + 1);
1945                         fputs("<remarks>", fp);
1946                         xml_file_put_escape_str(fp, item->remarks);
1947                         fputs("</remarks>\n", fp);
1948
1949                         tab_indent_out(fp, level);
1950                         fputs("</item>\n", fp);
1951                 }
1952         }
1953 }
1954
1955 static void tab_indent_out(FILE *fp, guint level)
1956 {
1957         gint i;
1958
1959         for (i = 0; i < level; i++)
1960                 fputs("    ", fp);
1961 }
1962
1963 static void key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
1964 {
1965         if (event && event->keyval == GDK_Escape)
1966                 addressbook_close();
1967 }
1968
1969 static gint addressbook_list_compare_func(GtkCList *clist,
1970                                           gconstpointer ptr1,
1971                                           gconstpointer ptr2)
1972 {
1973         AddressObject *obj1 = ((GtkCListRow *)ptr1)->data;
1974         AddressObject *obj2 = ((GtkCListRow *)ptr2)->data;
1975         gchar *name1, *name2;
1976
1977         if (obj1) {
1978                 if (obj1->type == ADDR_ITEM)
1979                         name1 = ADDRESS_ITEM(obj1)->name;
1980                 else if (obj1->type == ADDR_GROUP)
1981                         name1 = ADDRESS_GROUP(obj1)->name;
1982                 else if (obj1->type == ADDR_FOLDER)
1983                         name1 = ADDRESS_FOLDER(obj1)->name;
1984                 else
1985                         name1 = NULL;
1986         } else
1987                 name1 = NULL;
1988
1989         if (obj2) {
1990                 if (obj2->type == ADDR_ITEM)
1991                         name2 = ADDRESS_ITEM(obj2)->name;
1992                 else if (obj2->type == ADDR_GROUP)
1993                         name2 = ADDRESS_GROUP(obj2)->name;
1994                 else if (obj2->type == ADDR_FOLDER)
1995                         name2 = ADDRESS_FOLDER(obj2)->name;
1996                 else
1997                         name2 = NULL;
1998         } else
1999                 name2 = NULL;
2000
2001         if (!name1)
2002                 return (name2 != NULL);
2003         if (!name2)
2004                 return -1;
2005
2006         return strcasecmp(name1, name2);
2007 }
2008
2009 static gint addressbook_obj_name_compare(gconstpointer a, gconstpointer b)
2010 {
2011         const AddressObject *obj = a;
2012         const gchar *name = b;
2013
2014         if (!obj || !name) return -1;
2015
2016         if (obj->type == ADDR_GROUP) {
2017                 AddressGroup *group = ADDRESS_GROUP(obj);
2018                 if (!group->name)
2019                         return -1;
2020                 else
2021                         return strcasecmp(group->name, name);
2022         } else if (obj->type == ADDR_FOLDER) {
2023                 AddressFolder *folder = ADDRESS_FOLDER(obj);
2024                 if (!folder->name)
2025                         return -1;
2026                 else
2027                         return strcasecmp(folder->name, name);
2028         } else if (obj->type == ADDR_ITEM) {
2029                 AddressItem *item = ADDRESS_ITEM(obj);
2030                 if (!item->name)
2031                         return -1;
2032                 else
2033                         return strcasecmp(item->name, name);
2034         } else
2035                 return -1;
2036 }