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>
49 #include "addressbook.h"
50 #include "manage_window.h"
51 #include "prefs_common.h"
52 #include "alertpanel.h"
53 #include "inputdialog.h"
62 #include "addr_compl.h"
64 #include "pixmaps/dir-close.xpm"
65 #include "pixmaps/dir-open.xpm"
66 #include "pixmaps/group.xpm"
73 } AddressBookColumnPos;
76 #define COL_NAME_WIDTH 144
77 #define COL_ADDRESS_WIDTH 144
79 #define COL_FOLDER_WIDTH 170
80 #define ADDRESSBOOK_WIDTH 640
81 #define ADDRESSBOOK_HEIGHT 340
83 static GdkPixmap *folderxpm;
84 static GdkBitmap *folderxpmmask;
85 static GdkPixmap *folderopenxpm;
86 static GdkBitmap *folderopenxpmmask;
87 static GdkPixmap *groupxpm;
88 static GdkBitmap *groupxpmmask;
90 static AddressBook addrbook;
92 static struct _AddressEdit
95 GtkWidget *name_entry;
96 GtkWidget *addr_entry;
99 GtkWidget *cancel_btn;
102 static void addressbook_create (gboolean show);
103 static gint addressbook_close (void);
104 static void addressbook_button_set_sensitive (void);
106 /* callback functions */
107 static void addressbook_del_clicked (GtkButton *button,
109 static void addressbook_reg_clicked (GtkButton *button,
111 static void addressbook_to_clicked (GtkButton *button,
114 static void addressbook_tree_selected (GtkCTree *ctree,
118 static void addressbook_list_selected (GtkCList *clist,
124 static void addressbook_entry_changed (GtkWidget *widget);
127 static void addressbook_list_button_pressed (GtkWidget *widget,
128 GdkEventButton *event,
130 static void addressbook_list_button_released (GtkWidget *widget,
131 GdkEventButton *event,
133 static void addressbook_tree_button_pressed (GtkWidget *ctree,
134 GdkEventButton *event,
136 static void addressbook_tree_button_released (GtkWidget *ctree,
137 GdkEventButton *event,
139 static void addressbook_popup_close (GtkMenuShell *menu_shell,
142 static void addressbook_new_folder_cb (gpointer data,
145 static void addressbook_new_group_cb (gpointer data,
148 static void addressbook_edit_folder_cb (gpointer data,
151 static void addressbook_delete_folder_cb (gpointer data,
155 static void addressbook_change_node_name (GtkCTreeNode *node,
157 static void addressbook_edit_group (GtkCTreeNode *group_node);
159 static void addressbook_edit_address_create (gboolean *cancelled);
160 static void edit_address_ok (GtkWidget *widget,
161 gboolean *cancelled);
162 static void edit_address_cancel (GtkWidget *widget,
163 gboolean *cancelled);
164 static gint edit_address_delete_event (GtkWidget *widget,
166 gboolean *cancelled);
167 static void edit_address_key_pressed (GtkWidget *widget,
169 gboolean *cancelled);
170 static AddressItem *addressbook_edit_address (AddressItem *item);
172 static void addressbook_new_address_cb (gpointer data,
175 static void addressbook_edit_address_cb (gpointer data,
178 static void addressbook_delete_address_cb (gpointer data,
182 static void close_cb (gpointer data,
186 static AddressItem *addressbook_parse_address (const gchar *str);
187 static void addressbook_append_to_compose_entry (AddressItem *item,
188 ComposeEntryType type);
190 static void addressbook_set_clist (AddressObject *obj);
192 static void addressbook_read_file (void);
193 static void addressbook_get_tree (XMLFile *file,
195 const gchar *folder_tag);
196 static void addressbook_add_objs (XMLFile *file,
199 static GtkCTreeNode *addressbook_add_object (GtkCTreeNode *node,
201 static void addressbook_delete_object (AddressObject *obj);
202 static AddressObject *addressbook_find_object_by_name
206 static AddressItem *addressbook_parse_item (XMLFile *file);
207 static void addressbook_xml_recursive_write (GtkCTreeNode *node,
209 static void addressbook_node_write_begin (GtkCTreeNode *node,
211 static void addressbook_node_write_end (GtkCTreeNode *node,
213 static void addressbook_write_items (FILE *fp,
216 static void tab_indent_out (FILE *fp,
219 static void key_pressed (GtkWidget *widget,
222 static gint addressbook_list_compare_func (GtkCList *clist,
225 static gint addressbook_obj_name_compare (gconstpointer a,
228 static GtkItemFactoryEntry addressbook_entries[] =
230 {N_("/_File"), NULL, NULL, 0, "<Branch>"},
231 {N_("/_File/New _address"), "<alt>N", addressbook_new_address_cb, 0, NULL},
232 {N_("/_File/New _group"), "<alt>G", addressbook_new_group_cb, 0, NULL},
233 {N_("/_File/New _folder"), "<alt>R", addressbook_new_folder_cb, 0, NULL},
234 {N_("/_File/---"), NULL, NULL, 0, "<Separator>"},
235 {N_("/_File/_Edit"), "<alt>Return", addressbook_edit_address_cb, 0, NULL},
236 {N_("/_File/_Delete"), NULL, addressbook_delete_address_cb, 0, NULL},
237 {N_("/_File/---"), NULL, NULL, 0, "<Separator>"},
238 {N_("/_File/_Close"), "<alt>W", close_cb, 0, NULL},
239 {N_("/_Help"), NULL, NULL, 0, "<LastBranch>"},
240 {N_("/_Help/_About"), NULL, about_show, 0, NULL}
243 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
245 {N_("/New _address"), NULL, addressbook_new_address_cb, 0, NULL},
246 {N_("/New _group"), NULL, addressbook_new_group_cb, 0, NULL},
247 {N_("/New _folder"), NULL, addressbook_new_folder_cb, 0, NULL},
248 {N_("/---"), NULL, NULL, 0, "<Separator>"},
249 {N_("/_Edit"), NULL, addressbook_edit_folder_cb, 0, NULL},
250 {N_("/_Delete"), NULL, addressbook_delete_folder_cb, 0, NULL}
253 static GtkItemFactoryEntry addressbook_list_popup_entries[] =
255 {N_("/New _address"), NULL, addressbook_new_address_cb, 0, NULL},
256 {N_("/New _group"), NULL, addressbook_new_group_cb, 0, NULL},
257 {N_("/New _folder"), NULL, addressbook_new_folder_cb, 0, NULL},
258 {N_("/---"), NULL, NULL, 0, "<Separator>"},
259 {N_("/_Edit"), NULL, addressbook_edit_address_cb, 0, NULL},
260 {N_("/_Delete"), NULL, addressbook_delete_address_cb, 0, NULL}
263 void addressbook_open(Compose *target)
265 if (!addrbook.window) {
266 addressbook_create(TRUE);
267 addressbook_read_file();
268 addrbook.open_folder = TRUE;
269 gtk_ctree_select(GTK_CTREE(addrbook.ctree),
270 GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
272 gtk_widget_hide(addrbook.window);
274 gtk_widget_show_all(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(gboolean show)
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);
530 gtk_widget_show_all(window);
533 static gint addressbook_close(void)
535 gtk_widget_hide(addrbook.window);
536 addressbook_export_to_file();
537 /* tell addr_compl that there's a new addressbook file */
538 invalidate_address_completion();
542 static void addressbook_button_set_sensitive(void)
544 gboolean to_sens = FALSE;
545 gboolean cc_sens = FALSE;
546 gboolean bcc_sens = FALSE;
548 if (!addrbook.window) return;
550 if (addrbook.target_compose) {
553 if (addrbook.target_compose->use_bcc)
557 gtk_widget_set_sensitive(addrbook.to_btn, to_sens);
558 gtk_widget_set_sensitive(addrbook.cc_btn, cc_sens);
559 gtk_widget_set_sensitive(addrbook.bcc_btn, bcc_sens);
562 static void addressbook_del_clicked(GtkButton *button, gpointer data)
564 GtkCList *clist = GTK_CLIST(addrbook.clist);
565 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
566 AddressObject *pobj, *obj;
570 if (!clist->selection) {
571 addressbook_delete_folder_cb(NULL, 0, NULL);
575 pobj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
576 g_return_if_fail(pobj != NULL);
578 if (alertpanel(_("Delete address(es)"),
579 _("Really delete the address(es)?"),
580 _("Yes"), _("No"), NULL) != G_ALERTDEFAULT)
583 for (cur = clist->selection; cur != NULL; cur = next) {
585 row = GPOINTER_TO_INT(cur->data);
587 obj = gtk_clist_get_row_data(clist, row);
590 if (pobj->type == ADDR_GROUP) {
591 AddressGroup *group = ADDRESS_GROUP(pobj);
592 group->items = g_list_remove(group->items, obj);
593 } else if (pobj->type == ADDR_FOLDER) {
594 AddressFolder *folder = ADDRESS_FOLDER(pobj);
596 folder->items = g_list_remove(folder->items, obj);
597 if (obj->type == ADDR_GROUP) {
600 node = gtk_ctree_find_by_row_data
601 (ctree, addrbook.opened, obj);
602 if (node) gtk_ctree_remove_node(ctree, node);
607 addressbook_delete_object(obj);
609 gtk_clist_remove(clist, row);
613 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
615 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
616 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
621 if (*gtk_entry_get_text(entry) == '\0') {
622 addressbook_new_address_cb(NULL, 0, NULL);
625 if (!addrbook.opened) return;
627 obj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
630 g_return_if_fail(obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER);
632 str = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
634 item = addressbook_parse_address(str);
637 if (addressbook_find_object_by_name
638 (addrbook.opened, item->name) != NULL) {
639 addressbook_delete_object(ADDRESS_OBJECT(item));
641 } else if (addressbook_edit_address(item) == NULL) {
642 addressbook_delete_object(ADDRESS_OBJECT(item));
648 item = addressbook_edit_address(NULL);
652 if (addressbook_find_object_by_name(addrbook.opened, item->name)) {
653 addressbook_delete_object(ADDRESS_OBJECT(item));
657 addressbook_add_object(addrbook.opened, ADDRESS_OBJECT(item));
658 addrbook.open_folder = TRUE;
659 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
662 static AddressItem *addressbook_parse_address(const gchar *str)
665 gchar *address = NULL;
670 Xalloca(buf, strlen(str) + 1, return NULL);
674 if (*buf == '\0') return NULL;
676 if ((start = strchr(buf, '<'))) {
681 name = g_strdup(buf);
684 if ((end = strchr(start, '>'))) {
688 address = g_strdup(start);
691 name = g_strdup(buf);
693 if (!name && !address) return NULL;
695 item = g_new(AddressItem, 1);
696 ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
698 item->address = address;
699 item->remarks = NULL;
704 static void addressbook_to_clicked(GtkButton *button, gpointer data)
706 GtkCList *clist = GTK_CLIST(addrbook.clist);
709 if (!addrbook.target_compose) return;
711 for (cur = clist->selection; cur != NULL; cur = cur->next) {
714 obj = gtk_clist_get_row_data(clist,
715 GPOINTER_TO_INT(cur->data));
718 if (obj->type == ADDR_ITEM) {
719 addressbook_append_to_compose_entry
720 (ADDRESS_ITEM(obj), (ComposeEntryType)data);
721 } else if (obj->type == ADDR_GROUP) {
725 group = ADDRESS_GROUP(obj);
726 for (cur_item = group->items; cur_item != NULL;
727 cur_item = cur_item->next) {
728 if (ADDRESS_OBJECT(cur_item->data)->type
731 addressbook_append_to_compose_entry
732 (ADDRESS_ITEM(cur_item->data),
733 (ComposeEntryType)data);
739 static void addressbook_append_to_compose_entry(AddressItem *item,
740 ComposeEntryType type)
742 Compose *compose = addrbook.target_compose;
744 if (item->name && item->address) {
747 buf = g_strdup_printf
748 ("%s <%s>", item->name, item->address);
749 compose_entry_append(compose, buf, type);
751 } else if (item->address)
752 compose_entry_append(compose, item->address, type);
755 static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
756 gint column, gpointer data)
760 addrbook.selected = node;
762 if (!addrbook.open_folder) return;
763 addrbook.open_folder = FALSE;
765 gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
767 obj = gtk_ctree_node_get_row_data(ctree, node);
768 g_return_if_fail(obj != NULL);
770 addrbook.opened = node;
772 if (obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER)
773 addressbook_set_clist(obj);
776 static void addressbook_list_selected(GtkCList *clist, gint row, gint column,
777 GdkEvent *event, gpointer data)
779 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
783 if (event && event->type == GDK_2BUTTON_PRESS) {
784 if (prefs_common.add_address_by_click &&
785 addrbook.target_compose)
786 addressbook_to_clicked(NULL, NULL);
788 addressbook_edit_address_cb(NULL, 0, NULL);
793 gtk_signal_handler_block_by_func
795 GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
798 gtk_entry_set_text(entry, "");
800 for (cur = clist->selection; cur != NULL; cur = cur->next) {
801 obj = gtk_clist_get_row_data(clist,
802 GPOINTER_TO_INT(cur->data));
803 g_return_if_fail(obj != NULL);
805 if (obj->type == ADDR_ITEM) {
808 item = ADDRESS_ITEM(obj);
809 if (item->name && item->address) {
812 buf = g_strdup_printf
813 ("%s <%s>", item->name, item->address);
814 if (*gtk_entry_get_text(entry) != '\0')
815 gtk_entry_append_text(entry, ", ");
816 gtk_entry_append_text(entry, buf);
818 } else if (item->address) {
819 if (*gtk_entry_get_text(entry) != '\0')
820 gtk_entry_append_text(entry, ", ");
821 gtk_entry_append_text(entry, item->address);
827 gtk_signal_handler_unblock_by_func
829 GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
834 static void addressbook_entry_changed(GtkWidget *widget)
836 GtkCList *clist = GTK_CLIST(addrbook.clist);
837 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
842 //if (clist->selection && clist->selection->next) return;
844 str = gtk_entry_get_text(entry);
846 gtk_clist_unselect_all(clist);
851 for (row = 0; row < clist->rows; row++) {
855 obj = ADDRESS_OBJECT(gtk_clist_get_row_data(clist, row));
857 if (obj->type == ADDR_ITEM)
858 name = ADDRESS_ITEM(obj)->name;
859 else if (obj->type == ADDR_GROUP)
860 name = ADDRESS_GROUP(obj)->name;
864 if (name && !strncasecmp(name, str, len)) {
865 gtk_clist_unselect_all(clist);
866 gtk_clist_select_row(clist, row, -1);
871 gtk_clist_unselect_all(clist);
875 static void addressbook_list_button_pressed(GtkWidget *widget,
876 GdkEventButton *event,
879 GtkCList *clist = GTK_CLIST(widget);
884 if (event->button != 3) return;
886 obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
888 g_return_if_fail(obj != NULL);
890 menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.list_popup));
892 if (gtk_clist_get_selection_info
893 (clist, event->x, event->y, &row, &column)) {
894 GtkCListRow *clist_row;
896 clist_row = g_list_nth(clist->row_list, row)->data;
897 if (clist_row->state != GTK_STATE_SELECTED) {
898 gtk_clist_unselect_all(clist);
899 gtk_clist_select_row(clist, row, column);
901 gtkut_clist_set_focus_row(clist, row);
903 menu_set_sensitive(addrbook.list_factory, "/Edit", TRUE);
904 menu_set_sensitive(addrbook.list_factory, "/Delete", TRUE);
907 menu_set_sensitive(addrbook.list_factory, "/New address", TRUE);
908 if (obj->type == ADDR_FOLDER) {
909 menu_set_sensitive(addrbook.list_factory, "/New folder", TRUE);
910 menu_set_sensitive(addrbook.list_factory, "/New group", TRUE);
913 gtk_menu_popup(GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
914 event->button, event->time);
917 static void addressbook_list_button_released(GtkWidget *widget,
918 GdkEventButton *event,
923 static void addressbook_tree_button_pressed(GtkWidget *ctree,
924 GdkEventButton *event,
927 GtkCList *clist = GTK_CLIST(ctree);
933 if (event->button == 1) {
934 addrbook.open_folder = TRUE;
937 if (event->button != 3) return;
939 if (!gtk_clist_get_selection_info
940 (clist, event->x, event->y, &row, &column)) return;
941 gtk_clist_select_row(clist, row, column);
943 obj = gtk_clist_get_row_data(clist, row);
944 g_return_if_fail(obj != NULL);
946 menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
948 if (obj->type == ADDR_FOLDER) {
949 node = gtk_ctree_node_nth(GTK_CTREE(ctree), row);
950 menu_set_sensitive(addrbook.tree_factory, "/New folder", TRUE);
951 menu_set_sensitive(addrbook.tree_factory, "/New group", TRUE);
952 if (node && GTK_CTREE_ROW(node)->level >= 2) {
953 menu_set_sensitive(addrbook.tree_factory,
955 menu_set_sensitive(addrbook.tree_factory,
958 } else if (obj->type == ADDR_GROUP) {
959 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
960 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
964 menu_set_sensitive(addrbook.tree_factory, "/New address", TRUE);
966 gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
967 event->button, event->time);
970 static void addressbook_tree_button_released(GtkWidget *ctree,
971 GdkEventButton *event,
974 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
975 gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree), addrbook.opened);
978 static void addressbook_popup_close(GtkMenuShell *menu_shell, gpointer data)
980 if (!addrbook.opened) return;
982 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
983 gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree),
987 static void addressbook_new_folder_cb(gpointer data, guint action,
990 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
992 AddressFolder *folder;
995 if (!addrbook.selected) return;
997 obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
998 g_return_if_fail(obj != NULL);
999 if (obj->type != ADDR_FOLDER) return;
1001 new_folder = input_dialog(_("New folder"),
1002 _("Input the name of new folder:"),
1004 if (!new_folder) return;
1005 g_strstrip(new_folder);
1006 if (*new_folder == '\0') {
1011 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1013 addressbook_obj_name_compare)) {
1014 alertpanel_error(_("The name already exists."));
1019 folder = g_new(AddressFolder, 1);
1020 ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1021 folder->name = g_strdup(new_folder);
1022 folder->items = NULL;
1024 addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(folder));
1028 if (addrbook.selected == addrbook.opened)
1029 addressbook_set_clist(obj);
1032 static void addressbook_new_group_cb(gpointer data, guint action,
1035 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1037 AddressGroup *group;
1040 if (!addrbook.selected) return;
1042 obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1043 g_return_if_fail(obj != NULL);
1044 if (obj->type != ADDR_FOLDER) return;
1046 new_group = input_dialog(_("New group"),
1047 _("Input the name of new group:"),
1049 if (!new_group) return;
1050 g_strstrip(new_group);
1051 if (*new_group == '\0') {
1056 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1058 addressbook_obj_name_compare)) {
1059 alertpanel_error(_("The name already exists."));
1064 group = g_new(AddressGroup, 1);
1065 ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1066 group->name = g_strdup(new_group);
1067 group->items = NULL;
1069 addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(group));
1073 if (addrbook.selected == addrbook.opened)
1074 addressbook_set_clist(obj);
1077 static void addressbook_change_node_name(GtkCTreeNode *node, const gchar *name)
1079 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1082 GdkPixmap *pix_cl, *pix_op;
1083 GdkBitmap *mask_cl, *mask_op;
1084 gboolean is_leaf, expanded;
1086 gtk_ctree_get_node_info(ctree, node, text, &spacing,
1087 &pix_cl, &mask_cl, &pix_op, &mask_op,
1088 &is_leaf, &expanded);
1089 gtk_ctree_set_node_info(ctree, node, name, spacing,
1090 pix_cl, mask_cl, pix_op, mask_op,
1094 static void addressbook_edit_group(GtkCTreeNode *group_node)
1096 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1097 GtkCList *clist = GTK_CLIST(addrbook.clist);
1099 AddressGroup *group;
1103 if (!group_node && clist->selection) {
1104 obj = gtk_clist_get_row_data(clist,
1105 GPOINTER_TO_INT(clist->selection->data));
1106 g_return_if_fail(obj != NULL);
1107 if (obj->type != ADDR_GROUP) return;
1108 node = gtk_ctree_find_by_row_data
1109 (ctree, addrbook.selected, obj);
1115 node = addrbook.selected;
1116 obj = gtk_ctree_node_get_row_data(ctree, node);
1117 g_return_if_fail(obj != NULL);
1118 if (obj->type != ADDR_GROUP) return;
1121 group = ADDRESS_GROUP(obj);
1123 new_name = input_dialog(_("Edit group"),
1124 _("Input the new name of group:"),
1126 if (!new_name) return;
1127 g_strstrip(new_name);
1128 if (*new_name == '\0') {
1133 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1135 addressbook_obj_name_compare)) {
1136 alertpanel_error(_("The name already exists."));
1141 g_free(group->name);
1142 group->name = g_strdup(new_name);
1144 addressbook_change_node_name(node, new_name);
1145 gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(node)->parent);
1149 addrbook.open_folder = TRUE;
1150 gtk_ctree_select(ctree, addrbook.opened);
1153 static void addressbook_edit_folder_cb(gpointer data, guint action,
1156 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1158 AddressFolder *folder;
1161 if (!addrbook.selected) return;
1162 if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1164 obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1165 g_return_if_fail(obj != NULL);
1166 g_return_if_fail(obj->type == ADDR_FOLDER || obj->type == ADDR_GROUP);
1168 if (obj->type == ADDR_GROUP) {
1169 addressbook_edit_group(addrbook.selected);
1173 folder = ADDRESS_FOLDER(obj);
1174 new_name = input_dialog(_("Edit folder"),
1175 _("Input the new name of folder:"),
1178 if (!new_name) return;
1179 g_strstrip(new_name);
1180 if (*new_name == '\0') {
1185 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1187 addressbook_obj_name_compare)) {
1188 alertpanel_error(_("The name already exists."));
1193 g_free(folder->name);
1194 folder->name = g_strdup(new_name);
1196 addressbook_change_node_name(addrbook.selected, new_name);
1197 gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1202 static void addressbook_delete_folder_cb(gpointer data, guint action,
1205 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1206 AddressObject *obj, *pobj;
1211 if (!addrbook.selected) return;
1212 if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1214 obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1215 g_return_if_fail(obj != NULL);
1217 if (obj->type == ADDR_GROUP)
1218 name = ADDRESS_GROUP(obj)->name;
1219 else if (obj->type == ADDR_FOLDER)
1220 name = ADDRESS_FOLDER(obj)->name;
1224 message = g_strdup_printf(_("Really delete `%s' ?"), name);
1225 aval = alertpanel(_("Delete"), message, _("Yes"), _("No"), NULL);
1227 if (aval != G_ALERTDEFAULT) return;
1229 pobj = gtk_ctree_node_get_row_data
1230 (ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1232 g_return_if_fail(pobj->type == ADDR_FOLDER);
1233 ADDRESS_FOLDER(pobj)->items =
1234 g_list_remove(ADDRESS_FOLDER(pobj)->items, obj);
1236 addressbook_delete_object(obj);
1237 addrbook.open_folder = TRUE;
1238 gtk_ctree_remove_node(ctree, addrbook.selected);
1239 addrbook.open_folder = FALSE;
1242 #define SET_LABEL_AND_ENTRY(str, entry, top) \
1244 label = gtk_label_new(str); \
1245 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
1246 GTK_FILL, 0, 0, 0); \
1247 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
1249 entry = gtk_entry_new(); \
1250 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
1251 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
1254 static void addressbook_edit_address_create(gboolean *cancelled)
1260 GtkWidget *name_entry;
1261 GtkWidget *addr_entry;
1262 GtkWidget *rem_entry;
1265 GtkWidget *cancel_btn;
1267 debug_print("Creating edit_address window...\n");
1269 window = gtk_window_new(GTK_WINDOW_DIALOG);
1270 gtk_widget_set_usize(window, 400, -1);
1271 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
1272 gtk_window_set_title(GTK_WINDOW(window), _("Edit address"));
1273 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1274 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1275 gtk_signal_connect(GTK_OBJECT(window), "delete_event",
1276 GTK_SIGNAL_FUNC(edit_address_delete_event),
1278 gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
1279 GTK_SIGNAL_FUNC(edit_address_key_pressed),
1282 vbox = gtk_vbox_new(FALSE, 8);
1283 gtk_container_add(GTK_CONTAINER(window), vbox);
1285 table = gtk_table_new(3, 2, FALSE);
1286 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
1287 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
1288 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
1290 SET_LABEL_AND_ENTRY(_("Name"), name_entry, 0);
1291 SET_LABEL_AND_ENTRY(_("Address"), addr_entry, 1);
1292 SET_LABEL_AND_ENTRY(_("Remarks"), rem_entry, 2);
1294 gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
1295 &cancel_btn, _("Cancel"), NULL, NULL);
1296 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
1297 gtk_widget_grab_default(ok_btn);
1299 gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
1300 GTK_SIGNAL_FUNC(edit_address_ok), cancelled);
1301 gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
1302 GTK_SIGNAL_FUNC(edit_address_cancel), cancelled);
1304 gtk_widget_show_all(vbox);
1306 addredit.window = window;
1307 addredit.name_entry = name_entry;
1308 addredit.addr_entry = addr_entry;
1309 addredit.rem_entry = rem_entry;
1310 addredit.ok_btn = ok_btn;
1311 addredit.cancel_btn = cancel_btn;
1314 static void edit_address_ok(GtkWidget *widget, gboolean *cancelled)
1320 static void edit_address_cancel(GtkWidget *widget, gboolean *cancelled)
1326 static gint edit_address_delete_event(GtkWidget *widget, GdkEventAny *event,
1327 gboolean *cancelled)
1335 static void edit_address_key_pressed(GtkWidget *widget, GdkEventKey *event,
1336 gboolean *cancelled)
1338 if (event && event->keyval == GDK_Escape) {
1344 static AddressItem *addressbook_edit_address(AddressItem *item)
1346 static gboolean cancelled;
1349 if (!addredit.window)
1350 addressbook_edit_address_create(&cancelled);
1351 gtk_widget_grab_focus(addredit.ok_btn);
1352 gtk_widget_grab_focus(addredit.name_entry);
1353 gtk_widget_show(addredit.window);
1354 manage_window_set_transient(GTK_WINDOW(addredit.window));
1356 gtk_entry_set_text(GTK_ENTRY(addredit.name_entry), "");
1357 gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry), "");
1358 gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry), "");
1362 gtk_entry_set_text(GTK_ENTRY(addredit.name_entry),
1365 gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry),
1368 gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry),
1373 gtk_widget_hide(addredit.window);
1374 if (cancelled == TRUE) return NULL;
1376 str = gtk_entry_get_text(GTK_ENTRY(addredit.name_entry));
1377 if (*str == '\0') return NULL;
1380 item = g_new0(AddressItem, 1);
1381 ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
1385 item->name = g_strdup(str);
1387 str = gtk_entry_get_text(GTK_ENTRY(addredit.addr_entry));
1388 g_free(item->address);
1390 item->address = NULL;
1392 item->address = g_strdup(str);
1394 str = gtk_entry_get_text(GTK_ENTRY(addredit.rem_entry));
1395 g_free(item->remarks);
1397 item->remarks = NULL;
1399 item->remarks = g_strdup(str);
1404 static void addressbook_new_address_cb(gpointer data, guint action,
1409 item = addressbook_edit_address(NULL);
1412 addressbook_add_object(addrbook.selected,
1413 ADDRESS_OBJECT(item));
1414 if (addrbook.selected == addrbook.opened) {
1415 addrbook.open_folder = TRUE;
1416 gtk_ctree_select(GTK_CTREE(addrbook.ctree),
1422 static void addressbook_edit_address_cb(gpointer data, guint action,
1425 GtkCList *clist = GTK_CLIST(addrbook.clist);
1428 if (!clist->selection) {
1429 addressbook_edit_folder_cb(NULL, 0, NULL);
1433 obj = gtk_clist_get_row_data(clist,
1434 GPOINTER_TO_INT(clist->selection->data));
1435 g_return_if_fail(obj != NULL);
1437 if (obj->type == ADDR_ITEM) {
1438 AddressItem *item = ADDRESS_ITEM(obj);
1440 if (addressbook_edit_address(item) == NULL) return;
1442 addrbook.open_folder = TRUE;
1443 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1446 } else if (obj->type == ADDR_GROUP) {
1447 addressbook_edit_group(NULL);
1451 static void addressbook_delete_address_cb(gpointer data, guint action,
1454 addressbook_del_clicked(NULL, NULL);
1457 static void close_cb(gpointer data, guint action, GtkWidget *widget)
1459 addressbook_close();
1462 static void addressbook_set_clist(AddressObject *obj)
1464 GtkCList *clist = GTK_CLIST(addrbook.clist);
1466 gchar *text[N_COLS];
1469 gtk_clist_clear(clist);
1473 gtk_clist_freeze(clist);
1475 gtk_clist_clear(clist);
1477 if (obj->type == ADDR_GROUP)
1478 items = ADDRESS_GROUP(obj)->items;
1479 else if (obj->type == ADDR_FOLDER)
1480 items = ADDRESS_FOLDER(obj)->items;
1482 gtk_clist_thaw(clist);
1486 for (; items != NULL; items = items->next) {
1487 AddressObject *iobj;
1490 iobj = ADDRESS_OBJECT(items->data);
1492 if (iobj->type == ADDR_GROUP) {
1493 AddressGroup *group;
1495 group = ADDRESS_GROUP(iobj);
1496 text[COL_NAME] = group->name;
1497 text[COL_ADDRESS] = NULL;
1498 text[COL_REMARKS] = NULL;
1499 row = gtk_clist_append(clist, text);
1500 gtk_clist_set_pixtext(clist, row, COL_NAME,
1502 groupxpm, groupxpmmask);
1503 gtk_clist_set_row_data(clist, row, iobj);
1504 } else if (iobj->type == ADDR_ITEM) {
1507 item = ADDRESS_ITEM(iobj);
1508 text[COL_NAME] = item->name;
1509 text[COL_ADDRESS] = item->address;
1510 text[COL_REMARKS] = item->remarks;
1511 row = gtk_clist_append(clist, text);
1512 gtk_clist_set_row_data(clist, row, iobj);
1516 gtk_clist_sort(clist);
1517 gtk_clist_thaw(clist);
1520 static void addressbook_read_file(void)
1525 debug_print(_("Reading addressbook file..."));
1527 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
1528 if ((file = xml_open_file(path)) == NULL) {
1529 debug_print(_("%s doesn't exist.\n"), path);
1531 addressbook_get_tree(NULL, addrbook.common, "common_address");
1532 addressbook_get_tree(NULL, addrbook.personal, "personal_address");
1539 if (xml_parse_next_tag(file) < 0 ||
1540 xml_compare_tag(file, "addressbook") == FALSE) {
1541 g_warning("Invalid addressbook data\n");
1542 xml_close_file(file);
1546 addressbook_get_tree(file, addrbook.common, "common_address");
1547 addressbook_get_tree(file, addrbook.personal, "personal_address");
1549 xml_close_file(file);
1551 debug_print(_("done.\n"));
1554 static void addressbook_get_tree(XMLFile *file, GtkCTreeNode *node,
1555 const gchar *folder_tag)
1557 AddressFolder *folder;
1559 g_return_if_fail(node != NULL);
1561 folder = g_new(AddressFolder, 1);
1562 ADDRESS_OBJECT(folder)->type = ADDR_FOLDER;
1563 folder->name = g_strdup(folder_tag);
1564 folder->items = NULL;
1565 gtk_ctree_node_set_row_data(GTK_CTREE(addrbook.ctree), node, folder);
1568 if (xml_parse_next_tag(file) < 0 ||
1569 xml_compare_tag(file, folder_tag) == FALSE) {
1570 g_warning("Invalid addressbook data\n");
1575 if (file) addressbook_add_objs(file, node);
1578 static void addressbook_add_objs(XMLFile *file, GtkCTreeNode *node)
1582 GtkCTreeNode *new_node;
1585 prev_level = file->level;
1586 if (xml_parse_next_tag(file) < 0) return;
1587 if (file->level < prev_level) return;
1589 if (xml_compare_tag(file, "group")) {
1590 AddressGroup *group;
1592 group = g_new(AddressGroup, 1);
1593 ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1594 attr = xml_get_current_tag_attr(file);
1596 group->name = g_strdup(((XMLAttr *)attr->data)->value);
1599 group->items = NULL;
1601 new_node = addressbook_add_object
1602 (node, ADDRESS_OBJECT(group));
1604 addressbook_add_objs(file, new_node);
1605 } else if (xml_compare_tag(file, "folder")) {
1606 AddressFolder *folder;
1608 folder = g_new(AddressFolder, 1);
1609 ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1610 attr = xml_get_current_tag_attr(file);
1612 folder->name = g_strdup(((XMLAttr *)attr->data)->value);
1614 folder->name = NULL;
1615 folder->items = NULL;
1617 new_node = addressbook_add_object
1618 (node, ADDRESS_OBJECT(folder));
1620 addressbook_add_objs(file, new_node);
1621 } else if (xml_compare_tag(file, "item")) {
1624 item = addressbook_parse_item(file);
1626 new_node = addressbook_add_object
1627 (node, ADDRESS_OBJECT(item));
1629 g_warning("Invalid tag\n");
1633 if (!new_node) return;
1637 static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
1640 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1641 GtkCTreeNode *added;
1642 AddressObject *pobj;
1644 g_return_val_if_fail(node != NULL, NULL);
1645 g_return_val_if_fail(obj != NULL, NULL);
1647 pobj = gtk_ctree_node_get_row_data(ctree, node);
1648 g_return_val_if_fail(pobj != NULL, NULL);
1649 if (pobj->type == ADDR_ITEM) {
1650 g_warning("Parent object mustn't be an item.\n");
1653 if (pobj->type == ADDR_FOLDER &&
1654 (obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER))
1655 gtk_ctree_expand(ctree, node);
1657 if (obj->type == ADDR_GROUP) {
1658 AddressGroup *group = ADDRESS_GROUP(obj);
1660 if (pobj->type != ADDR_FOLDER) {
1661 g_warning("Group can't be added in another group.\n");
1665 added = gtk_ctree_insert_node(ctree, node, NULL,
1666 &group->name, FOLDER_SPACING,
1667 groupxpm, groupxpmmask,
1668 groupxpm, groupxpmmask,
1670 gtk_ctree_node_set_row_data(ctree, added, obj);
1671 } else if (obj->type == ADDR_FOLDER) {
1672 AddressFolder *folder = ADDRESS_FOLDER(obj);
1674 if (pobj->type != ADDR_FOLDER) {
1675 g_warning("Group can't contain folder.\n");
1679 added = gtk_ctree_insert_node(ctree, node, NULL,
1680 &folder->name, FOLDER_SPACING,
1681 folderxpm, folderxpmmask,
1682 folderopenxpm, folderopenxpmmask,
1684 gtk_ctree_node_set_row_data(ctree, added, obj);
1689 if (obj->type == ADDR_GROUP || obj->type == ADDR_ITEM) {
1690 if (pobj->type == ADDR_GROUP) {
1691 AddressGroup *group = ADDRESS_GROUP(pobj);
1693 group->items = g_list_append(group->items, obj);
1694 } else if (pobj->type == ADDR_FOLDER) {
1695 AddressFolder *folder = ADDRESS_FOLDER(pobj);
1697 folder->items = g_list_append(folder->items, obj);
1701 gtk_ctree_sort_node(ctree, node);
1706 static void addressbook_delete_object(AddressObject *obj)
1710 if (obj->type == ADDR_ITEM) {
1711 AddressItem *item = ADDRESS_ITEM(obj);
1714 g_free(item->address);
1715 g_free(item->remarks);
1717 } else if (obj->type == ADDR_GROUP) {
1718 AddressGroup *group = ADDRESS_GROUP(obj);
1720 g_free(group->name);
1721 while (group->items != NULL) {
1722 addressbook_delete_object
1723 (ADDRESS_OBJECT(group->items->data));
1724 group->items = g_list_remove(group->items,
1725 group->items->data);
1728 } else if (obj->type == ADDR_FOLDER) {
1729 AddressFolder *folder = ADDRESS_FOLDER(obj);
1731 g_free(folder->name);
1732 while (folder->items != NULL) {
1733 addressbook_delete_object
1734 (ADDRESS_OBJECT(folder->items->data));
1735 folder->items = g_list_remove(folder->items,
1736 folder->items->data);
1742 static AddressObject *addressbook_find_object_by_name(GtkCTreeNode *node,
1745 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1749 g_return_val_if_fail(node != NULL, NULL);
1751 obj = gtk_ctree_node_get_row_data(ctree, node);
1752 g_return_val_if_fail(obj != NULL, NULL);
1754 if (obj->type == ADDR_GROUP) {
1755 AddressGroup *group = ADDRESS_GROUP(obj);
1757 found = g_list_find_custom(group->items, (gpointer)name,
1758 addressbook_obj_name_compare);
1759 if (found) return ADDRESS_OBJECT(found->data);
1760 } else if (obj->type == ADDR_FOLDER) {
1761 AddressFolder *folder = ADDRESS_FOLDER(obj);
1763 found = g_list_find_custom(folder->items, (gpointer)name,
1764 addressbook_obj_name_compare);
1765 if (found) return ADDRESS_OBJECT(found->data);
1766 } else if (obj->type == ADDR_ITEM) {
1767 if (!addressbook_obj_name_compare(obj, name)) return obj;
1773 #define PARSE_ITEM_ERROR() \
1775 g_warning("addressbook_parse_item(): Parse error\n"); \
1776 g_free(item->name); \
1777 g_free(item->address); \
1778 g_free(item->remarks); \
1783 static AddressItem *addressbook_parse_item(XMLFile *file)
1789 item = g_new0(AddressItem, 1);
1790 ADDRESS_OBJECT(item)->type = ADDR_ITEM;
1792 level = file->level;
1794 while (xml_parse_next_tag(file) == 0) {
1795 if (file->level < level) return item;
1796 if (file->level == level) break;
1798 element = xml_get_element(file);
1800 if (xml_compare_tag(file, "name")) {
1801 item->name = element;
1802 } else if (xml_compare_tag(file, "address")) {
1803 item->address = element;
1804 } else if (xml_compare_tag(file, "remarks")) {
1805 item->remarks = element;
1808 if (xml_parse_next_tag(file) < 0) break;
1809 if (file->level != level) break;
1815 void addressbook_export_to_file(void)
1820 if (!addrbook.ctree) return;
1822 debug_print(_("Exporting addressbook to file..."));
1824 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
1825 if ((pfile = prefs_write_open(path)) == NULL) {
1831 fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
1832 conv_get_current_charset_str());
1833 fputs("<addressbook>\n\n", pfile->fp);
1835 addressbook_xml_recursive_write(NULL, pfile->fp);
1837 fputs("</addressbook>\n", pfile->fp);
1839 if (prefs_write_close(pfile) < 0) {
1840 g_warning(_("failed to write addressbook data.\n"));
1844 debug_print(_("done.\n"));
1847 /* Most part of this function was taken from gtk_ctree_pre_recursive() and
1848 gtk_ctree_post_recursive(). */
1849 static void addressbook_xml_recursive_write(GtkCTreeNode *node, FILE *fp)
1855 work = GTK_CTREE_ROW(node)->children;
1856 addressbook_node_write_begin(node, fp);
1858 work = GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list);
1861 tmp = GTK_CTREE_ROW(work)->sibling;
1862 addressbook_xml_recursive_write(work, fp);
1867 addressbook_node_write_end(node, fp);
1870 static void addressbook_node_write_begin(GtkCTreeNode *node, FILE *fp)
1874 obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
1875 g_return_if_fail(obj != NULL);
1877 if (obj->type == ADDR_FOLDER) {
1878 AddressFolder *folder = ADDRESS_FOLDER(obj);
1880 if (GTK_CTREE_ROW(node)->level == 1) {
1881 fprintf(fp, "<%s>\n", folder->name);
1883 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1884 fputs("<folder name=\"", fp);
1885 xml_file_put_escape_str(fp, folder->name);
1888 } else if (obj->type == ADDR_GROUP) {
1889 AddressGroup *group = ADDRESS_GROUP(obj);
1891 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1892 fputs("<group name=\"", fp);
1893 xml_file_put_escape_str(fp, group->name);
1898 static void addressbook_node_write_end(GtkCTreeNode *node, FILE *fp)
1902 obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
1903 g_return_if_fail(obj != NULL);
1905 if (obj->type == ADDR_FOLDER) {
1906 AddressFolder *folder = ADDRESS_FOLDER(obj);
1908 addressbook_write_items(fp, folder->items,
1909 GTK_CTREE_ROW(node)->level);
1911 if (GTK_CTREE_ROW(node)->level == 1) {
1912 fprintf(fp, "</%s>\n\n", folder->name);
1914 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1915 fputs("</folder>\n", fp);
1917 } else if (obj->type == ADDR_GROUP) {
1918 AddressGroup *group = ADDRESS_GROUP(obj);
1920 addressbook_write_items(fp, group->items,
1921 GTK_CTREE_ROW(node)->level);
1923 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
1924 fputs("</group>\n", fp);
1928 static void addressbook_write_items(FILE *fp, GList *items, guint level)
1932 for (; items != NULL; items = items->next) {
1933 if (ADDRESS_OBJECT_TYPE(items->data) == ADDR_ITEM) {
1934 item = ADDRESS_ITEM(items->data);
1936 tab_indent_out(fp, level);
1937 fputs("<item>\n", fp);
1939 tab_indent_out(fp, level + 1);
1940 fputs("<name>", fp);
1941 xml_file_put_escape_str(fp, item->name);
1942 fputs("</name>\n", fp);
1944 tab_indent_out(fp, level + 1);
1945 fputs("<address>", fp);
1946 xml_file_put_escape_str(fp, item->address);
1947 fputs("</address>\n", fp);
1949 tab_indent_out(fp, level + 1);
1950 fputs("<remarks>", fp);
1951 xml_file_put_escape_str(fp, item->remarks);
1952 fputs("</remarks>\n", fp);
1954 tab_indent_out(fp, level);
1955 fputs("</item>\n", fp);
1960 static void tab_indent_out(FILE *fp, guint level)
1964 for (i = 0; i < level; i++)
1968 static void key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
1970 if (event && event->keyval == GDK_Escape)
1971 addressbook_close();
1974 static gint addressbook_list_compare_func(GtkCList *clist,
1978 AddressObject *obj1 = ((GtkCListRow *)ptr1)->data;
1979 AddressObject *obj2 = ((GtkCListRow *)ptr2)->data;
1980 gchar *name1, *name2;
1983 if (obj1->type == ADDR_ITEM)
1984 name1 = ADDRESS_ITEM(obj1)->name;
1985 else if (obj1->type == ADDR_GROUP)
1986 name1 = ADDRESS_GROUP(obj1)->name;
1987 else if (obj1->type == ADDR_FOLDER)
1988 name1 = ADDRESS_FOLDER(obj1)->name;
1995 if (obj2->type == ADDR_ITEM)
1996 name2 = ADDRESS_ITEM(obj2)->name;
1997 else if (obj2->type == ADDR_GROUP)
1998 name2 = ADDRESS_GROUP(obj2)->name;
1999 else if (obj2->type == ADDR_FOLDER)
2000 name2 = ADDRESS_FOLDER(obj2)->name;
2007 return (name2 != NULL);
2011 return strcasecmp(name1, name2);
2014 static gint addressbook_obj_name_compare(gconstpointer a, gconstpointer b)
2016 const AddressObject *obj = a;
2017 const gchar *name = b;
2019 if (!obj || !name) return -1;
2021 if (obj->type == ADDR_GROUP) {
2022 AddressGroup *group = ADDRESS_GROUP(obj);
2026 return strcasecmp(group->name, name);
2027 } else if (obj->type == ADDR_FOLDER) {
2028 AddressFolder *folder = ADDRESS_FOLDER(obj);
2032 return strcasecmp(folder->name, name);
2033 } else if (obj->type == ADDR_ITEM) {
2034 AddressItem *item = ADDRESS_ITEM(obj);
2038 return strcasecmp(item->name, name);
2046 gboolean init; /* if FALSE should init jump buffer */
2047 GtkCTreeNode *node_found; /* match (can be used to backtrack folders) */
2048 AddressObject *addr_found; /* match */
2049 int level; /* current recursion level (0 is root level) */
2050 jmp_buf jumper; /* jump buffer */
2054 FindObject ancestor;
2055 const gchar *groupname;
2059 FindObject ancestor;
2061 const gchar *address;
2065 FindObject ancestor;
2069 typedef gboolean (*ADDRESSBOOK_TRAVERSE_FUNC)(AddressObject *node, gpointer data);
2073 static gboolean traverse_find_group_by_name(AddressObject *ao, FindGroup *find)
2075 AddressFolder *folder;
2076 AddressGroup *group;
2078 /* a group or folder: both are groups */
2079 if (ADDRESS_OBJECT_TYPE(ao) == ADDR_GROUP) {
2080 group = ADDRESS_GROUP(ao);
2081 if (0 == g_strcasecmp(group->name, find->groupname)) {
2085 else if (ADDRESS_OBJECT_TYPE(ao) == ADDR_FOLDER) {
2086 folder = ADDRESS_FOLDER(ao);
2087 if (0 == g_strcasecmp(folder->name, find->groupname)) {
2094 static gboolean traverse_find_name_email(AddressObject *ao, FindAddress *find)
2097 if (ADDRESS_OBJECT_TYPE(ao) == ADDR_ITEM) {
2098 gboolean nmatch = FALSE, amatch = FALSE;
2099 item = ADDRESS_ITEM(ao);
2101 * o only match at the first characters in item strings
2102 * o match either name or address */
2103 if (find->name && item->name) {
2104 nmatch = item->name == strcasestr(item->name, find->name);
2106 if (find->address && item->address) {
2107 amatch = item->address == strcasestr(item->address, find->address);
2109 return nmatch || amatch;
2114 static gboolean traverse_find_all_groups(AddressObject *ao, FindAllGroups *find)
2116 /* NOTE: added strings come from the address book. should perhaps
2117 * strdup() them, especially if the address book is invalidated */
2118 if (ADDRESS_OBJECT_TYPE(ao) == ADDR_FOLDER) {
2119 AddressFolder *folder = ADDRESS_FOLDER(ao);
2120 find->grouplist = g_list_insert_sorted(find->grouplist, (gpointer) folder->name, (GCompareFunc) g_strcasecmp);
2122 else if (ADDRESS_OBJECT_TYPE(ao) == ADDR_GROUP) {
2123 AddressGroup *group = ADDRESS_GROUP(ao);
2124 find->grouplist = g_list_insert_sorted(find->grouplist, (gpointer) group->name, (GCompareFunc) g_strcasecmp);
2129 /* addressbook_traverse() - traverses all address objects stored in the address book.
2130 * for some reason gtkctree's recursive tree functions don't allow a premature return,
2131 * which is what we need if we need to enumerate the tree and check for a condition
2132 * and then skipping other nodes. */
2133 static AddressObject *addressbook_traverse(GtkCTreeNode *node, ADDRESSBOOK_TRAVERSE_FUNC func, FindObject *data, int level)
2135 GtkCTreeNode *current, *tmp;
2138 if (data->init == FALSE) {
2139 /* initialize non-local exit */
2142 /* HANDLE NON-LOCAL EXIT */
2143 if (setjmp(data->jumper)) {
2144 return data->addr_found;
2148 /* actual recursive code */
2150 current = GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list);
2157 tmp = GTK_CTREE_ROW(current)->sibling;
2158 ao = (AddressObject *) gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), current);
2162 next = (ADDRESS_OBJECT_TYPE(ao) == ADDR_FOLDER) ?
2163 g_list_first(((ADDRESS_FOLDER(ao))->items)) :
2164 (ADDRESS_OBJECT_TYPE(ao) == ADDR_GROUP) ?
2165 g_list_first(((ADDRESS_GROUP(ao))->items)) : NULL;
2168 /* NOTE: first iteration of the root calls callback for the tree
2169 * node, other iterations call callback for the address book items */
2170 if (func(ao, data)) {
2172 data->node_found = current;
2173 data->addr_found = ao;
2174 longjmp(data->jumper, 1);
2176 /* ctree node only stores folders and groups. now descend into
2177 * address object data, searching for address items. */
2178 for ( ; next && ADDRESS_OBJECT_TYPE((next->data)) != ADDR_ITEM
2179 ; next = g_list_next(next))
2181 ao = next ? (AddressObject *) next->data : NULL;
2182 next = next ? g_list_next(next) : NULL;
2185 /* check the children (if level permits) */
2186 if (level == -1 || data->level < level) {
2187 current = GTK_CTREE_ROW(current)->children;
2190 addressbook_traverse(current, func, data, level);
2194 /* check the siblings */
2200 static GtkCTreeNode *addressbook_get_group_node(const gchar *name)
2202 FindGroup fg = { { FALSE, NULL, NULL }, NULL };
2203 fg.groupname = name;
2204 addressbook_traverse(NULL, (void *)traverse_find_group_by_name, (FindObject *)&fg, -1);
2205 return fg.ancestor.node_found;
2208 static void addressbook_free_item(AddressItem *item)
2211 if (item->name) g_free(item->name);
2212 if (item->address) g_free(item->address);
2213 if (item->remarks) g_free(item->remarks);
2218 static AddressItem *addressbook_alloc_item(const gchar *name, const gchar *address, const gchar *remarks)
2220 AddressItem *item = g_new0(AddressItem, 1);
2223 item->obj.type = ADDR_ITEM;
2224 if (item->name = g_strdup(name))
2225 if (item->address = g_strdup(address)) {
2227 item->remarks = g_strdup(remarks);
2232 addressbook_free_item(item);
2238 /* public provisional API */
2240 /* addressbook_access() - should be called before using any of the following apis. it
2241 * reloads the address book. */
2242 void addressbook_access(void)
2244 log_message("accessing address book\n");
2245 if (!addrbook.window) {
2246 addressbook_create(FALSE);
2247 addressbook_read_file();
2248 addrbook.open_folder = TRUE;
2249 gtk_ctree_select(GTK_CTREE(addrbook.ctree), GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
2253 /* addressbook_unaccess() - should only be called after changing the address book's
2255 void addressbook_unaccess(void)
2257 log_message("unaccessing address book\n");
2258 addressbook_export_to_file();
2259 invalidate_address_completion();
2262 const gchar *addressbook_get_personal_folder_name(void)
2264 return _("Personal addresses"); /* human readable */
2267 const gchar *addressbook_get_common_folder_name(void)
2269 return _("Common addresses"); /* human readable */
2272 /* addressbook_find_group_by_name() - finds a group (folder or group) by
2274 AddressObject *addressbook_find_group_by_name(const gchar *name)
2276 FindGroup fg = { { FALSE, NULL, NULL } };
2279 /* initialize obj members */
2280 fg.groupname = name;
2281 ao = addressbook_traverse(NULL,
2282 (ADDRESSBOOK_TRAVERSE_FUNC)traverse_find_group_by_name,
2283 (FindObject *)&fg, -1);
2287 /* addressbook_find_contact() - finds an address item by either name or address
2288 * or both. the comparison is done on the first few characters of the strings */
2289 AddressObject *addressbook_find_contact(const gchar *name, const gchar *address)
2291 FindAddress fa = { { FALSE, NULL, NULL } };
2295 fa.address = address;
2296 ao = addressbook_traverse(NULL, (ADDRESSBOOK_TRAVERSE_FUNC)traverse_find_name_email,
2297 (FindObject *)&fa, -1);
2301 /* addressbook_get_group_list() - returns a list of strings with group names (both
2302 * groups and folders). free the list using g_list_free(). note that another
2303 * call may invalidate the returned list */
2304 GList *addressbook_get_group_list(void)
2306 FindAllGroups fag = { { FALSE, NULL, NULL }, NULL };
2307 addressbook_traverse(NULL, (ADDRESSBOOK_TRAVERSE_FUNC)traverse_find_all_groups,
2308 (FindObject *)&fag, -1);
2309 return fag.grouplist;
2312 /* addressbook_add_contact() - adds a contact to the address book. returns 1
2313 * if succesful else error */
2314 gint addressbook_add_contact(const gchar *group, const gchar *name, const gchar *address,
2315 const gchar *remarks)
2319 FindAddress fa = { { FALSE, NULL, NULL } };
2321 /* a healthy mix of hiro's and my code */
2322 if (name == NULL || strlen(name) == 0
2323 || address == NULL || strlen(address) == 0
2324 || group == NULL || strlen(group) == 0) {
2327 node = addressbook_get_group_node(group);
2332 /* check if it's already in this group */
2334 fa.address = address;
2336 if (addressbook_traverse(node, (gpointer)traverse_find_name_email, (gpointer)&fa, 0)) {
2337 log_message("address <%s> already in %s\n", address, group);
2341 item = addressbook_alloc_item(name, address, remarks);
2346 if (!addressbook_add_object(node, (AddressObject *)item)) {
2347 addressbook_free_item(item);
2351 /* make sure it's updated if selected */
2352 log_message("updating addressbook widgets\n");
2353 addrbook.open_folder = TRUE;
2354 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
2356 /* not saved yet. only after unaccessing the address book */
2360 static void group_object_data_destroy(gchar *group)
2375 static void addressbook_destroy_contact(ContactInfo *ci)
2377 g_return_if_fail(ci != NULL);
2378 if (ci->name) g_free(ci->name);
2379 if (ci->address) g_free(ci->address);
2380 if (ci->remarks) g_free(ci->remarks);
2384 static ContactInfo *addressbook_new_contact(const gchar *name, const gchar *address, const gchar *remarks)
2386 ContactInfo *ci = g_new0(ContactInfo, 1);
2388 g_return_val_if_fail(ci != NULL, NULL);
2389 g_return_val_if_fail(address != NULL, NULL); /* address should be valid */
2390 ci->name = name ? g_strdup(name) : NULL;
2391 ci->address = g_strdup(address);
2392 ci->remarks = remarks ? g_strdup(remarks) : NULL;
2393 if (NULL == ci->address) {
2394 addressbook_destroy_contact(ci);
2400 static void addressbook_group_menu_selected(GtkMenuItem *menuitem,
2403 const gchar *group_name = (const gchar *) gtk_object_get_data(GTK_OBJECT(menuitem),
2407 g_warning("%s(%d) - invalid group name\n", __FILE__, __LINE__);
2410 g_return_if_fail(group_name != NULL);
2412 g_message("selected group %s from menu\n", group_name);
2413 g_message("selected %s <%s>\n", data->name ? data->name : data->address, data->address);
2415 addressbook_access();
2416 addressbook_add_contact(group_name, data->name ? data->name : data->address,
2417 data->address, data->remarks ? data->remarks : data->address);
2418 addressbook_unaccess();
2423 /* addressbook_add_contact_by_meny() - launches menu with group items. submenu may be
2424 * the menu item in the parent menu, or NULL for a normal right-click context menu */
2425 gboolean addressbook_add_contact_by_menu(GtkWidget *submenu,
2427 const gchar *address,
2428 const gchar *remarks)
2430 GtkWidget *menu, *menuitem;
2431 GList *groups, *tmp;
2434 ci = addressbook_new_contact(name, address, remarks);
2435 g_return_val_if_fail(ci != NULL, FALSE);
2437 addressbook_access();
2438 groups = addressbook_get_group_list();
2439 g_return_val_if_fail(groups != NULL, (addressbook_destroy_contact(ci), FALSE));
2441 menu = gtk_menu_new();
2442 g_return_val_if_fail(menu != NULL, (g_list_free(groups), addressbook_destroy_contact(ci), FALSE));
2444 /* add groups to menu */
2445 for (tmp = g_list_first(groups); tmp != NULL; tmp = g_list_next(tmp)) {
2446 const gchar *display_name;
2447 gchar *original_name = (gchar *) tmp->data;
2449 if (!g_strcasecmp(original_name, "personal_address")) {
2450 display_name = addressbook_get_personal_folder_name();
2452 else if (!g_strcasecmp(original_name, "common_address")) {
2453 display_name = addressbook_get_common_folder_name();
2456 display_name = original_name;
2459 original_name = g_strdup(original_name);
2460 menuitem = gtk_menu_item_new_with_label(display_name);
2461 /* register the duplicated string pointer as object data,
2462 * so we get the opportunity to free it */
2463 gtk_object_set_data_full(GTK_OBJECT(menuitem), "group_name",
2465 (GtkDestroyNotify) group_object_data_destroy);
2466 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
2467 GTK_SIGNAL_FUNC(addressbook_group_menu_selected),
2469 gtk_menu_append(GTK_MENU(menu), menuitem);
2470 gtk_widget_show(menuitem);
2473 gtk_widget_show(menu);
2476 gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu), menu);
2477 gtk_widget_set_sensitive(GTK_WIDGET(submenu), TRUE);
2480 gtk_widget_grab_focus(GTK_WIDGET(menu));
2481 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME);
2484 if (groups) g_list_free(groups);