2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999,2000 Hiroyuki Yamamoto
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.
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.
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.
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>
48 #include "addressbook.h"
49 #include "manage_window.h"
50 #include "prefs_common.h"
51 #include "alertpanel.h"
52 #include "inputdialog.h"
61 #include "addr_compl.h"
63 #include "pixmaps/dir-close.xpm"
64 #include "pixmaps/dir-open.xpm"
65 #include "pixmaps/group.xpm"
72 } AddressBookColumnPos;
75 #define COL_NAME_WIDTH 144
76 #define COL_ADDRESS_WIDTH 144
78 #define COL_FOLDER_WIDTH 170
79 #define ADDRESSBOOK_WIDTH 640
80 #define ADDRESSBOOK_HEIGHT 340
82 static GdkPixmap *folderxpm;
83 static GdkBitmap *folderxpmmask;
84 static GdkPixmap *folderopenxpm;
85 static GdkBitmap *folderopenxpmmask;
86 static GdkPixmap *groupxpm;
87 static GdkBitmap *groupxpmmask;
89 static AddressBook addrbook;
91 static struct _AddressEdit
94 GtkWidget *name_entry;
95 GtkWidget *addr_entry;
98 GtkWidget *cancel_btn;
101 static void addressbook_create (void);
102 static gint addressbook_close (void);
103 static void addressbook_button_set_sensitive (void);
105 /* callback functions */
106 static void addressbook_del_clicked (GtkButton *button,
108 static void addressbook_reg_clicked (GtkButton *button,
110 static void addressbook_to_clicked (GtkButton *button,
113 static void addressbook_tree_selected (GtkCTree *ctree,
117 static void addressbook_list_selected (GtkCList *clist,
123 static void addressbook_entry_changed (GtkWidget *widget);
126 static void addressbook_list_button_pressed (GtkWidget *widget,
127 GdkEventButton *event,
129 static void addressbook_list_button_released (GtkWidget *widget,
130 GdkEventButton *event,
132 static void addressbook_tree_button_pressed (GtkWidget *ctree,
133 GdkEventButton *event,
135 static void addressbook_tree_button_released (GtkWidget *ctree,
136 GdkEventButton *event,
138 static void addressbook_popup_close (GtkMenuShell *menu_shell,
141 static void addressbook_new_folder_cb (gpointer data,
144 static void addressbook_new_group_cb (gpointer data,
147 static void addressbook_edit_folder_cb (gpointer data,
150 static void addressbook_delete_folder_cb (gpointer data,
154 static void addressbook_change_node_name (GtkCTreeNode *node,
156 static void addressbook_edit_group (GtkCTreeNode *group_node);
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,
165 gboolean *cancelled);
166 static void edit_address_key_pressed (GtkWidget *widget,
168 gboolean *cancelled);
169 static AddressItem *addressbook_edit_address (AddressItem *item);
171 static void addressbook_new_address_cb (gpointer data,
174 static void addressbook_edit_address_cb (gpointer data,
177 static void addressbook_delete_address_cb (gpointer data,
181 static void close_cb (gpointer data,
185 static AddressItem *addressbook_parse_address (const gchar *str);
186 static void addressbook_append_to_compose_entry (AddressItem *item,
187 ComposeEntryType type);
189 static void addressbook_set_clist (AddressObject *obj);
191 static void addressbook_read_file (void);
192 static void addressbook_get_tree (XMLFile *file,
194 const gchar *folder_tag);
195 static void addressbook_add_objs (XMLFile *file,
198 static GtkCTreeNode *addressbook_add_object (GtkCTreeNode *node,
200 static void addressbook_delete_object (AddressObject *obj);
201 static AddressObject *addressbook_find_object_by_name
205 static AddressItem *addressbook_parse_item (XMLFile *file);
206 static void addressbook_xml_recursive_write (GtkCTreeNode *node,
208 static void addressbook_node_write_begin (GtkCTreeNode *node,
210 static void addressbook_node_write_end (GtkCTreeNode *node,
212 static void addressbook_write_items (FILE *fp,
215 static void tab_indent_out (FILE *fp,
218 static void key_pressed (GtkWidget *widget,
221 static gint addressbook_list_compare_func (GtkCList *clist,
224 static gint addressbook_obj_name_compare (gconstpointer a,
227 static GtkItemFactoryEntry addressbook_entries[] =
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}
242 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
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}
252 static GtkItemFactoryEntry addressbook_list_popup_entries[] =
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}
262 void addressbook_open(Compose *target)
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));
271 gtk_widget_hide(addrbook.window);
273 gtk_widget_show(addrbook.window);
275 addressbook_set_target_compose(target);
278 void addressbook_set_target_compose(Compose *target)
280 addrbook.target_compose = target;
282 addressbook_button_set_sensitive();
285 Compose *addressbook_get_target_compose(void)
287 return addrbook.target_compose;
290 static void addressbook_create(void)
296 GtkWidget *ctree_swin;
298 GtkWidget *clist_vbox;
299 GtkWidget *clist_swin;
312 GtkWidget *tree_popup;
313 GtkWidget *list_popup;
314 GtkItemFactory *tree_factory;
315 GtkItemFactory *list_factory;
318 gchar *titles[N_COLS] = {_("Name"), _("E-Mail address"), _("Remarks")};
322 debug_print("Creating addressbook window...\n");
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);
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);
340 vbox = gtk_vbox_new(FALSE, 0);
341 gtk_container_add(GTK_CONTAINER(window), vbox);
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);
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);
353 ctree_swin = gtk_scrolled_window_new(NULL, NULL);
354 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ctree_swin),
355 GTK_POLICY_AUTOMATIC,
357 gtk_widget_set_usize(ctree_swin, COL_FOLDER_WIDTH + 40, -1);
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);
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),
375 gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
376 GTK_SIGNAL_FUNC(addressbook_tree_button_released),
379 clist_vbox = gtk_vbox_new(FALSE, 4);
381 clist_swin = gtk_scrolled_window_new(NULL, NULL);
382 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
383 GTK_POLICY_AUTOMATIC,
385 gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
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,
392 gtk_clist_set_column_width(GTK_CLIST(clist), COL_ADDRESS,
394 gtk_clist_set_compare_func(GTK_CLIST(clist),
395 addressbook_list_compare_func);
397 for (i = 0; i < N_COLS; i++)
398 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button,
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),
406 gtk_signal_connect(GTK_OBJECT(clist), "button_release_event",
407 GTK_SIGNAL_FUNC(addressbook_list_button_released),
410 hbox = gtk_hbox_new(FALSE, 4);
411 gtk_box_pack_start(GTK_BOX(clist_vbox), hbox, FALSE, FALSE, 0);
413 label = gtk_label_new(_("Name:"));
414 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
416 entry = gtk_entry_new();
417 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
419 address_completion_register_entry(GTK_ENTRY(entry));
422 gtk_signal_connect(GTK_OBJECT(entry), "changed",
423 GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
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);
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);
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);
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);
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);
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));
474 PIXMAP_CREATE(window, folderxpm, folderxpmmask, DIRECTORY_CLOSE_XPM);
475 PIXMAP_CREATE(window, folderopenxpm, folderopenxpmmask,
477 PIXMAP_CREATE(window, groupxpm, groupxpmmask, group_xpm);
479 text = _("Common address");
481 gtk_ctree_insert_node(GTK_CTREE(ctree),
482 NULL, NULL, &text, FOLDER_SPACING,
483 folderxpm, folderxpmmask,
484 folderopenxpm, folderopenxpmmask,
486 text = _("Personal address");
488 gtk_ctree_insert_node(GTK_CTREE(ctree),
489 NULL, NULL, &text, FOLDER_SPACING,
490 folderxpm, folderxpmmask,
491 folderopenxpm, folderopenxpmmask,
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,
499 "<AddressBookTree>", &tree_factory,
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,
507 "<AddressBookList>", &list_factory,
510 addrbook.window = window;
511 addrbook.ctree = ctree;
512 addrbook.clist = clist;
513 addrbook.entry = entry;
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;
522 addrbook.tree_popup = tree_popup;
523 addrbook.list_popup = list_popup;
524 addrbook.tree_factory = tree_factory;
525 addrbook.list_factory = list_factory;
527 address_completion_start(window);
529 gtk_widget_show_all(window);
532 static gint addressbook_close(void)
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();
541 static void addressbook_button_set_sensitive(void)
543 gboolean to_sens = FALSE;
544 gboolean cc_sens = FALSE;
545 gboolean bcc_sens = FALSE;
547 if (!addrbook.window) return;
549 if (addrbook.target_compose) {
552 if (addrbook.target_compose->use_bcc)
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);
561 static void addressbook_del_clicked(GtkButton *button, gpointer data)
563 GtkCList *clist = GTK_CLIST(addrbook.clist);
564 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
565 AddressObject *pobj, *obj;
569 if (!clist->selection) {
570 addressbook_delete_folder_cb(NULL, 0, NULL);
574 pobj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
575 g_return_if_fail(pobj != NULL);
577 if (alertpanel(_("Delete address(es)"),
578 _("Really delete the address(es)?"),
579 _("Yes"), _("No"), NULL) != G_ALERTDEFAULT)
582 for (cur = clist->selection; cur != NULL; cur = next) {
584 row = GPOINTER_TO_INT(cur->data);
586 obj = gtk_clist_get_row_data(clist, row);
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);
595 folder->items = g_list_remove(folder->items, obj);
596 if (obj->type == ADDR_GROUP) {
599 node = gtk_ctree_find_by_row_data
600 (ctree, addrbook.opened, obj);
601 if (node) gtk_ctree_remove_node(ctree, node);
606 addressbook_delete_object(obj);
608 gtk_clist_remove(clist, row);
612 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
614 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
615 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
620 if (*gtk_entry_get_text(entry) == '\0') {
621 addressbook_new_address_cb(NULL, 0, NULL);
624 if (!addrbook.opened) return;
626 obj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
629 g_return_if_fail(obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER);
631 str = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
633 item = addressbook_parse_address(str);
636 if (addressbook_find_object_by_name
637 (addrbook.opened, item->name) != NULL) {
638 addressbook_delete_object(ADDRESS_OBJECT(item));
640 } else if (addressbook_edit_address(item) == NULL) {
641 addressbook_delete_object(ADDRESS_OBJECT(item));
647 item = addressbook_edit_address(NULL);
651 if (addressbook_find_object_by_name(addrbook.opened, item->name)) {
652 addressbook_delete_object(ADDRESS_OBJECT(item));
656 addressbook_add_object(addrbook.opened, ADDRESS_OBJECT(item));
657 addrbook.open_folder = TRUE;
658 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
661 static AddressItem *addressbook_parse_address(const gchar *str)
664 gchar *address = NULL;
669 Xalloca(buf, strlen(str) + 1, return NULL);
673 if (*buf == '\0') return NULL;
675 if ((start = strchr(buf, '<'))) {
680 name = g_strdup(buf);
683 if ((end = strchr(start, '>'))) {
687 address = g_strdup(start);
690 name = g_strdup(buf);
692 if (!name && !address) return NULL;
694 item = g_new(AddressItem, 1);
695 ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
697 item->address = address;
698 item->remarks = NULL;
703 static void addressbook_to_clicked(GtkButton *button, gpointer data)
705 GtkCList *clist = GTK_CLIST(addrbook.clist);
708 if (!addrbook.target_compose) return;
710 for (cur = clist->selection; cur != NULL; cur = cur->next) {
713 obj = gtk_clist_get_row_data(clist,
714 GPOINTER_TO_INT(cur->data));
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) {
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
730 addressbook_append_to_compose_entry
731 (ADDRESS_ITEM(cur_item->data),
732 (ComposeEntryType)data);
738 static void addressbook_append_to_compose_entry(AddressItem *item,
739 ComposeEntryType type)
741 Compose *compose = addrbook.target_compose;
743 if (item->name && item->address) {
746 buf = g_strdup_printf
747 ("%s <%s>", item->name, item->address);
748 compose_entry_append(compose, buf, type);
750 } else if (item->address)
751 compose_entry_append(compose, item->address, type);
754 static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
755 gint column, gpointer data)
759 addrbook.selected = node;
761 if (!addrbook.open_folder) return;
762 addrbook.open_folder = FALSE;
764 gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
766 obj = gtk_ctree_node_get_row_data(ctree, node);
767 g_return_if_fail(obj != NULL);
769 addrbook.opened = node;
771 if (obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER)
772 addressbook_set_clist(obj);
775 static void addressbook_list_selected(GtkCList *clist, gint row, gint column,
776 GdkEvent *event, gpointer data)
778 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
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);
787 addressbook_edit_address_cb(NULL, 0, NULL);
792 gtk_signal_handler_block_by_func
794 GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
797 gtk_entry_set_text(entry, "");
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);
804 if (obj->type == ADDR_ITEM) {
807 item = ADDRESS_ITEM(obj);
808 if (item->name && item->address) {
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);
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);
826 gtk_signal_handler_unblock_by_func
828 GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
833 static void addressbook_entry_changed(GtkWidget *widget)
835 GtkCList *clist = GTK_CLIST(addrbook.clist);
836 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
841 //if (clist->selection && clist->selection->next) return;
843 str = gtk_entry_get_text(entry);
845 gtk_clist_unselect_all(clist);
850 for (row = 0; row < clist->rows; row++) {
854 obj = ADDRESS_OBJECT(gtk_clist_get_row_data(clist, row));
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;
863 if (name && !strncasecmp(name, str, len)) {
864 gtk_clist_unselect_all(clist);
865 gtk_clist_select_row(clist, row, -1);
870 gtk_clist_unselect_all(clist);
874 static void addressbook_list_button_pressed(GtkWidget *widget,
875 GdkEventButton *event,
878 GtkCList *clist = GTK_CLIST(widget);
883 if (event->button != 3) return;
885 obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
887 g_return_if_fail(obj != NULL);
889 menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.list_popup));
891 if (gtk_clist_get_selection_info
892 (clist, event->x, event->y, &row, &column)) {
893 GtkCListRow *clist_row;
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);
900 gtkut_clist_set_focus_row(clist, row);
902 menu_set_sensitive(addrbook.list_factory, "/Edit", TRUE);
903 menu_set_sensitive(addrbook.list_factory, "/Delete", TRUE);
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);
912 gtk_menu_popup(GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
913 event->button, event->time);
916 static void addressbook_list_button_released(GtkWidget *widget,
917 GdkEventButton *event,
922 static void addressbook_tree_button_pressed(GtkWidget *ctree,
923 GdkEventButton *event,
926 GtkCList *clist = GTK_CLIST(ctree);
932 if (event->button == 1) {
933 addrbook.open_folder = TRUE;
936 if (event->button != 3) return;
938 if (!gtk_clist_get_selection_info
939 (clist, event->x, event->y, &row, &column)) return;
940 gtk_clist_select_row(clist, row, column);
942 obj = gtk_clist_get_row_data(clist, row);
943 g_return_if_fail(obj != NULL);
945 menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
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,
954 menu_set_sensitive(addrbook.tree_factory,
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);
963 menu_set_sensitive(addrbook.tree_factory, "/New address", TRUE);
965 gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
966 event->button, event->time);
969 static void addressbook_tree_button_released(GtkWidget *ctree,
970 GdkEventButton *event,
973 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
974 gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree), addrbook.opened);
977 static void addressbook_popup_close(GtkMenuShell *menu_shell, gpointer data)
979 if (!addrbook.opened) return;
981 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
982 gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree),
986 static void addressbook_new_folder_cb(gpointer data, guint action,
989 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
991 AddressFolder *folder;
994 if (!addrbook.selected) return;
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;
1000 new_folder = input_dialog(_("New folder"),
1001 _("Input the name of new folder:"),
1003 if (!new_folder) return;
1004 g_strstrip(new_folder);
1005 if (*new_folder == '\0') {
1010 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1012 addressbook_obj_name_compare)) {
1013 alertpanel_error(_("The name already exists."));
1018 folder = g_new(AddressFolder, 1);
1019 ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1020 folder->name = g_strdup(new_folder);
1021 folder->items = NULL;
1023 addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(folder));
1027 if (addrbook.selected == addrbook.opened)
1028 addressbook_set_clist(obj);
1031 static void addressbook_new_group_cb(gpointer data, guint action,
1034 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1036 AddressGroup *group;
1039 if (!addrbook.selected) return;
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;
1045 new_group = input_dialog(_("New group"),
1046 _("Input the name of new group:"),
1048 if (!new_group) return;
1049 g_strstrip(new_group);
1050 if (*new_group == '\0') {
1055 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1057 addressbook_obj_name_compare)) {
1058 alertpanel_error(_("The name already exists."));
1063 group = g_new(AddressGroup, 1);
1064 ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1065 group->name = g_strdup(new_group);
1066 group->items = NULL;
1068 addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(group));
1072 if (addrbook.selected == addrbook.opened)
1073 addressbook_set_clist(obj);
1076 static void addressbook_change_node_name(GtkCTreeNode *node, const gchar *name)
1078 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1081 GdkPixmap *pix_cl, *pix_op;
1082 GdkBitmap *mask_cl, *mask_op;
1083 gboolean is_leaf, expanded;
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,
1093 static void addressbook_edit_group(GtkCTreeNode *group_node)
1095 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1096 GtkCList *clist = GTK_CLIST(addrbook.clist);
1098 AddressGroup *group;
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);
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;
1120 group = ADDRESS_GROUP(obj);
1122 new_name = input_dialog(_("Edit group"),
1123 _("Input the new name of group:"),
1125 if (!new_name) return;
1126 g_strstrip(new_name);
1127 if (*new_name == '\0') {
1132 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1134 addressbook_obj_name_compare)) {
1135 alertpanel_error(_("The name already exists."));
1140 g_free(group->name);
1141 group->name = g_strdup(new_name);
1143 addressbook_change_node_name(node, new_name);
1144 gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(node)->parent);
1148 addrbook.open_folder = TRUE;
1149 gtk_ctree_select(ctree, addrbook.opened);
1152 static void addressbook_edit_folder_cb(gpointer data, guint action,
1155 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1157 AddressFolder *folder;
1160 if (!addrbook.selected) return;
1161 if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
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);
1167 if (obj->type == ADDR_GROUP) {
1168 addressbook_edit_group(addrbook.selected);
1172 folder = ADDRESS_FOLDER(obj);
1173 new_name = input_dialog(_("Edit folder"),
1174 _("Input the new name of folder:"),
1177 if (!new_name) return;
1178 g_strstrip(new_name);
1179 if (*new_name == '\0') {
1184 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1186 addressbook_obj_name_compare)) {
1187 alertpanel_error(_("The name already exists."));
1192 g_free(folder->name);
1193 folder->name = g_strdup(new_name);
1195 addressbook_change_node_name(addrbook.selected, new_name);
1196 gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1201 static void addressbook_delete_folder_cb(gpointer data, guint action,
1204 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1205 AddressObject *obj, *pobj;
1210 if (!addrbook.selected) return;
1211 if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1213 obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1214 g_return_if_fail(obj != NULL);
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;
1223 message = g_strdup_printf(_("Really delete `%s' ?"), name);
1224 aval = alertpanel(_("Delete"), message, _("Yes"), _("No"), NULL);
1226 if (aval != G_ALERTDEFAULT) return;
1228 pobj = gtk_ctree_node_get_row_data
1229 (ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1231 g_return_if_fail(pobj->type == ADDR_FOLDER);
1232 ADDRESS_FOLDER(pobj)->items =
1233 g_list_remove(ADDRESS_FOLDER(pobj)->items, obj);
1235 addressbook_delete_object(obj);
1236 addrbook.open_folder = TRUE;
1237 gtk_ctree_remove_node(ctree, addrbook.selected);
1238 addrbook.open_folder = FALSE;
1241 #define SET_LABEL_AND_ENTRY(str, entry, top) \
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); \
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); \
1253 static void addressbook_edit_address_create(gboolean *cancelled)
1259 GtkWidget *name_entry;
1260 GtkWidget *addr_entry;
1261 GtkWidget *rem_entry;
1264 GtkWidget *cancel_btn;
1266 debug_print("Creating edit_address window...\n");
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),
1277 gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
1278 GTK_SIGNAL_FUNC(edit_address_key_pressed),
1281 vbox = gtk_vbox_new(FALSE, 8);
1282 gtk_container_add(GTK_CONTAINER(window), vbox);
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);
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);
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);
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);
1303 gtk_widget_show_all(vbox);
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;
1313 static void edit_address_ok(GtkWidget *widget, gboolean *cancelled)
1319 static void edit_address_cancel(GtkWidget *widget, gboolean *cancelled)
1325 static gint edit_address_delete_event(GtkWidget *widget, GdkEventAny *event,
1326 gboolean *cancelled)
1334 static void edit_address_key_pressed(GtkWidget *widget, GdkEventKey *event,
1335 gboolean *cancelled)
1337 if (event && event->keyval == GDK_Escape) {
1343 static AddressItem *addressbook_edit_address(AddressItem *item)
1345 static gboolean cancelled;
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));
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), "");
1361 gtk_entry_set_text(GTK_ENTRY(addredit.name_entry),
1364 gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry),
1367 gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry),
1372 gtk_widget_hide(addredit.window);
1373 if (cancelled == TRUE) return NULL;
1375 str = gtk_entry_get_text(GTK_ENTRY(addredit.name_entry));
1376 if (*str == '\0') return NULL;
1379 item = g_new0(AddressItem, 1);
1380 ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
1384 item->name = g_strdup(str);
1386 str = gtk_entry_get_text(GTK_ENTRY(addredit.addr_entry));
1387 g_free(item->address);
1389 item->address = NULL;
1391 item->address = g_strdup(str);
1393 str = gtk_entry_get_text(GTK_ENTRY(addredit.rem_entry));
1394 g_free(item->remarks);
1396 item->remarks = NULL;
1398 item->remarks = g_strdup(str);
1403 static void addressbook_new_address_cb(gpointer data, guint action,
1408 item = addressbook_edit_address(NULL);
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),
1421 static void addressbook_edit_address_cb(gpointer data, guint action,
1424 GtkCList *clist = GTK_CLIST(addrbook.clist);
1427 if (!clist->selection) {
1428 addressbook_edit_folder_cb(NULL, 0, NULL);
1432 obj = gtk_clist_get_row_data(clist,
1433 GPOINTER_TO_INT(clist->selection->data));
1434 g_return_if_fail(obj != NULL);
1436 if (obj->type == ADDR_ITEM) {
1437 AddressItem *item = ADDRESS_ITEM(obj);
1439 if (addressbook_edit_address(item) == NULL) return;
1441 addrbook.open_folder = TRUE;
1442 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1445 } else if (obj->type == ADDR_GROUP) {
1446 addressbook_edit_group(NULL);
1450 static void addressbook_delete_address_cb(gpointer data, guint action,
1453 addressbook_del_clicked(NULL, NULL);
1456 static void close_cb(gpointer data, guint action, GtkWidget *widget)
1458 addressbook_close();
1461 static void addressbook_set_clist(AddressObject *obj)
1463 GtkCList *clist = GTK_CLIST(addrbook.clist);
1465 gchar *text[N_COLS];
1468 gtk_clist_clear(clist);
1472 gtk_clist_freeze(clist);
1474 gtk_clist_clear(clist);
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;
1481 gtk_clist_thaw(clist);
1485 for (; items != NULL; items = items->next) {
1486 AddressObject *iobj;
1489 iobj = ADDRESS_OBJECT(items->data);
1491 if (iobj->type == ADDR_GROUP) {
1492 AddressGroup *group;
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,
1501 groupxpm, groupxpmmask);
1502 gtk_clist_set_row_data(clist, row, iobj);
1503 } else if (iobj->type == ADDR_ITEM) {
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);
1515 gtk_clist_sort(clist);
1516 gtk_clist_thaw(clist);
1519 static void addressbook_read_file(void)
1524 debug_print(_("Reading addressbook file..."));
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);
1530 addressbook_get_tree(NULL, addrbook.common, "common_address");
1531 addressbook_get_tree(NULL, addrbook.personal, "personal_address");
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);
1545 addressbook_get_tree(file, addrbook.common, "common_address");
1546 addressbook_get_tree(file, addrbook.personal, "personal_address");
1548 xml_close_file(file);
1550 debug_print(_("done.\n"));
1553 static void addressbook_get_tree(XMLFile *file, GtkCTreeNode *node,
1554 const gchar *folder_tag)
1556 AddressFolder *folder;
1558 g_return_if_fail(node != NULL);
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);
1567 if (xml_parse_next_tag(file) < 0 ||
1568 xml_compare_tag(file, folder_tag) == FALSE) {
1569 g_warning("Invalid addressbook data\n");
1574 if (file) addressbook_add_objs(file, node);
1577 static void addressbook_add_objs(XMLFile *file, GtkCTreeNode *node)
1581 GtkCTreeNode *new_node;
1584 prev_level = file->level;
1585 if (xml_parse_next_tag(file) < 0) return;
1586 if (file->level < prev_level) return;
1588 if (xml_compare_tag(file, "group")) {
1589 AddressGroup *group;
1591 group = g_new(AddressGroup, 1);
1592 ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1593 attr = xml_get_current_tag_attr(file);
1595 group->name = g_strdup(((XMLAttr *)attr->data)->value);
1598 group->items = NULL;
1600 new_node = addressbook_add_object
1601 (node, ADDRESS_OBJECT(group));
1603 addressbook_add_objs(file, new_node);
1604 } else if (xml_compare_tag(file, "folder")) {
1605 AddressFolder *folder;
1607 folder = g_new(AddressFolder, 1);
1608 ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1609 attr = xml_get_current_tag_attr(file);
1611 folder->name = g_strdup(((XMLAttr *)attr->data)->value);
1613 folder->name = NULL;
1614 folder->items = NULL;
1616 new_node = addressbook_add_object
1617 (node, ADDRESS_OBJECT(folder));
1619 addressbook_add_objs(file, new_node);
1620 } else if (xml_compare_tag(file, "item")) {
1623 item = addressbook_parse_item(file);
1625 new_node = addressbook_add_object
1626 (node, ADDRESS_OBJECT(item));
1628 g_warning("Invalid tag\n");
1632 if (!new_node) return;
1636 static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
1639 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1640 GtkCTreeNode *added;
1641 AddressObject *pobj;
1643 g_return_val_if_fail(node != NULL, NULL);
1644 g_return_val_if_fail(obj != NULL, NULL);
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");
1652 if (pobj->type == ADDR_FOLDER &&
1653 (obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER))
1654 gtk_ctree_expand(ctree, node);
1656 if (obj->type == ADDR_GROUP) {
1657 AddressGroup *group = ADDRESS_GROUP(obj);
1659 if (pobj->type != ADDR_FOLDER) {
1660 g_warning("Group can't be added in another group.\n");
1664 added = gtk_ctree_insert_node(ctree, node, NULL,
1665 &group->name, FOLDER_SPACING,
1666 groupxpm, groupxpmmask,
1667 groupxpm, groupxpmmask,
1669 gtk_ctree_node_set_row_data(ctree, added, obj);
1670 } else if (obj->type == ADDR_FOLDER) {
1671 AddressFolder *folder = ADDRESS_FOLDER(obj);
1673 if (pobj->type != ADDR_FOLDER) {
1674 g_warning("Group can't contain folder.\n");
1678 added = gtk_ctree_insert_node(ctree, node, NULL,
1679 &folder->name, FOLDER_SPACING,
1680 folderxpm, folderxpmmask,
1681 folderopenxpm, folderopenxpmmask,
1683 gtk_ctree_node_set_row_data(ctree, added, obj);
1688 if (obj->type == ADDR_GROUP || obj->type == ADDR_ITEM) {
1689 if (pobj->type == ADDR_GROUP) {
1690 AddressGroup *group = ADDRESS_GROUP(pobj);
1692 group->items = g_list_append(group->items, obj);
1693 } else if (pobj->type == ADDR_FOLDER) {
1694 AddressFolder *folder = ADDRESS_FOLDER(pobj);
1696 folder->items = g_list_append(folder->items, obj);
1700 gtk_ctree_sort_node(ctree, node);
1705 static void addressbook_delete_object(AddressObject *obj)
1709 if (obj->type == ADDR_ITEM) {
1710 AddressItem *item = ADDRESS_ITEM(obj);
1713 g_free(item->address);
1714 g_free(item->remarks);
1716 } else if (obj->type == ADDR_GROUP) {
1717 AddressGroup *group = ADDRESS_GROUP(obj);
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);
1727 } else if (obj->type == ADDR_FOLDER) {
1728 AddressFolder *folder = ADDRESS_FOLDER(obj);
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);
1741 static AddressObject *addressbook_find_object_by_name(GtkCTreeNode *node,
1744 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1748 g_return_val_if_fail(node != NULL, NULL);
1750 obj = gtk_ctree_node_get_row_data(ctree, node);
1751 g_return_val_if_fail(obj != NULL, NULL);
1753 if (obj->type == ADDR_GROUP) {
1754 AddressGroup *group = ADDRESS_GROUP(obj);
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);
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;
1772 #define PARSE_ITEM_ERROR() \
1774 g_warning("addressbook_parse_item(): Parse error\n"); \
1775 g_free(item->name); \
1776 g_free(item->address); \
1777 g_free(item->remarks); \
1782 static AddressItem *addressbook_parse_item(XMLFile *file)
1788 item = g_new0(AddressItem, 1);
1789 ADDRESS_OBJECT(item)->type = ADDR_ITEM;
1791 level = file->level;
1793 while (xml_parse_next_tag(file) == 0) {
1794 if (file->level < level) return item;
1795 if (file->level == level) break;
1797 element = xml_get_element(file);
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;
1807 if (xml_parse_next_tag(file) < 0) break;
1808 if (file->level != level) break;
1814 void addressbook_export_to_file(void)
1819 if (!addrbook.ctree) return;
1821 debug_print(_("Exporting addressbook to file..."));
1823 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
1824 if ((pfile = prefs_write_open(path)) == NULL) {
1830 fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
1831 conv_get_current_charset_str());
1832 fputs("<addressbook>\n\n", pfile->fp);
1834 addressbook_xml_recursive_write(NULL, pfile->fp);
1836 fputs("</addressbook>\n", pfile->fp);
1838 if (prefs_write_close(pfile) < 0) {
1839 g_warning(_("failed to write addressbook data.\n"));
1843 debug_print(_("done.\n"));
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)
1854 work = GTK_CTREE_ROW(node)->children;
1855 addressbook_node_write_begin(node, fp);
1857 work = GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list);
1860 tmp = GTK_CTREE_ROW(work)->sibling;
1861 addressbook_xml_recursive_write(work, fp);
1866 addressbook_node_write_end(node, fp);
1869 static void addressbook_node_write_begin(GtkCTreeNode *node, FILE *fp)
1873 obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
1874 g_return_if_fail(obj != NULL);
1876 if (obj->type == ADDR_FOLDER) {
1877 AddressFolder *folder = ADDRESS_FOLDER(obj);
1879 if (GTK_CTREE_ROW(node)->level == 1) {
1880 fprintf(fp, "<%s>\n", folder->name);
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);
1887 } else if (obj->type == ADDR_GROUP) {
1888 AddressGroup *group = ADDRESS_GROUP(obj);
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);
1897 static void addressbook_node_write_end(GtkCTreeNode *node, FILE *fp)
1901 obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
1902 g_return_if_fail(obj != NULL);
1904 if (obj->type == ADDR_FOLDER) {
1905 AddressFolder *folder = ADDRESS_FOLDER(obj);
1907 addressbook_write_items(fp, folder->items,
1908 GTK_CTREE_ROW(node)->level);
1910 if (GTK_CTREE_ROW(node)->level == 1) {
1911 fprintf(fp, "</%s>\n\n", folder->name);
1913 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1914 fputs("</folder>\n", fp);
1916 } else if (obj->type == ADDR_GROUP) {
1917 AddressGroup *group = ADDRESS_GROUP(obj);
1919 addressbook_write_items(fp, group->items,
1920 GTK_CTREE_ROW(node)->level);
1922 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1923 fputs("</group>\n", fp);
1927 static void addressbook_write_items(FILE *fp, GList *items, guint level)
1931 for (; items != NULL; items = items->next) {
1932 if (ADDRESS_OBJECT_TYPE(items->data) == ADDR_ITEM) {
1933 item = ADDRESS_ITEM(items->data);
1935 tab_indent_out(fp, level);
1936 fputs("<item>\n", fp);
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);
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);
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);
1953 tab_indent_out(fp, level);
1954 fputs("</item>\n", fp);
1959 static void tab_indent_out(FILE *fp, guint level)
1963 for (i = 0; i < level; i++)
1967 static void key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
1969 if (event && event->keyval == GDK_Escape)
1970 addressbook_close();
1973 static gint addressbook_list_compare_func(GtkCList *clist,
1977 AddressObject *obj1 = ((GtkCListRow *)ptr1)->data;
1978 AddressObject *obj2 = ((GtkCListRow *)ptr2)->data;
1979 gchar *name1, *name2;
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;
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;
2006 return (name2 != NULL);
2010 return strcasecmp(name1, name2);
2013 static gint addressbook_obj_name_compare(gconstpointer a, gconstpointer b)
2015 const AddressObject *obj = a;
2016 const gchar *name = b;
2018 if (!obj || !name) return -1;
2020 if (obj->type == ADDR_GROUP) {
2021 AddressGroup *group = ADDRESS_GROUP(obj);
2025 return strcasecmp(group->name, name);
2026 } else if (obj->type == ADDR_FOLDER) {
2027 AddressFolder *folder = ADDRESS_FOLDER(obj);
2031 return strcasecmp(folder->name, name);
2032 } else if (obj->type == ADDR_ITEM) {
2033 AddressItem *item = ADDRESS_ITEM(obj);
2037 return strcasecmp(item->name, name);