Return Receipt
[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                 if (prefs_common.add_address_by_click &&
784                     addrbook.target_compose)
785                         addressbook_to_clicked(NULL, NULL);
786                 else
787                         addressbook_edit_address_cb(NULL, 0, NULL);
788                 return;
789         }
790
791 #if 0
792         gtk_signal_handler_block_by_func
793                 (GTK_OBJECT(entry),
794                  GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
795 #endif           
796
797         gtk_entry_set_text(entry, "");
798
799         for (cur = clist->selection; cur != NULL; cur = cur->next) {
800                 obj = gtk_clist_get_row_data(clist,
801                                              GPOINTER_TO_INT(cur->data));
802                 g_return_if_fail(obj != NULL);
803
804                 if (obj->type == ADDR_ITEM) {
805                         AddressItem *item;
806
807                         item = ADDRESS_ITEM(obj);
808                         if (item->name && item->address) {
809                                 gchar *buf;
810
811                                 buf = g_strdup_printf
812                                         ("%s <%s>", item->name, item->address);
813                                 if (*gtk_entry_get_text(entry) != '\0')
814                                         gtk_entry_append_text(entry, ", ");
815                                 gtk_entry_append_text(entry, buf);
816                                 g_free(buf);
817                         } else if (item->address) {
818                                 if (*gtk_entry_get_text(entry) != '\0')
819                                         gtk_entry_append_text(entry, ", ");
820                                 gtk_entry_append_text(entry, item->address);
821                         }
822                 }
823         }
824
825 #if 0
826         gtk_signal_handler_unblock_by_func
827                 (GTK_OBJECT(entry),
828                  GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
829 #endif           
830 }
831
832 #if 0
833 static void addressbook_entry_changed(GtkWidget *widget)
834 {
835         GtkCList *clist = GTK_CLIST(addrbook.clist);
836         GtkEntry *entry = GTK_ENTRY(addrbook.entry);
837         const gchar *str;
838         gint len;
839         gint row;
840
841         //if (clist->selection && clist->selection->next) return;
842
843         str = gtk_entry_get_text(entry);
844         if (*str == '\0') {
845                 gtk_clist_unselect_all(clist);
846                 return;
847         }
848         len = strlen(str);
849
850         for (row = 0; row < clist->rows; row++) {
851                 AddressObject *obj;
852                 const gchar *name;
853
854                 obj = ADDRESS_OBJECT(gtk_clist_get_row_data(clist, row));
855                 if (!obj) continue;
856                 if (obj->type == ADDR_ITEM)
857                         name = ADDRESS_ITEM(obj)->name;
858                 else if (obj->type == ADDR_GROUP)
859                         name = ADDRESS_GROUP(obj)->name;
860                 else
861                         continue;
862
863                 if (name && !strncasecmp(name, str, len)) {
864                         gtk_clist_unselect_all(clist);
865                         gtk_clist_select_row(clist, row, -1);
866                         return;
867                 }
868         }
869
870         gtk_clist_unselect_all(clist);
871 }
872 #endif
873
874 static void addressbook_list_button_pressed(GtkWidget *widget,
875                                             GdkEventButton *event,
876                                             gpointer data)
877 {
878         GtkCList *clist = GTK_CLIST(widget);
879         gint row, column;
880         AddressObject *obj;
881
882         if (!event) return;
883         if (event->button != 3) return;
884
885         obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
886                                           addrbook.opened);
887         g_return_if_fail(obj != NULL);
888
889         menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.list_popup));
890
891         if (gtk_clist_get_selection_info
892                 (clist, event->x, event->y, &row, &column)) {
893                 GtkCListRow *clist_row;
894
895                 clist_row = g_list_nth(clist->row_list, row)->data;
896                 if (clist_row->state != GTK_STATE_SELECTED) {
897                         gtk_clist_unselect_all(clist);
898                         gtk_clist_select_row(clist, row, column);
899                 }
900                 gtkut_clist_set_focus_row(clist, row);
901
902                 menu_set_sensitive(addrbook.list_factory, "/Edit", TRUE);
903                 menu_set_sensitive(addrbook.list_factory, "/Delete", TRUE);
904         }
905
906         menu_set_sensitive(addrbook.list_factory, "/New address", TRUE);
907         if (obj->type == ADDR_FOLDER) {
908                 menu_set_sensitive(addrbook.list_factory, "/New folder", TRUE);
909                 menu_set_sensitive(addrbook.list_factory, "/New group", TRUE);
910         }
911
912         gtk_menu_popup(GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
913                        event->button, event->time);
914 }
915
916 static void addressbook_list_button_released(GtkWidget *widget,
917                                              GdkEventButton *event,
918                                              gpointer data)
919 {
920 }
921
922 static void addressbook_tree_button_pressed(GtkWidget *ctree,
923                                             GdkEventButton *event,
924                                             gpointer data)
925 {
926         GtkCList *clist = GTK_CLIST(ctree);
927         gint row, column;
928         AddressObject *obj;
929         GtkCTreeNode *node;
930
931         if (!event) return;
932         if (event->button == 1) {
933                 addrbook.open_folder = TRUE;
934                 return;
935         }
936         if (event->button != 3) return;
937
938         if (!gtk_clist_get_selection_info
939                 (clist, event->x, event->y, &row, &column)) return;
940         gtk_clist_select_row(clist, row, column);
941
942         obj = gtk_clist_get_row_data(clist, row);
943         g_return_if_fail(obj != NULL);
944
945         menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
946
947         if (obj->type == ADDR_FOLDER) {
948                 node = gtk_ctree_node_nth(GTK_CTREE(ctree), row);
949                 menu_set_sensitive(addrbook.tree_factory, "/New folder", TRUE);
950                 menu_set_sensitive(addrbook.tree_factory, "/New group", TRUE);
951                 if (node && GTK_CTREE_ROW(node)->level >= 2) {
952                         menu_set_sensitive(addrbook.tree_factory,
953                                            "/Edit", TRUE);
954                         menu_set_sensitive(addrbook.tree_factory,
955                                            "/Delete", TRUE);
956                 }
957         } else if (obj->type == ADDR_GROUP) {
958                 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
959                 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
960         } else
961                 return;
962
963         menu_set_sensitive(addrbook.tree_factory, "/New address", TRUE);
964
965         gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
966                        event->button, event->time);
967 }
968
969 static void addressbook_tree_button_released(GtkWidget *ctree,
970                                              GdkEventButton *event,
971                                              gpointer data)
972 {
973         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
974         gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree), addrbook.opened);
975 }
976
977 static void addressbook_popup_close(GtkMenuShell *menu_shell, gpointer data)
978 {
979         if (!addrbook.opened) return;
980
981         gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
982         gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree),
983                                   addrbook.opened);
984 }
985
986 static void addressbook_new_folder_cb(gpointer data, guint action,
987                                       GtkWidget *widget)
988 {
989         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
990         AddressObject *obj;
991         AddressFolder *folder;
992         gchar *new_folder;
993
994         if (!addrbook.selected) return;
995
996         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
997         g_return_if_fail(obj != NULL);
998         if (obj->type != ADDR_FOLDER) return;
999
1000         new_folder = input_dialog(_("New folder"),
1001                                   _("Input the name of new folder:"),
1002                                   _("NewFolder"));
1003         if (!new_folder) return;
1004         g_strstrip(new_folder);
1005         if (*new_folder == '\0') {
1006                 g_free(new_folder);
1007                 return;
1008         }
1009
1010         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1011                                               new_folder,
1012                                               addressbook_obj_name_compare)) {
1013                 alertpanel_error(_("The name already exists."));
1014                 g_free(new_folder);
1015                 return;
1016         }
1017
1018         folder = g_new(AddressFolder, 1);
1019         ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1020         folder->name = g_strdup(new_folder);
1021         folder->items = NULL;
1022
1023         addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(folder));
1024
1025         g_free(new_folder);
1026
1027         if (addrbook.selected == addrbook.opened)
1028                 addressbook_set_clist(obj);
1029 }
1030
1031 static void addressbook_new_group_cb(gpointer data, guint action,
1032                                      GtkWidget *widget)
1033 {
1034         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1035         AddressObject *obj;
1036         AddressGroup *group;
1037         gchar *new_group;
1038
1039         if (!addrbook.selected) return;
1040
1041         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1042         g_return_if_fail(obj != NULL);
1043         if (obj->type != ADDR_FOLDER) return;
1044
1045         new_group = input_dialog(_("New group"),
1046                                  _("Input the name of new group:"),
1047                                   _("NewGroup"));
1048         if (!new_group) return;
1049         g_strstrip(new_group);
1050         if (*new_group == '\0') {
1051                 g_free(new_group);
1052                 return;
1053         }
1054
1055         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1056                                               new_group,
1057                                               addressbook_obj_name_compare)) {
1058                 alertpanel_error(_("The name already exists."));
1059                 g_free(new_group);
1060                 return;
1061         }
1062
1063         group = g_new(AddressGroup, 1);
1064         ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1065         group->name = g_strdup(new_group);
1066         group->items = NULL;
1067
1068         addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(group));
1069
1070         g_free(new_group);
1071
1072         if (addrbook.selected == addrbook.opened)
1073                 addressbook_set_clist(obj);
1074 }
1075
1076 static void addressbook_change_node_name(GtkCTreeNode *node, const gchar *name)
1077 {
1078         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1079         gchar *text[1];
1080         guint8 spacing;
1081         GdkPixmap *pix_cl, *pix_op;
1082         GdkBitmap *mask_cl, *mask_op;
1083         gboolean is_leaf, expanded;
1084
1085         gtk_ctree_get_node_info(ctree, node, text, &spacing,
1086                                 &pix_cl, &mask_cl, &pix_op, &mask_op,
1087                                 &is_leaf, &expanded);
1088         gtk_ctree_set_node_info(ctree, node, name, spacing,
1089                                 pix_cl, mask_cl, pix_op, mask_op,
1090                                 is_leaf, expanded);
1091 }
1092
1093 static void addressbook_edit_group(GtkCTreeNode *group_node)
1094 {
1095         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1096         GtkCList *clist = GTK_CLIST(addrbook.clist);
1097         AddressObject *obj;
1098         AddressGroup *group;
1099         gchar *new_name;
1100         GtkCTreeNode *node;
1101
1102         if (!group_node && clist->selection) {
1103                 obj = gtk_clist_get_row_data(clist,
1104                                              GPOINTER_TO_INT(clist->selection->data));
1105                 g_return_if_fail(obj != NULL);
1106                 if (obj->type != ADDR_GROUP) return;
1107                 node = gtk_ctree_find_by_row_data
1108                         (ctree, addrbook.selected, obj);
1109                 if (!node) return;
1110         } else {
1111                 if (group_node)
1112                         node = group_node;
1113                 else
1114                         node = addrbook.selected;
1115                 obj = gtk_ctree_node_get_row_data(ctree, node);
1116                 g_return_if_fail(obj != NULL);
1117                 if (obj->type != ADDR_GROUP) return;
1118         }
1119
1120         group = ADDRESS_GROUP(obj);
1121
1122         new_name = input_dialog(_("Edit group"),
1123                                 _("Input the new name of group:"),
1124                                 group->name);
1125         if (!new_name) return;
1126         g_strstrip(new_name);
1127         if (*new_name == '\0') {
1128                 g_free(new_name);
1129                 return;
1130         }
1131
1132         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1133                                               new_name,
1134                                               addressbook_obj_name_compare)) {
1135                 alertpanel_error(_("The name already exists."));
1136                 g_free(new_name);
1137                 return;
1138         }
1139
1140         g_free(group->name);
1141         group->name = g_strdup(new_name);
1142
1143         addressbook_change_node_name(node, new_name);
1144         gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(node)->parent);
1145
1146         g_free(new_name);
1147
1148         addrbook.open_folder = TRUE;
1149         gtk_ctree_select(ctree, addrbook.opened);
1150 }
1151
1152 static void addressbook_edit_folder_cb(gpointer data, guint action,
1153                                        GtkWidget *widget)
1154 {
1155         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1156         AddressObject *obj;
1157         AddressFolder *folder;
1158         gchar *new_name;
1159
1160         if (!addrbook.selected) return;
1161         if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1162
1163         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1164         g_return_if_fail(obj != NULL);
1165         g_return_if_fail(obj->type == ADDR_FOLDER || obj->type == ADDR_GROUP);
1166
1167         if (obj->type == ADDR_GROUP) {
1168                 addressbook_edit_group(addrbook.selected);
1169                 return;
1170         }
1171
1172         folder = ADDRESS_FOLDER(obj);
1173         new_name = input_dialog(_("Edit folder"),
1174                                 _("Input the new name of folder:"),
1175                                 folder->name);
1176
1177         if (!new_name) return;
1178         g_strstrip(new_name);
1179         if (*new_name == '\0') {
1180                 g_free(new_name);
1181                 return;
1182         }
1183
1184         if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1185                                               new_name,
1186                                               addressbook_obj_name_compare)) {
1187                 alertpanel_error(_("The name already exists."));
1188                 g_free(new_name);
1189                 return;
1190         }
1191
1192         g_free(folder->name);
1193         folder->name = g_strdup(new_name);
1194
1195         addressbook_change_node_name(addrbook.selected, new_name);
1196         gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1197
1198         g_free(new_name);
1199 }
1200
1201 static void addressbook_delete_folder_cb(gpointer data, guint action,
1202                                          GtkWidget *widget)
1203 {
1204         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1205         AddressObject *obj, *pobj;
1206         gchar *name;
1207         gchar *message;
1208         AlertValue aval;
1209
1210         if (!addrbook.selected) return;
1211         if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1212
1213         obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1214         g_return_if_fail(obj != NULL);
1215
1216         if (obj->type == ADDR_GROUP)
1217                 name = ADDRESS_GROUP(obj)->name;
1218         else if (obj->type == ADDR_FOLDER)
1219                 name = ADDRESS_FOLDER(obj)->name;
1220         else
1221                 return;
1222
1223         message = g_strdup_printf(_("Really delete `%s' ?"), name);
1224         aval = alertpanel(_("Delete"), message, _("Yes"), _("No"), NULL);
1225         g_free(message);
1226         if (aval != G_ALERTDEFAULT) return;
1227
1228         pobj = gtk_ctree_node_get_row_data
1229                 (ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1230         if (!pobj) return;
1231         g_return_if_fail(pobj->type == ADDR_FOLDER);
1232         ADDRESS_FOLDER(pobj)->items =
1233                 g_list_remove(ADDRESS_FOLDER(pobj)->items, obj);
1234
1235         addressbook_delete_object(obj);
1236         addrbook.open_folder = TRUE;
1237         gtk_ctree_remove_node(ctree, addrbook.selected);
1238         addrbook.open_folder = FALSE;
1239 }
1240
1241 #define SET_LABEL_AND_ENTRY(str, entry, top) \
1242 { \
1243         label = gtk_label_new(str); \
1244         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
1245                          GTK_FILL, 0, 0, 0); \
1246         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
1247  \
1248         entry = gtk_entry_new(); \
1249         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
1250                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
1251 }
1252
1253 static void addressbook_edit_address_create(gboolean *cancelled)
1254 {
1255         GtkWidget *window;
1256         GtkWidget *vbox;
1257         GtkWidget *table;
1258         GtkWidget *label;
1259         GtkWidget *name_entry;
1260         GtkWidget *addr_entry;
1261         GtkWidget *rem_entry;
1262         GtkWidget *hbbox;
1263         GtkWidget *ok_btn;
1264         GtkWidget *cancel_btn;
1265
1266         debug_print("Creating edit_address window...\n");
1267
1268         window = gtk_window_new(GTK_WINDOW_DIALOG);
1269         gtk_widget_set_usize(window, 400, -1);
1270         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
1271         gtk_window_set_title(GTK_WINDOW(window), _("Edit address"));
1272         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1273         gtk_window_set_modal(GTK_WINDOW(window), TRUE); 
1274         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
1275                            GTK_SIGNAL_FUNC(edit_address_delete_event),
1276                            cancelled);
1277         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
1278                            GTK_SIGNAL_FUNC(edit_address_key_pressed),
1279                            cancelled);
1280
1281         vbox = gtk_vbox_new(FALSE, 8);
1282         gtk_container_add(GTK_CONTAINER(window), vbox);
1283
1284         table = gtk_table_new(3, 2, FALSE);
1285         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
1286         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
1287         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
1288
1289         SET_LABEL_AND_ENTRY(_("Name"),    name_entry, 0);
1290         SET_LABEL_AND_ENTRY(_("Address"), addr_entry, 1);
1291         SET_LABEL_AND_ENTRY(_("Remarks"), rem_entry,  2);
1292
1293         gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
1294                                 &cancel_btn, _("Cancel"), NULL, NULL);
1295         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
1296         gtk_widget_grab_default(ok_btn);
1297
1298         gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
1299                            GTK_SIGNAL_FUNC(edit_address_ok), cancelled);
1300         gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
1301                            GTK_SIGNAL_FUNC(edit_address_cancel), cancelled);
1302
1303         gtk_widget_show_all(vbox);
1304
1305         addredit.window     = window;
1306         addredit.name_entry = name_entry;
1307         addredit.addr_entry = addr_entry;
1308         addredit.rem_entry  = rem_entry;
1309         addredit.ok_btn     = ok_btn;
1310         addredit.cancel_btn = cancel_btn;
1311 }
1312
1313 static void edit_address_ok(GtkWidget *widget, gboolean *cancelled)
1314 {
1315         *cancelled = FALSE;
1316         gtk_main_quit();
1317 }
1318
1319 static void edit_address_cancel(GtkWidget *widget, gboolean *cancelled)
1320 {
1321         *cancelled = TRUE;
1322         gtk_main_quit();
1323 }
1324
1325 static gint edit_address_delete_event(GtkWidget *widget, GdkEventAny *event,
1326                                       gboolean *cancelled)
1327 {
1328         *cancelled = TRUE;
1329         gtk_main_quit();
1330
1331         return TRUE;
1332 }
1333
1334 static void edit_address_key_pressed(GtkWidget *widget, GdkEventKey *event,
1335                                      gboolean *cancelled)
1336 {
1337         if (event && event->keyval == GDK_Escape) {
1338                 *cancelled = TRUE;
1339                 gtk_main_quit();
1340         }
1341 }
1342
1343 static AddressItem *addressbook_edit_address(AddressItem *item)
1344 {
1345         static gboolean cancelled;
1346         const gchar *str;
1347
1348         if (!addredit.window)
1349                 addressbook_edit_address_create(&cancelled);
1350         gtk_widget_grab_focus(addredit.ok_btn);
1351         gtk_widget_grab_focus(addredit.name_entry);
1352         gtk_widget_show(addredit.window);
1353         manage_window_set_transient(GTK_WINDOW(addredit.window));
1354
1355         gtk_entry_set_text(GTK_ENTRY(addredit.name_entry), "");
1356         gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry), "");
1357         gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry),  "");
1358
1359         if (item) {
1360                 if (item->name)
1361                         gtk_entry_set_text(GTK_ENTRY(addredit.name_entry),
1362                                            item->name);
1363                 if (item->address)
1364                         gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry),
1365                                            item->address);
1366                 if (item->remarks)
1367                         gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry),
1368                                            item->remarks);
1369         }
1370
1371         gtk_main();
1372         gtk_widget_hide(addredit.window);
1373         if (cancelled == TRUE) return NULL;
1374
1375         str = gtk_entry_get_text(GTK_ENTRY(addredit.name_entry));
1376         if (*str == '\0') return NULL;
1377
1378         if (!item) {
1379                 item = g_new0(AddressItem, 1);
1380                 ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
1381         }
1382
1383         g_free(item->name);
1384         item->name = g_strdup(str);
1385
1386         str = gtk_entry_get_text(GTK_ENTRY(addredit.addr_entry));
1387         g_free(item->address);
1388         if (*str == '\0')
1389                 item->address = NULL;
1390         else
1391                 item->address = g_strdup(str);
1392
1393         str = gtk_entry_get_text(GTK_ENTRY(addredit.rem_entry));
1394         g_free(item->remarks);
1395         if (*str == '\0')
1396                 item->remarks = NULL;
1397         else
1398                 item->remarks = g_strdup(str);
1399
1400         return item;
1401 }
1402
1403 static void addressbook_new_address_cb(gpointer data, guint action,
1404                                        GtkWidget *widget)
1405 {
1406         AddressItem *item;
1407
1408         item = addressbook_edit_address(NULL);
1409
1410         if (item) {
1411                 addressbook_add_object(addrbook.selected,
1412                                        ADDRESS_OBJECT(item));
1413                 if (addrbook.selected == addrbook.opened) {
1414                         addrbook.open_folder = TRUE;
1415                         gtk_ctree_select(GTK_CTREE(addrbook.ctree),
1416                                          addrbook.opened);
1417                 }
1418         }
1419 }
1420
1421 static void addressbook_edit_address_cb(gpointer data, guint action,
1422                                         GtkWidget *widget)
1423 {
1424         GtkCList *clist = GTK_CLIST(addrbook.clist);
1425         AddressObject *obj;
1426
1427         if (!clist->selection) {
1428                 addressbook_edit_folder_cb(NULL, 0, NULL);
1429                 return;
1430         }
1431
1432         obj = gtk_clist_get_row_data(clist,
1433                                      GPOINTER_TO_INT(clist->selection->data));
1434         g_return_if_fail(obj != NULL);
1435
1436         if (obj->type == ADDR_ITEM) {
1437                 AddressItem *item = ADDRESS_ITEM(obj);
1438
1439                 if (addressbook_edit_address(item) == NULL) return;
1440
1441                 addrbook.open_folder = TRUE;
1442                 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1443
1444                 return;
1445         } else if (obj->type == ADDR_GROUP) {
1446                 addressbook_edit_group(NULL);
1447         }
1448 }
1449
1450 static void addressbook_delete_address_cb(gpointer data, guint action,
1451                                           GtkWidget *widget)
1452 {
1453         addressbook_del_clicked(NULL, NULL);
1454 }
1455
1456 static void close_cb(gpointer data, guint action, GtkWidget *widget)
1457 {
1458         addressbook_close();
1459 }
1460
1461 static void addressbook_set_clist(AddressObject *obj)
1462 {
1463         GtkCList *clist = GTK_CLIST(addrbook.clist);
1464         GList *items;
1465         gchar *text[N_COLS];
1466
1467         if (!obj) {
1468                 gtk_clist_clear(clist);
1469                 return;
1470         }
1471
1472         gtk_clist_freeze(clist);
1473
1474         gtk_clist_clear(clist);
1475
1476         if (obj->type == ADDR_GROUP)
1477                 items = ADDRESS_GROUP(obj)->items;
1478         else if (obj->type == ADDR_FOLDER)
1479                 items = ADDRESS_FOLDER(obj)->items;
1480         else {
1481                 gtk_clist_thaw(clist);
1482                 return;
1483         }
1484
1485         for (; items != NULL; items = items->next) {
1486                 AddressObject *iobj;
1487                 gint row;
1488
1489                 iobj = ADDRESS_OBJECT(items->data);
1490
1491                 if (iobj->type == ADDR_GROUP) {
1492                         AddressGroup *group;
1493
1494                         group = ADDRESS_GROUP(iobj);
1495                         text[COL_NAME]    = group->name;
1496                         text[COL_ADDRESS] = NULL;
1497                         text[COL_REMARKS] = NULL;
1498                         row = gtk_clist_append(clist, text);
1499                         gtk_clist_set_pixtext(clist, row, COL_NAME,
1500                                               group->name, 4,
1501                                               groupxpm, groupxpmmask);
1502                         gtk_clist_set_row_data(clist, row, iobj);
1503                 } else if (iobj->type == ADDR_ITEM) {
1504                         AddressItem *item;
1505
1506                         item = ADDRESS_ITEM(iobj);
1507                         text[COL_NAME]    = item->name;
1508                         text[COL_ADDRESS] = item->address;
1509                         text[COL_REMARKS] = item->remarks;
1510                         row = gtk_clist_append(clist, text);
1511                         gtk_clist_set_row_data(clist, row, iobj);
1512                 }
1513         }
1514
1515         gtk_clist_sort(clist);
1516         gtk_clist_thaw(clist);
1517 }
1518
1519 static void addressbook_read_file(void)
1520 {
1521         XMLFile *file;
1522         gchar *path;
1523
1524         debug_print(_("Reading addressbook file..."));
1525
1526         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
1527         if ((file = xml_open_file(path)) == NULL) {
1528                 debug_print(_("%s doesn't exist.\n"), path);
1529                 g_free(path);
1530                 addressbook_get_tree(NULL, addrbook.common, "common_address");
1531                 addressbook_get_tree(NULL, addrbook.personal, "personal_address");
1532                 return;
1533         }
1534         g_free(path);
1535
1536         xml_get_dtd(file);
1537
1538         if (xml_parse_next_tag(file) < 0 ||
1539             xml_compare_tag(file, "addressbook") == FALSE) {
1540                 g_warning("Invalid addressbook data\n");
1541                 xml_close_file(file);
1542                 return;
1543         }
1544
1545         addressbook_get_tree(file, addrbook.common, "common_address");
1546         addressbook_get_tree(file, addrbook.personal, "personal_address");
1547
1548         xml_close_file(file);
1549
1550         debug_print(_("done.\n"));
1551 }
1552
1553 static void addressbook_get_tree(XMLFile *file, GtkCTreeNode *node,
1554                                  const gchar *folder_tag)
1555 {
1556         AddressFolder *folder;
1557
1558         g_return_if_fail(node != NULL);
1559
1560         folder = g_new(AddressFolder, 1);
1561         ADDRESS_OBJECT(folder)->type = ADDR_FOLDER;
1562         folder->name = g_strdup(folder_tag);
1563         folder->items = NULL;
1564         gtk_ctree_node_set_row_data(GTK_CTREE(addrbook.ctree), node, folder);
1565
1566         if (file) {
1567                 if (xml_parse_next_tag(file) < 0 ||
1568                     xml_compare_tag(file, folder_tag) == FALSE) {
1569                         g_warning("Invalid addressbook data\n");
1570                         return;
1571                 }
1572         }
1573
1574         if (file) addressbook_add_objs(file, node);
1575 }
1576
1577 static void addressbook_add_objs(XMLFile *file, GtkCTreeNode *node)
1578 {
1579         GList *attr;
1580         guint prev_level;
1581         GtkCTreeNode *new_node;
1582
1583         for (;;) {
1584                 prev_level = file->level;
1585                 if (xml_parse_next_tag(file) < 0) return;
1586                 if (file->level < prev_level) return;
1587
1588                 if (xml_compare_tag(file, "group")) {
1589                         AddressGroup *group;
1590
1591                         group = g_new(AddressGroup, 1);
1592                         ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1593                         attr = xml_get_current_tag_attr(file);
1594                         if (attr)
1595                                 group->name = g_strdup(((XMLAttr *)attr->data)->value);
1596                         else
1597                                 group->name = NULL;
1598                         group->items = NULL;
1599
1600                         new_node = addressbook_add_object
1601                                 (node, ADDRESS_OBJECT(group));
1602
1603                         addressbook_add_objs(file, new_node);
1604                 } else if (xml_compare_tag(file, "folder")) {
1605                         AddressFolder *folder;
1606
1607                         folder = g_new(AddressFolder, 1);
1608                         ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1609                         attr = xml_get_current_tag_attr(file);
1610                         if (attr)
1611                                 folder->name = g_strdup(((XMLAttr *)attr->data)->value);
1612                         else
1613                                 folder->name = NULL;
1614                         folder->items = NULL;
1615
1616                         new_node = addressbook_add_object
1617                                 (node, ADDRESS_OBJECT(folder));
1618
1619                         addressbook_add_objs(file, new_node);
1620                 } else if (xml_compare_tag(file, "item")) {
1621                         AddressItem *item;
1622
1623                         item = addressbook_parse_item(file);
1624                         if (!item) return;
1625                         new_node = addressbook_add_object
1626                                 (node, ADDRESS_OBJECT(item));
1627                 } else {
1628                         g_warning("Invalid tag\n");
1629                         return;
1630                 }
1631
1632                 if (!new_node) return;
1633         }
1634 }
1635
1636 static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
1637                                             AddressObject *obj)
1638 {
1639         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1640         GtkCTreeNode *added;
1641         AddressObject *pobj;
1642
1643         g_return_val_if_fail(node != NULL, NULL);
1644         g_return_val_if_fail(obj  != NULL, NULL);
1645
1646         pobj = gtk_ctree_node_get_row_data(ctree, node);
1647         g_return_val_if_fail(pobj != NULL, NULL);
1648         if (pobj->type == ADDR_ITEM) {
1649                 g_warning("Parent object mustn't be an item.\n");
1650                 return NULL;
1651         }
1652         if (pobj->type == ADDR_FOLDER &&
1653             (obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER))
1654                 gtk_ctree_expand(ctree, node);
1655
1656         if (obj->type == ADDR_GROUP) {
1657                 AddressGroup *group = ADDRESS_GROUP(obj);
1658
1659                 if (pobj->type != ADDR_FOLDER) {
1660                         g_warning("Group can't be added in another group.\n");
1661                         return NULL;
1662                 }
1663
1664                 added = gtk_ctree_insert_node(ctree, node, NULL,
1665                                               &group->name, FOLDER_SPACING,
1666                                               groupxpm, groupxpmmask,
1667                                               groupxpm, groupxpmmask,
1668                                               TRUE, FALSE);
1669                 gtk_ctree_node_set_row_data(ctree, added, obj);
1670         } else if (obj->type == ADDR_FOLDER) {
1671                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1672
1673                 if (pobj->type != ADDR_FOLDER) {
1674                         g_warning("Group can't contain folder.\n");
1675                         return NULL;
1676                 }
1677
1678                 added = gtk_ctree_insert_node(ctree, node, NULL,
1679                                               &folder->name, FOLDER_SPACING,
1680                                               folderxpm, folderxpmmask,
1681                                               folderopenxpm, folderopenxpmmask,
1682                                               FALSE, FALSE);
1683                 gtk_ctree_node_set_row_data(ctree, added, obj);
1684         } else {
1685                 added = node;
1686         }
1687
1688         if (obj->type == ADDR_GROUP || obj->type == ADDR_ITEM) {
1689                 if (pobj->type == ADDR_GROUP) {
1690                         AddressGroup *group = ADDRESS_GROUP(pobj);
1691
1692                         group->items = g_list_append(group->items, obj);
1693                 } else if (pobj->type == ADDR_FOLDER) {
1694                         AddressFolder *folder = ADDRESS_FOLDER(pobj);
1695
1696                         folder->items = g_list_append(folder->items, obj);
1697                 }
1698         }
1699
1700         gtk_ctree_sort_node(ctree, node);
1701
1702         return added;
1703 }
1704
1705 static void addressbook_delete_object(AddressObject *obj)
1706 {
1707         if (!obj) return;
1708
1709         if (obj->type == ADDR_ITEM) {
1710                 AddressItem *item = ADDRESS_ITEM(obj);
1711
1712                 g_free(item->name);
1713                 g_free(item->address);
1714                 g_free(item->remarks);
1715                 g_free(item);
1716         } else if (obj->type == ADDR_GROUP) {
1717                 AddressGroup *group = ADDRESS_GROUP(obj);
1718
1719                 g_free(group->name);
1720                 while (group->items != NULL) {
1721                         addressbook_delete_object
1722                                 (ADDRESS_OBJECT(group->items->data));
1723                         group->items = g_list_remove(group->items,
1724                                                      group->items->data);
1725                 }
1726                 g_free(group);
1727         } else if (obj->type == ADDR_FOLDER) {
1728                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1729
1730                 g_free(folder->name);
1731                 while (folder->items != NULL) {
1732                         addressbook_delete_object
1733                                 (ADDRESS_OBJECT(folder->items->data));
1734                         folder->items = g_list_remove(folder->items,
1735                                                       folder->items->data);
1736                 }
1737                 g_free(folder);
1738         }
1739 }
1740
1741 static AddressObject *addressbook_find_object_by_name(GtkCTreeNode *node,
1742                                                       const gchar *name)
1743 {
1744         GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1745         AddressObject *obj;
1746         GList *found;
1747
1748         g_return_val_if_fail(node != NULL, NULL);
1749
1750         obj = gtk_ctree_node_get_row_data(ctree, node);
1751         g_return_val_if_fail(obj != NULL, NULL);
1752
1753         if (obj->type == ADDR_GROUP) {
1754                 AddressGroup *group = ADDRESS_GROUP(obj);
1755
1756                 found = g_list_find_custom(group->items, (gpointer)name,
1757                                            addressbook_obj_name_compare);
1758                 if (found) return ADDRESS_OBJECT(found->data);
1759         } else if (obj->type == ADDR_FOLDER) {
1760                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1761
1762                 found = g_list_find_custom(folder->items, (gpointer)name,
1763                                            addressbook_obj_name_compare);
1764                 if (found) return ADDRESS_OBJECT(found->data);
1765         } else if (obj->type == ADDR_ITEM) {
1766                 if (!addressbook_obj_name_compare(obj, name)) return obj;
1767         }
1768
1769         return NULL;
1770 }
1771
1772 #define PARSE_ITEM_ERROR() \
1773 { \
1774         g_warning("addressbook_parse_item(): Parse error\n"); \
1775         g_free(item->name); \
1776         g_free(item->address); \
1777         g_free(item->remarks); \
1778         g_free(item); \
1779         return NULL; \
1780 }
1781
1782 static AddressItem *addressbook_parse_item(XMLFile *file)
1783 {
1784         gchar *element;
1785         AddressItem *item;
1786         guint level;
1787
1788         item = g_new0(AddressItem, 1);
1789         ADDRESS_OBJECT(item)->type = ADDR_ITEM;
1790
1791         level = file->level;
1792
1793         while (xml_parse_next_tag(file) == 0) {
1794                 if (file->level < level) return item;
1795                 if (file->level == level) break;
1796
1797                 element = xml_get_element(file);
1798
1799                 if (xml_compare_tag(file, "name")) {
1800                         item->name = element;
1801                 } else if (xml_compare_tag(file, "address")) {
1802                         item->address = element;
1803                 } else if (xml_compare_tag(file, "remarks")) {
1804                         item->remarks = element;
1805                 }
1806
1807                 if (xml_parse_next_tag(file) < 0) break;
1808                 if (file->level != level) break;
1809         }
1810
1811         PARSE_ITEM_ERROR();
1812 }
1813
1814 void addressbook_export_to_file(void)
1815 {
1816         PrefFile *pfile;
1817         gchar *path;
1818
1819         if (!addrbook.ctree) return;
1820
1821         debug_print(_("Exporting addressbook to file..."));
1822
1823         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
1824         if ((pfile = prefs_write_open(path)) == NULL) {
1825                 g_free(path);
1826                 return;
1827         }
1828         g_free(path);
1829
1830         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
1831                 conv_get_current_charset_str());
1832         fputs("<addressbook>\n\n", pfile->fp);
1833
1834         addressbook_xml_recursive_write(NULL, pfile->fp);
1835
1836         fputs("</addressbook>\n", pfile->fp);
1837
1838         if (prefs_write_close(pfile) < 0) {
1839                 g_warning(_("failed to write addressbook data.\n"));
1840                 return;
1841         }
1842
1843         debug_print(_("done.\n"));
1844 }
1845
1846 /* Most part of this function was taken from gtk_ctree_pre_recursive() and
1847    gtk_ctree_post_recursive(). */
1848 static void addressbook_xml_recursive_write(GtkCTreeNode *node, FILE *fp)
1849 {
1850         GtkCTreeNode *work;
1851         GtkCTreeNode *tmp;
1852
1853         if (node) {
1854                 work = GTK_CTREE_ROW(node)->children;
1855                 addressbook_node_write_begin(node, fp);
1856         } else
1857                 work = GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list);
1858
1859         while (work) {
1860                 tmp = GTK_CTREE_ROW(work)->sibling;
1861                 addressbook_xml_recursive_write(work, fp);
1862                 work = tmp;
1863         }
1864
1865         if (node)
1866                 addressbook_node_write_end(node, fp);
1867 }
1868
1869 static void addressbook_node_write_begin(GtkCTreeNode *node, FILE *fp)
1870 {
1871         AddressObject *obj;
1872
1873         obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
1874         g_return_if_fail(obj != NULL);
1875
1876         if (obj->type == ADDR_FOLDER) {
1877                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1878
1879                 if (GTK_CTREE_ROW(node)->level == 1) {
1880                         fprintf(fp, "<%s>\n", folder->name);
1881                 } else {
1882                         tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1883                         fputs("<folder name=\"", fp);
1884                         xml_file_put_escape_str(fp, folder->name);
1885                         fputs("\">\n", fp);
1886                 }
1887         } else if (obj->type == ADDR_GROUP) {
1888                 AddressGroup *group = ADDRESS_GROUP(obj);
1889
1890                 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1891                 fputs("<group name=\"", fp);
1892                 xml_file_put_escape_str(fp, group->name);
1893                 fputs("\">\n", fp);
1894         }
1895 }
1896
1897 static void addressbook_node_write_end(GtkCTreeNode *node, FILE *fp)
1898 {
1899         AddressObject *obj;
1900
1901         obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
1902         g_return_if_fail(obj != NULL);
1903
1904         if (obj->type == ADDR_FOLDER) {
1905                 AddressFolder *folder = ADDRESS_FOLDER(obj);
1906
1907                 addressbook_write_items(fp, folder->items,
1908                                         GTK_CTREE_ROW(node)->level);
1909
1910                 if (GTK_CTREE_ROW(node)->level == 1) {
1911                         fprintf(fp, "</%s>\n\n", folder->name);
1912                 } else {
1913                         tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1914                         fputs("</folder>\n", fp);
1915                 }
1916         } else if (obj->type == ADDR_GROUP) {
1917                 AddressGroup *group = ADDRESS_GROUP(obj);
1918
1919                 addressbook_write_items(fp, group->items,
1920                                         GTK_CTREE_ROW(node)->level);
1921
1922                 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1923                 fputs("</group>\n", fp);
1924         }
1925 }
1926
1927 static void addressbook_write_items(FILE *fp, GList *items, guint level)
1928 {
1929         AddressItem *item;
1930
1931         for (; items != NULL; items = items->next) {
1932                 if (ADDRESS_OBJECT_TYPE(items->data) == ADDR_ITEM) {
1933                         item = ADDRESS_ITEM(items->data);
1934
1935                         tab_indent_out(fp, level);
1936                         fputs("<item>\n", fp);
1937
1938                         tab_indent_out(fp, level + 1);
1939                         fputs("<name>", fp);
1940                         xml_file_put_escape_str(fp, item->name);
1941                         fputs("</name>\n", fp);
1942
1943                         tab_indent_out(fp, level + 1);
1944                         fputs("<address>", fp);
1945                         xml_file_put_escape_str(fp, item->address);
1946                         fputs("</address>\n", fp);
1947
1948                         tab_indent_out(fp, level + 1);
1949                         fputs("<remarks>", fp);
1950                         xml_file_put_escape_str(fp, item->remarks);
1951                         fputs("</remarks>\n", fp);
1952
1953                         tab_indent_out(fp, level);
1954                         fputs("</item>\n", fp);
1955                 }
1956         }
1957 }
1958
1959 static void tab_indent_out(FILE *fp, guint level)
1960 {
1961         gint i;
1962
1963         for (i = 0; i < level; i++)
1964                 fputs("    ", fp);
1965 }
1966
1967 static void key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
1968 {
1969         if (event && event->keyval == GDK_Escape)
1970                 addressbook_close();
1971 }
1972
1973 static gint addressbook_list_compare_func(GtkCList *clist,
1974                                           gconstpointer ptr1,
1975                                           gconstpointer ptr2)
1976 {
1977         AddressObject *obj1 = ((GtkCListRow *)ptr1)->data;
1978         AddressObject *obj2 = ((GtkCListRow *)ptr2)->data;
1979         gchar *name1, *name2;
1980
1981         if (obj1) {
1982                 if (obj1->type == ADDR_ITEM)
1983                         name1 = ADDRESS_ITEM(obj1)->name;
1984                 else if (obj1->type == ADDR_GROUP)
1985                         name1 = ADDRESS_GROUP(obj1)->name;
1986                 else if (obj1->type == ADDR_FOLDER)
1987                         name1 = ADDRESS_FOLDER(obj1)->name;
1988                 else
1989                         name1 = NULL;
1990         } else
1991                 name1 = NULL;
1992
1993         if (obj2) {
1994                 if (obj2->type == ADDR_ITEM)
1995                         name2 = ADDRESS_ITEM(obj2)->name;
1996                 else if (obj2->type == ADDR_GROUP)
1997                         name2 = ADDRESS_GROUP(obj2)->name;
1998                 else if (obj2->type == ADDR_FOLDER)
1999                         name2 = ADDRESS_FOLDER(obj2)->name;
2000                 else
2001                         name2 = NULL;
2002         } else
2003                 name2 = NULL;
2004
2005         if (!name1)
2006                 return (name2 != NULL);
2007         if (!name2)
2008                 return -1;
2009
2010         return strcasecmp(name1, name2);
2011 }
2012
2013 static gint addressbook_obj_name_compare(gconstpointer a, gconstpointer b)
2014 {
2015         const AddressObject *obj = a;
2016         const gchar *name = b;
2017
2018         if (!obj || !name) return -1;
2019
2020         if (obj->type == ADDR_GROUP) {
2021                 AddressGroup *group = ADDRESS_GROUP(obj);
2022                 if (!group->name)
2023                         return -1;
2024                 else
2025                         return strcasecmp(group->name, name);
2026         } else if (obj->type == ADDR_FOLDER) {
2027                 AddressFolder *folder = ADDRESS_FOLDER(obj);
2028                 if (!folder->name)
2029                         return -1;
2030                 else
2031                         return strcasecmp(folder->name, name);
2032         } else if (obj->type == ADDR_ITEM) {
2033                 AddressItem *item = ADDRESS_ITEM(obj);
2034                 if (!item->name)
2035                         return -1;
2036                 else
2037                         return strcasecmp(item->name, name);
2038         } else
2039                 return -1;
2040 }