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 "addressitem.h"
66 #include "editvcard.h"
70 #include "editjpilot.h"
78 // Interval to check for LDAP search results
79 // #define ADDRESSBOOK_LDAP_TIMER_INTERVAL 100
80 #define ADDRESSBOOK_LDAP_BUSYMSG "Busy"
84 #include "pixmaps/dir-close.xpm"
85 #include "pixmaps/dir-open.xpm"
86 #include "pixmaps/group.xpm"
87 #include "pixmaps/vcard.xpm"
89 #include "pixmaps/jpilot.xpm"
90 #include "pixmaps/category.xpm"
93 #include "pixmaps/ldap.xpm"
96 // XML tag names for top level folders
97 #define ADDRESS_TAG_COMMON "common_address"
98 #define ADDRESS_TAG_PERSONAL "personal_address"
99 #define ADDRESS_TAG_VCARD "vcard_list"
101 #define ADDRESS_TAG_JPILOT "jpilot_list"
104 #define ADDRESS_TAG_LDAP "ldap_list"
112 } AddressBookColumnPos;
115 #define COL_NAME_WIDTH 144
116 #define COL_ADDRESS_WIDTH 156
118 #define COL_FOLDER_WIDTH 170
119 #define ADDRESSBOOK_WIDTH 640
120 #define ADDRESSBOOK_HEIGHT 360
122 #define ADDRESSBOOK_MSGBUF_SIZE 2048
124 static GdkPixmap *folderxpm;
125 static GdkBitmap *folderxpmmask;
126 static GdkPixmap *folderopenxpm;
127 static GdkBitmap *folderopenxpmmask;
128 static GdkPixmap *groupxpm;
129 static GdkBitmap *groupxpmmask;
130 static GdkPixmap *vcardxpm;
131 static GdkBitmap *vcardxpmmask;
133 static GdkPixmap *jpilotxpm;
134 static GdkBitmap *jpilotxpmmask;
135 static GdkPixmap *categoryxpm;
136 static GdkBitmap *categoryxpmmask;
139 static GdkPixmap *ldapxpm;
140 static GdkBitmap *ldapxpmmask;
143 // Pilot library indicator (set at run-time)
144 static _have_pilot_library_;
146 // LDAP library indicator (set at run-time)
147 static _have_ldap_library_;
150 static gchar addressbook_msgbuf[ ADDRESSBOOK_MSGBUF_SIZE ];
152 static AddressBook addrbook;
154 static struct _AddressEdit
157 GtkWidget *name_entry;
158 GtkWidget *addr_entry;
159 GtkWidget *rem_entry;
161 GtkWidget *cancel_btn;
164 static void addressbook_create (gboolean show);
165 static gint addressbook_close (void);
166 static void addressbook_button_set_sensitive (void);
168 /* callback functions */
169 static void addressbook_del_clicked (GtkButton *button,
171 static void addressbook_reg_clicked (GtkButton *button,
173 static void addressbook_to_clicked (GtkButton *button,
175 static void addressbook_lup_clicked (GtkButton *button,
178 static void addressbook_tree_selected (GtkCTree *ctree,
182 static void addressbook_list_selected (GtkCList *clist,
187 static void addressbook_entry_gotfocus (GtkWidget *widget);
190 static void addressbook_entry_changed (GtkWidget *widget);
193 static void addressbook_list_button_pressed (GtkWidget *widget,
194 GdkEventButton *event,
196 static void addressbook_list_button_released (GtkWidget *widget,
197 GdkEventButton *event,
199 static void addressbook_tree_button_pressed (GtkWidget *ctree,
200 GdkEventButton *event,
202 static void addressbook_tree_button_released (GtkWidget *ctree,
203 GdkEventButton *event,
205 static void addressbook_popup_close (GtkMenuShell *menu_shell,
208 static void addressbook_new_folder_cb (gpointer data,
211 static void addressbook_new_group_cb (gpointer data,
214 static void addressbook_edit_folder_cb (gpointer data,
217 static void addressbook_delete_folder_cb (gpointer data,
221 static void addressbook_change_node_name (GtkCTreeNode *node,
223 static void addressbook_edit_group (GtkCTreeNode *group_node);
225 static void addressbook_edit_address_create (gboolean *cancelled);
226 static void edit_address_ok (GtkWidget *widget,
227 gboolean *cancelled);
228 static void edit_address_cancel (GtkWidget *widget,
229 gboolean *cancelled);
230 static gint edit_address_delete_event (GtkWidget *widget,
232 gboolean *cancelled);
233 static void edit_address_key_pressed (GtkWidget *widget,
235 gboolean *cancelled);
236 static AddressItem *addressbook_edit_address (AddressItem *item);
238 static void addressbook_new_address_cb (gpointer data,
241 static void addressbook_edit_address_cb (gpointer data,
244 static void addressbook_delete_address_cb (gpointer data,
248 static void close_cb (gpointer data,
253 static void addressbook_new_vcard_cb ( gpointer data,
259 static void addressbook_new_jpilot_cb ( gpointer data,
266 static void addressbook_new_ldap_cb ( gpointer data,
271 static AddressItem *addressbook_parse_address (const gchar *str);
272 static void addressbook_append_to_compose_entry (AddressItem *item,
273 ComposeEntryType type);
275 static void addressbook_set_clist (AddressObject *obj);
277 static void addressbook_read_file (void);
278 static void addressbook_get_tree (XMLFile *file,
280 const gchar *folder_tag);
281 static void addressbook_add_objs (XMLFile *file,
284 static GtkCTreeNode *addressbook_add_object (GtkCTreeNode *node,
286 static void addressbook_delete_object (AddressObject *obj);
287 static AddressObject *addressbook_find_object_by_name
291 static AddressItem *addressbook_parse_item (XMLFile *file);
292 static void addressbook_xml_recursive_write (GtkCTreeNode *node,
294 static void addressbook_node_write_begin (GtkCTreeNode *node,
296 static void addressbook_node_write_end (GtkCTreeNode *node,
298 static void addressbook_write_items (FILE *fp,
301 static void tab_indent_out (FILE *fp,
304 static void key_pressed (GtkWidget *widget,
307 static gint addressbook_list_compare_func (GtkCList *clist,
310 static gint addressbook_obj_name_compare (gconstpointer a,
313 static AddressVCard *addressbook_parse_vcard ( XMLFile *file );
314 static void addressbook_write_vcard ( FILE *fp,
317 static void addressbook_vcard_show_message ( VCardFile *vcf );
320 static AddressJPilot *addressbook_parse_jpilot ( XMLFile *file );
321 static void addressbook_write_jpilot ( FILE *fp,
322 AddressJPilot *jpilot,
324 static void addressbook_jpilot_show_message ( JPilotFile *jpf );
327 static AddressLDAP *addressbook_parse_ldap ( XMLFile *file );
328 static void addressbook_write_ldap ( FILE *fp,
331 static void addressbook_ldap_show_message ( SyldapServer *server );
334 static GtkItemFactoryEntry addressbook_entries[] =
336 {N_("/_File"), NULL, NULL, 0, "<Branch>"},
337 {N_("/_File/New _Address"), "<alt>N", addressbook_new_address_cb, 0, NULL},
338 {N_("/_File/New _Group"), "<alt>G", addressbook_new_group_cb, 0, NULL},
339 {N_("/_File/New _Folder"), "<alt>R", addressbook_new_folder_cb, 0, NULL},
340 {N_("/_File/New _V-Card"), "<alt>D", addressbook_new_vcard_cb, 0, NULL},
342 {N_("/_File/New _J-Pilot"), "<alt>J", addressbook_new_jpilot_cb, 0, NULL},
345 {N_("/_File/New _Server"), "<alt>S", addressbook_new_ldap_cb, 0, NULL},
347 {N_("/_File/---"), NULL, NULL, 0, "<Separator>"},
348 {N_("/_File/_Edit"), "<alt>Return", addressbook_edit_address_cb, 0, NULL},
349 {N_("/_File/_Delete"), NULL, addressbook_delete_address_cb, 0, NULL},
350 {N_("/_File/---"), NULL, NULL, 0, "<Separator>"},
351 {N_("/_File/_Close"), "<alt>W", close_cb, 0, NULL},
352 {N_("/_Help"), NULL, NULL, 0, "<LastBranch>"},
353 {N_("/_Help/_About"), NULL, about_show, 0, NULL}
356 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
358 {N_("/New _Address"), NULL, addressbook_new_address_cb, 0, NULL},
359 {N_("/New _Group"), NULL, addressbook_new_group_cb, 0, NULL},
360 {N_("/New _Folder"), NULL, addressbook_new_folder_cb, 0, NULL},
361 {N_("/New _V-Card"), NULL, addressbook_new_vcard_cb, 0, NULL},
363 {N_("/New _J-Pilot"), NULL, addressbook_new_jpilot_cb, 0, NULL},
366 {N_("/New _Server"), NULL, addressbook_new_ldap_cb, 0, NULL},
368 {N_("/---"), NULL, NULL, 0, "<Separator>"},
369 {N_("/_Edit"), NULL, addressbook_edit_folder_cb, 0, NULL},
370 {N_("/_Delete"), NULL, addressbook_delete_folder_cb, 0, NULL}
373 static GtkItemFactoryEntry addressbook_list_popup_entries[] =
375 {N_("/New _Address"), NULL, addressbook_new_address_cb, 0, NULL},
376 {N_("/New _Group"), NULL, addressbook_new_group_cb, 0, NULL},
377 {N_("/New _Folder"), NULL, addressbook_new_folder_cb, 0, NULL},
378 {N_("/---"), NULL, NULL, 0, "<Separator>"},
379 {N_("/_Edit"), NULL, addressbook_edit_address_cb, 0, NULL},
380 {N_("/_Delete"), NULL, addressbook_delete_address_cb, 0, NULL}
383 void addressbook_open(Compose *target)
385 if (!addrbook.window) {
386 addressbook_create(TRUE);
387 addressbook_read_file();
388 addrbook.open_folder = TRUE;
389 gtk_ctree_select(GTK_CTREE(addrbook.ctree),
390 GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
392 gtk_widget_hide(addrbook.window);
394 gtk_widget_show_all(addrbook.window);
396 addressbook_set_target_compose(target);
399 void addressbook_set_target_compose(Compose *target)
401 addrbook.target_compose = target;
403 addressbook_button_set_sensitive();
406 Compose *addressbook_get_target_compose(void)
408 return addrbook.target_compose;
411 static void addressbook_create(gboolean show)
417 GtkWidget *ctree_swin;
419 GtkWidget *clist_vbox;
420 GtkWidget *clist_swin;
426 GtkWidget *statusbar;
436 GtkWidget *tree_popup;
437 GtkWidget *list_popup;
438 GtkItemFactory *tree_factory;
439 GtkItemFactory *list_factory;
440 GtkItemFactory *menu_factory;
443 gchar *titles[N_COLS] = {_("Name"), _("E-Mail address"), _("Remarks")};
447 debug_print("Creating addressbook window...\n");
449 // Global flag if we have library installed (at run-time)
450 _have_pilot_library_ = FALSE;
451 _have_ldap_library_ = FALSE;
453 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
454 gtk_window_set_title(GTK_WINDOW(window), _("Address book"));
455 gtk_widget_set_usize(window, ADDRESSBOOK_WIDTH, ADDRESSBOOK_HEIGHT);
456 //gtk_container_set_border_width(GTK_CONTAINER(window), BORDER_WIDTH);
457 gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
458 gtk_widget_realize(window);
460 gtk_signal_connect(GTK_OBJECT(window), "delete_event",
461 GTK_SIGNAL_FUNC(addressbook_close), NULL);
462 gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
463 GTK_SIGNAL_FUNC(key_pressed), NULL);
464 gtk_signal_connect(GTK_OBJECT(window), "focus_in_event",
465 GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
466 gtk_signal_connect(GTK_OBJECT(window), "focus_out_event",
467 GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
469 vbox = gtk_vbox_new(FALSE, 0);
470 gtk_container_add(GTK_CONTAINER(window), vbox);
472 n_entries = sizeof(addressbook_entries) /
473 sizeof(addressbook_entries[0]);
474 menubar = menubar_create(window, addressbook_entries, n_entries,
475 "<AddressBook>", NULL);
476 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
477 menu_factory = gtk_item_factory_from_widget(menubar);
479 vbox2 = gtk_vbox_new(FALSE, 4);
480 gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
481 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
483 ctree_swin = gtk_scrolled_window_new(NULL, NULL);
484 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ctree_swin),
485 GTK_POLICY_AUTOMATIC,
487 gtk_widget_set_usize(ctree_swin, COL_FOLDER_WIDTH + 40, -1);
489 ctree = gtk_ctree_new(1, 0);
490 gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
491 gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
492 gtk_clist_set_column_width(GTK_CLIST(ctree), 0, COL_FOLDER_WIDTH);
493 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
494 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
495 GTK_CTREE_EXPANDER_SQUARE);
496 gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
497 gtk_clist_set_compare_func(GTK_CLIST(ctree),
498 addressbook_list_compare_func);
500 gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
501 GTK_SIGNAL_FUNC(addressbook_tree_selected), NULL);
502 gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
503 GTK_SIGNAL_FUNC(addressbook_tree_button_pressed),
505 gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
506 GTK_SIGNAL_FUNC(addressbook_tree_button_released),
509 clist_vbox = gtk_vbox_new(FALSE, 4);
511 clist_swin = gtk_scrolled_window_new(NULL, NULL);
512 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
513 GTK_POLICY_AUTOMATIC,
515 gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
517 clist = gtk_clist_new_with_titles(N_COLS, titles);
518 gtk_container_add(GTK_CONTAINER(clist_swin), clist);
519 gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_EXTENDED);
520 gtk_clist_set_column_width(GTK_CLIST(clist), COL_NAME,
522 gtk_clist_set_column_width(GTK_CLIST(clist), COL_ADDRESS,
524 gtk_clist_set_compare_func(GTK_CLIST(clist),
525 addressbook_list_compare_func);
527 for (i = 0; i < N_COLS; i++)
528 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button,
531 gtk_signal_connect(GTK_OBJECT(clist), "select_row",
532 GTK_SIGNAL_FUNC(addressbook_list_selected), NULL);
533 gtk_signal_connect(GTK_OBJECT(clist), "button_press_event",
534 GTK_SIGNAL_FUNC(addressbook_list_button_pressed),
536 gtk_signal_connect(GTK_OBJECT(clist), "button_release_event",
537 GTK_SIGNAL_FUNC(addressbook_list_button_released),
540 hbox = gtk_hbox_new(FALSE, 4);
541 gtk_box_pack_start(GTK_BOX(clist_vbox), hbox, FALSE, FALSE, 0);
543 label = gtk_label_new(_("Name:"));
544 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
546 entry = gtk_entry_new();
547 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
549 address_completion_register_entry(GTK_ENTRY(entry));
550 gtk_signal_connect(GTK_OBJECT(entry), "focus_in_event",
551 GTK_SIGNAL_FUNC(addressbook_entry_gotfocus), NULL);
554 gtk_signal_connect(GTK_OBJECT(entry), "changed",
555 GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
558 paned = gtk_hpaned_new();
559 gtk_box_pack_start(GTK_BOX(vbox2), paned, TRUE, TRUE, 0);
560 gtk_paned_add1(GTK_PANED(paned), ctree_swin);
561 gtk_paned_add2(GTK_PANED(paned), clist_vbox);
564 hsbox = gtk_hbox_new(FALSE, 0);
565 gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
566 statusbar = gtk_statusbar_new();
567 gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
570 hbbox = gtk_hbutton_box_new();
571 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
572 gtk_button_box_set_spacing(GTK_BUTTON_BOX(hbbox), 2);
573 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
575 del_btn = gtk_button_new_with_label(_("Delete"));
576 GTK_WIDGET_SET_FLAGS(del_btn, GTK_CAN_DEFAULT);
577 gtk_box_pack_start(GTK_BOX(hbbox), del_btn, TRUE, TRUE, 0);
578 reg_btn = gtk_button_new_with_label(_("Add"));
579 GTK_WIDGET_SET_FLAGS(reg_btn, GTK_CAN_DEFAULT);
580 gtk_box_pack_start(GTK_BOX(hbbox), reg_btn, TRUE, TRUE, 0);
581 lup_btn = gtk_button_new_with_label(_("Lookup"));
582 GTK_WIDGET_SET_FLAGS(lup_btn, GTK_CAN_DEFAULT);
583 gtk_box_pack_start(GTK_BOX(hbbox), lup_btn, TRUE, TRUE, 0);
585 gtk_signal_connect(GTK_OBJECT(del_btn), "clicked",
586 GTK_SIGNAL_FUNC(addressbook_del_clicked), NULL);
587 gtk_signal_connect(GTK_OBJECT(reg_btn), "clicked",
588 GTK_SIGNAL_FUNC(addressbook_reg_clicked), NULL);
589 gtk_signal_connect(GTK_OBJECT(lup_btn), "clicked",
590 GTK_SIGNAL_FUNC(addressbook_lup_clicked), NULL);
592 to_btn = gtk_button_new_with_label
593 (prefs_common.trans_hdr ? _("To:") : "To:");
594 GTK_WIDGET_SET_FLAGS(to_btn, GTK_CAN_DEFAULT);
595 gtk_box_pack_start(GTK_BOX(hbbox), to_btn, TRUE, TRUE, 0);
596 cc_btn = gtk_button_new_with_label
597 (prefs_common.trans_hdr ? _("Cc:") : "Cc:");
598 GTK_WIDGET_SET_FLAGS(cc_btn, GTK_CAN_DEFAULT);
599 gtk_box_pack_start(GTK_BOX(hbbox), cc_btn, TRUE, TRUE, 0);
600 bcc_btn = gtk_button_new_with_label
601 (prefs_common.trans_hdr ? _("Bcc:") : "Bcc:");
602 GTK_WIDGET_SET_FLAGS(bcc_btn, GTK_CAN_DEFAULT);
603 gtk_box_pack_start(GTK_BOX(hbbox), bcc_btn, TRUE, TRUE, 0);
605 gtk_signal_connect(GTK_OBJECT(to_btn), "clicked",
606 GTK_SIGNAL_FUNC(addressbook_to_clicked),
607 GINT_TO_POINTER(COMPOSE_TO));
608 gtk_signal_connect(GTK_OBJECT(cc_btn), "clicked",
609 GTK_SIGNAL_FUNC(addressbook_to_clicked),
610 GINT_TO_POINTER(COMPOSE_CC));
611 gtk_signal_connect(GTK_OBJECT(bcc_btn), "clicked",
612 GTK_SIGNAL_FUNC(addressbook_to_clicked),
613 GINT_TO_POINTER(COMPOSE_BCC));
615 PIXMAP_CREATE(window, folderxpm, folderxpmmask, DIRECTORY_CLOSE_XPM);
616 PIXMAP_CREATE(window, folderopenxpm, folderopenxpmmask,
618 PIXMAP_CREATE(window, groupxpm, groupxpmmask, group_xpm);
619 PIXMAP_CREATE(window, vcardxpm, vcardxpmmask, vcard_xpm);
621 PIXMAP_CREATE(window, jpilotxpm, jpilotxpmmask, jpilot_xpm);
622 PIXMAP_CREATE(window, categoryxpm, categoryxpmmask, category_xpm);
625 PIXMAP_CREATE(window, ldapxpm, ldapxpmmask, ldap_xpm);
628 text = _("Common address");
630 gtk_ctree_insert_node(GTK_CTREE(ctree),
631 NULL, NULL, &text, FOLDER_SPACING,
632 folderxpm, folderxpmmask,
633 folderopenxpm, folderopenxpmmask,
635 text = _("Personal address");
637 gtk_ctree_insert_node(GTK_CTREE(ctree),
638 NULL, NULL, &text, FOLDER_SPACING,
639 folderxpm, folderxpmmask,
640 folderopenxpm, folderopenxpmmask,
645 gtk_ctree_insert_node(GTK_CTREE(ctree),
646 NULL, NULL, &text, FOLDER_SPACING,
647 folderxpm, folderxpmmask,
648 folderopenxpm, folderopenxpmmask,
654 gtk_ctree_insert_node(GTK_CTREE(ctree),
655 NULL, NULL, &text, FOLDER_SPACING,
656 folderxpm, folderxpmmask,
657 folderopenxpm, folderopenxpmmask,
659 if( jpilot_test_pilot_lib() ) {
660 _have_pilot_library_ = TRUE;
661 menu_set_sensitive( menu_factory, "/File/New J-Pilot", TRUE );
664 menu_set_sensitive( menu_factory, "/File/New J-Pilot", FALSE );
669 text = _("Directory");
671 gtk_ctree_insert_node(GTK_CTREE(ctree),
672 NULL, NULL, &text, FOLDER_SPACING,
673 folderxpm, folderxpmmask,
674 folderopenxpm, folderopenxpmmask,
676 if( syldap_test_ldap_lib() ) {
677 _have_ldap_library_ = TRUE;
678 menu_set_sensitive( menu_factory, "/File/New Server", TRUE );
681 menu_set_sensitive( menu_factory, "/File/New Server", FALSE );
686 n_entries = sizeof(addressbook_tree_popup_entries) /
687 sizeof(addressbook_tree_popup_entries[0]);
688 tree_popup = menu_create_items(addressbook_tree_popup_entries,
690 "<AddressBookTree>", &tree_factory,
692 gtk_signal_connect(GTK_OBJECT(tree_popup), "selection_done",
693 GTK_SIGNAL_FUNC(addressbook_popup_close), NULL);
694 n_entries = sizeof(addressbook_list_popup_entries) /
695 sizeof(addressbook_list_popup_entries[0]);
696 list_popup = menu_create_items(addressbook_list_popup_entries,
698 "<AddressBookList>", &list_factory,
701 addrbook.window = window;
702 addrbook.menubar = menubar;
703 addrbook.ctree = ctree;
704 addrbook.clist = clist;
705 addrbook.entry = entry;
706 addrbook.statusbar = statusbar;
707 addrbook.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Addressbook Window" );
709 addrbook.del_btn = del_btn;
710 addrbook.reg_btn = reg_btn;
711 addrbook.lup_btn = lup_btn;
712 addrbook.to_btn = to_btn;
713 addrbook.cc_btn = cc_btn;
714 addrbook.bcc_btn = bcc_btn;
716 addrbook.tree_popup = tree_popup;
717 addrbook.list_popup = list_popup;
718 addrbook.tree_factory = tree_factory;
719 addrbook.list_factory = list_factory;
720 addrbook.menu_factory = menu_factory;
722 address_completion_start(window);
725 gtk_widget_show_all(window);
728 static gint addressbook_close(void)
730 gtk_widget_hide(addrbook.window);
731 addressbook_export_to_file();
732 /* tell addr_compl that there's a new addressbook file */
733 invalidate_address_completion();
737 static void addressbook_status_show( gchar *msg ) {
738 if( addrbook.statusbar != NULL ) {
739 gtk_statusbar_pop( GTK_STATUSBAR(addrbook.statusbar), addrbook.status_cid );
741 gtk_statusbar_push( GTK_STATUSBAR(addrbook.statusbar), addrbook.status_cid, msg );
746 static void addressbook_button_set_sensitive(void)
748 gboolean to_sens = FALSE;
749 gboolean cc_sens = FALSE;
750 gboolean bcc_sens = FALSE;
752 if (!addrbook.window) return;
754 if (addrbook.target_compose) {
757 if (addrbook.target_compose->use_bcc)
761 gtk_widget_set_sensitive(addrbook.to_btn, to_sens);
762 gtk_widget_set_sensitive(addrbook.cc_btn, cc_sens);
763 gtk_widget_set_sensitive(addrbook.bcc_btn, bcc_sens);
766 static void addressbook_del_clicked(GtkButton *button, gpointer data)
768 GtkCList *clist = GTK_CLIST(addrbook.clist);
769 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
770 AddressObject *pobj, *obj;
775 if (!clist->selection) {
776 addressbook_delete_folder_cb(NULL, 0, NULL);
780 pobj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
781 g_return_if_fail(pobj != NULL);
783 if (alertpanel(_("Delete address(es)"),
784 _("Really delete the address(es)?"),
785 _("Yes"), _("No"), NULL) != G_ALERTDEFAULT)
788 for (cur = clist->selection; cur != NULL; cur = next) {
790 row = GPOINTER_TO_INT(cur->data);
793 obj = gtk_clist_get_row_data(clist, row);
796 if (pobj->type == ADDR_GROUP) {
797 AddressGroup *group = ADDRESS_GROUP(pobj);
798 group->items = g_list_remove(group->items, obj);
799 } else if (pobj->type == ADDR_FOLDER) {
800 AddressFolder *folder = ADDRESS_FOLDER(pobj);
802 folder->items = g_list_remove(folder->items, obj);
803 if (obj->type == ADDR_GROUP) {
806 else if (obj->type == ADDR_VCARD) {
809 else if (obj->type == ADDR_JPILOT) {
812 else if (obj->type == ADDR_LDAP) {
818 node = gtk_ctree_find_by_row_data
819 (ctree, addrbook.opened, obj);
820 if (node) gtk_ctree_remove_node(ctree, node);
825 addressbook_delete_object(obj);
827 gtk_clist_remove(clist, row);
831 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
833 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
834 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
839 if (*gtk_entry_get_text(entry) == '\0') {
840 addressbook_new_address_cb(NULL, 0, NULL);
843 if (!addrbook.opened) return;
845 obj = gtk_ctree_node_get_row_data(ctree, addrbook.opened);
848 g_return_if_fail(obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER);
850 str = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
852 item = addressbook_parse_address(str);
855 if (addressbook_find_object_by_name
856 (addrbook.opened, item->name) != NULL) {
857 addressbook_delete_object(ADDRESS_OBJECT(item));
859 } else if (addressbook_edit_address(item) == NULL) {
860 addressbook_delete_object(ADDRESS_OBJECT(item));
866 item = addressbook_edit_address(NULL);
870 if (addressbook_find_object_by_name(addrbook.opened, item->name)) {
871 addressbook_delete_object(ADDRESS_OBJECT(item));
875 addressbook_add_object(addrbook.opened, ADDRESS_OBJECT(item));
876 addrbook.open_folder = TRUE;
877 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
880 static AddressItem *addressbook_parse_address(const gchar *str)
883 gchar *address = NULL;
888 Xalloca(buf, strlen(str) + 1, return NULL);
892 if (*buf == '\0') return NULL;
894 if ((start = strchr(buf, '<'))) {
899 name = g_strdup(buf);
902 if ((end = strchr(start, '>'))) {
906 address = g_strdup(start);
909 name = g_strdup(buf);
911 if (!name && !address) return NULL;
913 item = mgu_create_address();
914 ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
916 item->address = address;
917 item->remarks = NULL;
922 static void addressbook_to_clicked(GtkButton *button, gpointer data)
924 GtkCList *clist = GTK_CLIST(addrbook.clist);
927 if (!addrbook.target_compose) return;
929 for (cur = clist->selection; cur != NULL; cur = cur->next) {
932 obj = gtk_clist_get_row_data(clist,
933 GPOINTER_TO_INT(cur->data));
936 if (obj->type == ADDR_ITEM) {
937 addressbook_append_to_compose_entry
938 (ADDRESS_ITEM(obj), (ComposeEntryType)data);
939 } else if (obj->type == ADDR_GROUP) {
943 group = ADDRESS_GROUP(obj);
944 for (cur_item = group->items; cur_item != NULL;
945 cur_item = cur_item->next) {
946 if (ADDRESS_OBJECT(cur_item->data)->type
949 addressbook_append_to_compose_entry
950 (ADDRESS_ITEM(cur_item->data),
951 (ComposeEntryType)data);
957 static void addressbook_append_to_compose_entry(AddressItem *item,
958 ComposeEntryType type)
960 Compose *compose = addrbook.target_compose;
962 if (item->name && item->address) {
965 buf = g_strdup_printf
966 ("%s <%s>", item->name, item->address);
967 compose_entry_append(compose, buf, type);
969 } else if (item->address)
970 compose_entry_append(compose, item->address, type);
973 static void addressbook_menubar_set_sensitive( gboolean sensitive ) {
974 menu_set_sensitive( addrbook.menu_factory, "/File/New Address", sensitive );
975 menu_set_sensitive( addrbook.menu_factory, "/File/New Group", sensitive );
976 menu_set_sensitive( addrbook.menu_factory, "/File/New Folder", sensitive );
977 menu_set_sensitive( addrbook.menu_factory, "/File/New V-Card", sensitive );
979 menu_set_sensitive( addrbook.menu_factory, "/File/New J-Pilot", sensitive );
982 menu_set_sensitive( addrbook.menu_factory, "/File/New Server", sensitive );
984 gtk_widget_set_sensitive( addrbook.reg_btn, sensitive );
985 gtk_widget_set_sensitive( addrbook.del_btn, sensitive );
988 static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode *node ) {
989 gboolean canEdit = TRUE;
990 if( obj->type == ADDR_FOLDER ) {
991 if( node == addrbook.common ) {
994 if( node == addrbook.personal ) {
997 if( node == addrbook.vcard ) {
999 menu_set_sensitive( addrbook.menu_factory, "/File/New V-Card", TRUE );
1002 else if( node == addrbook.jpilot ) {
1004 if( _have_pilot_library_ ) {
1005 menu_set_sensitive( addrbook.menu_factory, "/File/New J-Pilot", TRUE );
1010 else if( node == addrbook.ldap ) {
1012 if( _have_ldap_library_ ) {
1013 menu_set_sensitive( addrbook.menu_factory, "/File/New Server", TRUE );
1018 menu_set_sensitive( addrbook.menu_factory, "/File/New Address", TRUE );
1019 menu_set_sensitive( addrbook.menu_factory, "/File/New Group", TRUE );
1020 menu_set_sensitive( addrbook.menu_factory, "/File/New Folder", TRUE );
1021 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1022 gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1025 else if( obj->type == ADDR_GROUP ) {
1026 menu_set_sensitive( addrbook.menu_factory, "/File/New Address", TRUE );
1027 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1028 gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1031 else if( obj->type == ADDR_JPILOT ) {
1032 if( ! _have_pilot_library_ ) canEdit = FALSE;
1034 else if( obj->type == ADDR_CATEGORY ) {
1039 else if( obj->type == ADDR_LDAP ) {
1040 if( ! _have_ldap_library_ ) canEdit = FALSE;
1043 menu_set_sensitive( addrbook.menu_factory, "/File/Edit", canEdit );
1044 menu_set_sensitive( addrbook.menu_factory, "/File/Delete", canEdit );
1047 static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
1048 gint column, gpointer data)
1052 addrbook.selected = node;
1053 addrbook.open_folder = FALSE;
1054 addressbook_status_show( "" );
1055 if( addrbook.entry != NULL ) {
1056 gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
1059 obj = gtk_ctree_node_get_row_data(ctree, node);
1060 if( obj == NULL ) return;
1062 addrbook.opened = node;
1064 if( obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER ||
1065 obj->type == ADDR_VCARD || obj->type == ADDR_JPILOT ||
1066 obj->type == ADDR_CATEGORY || obj->type == ADDR_LDAP ) {
1067 addressbook_set_clist(obj);
1070 if( obj->type == ADDR_VCARD ) {
1073 vcf = ADDRESS_VCARD(obj)->cardFile;
1074 vcard_read_data( vcf );
1075 addressbook_vcard_show_message( vcf );
1076 ADDRESS_VCARD(obj)->items = vcard_get_address_list( vcf );
1077 addressbook_set_clist( obj );
1080 else if( obj->type == ADDR_JPILOT ) {
1081 if( _have_pilot_library_ ) {
1084 GList *catList, *catNode;
1085 AddressCategory *acat;
1086 GtkCTreeNode *childNode, *nextNode;
1087 GtkCTreeRow *currRow;
1089 jpf = ADDRESS_JPILOT(obj)->pilotFile;
1090 addressbook_jpilot_show_message( jpf );
1091 if( jpilot_get_modified( jpf ) ) {
1092 jpilot_read_data( jpf );
1093 catList = jpilot_get_category_items( jpf );
1095 // Remove existing categories
1096 currRow = GTK_CTREE_ROW( node );
1098 while( nextNode = currRow->children ) {
1099 gtk_ctree_remove_node( ctree, nextNode );
1103 // Load new categories into the tree.
1106 AddressItem *item = catNode->data;
1107 acat = g_new(AddressCategory, 1);
1108 ADDRESS_OBJECT_TYPE(acat) = ADDR_CATEGORY;
1109 acat->name = g_strdup( item->name );
1111 acat->pilotFile = jpf;
1112 acat->category = item;
1113 catNode = g_list_next( catNode );
1114 addressbook_add_object(node, ADDRESS_OBJECT(acat));
1117 ADDRESS_JPILOT(obj)->items = catList;
1119 addressbook_set_clist( obj );
1122 else if( obj->type == ADDR_CATEGORY ) {
1123 if( _have_pilot_library_ ) {
1127 jpf = ADDRESS_JPILOT(obj)->pilotFile;
1128 if( jpilot_get_modified( jpf ) ) {
1129 // Force parent to be reloaded
1130 gtk_ctree_select( GTK_CTREE(addrbook.ctree), GTK_CTREE_ROW(node)->parent);
1131 gtk_ctree_expand( GTK_CTREE(addrbook.ctree), GTK_CTREE_ROW(node)->parent);
1134 AddressItem *item = NULL;
1135 AddressCategory *acat = ADDRESS_CATEGORY(obj);
1136 if( acat ) item = acat->category;
1138 ADDRESS_CATEGORY(obj)->items =
1139 jpilot_get_address_list_cat( jpf, item->categoryID );
1141 addressbook_set_clist( obj );
1147 else if( obj->type == ADDR_LDAP ) {
1148 if( _have_ldap_library_ ) {
1150 SyldapServer *server;
1151 server = ADDRESS_LDAP(obj)->ldapServer;
1152 addressbook_ldap_show_message( server );
1153 if( ! server->busyFlag ) {
1154 ADDRESS_LDAP(obj)->items = syldap_get_address_list( server );
1155 addressbook_set_clist( obj );
1161 // Setup main menu selections
1162 addressbook_menubar_set_sensitive( FALSE );
1163 addressbook_menuitem_set_sensitive( obj, node );
1166 static void addressbook_list_selected(GtkCList *clist, gint row, gint column,
1167 GdkEvent *event, gpointer data)
1169 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
1173 if (event && event->type == GDK_2BUTTON_PRESS) {
1174 if (prefs_common.add_address_by_click &&
1175 addrbook.target_compose)
1176 addressbook_to_clicked(NULL, NULL);
1178 addressbook_edit_address_cb(NULL, 0, NULL);
1183 gtk_signal_handler_block_by_func
1185 GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
1188 gtk_entry_set_text(entry, "");
1190 for (cur = clist->selection; cur != NULL; cur = cur->next) {
1191 obj = gtk_clist_get_row_data(clist,
1192 GPOINTER_TO_INT(cur->data));
1193 g_return_if_fail(obj != NULL);
1195 if (obj->type == ADDR_ITEM) {
1198 item = ADDRESS_ITEM(obj);
1199 if (item->name && item->address) {
1202 buf = g_strdup_printf
1203 ("%s <%s>", item->name, item->address);
1204 if (*gtk_entry_get_text(entry) != '\0')
1205 gtk_entry_append_text(entry, ", ");
1206 gtk_entry_append_text(entry, buf);
1208 } else if (item->address) {
1209 if (*gtk_entry_get_text(entry) != '\0')
1210 gtk_entry_append_text(entry, ", ");
1211 gtk_entry_append_text(entry, item->address);
1217 gtk_signal_handler_unblock_by_func
1219 GTK_SIGNAL_FUNC(addressbook_entry_changed), NULL);
1224 static void addressbook_entry_changed(GtkWidget *widget)
1226 GtkCList *clist = GTK_CLIST(addrbook.clist);
1227 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
1232 //if (clist->selection && clist->selection->next) return;
1234 str = gtk_entry_get_text(entry);
1236 gtk_clist_unselect_all(clist);
1241 for (row = 0; row < clist->rows; row++) {
1245 obj = ADDRESS_OBJECT(gtk_clist_get_row_data(clist, row));
1247 if (obj->type == ADDR_ITEM)
1248 name = ADDRESS_ITEM(obj)->name;
1249 else if (obj->type == ADDR_GROUP)
1250 name = ADDRESS_GROUP(obj)->name;
1254 if (name && !strncasecmp(name, str, len)) {
1255 gtk_clist_unselect_all(clist);
1256 gtk_clist_select_row(clist, row, -1);
1261 gtk_clist_unselect_all(clist);
1265 static void addressbook_entry_gotfocus( GtkWidget *widget ) {
1266 gtk_editable_select_region( GTK_EDITABLE(addrbook.entry), 0, -1 );
1269 static void addressbook_list_button_pressed(GtkWidget *widget,
1270 GdkEventButton *event,
1273 GtkCList *clist = GTK_CLIST(widget);
1276 AddressObject *obj, *pobj;
1280 obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
1282 g_return_if_fail(obj != NULL);
1284 pobj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), addrbook.selected);
1286 if( pobj->type == ADDR_VCARD ||
1287 pobj->type == ADDR_JPILOT ||
1288 pobj->type == ADDR_CATEGORY ||
1289 pobj->type == ADDR_LDAP ) {
1290 menu_set_sensitive(addrbook.menu_factory, "/File/Edit", FALSE);
1291 menu_set_sensitive(addrbook.menu_factory, "/File/Delete", FALSE);
1295 if (event->button != 3) return;
1296 menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.list_popup));
1298 if (gtk_clist_get_selection_info
1299 (clist, event->x, event->y, &row, &column)) {
1300 GtkCListRow *clist_row;
1302 clist_row = g_list_nth(clist->row_list, row)->data;
1303 if (clist_row->state != GTK_STATE_SELECTED) {
1304 gtk_clist_unselect_all(clist);
1305 gtk_clist_select_row(clist, row, column);
1307 gtkut_clist_set_focus_row(clist, row);
1309 if( obj->type != ADDR_VCARD &&
1310 obj->type != ADDR_JPILOT &&
1311 obj->type != ADDR_CATEGORY &&
1312 obj->type != ADDR_LDAP ) {
1313 menu_set_sensitive(addrbook.list_factory, "/Edit", TRUE);
1314 menu_set_sensitive(addrbook.list_factory, "/Delete", TRUE);
1318 if( !( addrbook.opened == addrbook.vcard ||
1319 addrbook.opened == addrbook.jpilot ||
1320 addrbook.opened == addrbook.ldap ) ) {
1322 if( obj->type == ADDR_FOLDER || obj->type == ADDR_GROUP ) {
1323 menu_set_sensitive(addrbook.list_factory, "/New Address", TRUE);
1324 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1325 gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1327 if (obj->type == ADDR_FOLDER) {
1328 menu_set_sensitive(addrbook.list_factory, "/New Folder", TRUE);
1329 menu_set_sensitive(addrbook.list_factory, "/New Group", TRUE);
1332 gtk_menu_popup(GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
1333 event->button, event->time);
1336 static void addressbook_list_button_released(GtkWidget *widget,
1337 GdkEventButton *event,
1342 static void addressbook_tree_button_pressed(GtkWidget *ctree,
1343 GdkEventButton *event,
1346 GtkCList *clist = GTK_CLIST(ctree);
1352 if (event->button == 1) {
1353 addrbook.open_folder = TRUE;
1356 if (event->button != 3) return;
1358 if (!gtk_clist_get_selection_info
1359 (clist, event->x, event->y, &row, &column)) return;
1360 gtk_clist_select_row(clist, row, column);
1362 obj = gtk_clist_get_row_data(clist, row);
1363 g_return_if_fail(obj != NULL);
1365 menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
1367 if (obj->type == ADDR_FOLDER) {
1368 node = gtk_ctree_node_nth(GTK_CTREE(ctree), row);
1369 if( node == addrbook.vcard ) {
1370 menu_set_sensitive(addrbook.tree_factory, "/New V-Card", TRUE);
1373 else if( node == addrbook.jpilot ) {
1374 if( _have_pilot_library_ ) {
1375 menu_set_sensitive(addrbook.tree_factory, "/New J-Pilot", TRUE);
1380 else if( node == addrbook.ldap ) {
1381 if( _have_ldap_library_ ) {
1382 menu_set_sensitive(addrbook.tree_factory, "/New Server", TRUE);
1387 menu_set_sensitive(addrbook.tree_factory, "/New Address", TRUE);
1388 menu_set_sensitive(addrbook.tree_factory, "/New Folder", TRUE);
1389 menu_set_sensitive(addrbook.tree_factory, "/New Group", TRUE);
1390 if (node && GTK_CTREE_ROW(node)->level >= 2) {
1391 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1392 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1394 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1395 gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1398 else if (obj->type == ADDR_GROUP) {
1399 menu_set_sensitive(addrbook.tree_factory, "/New Address", TRUE);
1400 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1401 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1402 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1403 gtk_widget_set_sensitive( addrbook.del_btn, TRUE );
1405 else if (obj->type == ADDR_VCARD) {
1406 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1407 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1410 else if (obj->type == ADDR_JPILOT) {
1411 if( _have_pilot_library_ ) {
1412 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1413 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1416 else if (obj->type == ADDR_CATEGORY) {
1417 if( _have_pilot_library_ ) {
1418 menu_set_sensitive(addrbook.tree_factory, "/Edit", FALSE);
1419 menu_set_sensitive(addrbook.tree_factory, "/Delete", FALSE);
1424 else if (obj->type == ADDR_LDAP) {
1425 if( _have_ldap_library_ ) {
1426 menu_set_sensitive(addrbook.tree_factory, "/Edit", TRUE);
1427 menu_set_sensitive(addrbook.tree_factory, "/Delete", TRUE);
1434 gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
1435 event->button, event->time);
1438 static void addressbook_tree_button_released(GtkWidget *ctree,
1439 GdkEventButton *event,
1442 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1443 gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree), addrbook.opened);
1446 static void addressbook_popup_close(GtkMenuShell *menu_shell, gpointer data)
1448 if (!addrbook.opened) return;
1450 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1451 gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree),
1455 static void addressbook_new_folder_cb(gpointer data, guint action,
1458 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1460 AddressFolder *folder;
1463 if (!addrbook.selected) return;
1465 obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1466 g_return_if_fail(obj != NULL);
1467 if (obj->type != ADDR_FOLDER) return;
1469 new_folder = input_dialog(_("New folder"),
1470 _("Input the name of new folder:"),
1472 if (!new_folder) return;
1473 g_strstrip(new_folder);
1474 if (*new_folder == '\0') {
1479 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1481 addressbook_obj_name_compare)) {
1482 alertpanel_error(_("The name already exists."));
1487 folder = g_new(AddressFolder, 1);
1488 ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
1489 folder->name = g_strdup(new_folder);
1490 folder->items = NULL;
1492 addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(folder));
1496 if (addrbook.selected == addrbook.opened)
1497 addressbook_set_clist(obj);
1500 static void addressbook_new_group_cb(gpointer data, guint action,
1503 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1505 AddressGroup *group;
1508 if (!addrbook.selected) return;
1510 obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1511 g_return_if_fail(obj != NULL);
1512 if (obj->type != ADDR_FOLDER) return;
1514 new_group = input_dialog(_("New group"),
1515 _("Input the name of new group:"),
1517 if (!new_group) return;
1518 g_strstrip(new_group);
1519 if (*new_group == '\0') {
1524 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1526 addressbook_obj_name_compare)) {
1527 alertpanel_error(_("The name already exists."));
1532 group = g_new(AddressGroup, 1);
1533 ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
1534 group->name = g_strdup(new_group);
1535 group->items = NULL;
1537 addressbook_add_object(addrbook.selected, ADDRESS_OBJECT(group));
1541 if (addrbook.selected == addrbook.opened)
1542 addressbook_set_clist(obj);
1545 static void addressbook_change_node_name(GtkCTreeNode *node, const gchar *name)
1547 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1550 GdkPixmap *pix_cl, *pix_op;
1551 GdkBitmap *mask_cl, *mask_op;
1552 gboolean is_leaf, expanded;
1554 gtk_ctree_get_node_info(ctree, node, text, &spacing,
1555 &pix_cl, &mask_cl, &pix_op, &mask_op,
1556 &is_leaf, &expanded);
1557 gtk_ctree_set_node_info(ctree, node, name, spacing,
1558 pix_cl, mask_cl, pix_op, mask_op,
1562 static void addressbook_edit_group(GtkCTreeNode *group_node)
1564 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1565 GtkCList *clist = GTK_CLIST(addrbook.clist);
1567 AddressGroup *group;
1571 if (!group_node && clist->selection) {
1572 obj = gtk_clist_get_row_data(clist,
1573 GPOINTER_TO_INT(clist->selection->data));
1574 g_return_if_fail(obj != NULL);
1575 if (obj->type != ADDR_GROUP) return;
1576 node = gtk_ctree_find_by_row_data
1577 (ctree, addrbook.selected, obj);
1583 node = addrbook.selected;
1584 obj = gtk_ctree_node_get_row_data(ctree, node);
1585 g_return_if_fail(obj != NULL);
1586 if (obj->type != ADDR_GROUP) return;
1589 group = ADDRESS_GROUP(obj);
1591 new_name = input_dialog(_("Edit group"),
1592 _("Input the new name of group:"),
1594 if (!new_name) return;
1595 g_strstrip(new_name);
1596 if (*new_name == '\0') {
1601 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1603 addressbook_obj_name_compare)) {
1604 alertpanel_error(_("The name already exists."));
1609 g_free(group->name);
1610 group->name = g_strdup(new_name);
1612 addressbook_change_node_name(node, new_name);
1613 gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(node)->parent);
1617 addrbook.open_folder = TRUE;
1618 gtk_ctree_select(ctree, addrbook.opened);
1621 static void addressbook_edit_folder_cb(gpointer data, guint action,
1624 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1626 AddressFolder *folder;
1627 gchar *new_name = NULL;
1628 GtkCTreeNode *node = NULL, *parentNode = NULL;
1630 if (!addrbook.selected) return;
1631 if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1633 obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1634 g_return_if_fail(obj != NULL);
1635 g_return_if_fail(obj->type == ADDR_FOLDER || obj->type == ADDR_GROUP ||
1636 obj->type == ADDR_VCARD || obj->type == ADDR_JPILOT ||
1637 obj->type == ADDR_CATEGORY || obj->type == ADDR_LDAP );
1639 if (obj->type == ADDR_GROUP) {
1640 addressbook_edit_group(addrbook.selected);
1644 if( obj->type == ADDR_VCARD ) {
1645 AddressVCard *vcard = ADDRESS_VCARD(obj);
1646 if( addressbook_edit_vcard( vcard ) == NULL ) return;
1647 new_name = vcard->name;
1648 parentNode = addrbook.vcard;
1651 else if( obj->type == ADDR_JPILOT ) {
1652 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
1653 if( ! _have_pilot_library_ ) return;
1654 if( addressbook_edit_jpilot( jpilot ) == NULL ) return;
1655 new_name = jpilot->name;
1656 parentNode = addrbook.jpilot;
1660 else if( obj->type == ADDR_LDAP ) {
1661 AddressLDAP *ldapi = ADDRESS_LDAP(obj);
1662 if( ! _have_ldap_library_ ) return;
1663 if( addressbook_edit_ldap( ldapi ) == NULL ) return;
1664 new_name = ldapi->name;
1665 parentNode = addrbook.ldap;
1669 if( new_name && parentNode) {
1670 // Update node in tree view
1671 node = gtk_ctree_find_by_row_data( ctree, addrbook.selected, obj );
1672 if( ! node ) return;
1673 addressbook_change_node_name( node, new_name );
1674 gtk_ctree_sort_node(ctree, parentNode);
1675 addrbook.open_folder = TRUE;
1676 gtk_ctree_select( GTK_CTREE(addrbook.ctree), node );
1680 folder = ADDRESS_FOLDER(obj);
1681 new_name = input_dialog(_("Edit folder"),
1682 _("Input the new name of folder:"),
1685 if (!new_name) return;
1686 g_strstrip(new_name);
1687 if (*new_name == '\0') {
1692 if (gtk_ctree_find_by_row_data_custom(ctree, addrbook.selected,
1694 addressbook_obj_name_compare)) {
1695 alertpanel_error(_("The name already exists."));
1700 g_free(folder->name);
1701 folder->name = g_strdup(new_name);
1703 addressbook_change_node_name(addrbook.selected, new_name);
1704 gtk_ctree_sort_node(ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1709 static void addressbook_delete_folder_cb(gpointer data, guint action,
1712 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1713 AddressObject *obj, *pobj;
1718 if (!addrbook.selected) return;
1719 if (GTK_CTREE_ROW(addrbook.selected)->level == 1) return;
1721 obj = gtk_ctree_node_get_row_data(ctree, addrbook.selected);
1722 g_return_if_fail(obj != NULL);
1724 if (obj->type == ADDR_GROUP)
1725 name = ADDRESS_GROUP(obj)->name;
1726 else if (obj->type == ADDR_FOLDER)
1727 name = ADDRESS_FOLDER(obj)->name;
1728 else if (obj->type == ADDR_VCARD)
1729 name = ADDRESS_VCARD(obj)->name;
1731 else if (obj->type == ADDR_JPILOT) {
1732 if( ! _have_pilot_library_ ) return;
1733 name = ADDRESS_JPILOT(obj)->name;
1737 else if (obj->type == ADDR_LDAP) {
1738 if( ! _have_ldap_library_ ) return;
1739 name = ADDRESS_LDAP(obj)->name;
1745 message = g_strdup_printf(_("Really delete `%s' ?"), name);
1746 aval = alertpanel(_("Delete"), message, _("Yes"), _("No"), NULL);
1748 if (aval != G_ALERTDEFAULT) return;
1750 pobj = gtk_ctree_node_get_row_data
1751 (ctree, GTK_CTREE_ROW(addrbook.selected)->parent);
1753 g_return_if_fail(pobj->type == ADDR_FOLDER);
1754 ADDRESS_FOLDER(pobj)->items =
1755 g_list_remove(ADDRESS_FOLDER(pobj)->items, obj);
1757 addressbook_delete_object(obj);
1758 addrbook.open_folder = TRUE;
1759 gtk_ctree_remove_node(ctree, addrbook.selected);
1760 addrbook.open_folder = FALSE;
1763 #define SET_LABEL_AND_ENTRY(str, entry, top) \
1765 label = gtk_label_new(str); \
1766 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
1767 GTK_FILL, 0, 0, 0); \
1768 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
1770 entry = gtk_entry_new(); \
1771 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
1772 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
1775 static void addressbook_edit_address_create(gboolean *cancelled)
1781 GtkWidget *name_entry;
1782 GtkWidget *addr_entry;
1783 GtkWidget *rem_entry;
1786 GtkWidget *cancel_btn;
1788 debug_print("Creating edit_address window...\n");
1790 window = gtk_window_new(GTK_WINDOW_DIALOG);
1791 gtk_widget_set_usize(window, 400, -1);
1792 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
1793 gtk_window_set_title(GTK_WINDOW(window), _("Edit address"));
1794 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
1795 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1796 gtk_signal_connect(GTK_OBJECT(window), "delete_event",
1797 GTK_SIGNAL_FUNC(edit_address_delete_event),
1799 gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
1800 GTK_SIGNAL_FUNC(edit_address_key_pressed),
1803 vbox = gtk_vbox_new(FALSE, 8);
1804 gtk_container_add(GTK_CONTAINER(window), vbox);
1806 table = gtk_table_new(3, 2, FALSE);
1807 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
1808 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
1809 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
1811 SET_LABEL_AND_ENTRY(_("Name"), name_entry, 0);
1812 SET_LABEL_AND_ENTRY(_("Address"), addr_entry, 1);
1813 SET_LABEL_AND_ENTRY(_("Remarks"), rem_entry, 2);
1815 gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
1816 &cancel_btn, _("Cancel"), NULL, NULL);
1817 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
1818 gtk_widget_grab_default(ok_btn);
1820 gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
1821 GTK_SIGNAL_FUNC(edit_address_ok), cancelled);
1822 gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
1823 GTK_SIGNAL_FUNC(edit_address_cancel), cancelled);
1825 gtk_widget_show_all(vbox);
1827 addredit.window = window;
1828 addredit.name_entry = name_entry;
1829 addredit.addr_entry = addr_entry;
1830 addredit.rem_entry = rem_entry;
1831 addredit.ok_btn = ok_btn;
1832 addredit.cancel_btn = cancel_btn;
1835 static void edit_address_ok(GtkWidget *widget, gboolean *cancelled)
1841 static void edit_address_cancel(GtkWidget *widget, gboolean *cancelled)
1847 static gint edit_address_delete_event(GtkWidget *widget, GdkEventAny *event,
1848 gboolean *cancelled)
1856 static void edit_address_key_pressed(GtkWidget *widget, GdkEventKey *event,
1857 gboolean *cancelled)
1859 if (event && event->keyval == GDK_Escape) {
1865 static AddressItem *addressbook_edit_address(AddressItem *item)
1867 static gboolean cancelled;
1870 if (!addredit.window)
1871 addressbook_edit_address_create(&cancelled);
1872 gtk_widget_grab_focus(addredit.ok_btn);
1873 gtk_widget_grab_focus(addredit.name_entry);
1874 gtk_widget_show(addredit.window);
1875 manage_window_set_transient(GTK_WINDOW(addredit.window));
1877 gtk_entry_set_text(GTK_ENTRY(addredit.name_entry), "");
1878 gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry), "");
1879 gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry), "");
1883 gtk_entry_set_text(GTK_ENTRY(addredit.name_entry),
1886 gtk_entry_set_text(GTK_ENTRY(addredit.addr_entry),
1889 gtk_entry_set_text(GTK_ENTRY(addredit.rem_entry),
1894 gtk_widget_hide(addredit.window);
1895 if (cancelled == TRUE) return NULL;
1897 str = gtk_entry_get_text(GTK_ENTRY(addredit.name_entry));
1898 if (*str == '\0') return NULL;
1901 item = mgu_create_address();
1902 ADDRESS_OBJECT_TYPE(item) = ADDR_ITEM;
1906 item->name = g_strdup(str);
1908 str = gtk_entry_get_text(GTK_ENTRY(addredit.addr_entry));
1909 g_free(item->address);
1911 item->address = NULL;
1913 item->address = g_strdup(str);
1915 str = gtk_entry_get_text(GTK_ENTRY(addredit.rem_entry));
1916 g_free(item->remarks);
1918 item->remarks = NULL;
1920 item->remarks = g_strdup(str);
1925 static void addressbook_new_address_cb(gpointer data, guint action,
1930 item = addressbook_edit_address(NULL);
1933 addressbook_add_object(addrbook.selected,
1934 ADDRESS_OBJECT(item));
1935 if (addrbook.selected == addrbook.opened) {
1936 addrbook.open_folder = TRUE;
1937 gtk_ctree_select(GTK_CTREE(addrbook.ctree),
1943 static void addressbook_edit_address_cb(gpointer data, guint action,
1946 GtkCList *clist = GTK_CLIST(addrbook.clist);
1948 AddressObject *obj, *pobj;
1949 GtkCTreeNode *node = NULL, *parentNode = NULL;
1952 if (!clist->selection) {
1953 addressbook_edit_folder_cb(NULL, 0, NULL);
1957 obj = gtk_clist_get_row_data(clist, GPOINTER_TO_INT(clist->selection->data));
1958 g_return_if_fail(obj != NULL);
1960 pobj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), addrbook.selected);
1962 if (obj->type == ADDR_ITEM) {
1963 AddressItem *item = ADDRESS_ITEM(obj);
1966 // Prevent edit of readonly items
1967 if( pobj->type == ADDR_VCARD ||
1968 pobj->type == ADDR_JPILOT ||
1969 pobj->type == ADDR_CATEGORY ||
1970 pobj->type == ADDR_LDAP ) return;
1973 if (addressbook_edit_address(item) == NULL) return;
1975 addrbook.open_folder = TRUE;
1976 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
1979 else if (obj->type == ADDR_GROUP) {
1980 addressbook_edit_group(NULL);
1983 else if( obj->type == ADDR_VCARD ) {
1984 AddressVCard *vcard = ADDRESS_VCARD(obj);
1985 if( addressbook_edit_vcard( vcard ) == NULL ) return;
1986 nodeName = vcard->name;
1987 parentNode = addrbook.vcard;
1990 else if( obj->type == ADDR_JPILOT ) {
1991 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
1992 if( addressbook_edit_jpilot( jpilot ) == NULL ) return;
1993 nodeName = jpilot->name;
1994 parentNode = addrbook.jpilot;
1998 else if( obj->type == ADDR_LDAP ) {
1999 AddressLDAP *ldapi = ADDRESS_LDAP(obj);
2000 if( addressbook_edit_ldap( ldapi ) == NULL ) return;
2001 nodeName = ldapi->name;
2002 parentNode = addrbook.ldap;
2009 // Update tree node with node name
2010 ctree = GTK_CTREE( addrbook.ctree );
2011 node = gtk_ctree_find_by_row_data( ctree, addrbook.selected, obj );
2012 if( ! node ) return;
2013 addressbook_change_node_name( node, nodeName );
2014 gtk_ctree_sort_node(ctree, parentNode );
2015 addrbook.open_folder = TRUE;
2016 gtk_ctree_select( ctree, addrbook.opened );
2019 static void addressbook_delete_address_cb(gpointer data, guint action,
2022 addressbook_del_clicked(NULL, NULL);
2025 static void close_cb(gpointer data, guint action, GtkWidget *widget)
2027 addressbook_close();
2030 static void addressbook_set_clist(AddressObject *obj)
2032 GtkCList *clist = GTK_CLIST(addrbook.clist);
2034 gchar *text[N_COLS];
2037 gtk_clist_clear(clist);
2041 gtk_clist_freeze(clist);
2042 gtk_clist_clear(clist);
2044 if (obj->type == ADDR_GROUP)
2045 items = ADDRESS_GROUP(obj)->items;
2046 else if (obj->type == ADDR_FOLDER) {
2047 items = ADDRESS_FOLDER(obj)->items;
2049 else if (obj->type == ADDR_VCARD) {
2050 items = ADDRESS_VCARD(obj)->items;
2053 else if (obj->type == ADDR_JPILOT) {
2054 items = ADDRESS_JPILOT(obj)->items;
2056 else if (obj->type == ADDR_CATEGORY) {
2057 items = ADDRESS_CATEGORY(obj)->items;
2061 else if (obj->type == ADDR_LDAP) {
2062 items = ADDRESS_LDAP(obj)->items;
2066 gtk_clist_thaw(clist);
2070 for (; items != NULL; items = items->next) {
2071 AddressObject *iobj;
2073 iobj = ADDRESS_OBJECT(items->data);
2074 if( iobj == NULL ) continue;
2076 if (iobj->type == ADDR_GROUP) {
2077 AddressGroup *group;
2079 group = ADDRESS_GROUP(iobj);
2080 text[COL_NAME] = group->name;
2081 text[COL_ADDRESS] = NULL;
2082 text[COL_REMARKS] = NULL;
2083 row = gtk_clist_append(clist, text);
2084 gtk_clist_set_pixtext(clist, row, COL_NAME,
2086 groupxpm, groupxpmmask);
2087 gtk_clist_set_row_data(clist, row, iobj);
2088 } if (iobj->type == ADDR_VCARD) {
2089 AddressVCard *vcard;
2091 vcard = ADDRESS_VCARD(iobj);
2092 text[COL_NAME] = vcard->name;
2093 text[COL_ADDRESS] = NULL;
2094 text[COL_REMARKS] = NULL;
2095 row = gtk_clist_append(clist, text);
2096 gtk_clist_set_pixtext(clist, row, COL_NAME,
2098 vcardxpm, vcardxpmmask);
2099 gtk_clist_set_row_data(clist, row, iobj);
2101 } if (iobj->type == ADDR_JPILOT) {
2102 AddressJPilot *jpilot;
2104 jpilot = ADDRESS_JPILOT(iobj);
2105 text[COL_NAME] = jpilot->name;
2106 text[COL_ADDRESS] = NULL;
2107 text[COL_REMARKS] = NULL;
2108 row = gtk_clist_append(clist, text);
2109 gtk_clist_set_pixtext(clist, row, COL_NAME,
2111 jpilotxpm, jpilotxpmmask);
2112 gtk_clist_set_row_data(clist, row, iobj);
2113 } if (iobj->type == ADDR_CATEGORY) {
2114 AddressCategory *category;
2116 category = ADDRESS_CATEGORY(iobj);
2117 text[COL_NAME] = category->name;
2118 text[COL_ADDRESS] = NULL;
2119 text[COL_REMARKS] = NULL;
2120 row = gtk_clist_append(clist, text);
2121 gtk_clist_set_pixtext(clist, row, COL_NAME,
2123 categoryxpm, categoryxpmmask);
2124 gtk_clist_set_row_data(clist, row, iobj);
2127 } if (iobj->type == ADDR_LDAP) {
2130 ldapi = ADDRESS_LDAP(iobj);
2131 text[COL_NAME] = ldapi->name;
2132 text[COL_ADDRESS] = NULL;
2133 text[COL_REMARKS] = NULL;
2134 row = gtk_clist_append(clist, text);
2135 gtk_clist_set_pixtext(clist, row, COL_NAME,
2137 ldapxpm, ldapxpmmask);
2138 gtk_clist_set_row_data(clist, row, iobj);
2140 } else if (iobj->type == ADDR_ITEM) {
2143 item = ADDRESS_ITEM(iobj);
2144 text[COL_NAME] = item->name;
2145 text[COL_ADDRESS] = item->address;
2146 text[COL_REMARKS] = item->remarks;
2147 row = gtk_clist_append(clist, text);
2148 gtk_clist_set_row_data(clist, row, iobj);
2152 gtk_clist_sort(clist);
2153 gtk_clist_thaw(clist);
2156 static void addressbook_read_file(void)
2161 debug_print(_("Reading addressbook file..."));
2163 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
2164 if ((file = xml_open_file(path)) == NULL) {
2165 debug_print(_("%s doesn't exist.\n"), path);
2167 addressbook_get_tree(NULL, addrbook.common, ADDRESS_TAG_COMMON);
2168 addressbook_get_tree(NULL, addrbook.personal, ADDRESS_TAG_PERSONAL);
2169 addressbook_get_tree(NULL, addrbook.vcard, ADDRESS_TAG_VCARD);
2171 addressbook_get_tree(NULL, addrbook.jpilot, ADDRESS_TAG_JPILOT);
2174 addressbook_get_tree(NULL, addrbook.ldap, ADDRESS_TAG_LDAP);
2182 if (xml_parse_next_tag(file) < 0 ||
2183 xml_compare_tag(file, "addressbook") == FALSE) {
2184 g_warning("Invalid addressbook data\n");
2185 xml_close_file(file);
2189 addressbook_get_tree(file, addrbook.common, ADDRESS_TAG_COMMON);
2190 addressbook_get_tree(file, addrbook.personal, ADDRESS_TAG_PERSONAL);
2191 addressbook_get_tree(file, addrbook.vcard, ADDRESS_TAG_VCARD);
2193 addressbook_get_tree(file, addrbook.jpilot, ADDRESS_TAG_JPILOT);
2196 addressbook_get_tree(file, addrbook.ldap, ADDRESS_TAG_LDAP);
2199 xml_close_file(file);
2201 debug_print(_("done.\n"));
2204 static void addressbook_get_tree(XMLFile *file, GtkCTreeNode *node,
2205 const gchar *folder_tag)
2207 AddressFolder *folder;
2209 g_return_if_fail(node != NULL);
2211 folder = g_new(AddressFolder, 1);
2212 ADDRESS_OBJECT(folder)->type = ADDR_FOLDER;
2213 folder->name = g_strdup(folder_tag);
2214 folder->items = NULL;
2215 gtk_ctree_node_set_row_data(GTK_CTREE(addrbook.ctree), node, folder);
2218 if (xml_parse_next_tag(file) < 0 ||
2219 xml_compare_tag(file, folder_tag) == FALSE) {
2220 g_warning("Invalid addressbook data\n");
2225 if (file) addressbook_add_objs(file, node);
2228 static void addressbook_add_objs(XMLFile *file, GtkCTreeNode *node)
2232 GtkCTreeNode *new_node;
2235 prev_level = file->level;
2236 if (xml_parse_next_tag(file) < 0) return;
2237 if (file->level < prev_level) return;
2239 if (xml_compare_tag(file, "group")) {
2240 AddressGroup *group;
2242 group = g_new(AddressGroup, 1);
2243 ADDRESS_OBJECT_TYPE(group) = ADDR_GROUP;
2244 attr = xml_get_current_tag_attr(file);
2246 group->name = g_strdup(((XMLAttr *)attr->data)->value);
2249 group->items = NULL;
2251 new_node = addressbook_add_object
2252 (node, ADDRESS_OBJECT(group));
2254 addressbook_add_objs(file, new_node);
2255 } else if (xml_compare_tag(file, "folder")) {
2256 AddressFolder *folder;
2258 folder = g_new(AddressFolder, 1);
2259 ADDRESS_OBJECT_TYPE(folder) = ADDR_FOLDER;
2260 attr = xml_get_current_tag_attr(file);
2262 folder->name = g_strdup(((XMLAttr *)attr->data)->value);
2264 folder->name = NULL;
2265 folder->items = NULL;
2267 new_node = addressbook_add_object
2268 (node, ADDRESS_OBJECT(folder));
2270 addressbook_add_objs(file, new_node);
2272 else if( xml_compare_tag( file, "vcard" ) ) {
2273 AddressVCard *vcard;
2274 vcard = addressbook_parse_vcard( file );
2275 if( ! vcard ) return;
2276 new_node = addressbook_add_object
2277 (node, ADDRESS_OBJECT(vcard));
2280 else if( xml_compare_tag( file, "jpilot" ) ) {
2281 AddressJPilot *jpilot;
2282 jpilot = addressbook_parse_jpilot( file );
2283 if( ! jpilot ) return;
2284 new_node = addressbook_add_object
2285 (node, ADDRESS_OBJECT(jpilot));
2289 else if( xml_compare_tag( file, "server" ) ) {
2291 ldapi = addressbook_parse_ldap( file );
2292 if( ! ldapi ) return;
2293 new_node = addressbook_add_object
2294 (node, ADDRESS_OBJECT(ldapi));
2297 else if (xml_compare_tag(file, "item")) {
2300 item = addressbook_parse_item(file);
2302 new_node = addressbook_add_object
2303 (node, ADDRESS_OBJECT(item));
2305 g_warning("Invalid tag\n");
2311 static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
2314 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2315 GtkCTreeNode *added;
2316 AddressObject *pobj;
2318 g_return_val_if_fail(node != NULL, NULL);
2319 g_return_val_if_fail(obj != NULL, NULL);
2321 pobj = gtk_ctree_node_get_row_data(ctree, node);
2322 g_return_val_if_fail(pobj != NULL, NULL);
2324 if (pobj->type == ADDR_ITEM) {
2325 g_warning("Parent object mustn't be an item.\n");
2328 if (pobj->type == ADDR_FOLDER &&
2329 (obj->type == ADDR_GROUP || obj->type == ADDR_FOLDER))
2330 gtk_ctree_expand(ctree, node);
2332 if (pobj->type == ADDR_FOLDER && obj->type == ADDR_VCARD )
2333 gtk_ctree_expand(ctree, node);
2335 if (pobj->type == ADDR_FOLDER && obj->type == ADDR_JPILOT )
2336 gtk_ctree_expand(ctree, node);
2338 if (pobj->type == ADDR_FOLDER && obj->type == ADDR_LDAP )
2339 gtk_ctree_expand(ctree, node);
2341 if (obj->type == ADDR_GROUP) {
2342 AddressGroup *group = ADDRESS_GROUP(obj);
2344 if (pobj->type != ADDR_FOLDER) {
2345 g_warning("Group can't be added in another group.\n");
2349 added = gtk_ctree_insert_node(ctree, node, NULL,
2350 &group->name, FOLDER_SPACING,
2351 groupxpm, groupxpmmask,
2352 groupxpm, groupxpmmask,
2354 gtk_ctree_node_set_row_data(ctree, added, obj);
2355 } else if (obj->type == ADDR_FOLDER) {
2356 AddressFolder *folder = ADDRESS_FOLDER(obj);
2358 if (pobj->type != ADDR_FOLDER) {
2359 g_warning("Group can't contain folder.\n");
2363 added = gtk_ctree_insert_node(ctree, node, NULL,
2364 &folder->name, FOLDER_SPACING,
2365 folderxpm, folderxpmmask,
2366 folderopenxpm, folderopenxpmmask,
2368 gtk_ctree_node_set_row_data(ctree, added, obj);
2371 else if (obj->type == ADDR_VCARD) {
2372 AddressVCard *vcard = ADDRESS_VCARD(obj);
2373 added = gtk_ctree_insert_node(ctree, node, NULL,
2374 &vcard->name, FOLDER_SPACING,
2375 vcardxpm, vcardxpmmask,
2376 vcardxpm, vcardxpmmask,
2378 gtk_ctree_node_set_row_data(ctree, added, obj);
2381 else if (obj->type == ADDR_JPILOT) {
2382 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
2383 added = gtk_ctree_insert_node(ctree, node, NULL,
2384 &jpilot->name, FOLDER_SPACING,
2385 jpilotxpm, jpilotxpmmask,
2386 jpilotxpm, jpilotxpmmask,
2388 gtk_ctree_node_set_row_data(ctree, added, obj);
2390 else if (obj->type == ADDR_CATEGORY) {
2391 AddressCategory *category = ADDRESS_CATEGORY(obj);
2392 added = gtk_ctree_insert_node(ctree, node, NULL,
2393 &category->name, FOLDER_SPACING,
2394 categoryxpm, categoryxpmmask,
2395 categoryxpm, categoryxpmmask,
2397 gtk_ctree_node_set_row_data(ctree, added, obj);
2401 else if (obj->type == ADDR_LDAP) {
2402 AddressLDAP *server = ADDRESS_LDAP(obj);
2403 added = gtk_ctree_insert_node(ctree, node, NULL,
2404 &server->name, FOLDER_SPACING,
2405 ldapxpm, ldapxpmmask,
2406 ldapxpm, ldapxpmmask,
2408 gtk_ctree_node_set_row_data(ctree, added, obj);
2415 if (obj->type == ADDR_GROUP || obj->type == ADDR_ITEM) {
2416 if (pobj->type == ADDR_GROUP) {
2417 AddressGroup *group = ADDRESS_GROUP(pobj);
2419 group->items = g_list_append(group->items, obj);
2420 } else if (pobj->type == ADDR_FOLDER) {
2421 AddressFolder *folder = ADDRESS_FOLDER(pobj);
2423 folder->items = g_list_append(folder->items, obj);
2427 if (pobj->type == ADDR_FOLDER) {
2428 if (obj->type == ADDR_VCARD || obj->type == ADDR_JPILOT || obj->type == ADDR_LDAP) {
2429 AddressFolder *folder = ADDRESS_FOLDER(pobj);
2430 folder->items = g_list_append(folder->items, obj);
2434 gtk_ctree_sort_node(ctree, node);
2439 static void addressbook_delete_object(AddressObject *obj)
2443 if (obj->type == ADDR_ITEM) {
2444 AddressItem *item = ADDRESS_ITEM(obj);
2446 mgu_free_address( item );
2447 } else if (obj->type == ADDR_GROUP) {
2448 AddressGroup *group = ADDRESS_GROUP(obj);
2450 g_free(group->name);
2451 while (group->items != NULL) {
2452 addressbook_delete_object
2453 (ADDRESS_OBJECT(group->items->data));
2454 group->items = g_list_remove(group->items,
2455 group->items->data);
2458 } else if (obj->type == ADDR_FOLDER) {
2459 AddressFolder *folder = ADDRESS_FOLDER(obj);
2461 g_free(folder->name);
2462 while (folder->items != NULL) {
2463 addressbook_delete_object
2464 (ADDRESS_OBJECT(folder->items->data));
2465 folder->items = g_list_remove(folder->items,
2466 folder->items->data);
2470 else if( obj->type == ADDR_VCARD ) {
2471 AddressVCard *vcard = ADDRESS_VCARD(obj);
2472 g_free( vcard->name );
2473 vcard_free( vcard->cardFile );
2474 vcard->cardFile = NULL;
2475 vcard->items = NULL;
2479 else if( obj->type == ADDR_JPILOT ) {
2480 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
2481 g_free( jpilot->name );
2482 jpilot_free( jpilot->pilotFile );
2483 jpilot->pilotFile = NULL;
2484 jpilot->items = NULL;
2489 else if( obj->type == ADDR_LDAP ) {
2490 AddressLDAP *ldapi = ADDRESS_LDAP(obj);
2491 g_free( ldapi->name );
2492 syldap_free( ldapi->ldapServer );
2493 ldapi->ldapServer = NULL;
2494 ldapi->items = NULL;
2500 static AddressObject *addressbook_find_object_by_name(GtkCTreeNode *node,
2503 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2507 g_return_val_if_fail(node != NULL, NULL);
2509 obj = gtk_ctree_node_get_row_data(ctree, node);
2510 g_return_val_if_fail(obj != NULL, NULL);
2512 if (obj->type == ADDR_GROUP) {
2513 AddressGroup *group = ADDRESS_GROUP(obj);
2515 found = g_list_find_custom(group->items, (gpointer)name,
2516 addressbook_obj_name_compare);
2517 if (found) return ADDRESS_OBJECT(found->data);
2518 } else if (obj->type == ADDR_FOLDER) {
2519 AddressFolder *folder = ADDRESS_FOLDER(obj);
2521 found = g_list_find_custom(folder->items, (gpointer)name,
2522 addressbook_obj_name_compare);
2523 if (found) return ADDRESS_OBJECT(found->data);
2524 } else if (obj->type == ADDR_ITEM) {
2525 if (!addressbook_obj_name_compare(obj, name)) return obj;
2531 static AddressItem *addressbook_parse_item(XMLFile *file)
2537 item = mgu_create_address();
2538 ADDRESS_OBJECT(item)->type = ADDR_ITEM;
2540 level = file->level;
2542 while (xml_parse_next_tag(file) == 0) {
2543 if (file->level < level) return item;
2544 if (file->level == level) break;
2546 element = xml_get_element(file);
2548 if (xml_compare_tag(file, "name")) {
2549 item->name = element;
2550 } else if (xml_compare_tag(file, "address")) {
2551 item->address = element;
2552 } else if (xml_compare_tag(file, "remarks")) {
2553 item->remarks = element;
2556 if (xml_parse_next_tag(file) < 0) break;
2557 if (file->level != level) break;
2560 g_warning("addressbook_parse_item(): Parse error\n");
2561 mgu_free_address( item );
2564 void addressbook_export_to_file(void)
2569 if (!addrbook.ctree) return;
2571 debug_print(_("Exporting addressbook to file..."));
2573 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESS_BOOK, NULL);
2574 if ((pfile = prefs_write_open(path)) == NULL) {
2580 fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
2581 conv_get_current_charset_str());
2582 fputs("<addressbook>\n\n", pfile->fp);
2584 addressbook_xml_recursive_write(NULL, pfile->fp);
2586 fputs("</addressbook>\n", pfile->fp);
2588 if (prefs_write_close(pfile) < 0) {
2589 g_warning(_("failed to write addressbook data.\n"));
2593 debug_print(_("done.\n"));
2596 /* Most part of this function was taken from gtk_ctree_pre_recursive() and
2597 gtk_ctree_post_recursive(). */
2598 static void addressbook_xml_recursive_write(GtkCTreeNode *node, FILE *fp)
2604 work = GTK_CTREE_ROW(node)->children;
2605 addressbook_node_write_begin(node, fp);
2607 work = GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list);
2610 tmp = GTK_CTREE_ROW(work)->sibling;
2611 addressbook_xml_recursive_write(work, fp);
2616 addressbook_node_write_end(node, fp);
2619 static void addressbook_node_write_begin(GtkCTreeNode *node, FILE *fp)
2623 obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
2624 g_return_if_fail(obj != NULL);
2626 if (obj->type == ADDR_FOLDER) {
2627 AddressFolder *folder = ADDRESS_FOLDER(obj);
2629 if (GTK_CTREE_ROW(node)->level == 1) {
2630 fprintf(fp, "<%s>\n", folder->name);
2632 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
2633 fputs("<folder name=\"", fp);
2634 xml_file_put_escape_str(fp, folder->name);
2637 } else if (obj->type == ADDR_GROUP) {
2638 AddressGroup *group = ADDRESS_GROUP(obj);
2640 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
2641 fputs("<group name=\"", fp);
2642 xml_file_put_escape_str(fp, group->name);
2647 static void addressbook_node_write_end(GtkCTreeNode *node, FILE *fp)
2651 obj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node);
2652 g_return_if_fail(obj != NULL);
2654 if (obj->type == ADDR_FOLDER) {
2655 AddressFolder *folder = ADDRESS_FOLDER(obj);
2657 addressbook_write_items(fp, folder->items,
2658 GTK_CTREE_ROW(node)->level);
2660 if (GTK_CTREE_ROW(node)->level == 1) {
2661 fprintf(fp, "</%s>\n\n", folder->name);
2663 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
2664 fputs("</folder>\n", fp);
2666 } else if (obj->type == ADDR_GROUP) {
2667 AddressGroup *group = ADDRESS_GROUP(obj);
2669 addressbook_write_items(fp, group->items,
2670 GTK_CTREE_ROW(node)->level);
2672 tab_indent_out(fp, GTK_CTREE_ROW(node)->level - 1);
2673 fputs("</group>\n", fp);
2675 else if (obj->type == ADDR_VCARD) {
2676 AddressVCard *vcard = ADDRESS_VCARD(obj);
2677 addressbook_write_vcard( fp, vcard, GTK_CTREE_ROW(node)->level);
2680 else if (obj->type == ADDR_JPILOT) {
2681 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
2682 addressbook_write_jpilot( fp, jpilot, GTK_CTREE_ROW(node)->level);
2686 else if (obj->type == ADDR_LDAP) {
2687 AddressLDAP *ldap = ADDRESS_LDAP(obj);
2688 addressbook_write_ldap( fp, ldap, GTK_CTREE_ROW(node)->level);
2693 static void addressbook_write_items(FILE *fp, GList *items, guint level)
2697 for (; items != NULL; items = items->next) {
2698 if (ADDRESS_OBJECT_TYPE(items->data) == ADDR_ITEM) {
2699 item = ADDRESS_ITEM(items->data);
2701 tab_indent_out(fp, level);
2702 fputs("<item>\n", fp);
2704 tab_indent_out(fp, level + 1);
2705 fputs("<name>", fp);
2706 xml_file_put_escape_str(fp, item->name);
2707 fputs("</name>\n", fp);
2709 tab_indent_out(fp, level + 1);
2710 fputs("<address>", fp);
2711 xml_file_put_escape_str(fp, item->address);
2712 fputs("</address>\n", fp);
2714 tab_indent_out(fp, level + 1);
2715 fputs("<remarks>", fp);
2716 xml_file_put_escape_str(fp, item->remarks);
2717 fputs("</remarks>\n", fp);
2719 tab_indent_out(fp, level);
2720 fputs("</item>\n", fp);
2725 static void tab_indent_out(FILE *fp, guint level)
2729 for (i = 0; i < level; i++)
2733 static void key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
2735 if (event && event->keyval == GDK_Escape)
2736 addressbook_close();
2739 static gint addressbook_list_compare_func(GtkCList *clist,
2743 AddressObject *obj1 = ((GtkCListRow *)ptr1)->data;
2744 AddressObject *obj2 = ((GtkCListRow *)ptr2)->data;
2745 gchar *name1, *name2;
2748 if (obj1->type == ADDR_ITEM)
2749 name1 = ADDRESS_ITEM(obj1)->name;
2750 else if (obj1->type == ADDR_GROUP)
2751 name1 = ADDRESS_GROUP(obj1)->name;
2752 else if (obj1->type == ADDR_FOLDER)
2753 name1 = ADDRESS_FOLDER(obj1)->name;
2754 else if (obj1->type == ADDR_VCARD)
2755 name1 = ADDRESS_VCARD(obj1)->name;
2757 else if (obj1->type == ADDR_JPILOT)
2758 name1 = ADDRESS_JPILOT(obj1)->name;
2759 else if (obj1->type == ADDR_CATEGORY)
2760 name1 = ADDRESS_CATEGORY(obj1)->name;
2763 else if (obj1->type == ADDR_LDAP)
2764 name1 = ADDRESS_LDAP(obj1)->name;
2772 if (obj2->type == ADDR_ITEM)
2773 name2 = ADDRESS_ITEM(obj2)->name;
2774 else if (obj2->type == ADDR_GROUP)
2775 name2 = ADDRESS_GROUP(obj2)->name;
2776 else if (obj2->type == ADDR_FOLDER)
2777 name2 = ADDRESS_FOLDER(obj2)->name;
2778 else if (obj2->type == ADDR_VCARD)
2779 name2 = ADDRESS_VCARD(obj2)->name;
2781 else if (obj2->type == ADDR_JPILOT)
2782 name2 = ADDRESS_JPILOT(obj2)->name;
2783 else if (obj2->type == ADDR_CATEGORY)
2784 name2 = ADDRESS_CATEGORY(obj2)->name;
2787 else if (obj2->type == ADDR_LDAP)
2788 name2 = ADDRESS_LDAP(obj2)->name;
2796 return (name2 != NULL);
2800 return strcasecmp(name1, name2);
2803 static gint addressbook_obj_name_compare(gconstpointer a, gconstpointer b)
2805 const AddressObject *obj = a;
2806 const gchar *name = b;
2808 if (!obj || !name) return -1;
2810 if (obj->type == ADDR_GROUP) {
2811 AddressGroup *group = ADDRESS_GROUP(obj);
2815 return strcasecmp(group->name, name);
2816 } else if (obj->type == ADDR_FOLDER) {
2817 AddressFolder *folder = ADDRESS_FOLDER(obj);
2821 return strcasecmp(folder->name, name);
2823 else if (obj->type == ADDR_VCARD) {
2824 AddressVCard *vcard = ADDRESS_VCARD(obj);
2828 return strcasecmp(vcard->name, name);
2831 else if (obj->type == ADDR_JPILOT) {
2832 AddressJPilot *jpilot = ADDRESS_JPILOT(obj);
2836 return strcasecmp(jpilot->name, name);
2838 else if (obj->type == ADDR_CATEGORY) {
2839 AddressCategory *category = ADDRESS_CATEGORY(obj);
2840 if (!category->name)
2843 return strcasecmp(category->name, name);
2847 else if (obj->type == ADDR_LDAP) {
2848 AddressLDAP *server = ADDRESS_LDAP(obj);
2852 return strcasecmp(server->name, name);
2855 else if (obj->type == ADDR_ITEM) {
2856 AddressItem *item = ADDRESS_ITEM(obj);
2860 return strcasecmp(item->name, name);
2865 static AddressVCard *addressbook_parse_vcard(XMLFile *file) {
2866 AddressVCard *item = NULL;
2869 gchar *name, *value;
2871 vcf = vcard_create();
2872 attr = xml_get_current_tag_attr( file );
2874 name = ((XMLAttr *)attr->data)->name;
2875 value = ((XMLAttr *)attr->data)->value;
2876 if( strcmp( name, "name" ) == 0 ) {
2877 vcard_set_name( vcf, value );
2879 else if( strcmp( name, "file" ) == 0) {
2880 vcard_set_file( vcf, value );
2882 attr = g_list_next( attr );
2886 if( xml_parse_next_tag( file ) >= 0 ) {
2887 if( vcard_validate( vcf ) ) {
2888 item = g_new( AddressVCard, 1 );
2889 ADDRESS_OBJECT(item)->type = ADDR_VCARD;
2890 item->name = g_strdup( vcf->name );
2891 item->cardFile = vcf;
2897 // Must be an invalid tag or data.
2898 g_warning( "addressbook_parse_vcard(): Parse error\n");
2905 static void addressbook_write_vcard( FILE *fp, AddressVCard *vcard, guint level ) {
2906 VCardFile *cardFile = vcard->cardFile;
2908 tab_indent_out(fp, 1);
2909 fputs("<vcard ", fp);
2910 fputs("name=\"", fp);
2911 xml_file_put_escape_str(fp, cardFile->name);
2913 fputs(" file=\"", fp);
2914 xml_file_put_escape_str(fp, cardFile->path);
2921 static AddressJPilot *addressbook_parse_jpilot(XMLFile *file) {
2922 AddressJPilot *item = NULL;
2925 gchar *name, *value;
2927 jpf = jpilot_create();
2928 attr = xml_get_current_tag_attr( file );
2930 name = ((XMLAttr *)attr->data)->name;
2931 value = ((XMLAttr *)attr->data)->value;
2932 if( strcmp( name, "name" ) == 0 ) {
2933 jpilot_set_name( jpf, value );
2935 else if( strcmp( name, "file" ) == 0 ) {
2936 jpilot_set_file( jpf, value );
2938 else if( strcmp( name, "custom-1" ) == 0 ) {
2939 jpilot_add_custom_label( jpf, value );
2941 else if( strcmp( name, "custom-2" ) == 0 ) {
2942 jpilot_add_custom_label( jpf, value );
2944 else if( strcmp( name, "custom-3" ) == 0 ) {
2945 jpilot_add_custom_label( jpf, value );
2947 else if( strcmp( name, "custom-4" ) == 0 ) {
2948 jpilot_add_custom_label( jpf, value );
2950 attr = g_list_next( attr );
2954 if( xml_parse_next_tag( file ) >= 0 ) {
2955 if( jpilot_validate( jpf ) ) {
2956 item = g_new( AddressJPilot, 1 );
2957 ADDRESS_OBJECT(item)->type = ADDR_JPILOT;
2958 item->name = g_strdup( jpf->name );
2959 item->pilotFile = jpf;
2965 // Must be an invalid tag or data.
2966 g_warning( "addressbook_parse_jpilot(): Parse error\n");
2973 static void addressbook_write_jpilot( FILE *fp, AddressJPilot *jpilot, guint level ) {
2974 JPilotFile *pilotFile = jpilot->pilotFile;
2978 GList *customLbl = jpilot_get_custom_labels( pilotFile );
2979 tab_indent_out(fp, 1);
2980 fputs("<jpilot ", fp);
2981 fputs("name=\"", fp);
2982 xml_file_put_escape_str(fp, pilotFile->name);
2983 fputs("\" file=\"", fp);
2984 xml_file_put_escape_str(fp, pilotFile->path);
2990 fprintf( fp, "custom-%d=\"", ind );
2991 xml_file_put_escape_str( fp, node->data );
2994 node = g_list_next( node );
3001 static void addressbook_new_vcard_cb( gpointer data, guint action, GtkWidget *widget ) {
3002 AddressVCard *vcard;
3004 if( addrbook.selected != addrbook.vcard ) return;
3005 vcard = addressbook_edit_vcard( NULL );
3007 addressbook_add_object( addrbook.selected, ADDRESS_OBJECT(vcard) );
3008 if( addrbook.selected == addrbook.opened ) {
3009 addrbook.open_folder = TRUE;
3010 gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
3015 static void addressbook_vcard_show_message( VCardFile *vcf ) {
3016 *addressbook_msgbuf = '\0';
3018 if( vcf->retVal == MGU_SUCCESS ) {
3019 sprintf( addressbook_msgbuf, "%s", vcf->name );
3022 sprintf( addressbook_msgbuf, "%s: %s", vcf->name, mgu_error2string( vcf->retVal ) );
3025 addressbook_status_show( addressbook_msgbuf );
3029 static void addressbook_new_jpilot_cb( gpointer data, guint action, GtkWidget *widget ) {
3030 AddressJPilot *jpilot;
3032 if( addrbook.selected != addrbook.jpilot ) return;
3033 if( ! _have_pilot_library_ ) return;
3034 jpilot = addressbook_edit_jpilot( NULL );
3036 addressbook_add_object( addrbook.selected, ADDRESS_OBJECT(jpilot) );
3037 if( addrbook.selected == addrbook.opened ) {
3038 addrbook.open_folder = TRUE;
3039 gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
3044 static void addressbook_jpilot_show_message( JPilotFile *jpf ) {
3045 *addressbook_msgbuf = '\0';
3047 if( jpf->retVal == MGU_SUCCESS ) {
3048 sprintf( addressbook_msgbuf, "%s", jpf->name );
3051 sprintf( addressbook_msgbuf, "%s: %s", jpf->name, mgu_error2string( jpf->retVal ) );
3054 addressbook_status_show( addressbook_msgbuf );
3060 static void addressbook_new_ldap_cb( gpointer data, guint action, GtkWidget *widget ) {
3063 if( addrbook.selected != addrbook.ldap ) return;
3064 if( ! _have_ldap_library_ ) return;
3065 ldapi = addressbook_edit_ldap( NULL );
3067 addressbook_add_object( addrbook.selected, ADDRESS_OBJECT(ldapi) );
3068 if( addrbook.selected == addrbook.opened ) {
3069 addrbook.open_folder = TRUE;
3070 gtk_ctree_select( GTK_CTREE(addrbook.ctree), addrbook.opened );
3075 static AddressLDAP *addressbook_parse_ldap(XMLFile *file) {
3076 AddressLDAP *item = NULL;
3077 SyldapServer *server;
3079 gchar *name, *value;
3082 server = syldap_create();
3083 attr = xml_get_current_tag_attr( file );
3085 name = ((XMLAttr *)attr->data)->name;
3086 value = ((XMLAttr *)attr->data)->value;
3087 ivalue = atoi( value );
3088 if( strcmp( name, "name" ) == 0 ) {
3089 syldap_set_name( server, value );
3091 else if( strcmp( name, "host" ) == 0 ) {
3092 syldap_set_host( server, value );
3094 else if( strcmp( name, "port" ) == 0 ) {
3095 syldap_set_port( server, ivalue );
3097 else if( strcmp( name, "base-dn" ) == 0 ) {
3098 syldap_set_base_dn( server, value );
3100 else if( strcmp( name, "bind-dn" ) == 0 ) {
3101 syldap_set_bind_dn( server, value );
3103 else if( strcmp( name, "bind-pass" ) == 0 ) {
3104 syldap_set_bind_password( server, value );
3106 else if( strcmp( name, "criteria" ) == 0 ) {
3107 syldap_set_search_criteria( server, value );
3109 else if( strcmp( name, "max-entry" ) == 0 ) {
3110 syldap_set_max_entries( server, ivalue );
3112 else if( strcmp( name, "timeout" ) == 0 ) {
3113 syldap_set_timeout( server, ivalue );
3115 attr = g_list_next( attr );
3119 if( xml_parse_next_tag( file ) >= 0 ) {
3120 item = g_new( AddressLDAP, 1 );
3121 ADDRESS_OBJECT(item)->type = ADDR_LDAP;
3122 item->name = g_strdup( server->name );
3123 item->ldapServer = server;
3128 // Must be an invalid tag or data.
3129 g_warning( "addressbook_parse_ldap(): Parse error\n");
3130 syldap_free( server );
3136 static void addressbook_write_ldap( FILE *fp, AddressLDAP *ldapi, guint level ) {
3137 SyldapServer *server = ldapi->ldapServer;
3139 tab_indent_out(fp, 1);
3140 fputs("<server ", fp);
3141 fputs("name=\"", fp);
3142 xml_file_put_escape_str(fp, server->name);
3143 fputs("\" host=\"", fp);
3144 xml_file_put_escape_str(fp, server->hostName);
3145 fprintf( fp, "\" port=\"%d", server->port);
3146 fputs("\" base-dn=\"", fp);
3147 xml_file_put_escape_str(fp, server->baseDN);
3148 fputs("\" bind-dn=\"", fp);
3149 xml_file_put_escape_str(fp, server->bindDN);
3150 fputs("\" bind-pass=\"", fp);
3151 xml_file_put_escape_str(fp, server->bindPass);
3152 fputs("\" criteria=\"", fp);
3153 xml_file_put_escape_str(fp, server->searchCriteria);
3154 fprintf( fp, "\" max-entry=\"%d", server->maxEntries);
3155 fprintf( fp, "\" timeout=\"%d", server->timeOut);
3156 fputs("\" />\n", fp);
3160 static void addressbook_ldap_show_message( SyldapServer *svr ) {
3161 *addressbook_msgbuf = '\0';
3163 if( svr->busyFlag ) {
3164 sprintf( addressbook_msgbuf, "%s: %s", svr->name, ADDRESSBOOK_LDAP_BUSYMSG );
3167 if( svr->retVal == MGU_SUCCESS ) {
3168 sprintf( addressbook_msgbuf, "%s", svr->name );
3171 sprintf( addressbook_msgbuf, "%s: %s", svr->name, mgu_error2string( svr->retVal ) );
3175 addressbook_status_show( addressbook_msgbuf );
3178 static gint ldapsearch_callback( SyldapServer *sls ) {
3179 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
3182 if( sls == NULL ) return;
3183 if( ! addrbook.selected ) return;
3184 if( GTK_CTREE_ROW( addrbook.selected )->level == 1 ) return;
3186 obj = gtk_ctree_node_get_row_data( ctree, addrbook.selected );
3187 if( obj == NULL ) return;
3188 if( obj->type == ADDR_LDAP ) {
3189 AddressLDAP *ldapi = ADDRESS_LDAP(obj);
3190 SyldapServer *server = ldapi->ldapServer;
3191 if( server == sls ) {
3192 if( ! _have_ldap_library_ ) return;
3194 gtk_widget_show_all(addrbook.window);
3195 ADDRESS_LDAP(obj)->items = syldap_get_address_list( sls );
3196 addressbook_set_clist( obj );
3197 addressbook_ldap_show_message( sls );
3198 gtk_widget_show_all(addrbook.window);
3205 * Lookup button handler.
3207 static void addressbook_lup_clicked( GtkButton *button, gpointer data ) {
3208 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
3213 SyldapServer *server;
3216 sLookup = gtk_editable_get_chars( GTK_EDITABLE(addrbook.entry), 0, -1 );
3217 g_strchomp( sLookup );
3219 if( ! addrbook.selected ) return;
3220 if( GTK_CTREE_ROW( addrbook.selected )->level == 1 ) return;
3222 obj = gtk_ctree_node_get_row_data( ctree, addrbook.selected );
3223 if( obj == NULL ) return;
3226 if( obj->type == ADDR_LDAP ) {
3227 ldapi = ADDRESS_LDAP(obj);
3228 server = ldapi->ldapServer;
3230 if( ! _have_ldap_library_ ) return;
3231 syldap_cancel_read( server );
3232 if( *sLookup == '\0' || strlen( sLookup ) < 1 ) return;
3233 syldap_set_search_value( server, sLookup );
3234 syldap_set_callback( server, ldapsearch_callback );
3235 syldap_read_data_th( server );
3236 addressbook_ldap_show_message( server );
3246 gboolean init; /* if FALSE should init jump buffer */
3247 GtkCTreeNode *node_found; /* match (can be used to backtrack folders) */
3248 AddressObject *addr_found; /* match */
3249 int level; /* current recursion level (0 is root level) */
3250 jmp_buf jumper; /* jump buffer */
3254 FindObject ancestor;
3255 const gchar *groupname;
3259 FindObject ancestor;
3261 const gchar *address;
3265 FindObject ancestor;
3269 typedef gboolean (*ADDRESSBOOK_TRAVERSE_FUNC)(AddressObject *node, gpointer data);
3273 static gboolean traverse_find_group_by_name(AddressObject *ao, FindGroup *find)
3275 AddressFolder *folder;
3276 AddressGroup *group;
3278 /* a group or folder: both are groups */
3279 if (ADDRESS_OBJECT_TYPE(ao) == ADDR_GROUP) {
3280 group = ADDRESS_GROUP(ao);
3281 if (0 == g_strcasecmp(group->name, find->groupname)) {
3285 else if (ADDRESS_OBJECT_TYPE(ao) == ADDR_FOLDER) {
3286 folder = ADDRESS_FOLDER(ao);
3287 if (0 == g_strcasecmp(folder->name, find->groupname)) {
3294 static gboolean traverse_find_name_email(AddressObject *ao, FindAddress *find)
3297 if (ADDRESS_OBJECT_TYPE(ao) == ADDR_ITEM) {
3298 gboolean nmatch = FALSE, amatch = FALSE;
3299 item = ADDRESS_ITEM(ao);
3301 * o only match at the first characters in item strings
3302 * o match either name or address */
3303 if (find->name && item->name) {
3304 nmatch = item->name == strcasestr(item->name, find->name);
3306 if (find->address && item->address) {
3307 amatch = item->address == strcasestr(item->address, find->address);
3309 return nmatch || amatch;
3314 static gboolean traverse_find_all_groups(AddressObject *ao, FindAllGroups *find)
3316 /* NOTE: added strings come from the address book. should perhaps
3317 * strdup() them, especially if the address book is invalidated */
3318 if (ADDRESS_OBJECT_TYPE(ao) == ADDR_FOLDER) {
3319 AddressFolder *folder = ADDRESS_FOLDER(ao);
3320 find->grouplist = g_list_insert_sorted(find->grouplist, (gpointer) folder->name, (GCompareFunc) g_strcasecmp);
3322 else if (ADDRESS_OBJECT_TYPE(ao) == ADDR_GROUP) {
3323 AddressGroup *group = ADDRESS_GROUP(ao);
3324 find->grouplist = g_list_insert_sorted(find->grouplist, (gpointer) group->name, (GCompareFunc) g_strcasecmp);
3329 /* addressbook_traverse() - traverses all address objects stored in the address book.
3330 * for some reason gtkctree's recursive tree functions don't allow a premature return,
3331 * which is what we need if we need to enumerate the tree and check for a condition
3332 * and then skipping other nodes. */
3333 static AddressObject *addressbook_traverse(GtkCTreeNode *node, ADDRESSBOOK_TRAVERSE_FUNC func, FindObject *data, int level)
3335 GtkCTreeNode *current, *tmp;
3338 if (data->init == FALSE) {
3339 /* initialize non-local exit */
3342 /* HANDLE NON-LOCAL EXIT */
3343 if (setjmp(data->jumper)) {
3344 return data->addr_found;
3348 /* actual recursive code */
3350 current = GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list);
3357 tmp = GTK_CTREE_ROW(current)->sibling;
3358 ao = (AddressObject *) gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), current);
3362 next = (ADDRESS_OBJECT_TYPE(ao) == ADDR_FOLDER) ?
3363 g_list_first(((ADDRESS_FOLDER(ao))->items)) :
3364 (ADDRESS_OBJECT_TYPE(ao) == ADDR_GROUP) ?
3365 g_list_first(((ADDRESS_GROUP(ao))->items)) : NULL;
3368 /* NOTE: first iteration of the root calls callback for the tree
3369 * node, other iterations call callback for the address book items */
3370 if (func(ao, data)) {
3372 data->node_found = current;
3373 data->addr_found = ao;
3374 longjmp(data->jumper, 1);
3376 /* ctree node only stores folders and groups. now descend into
3377 * address object data, searching for address items. */
3378 for ( ; next && ADDRESS_OBJECT_TYPE((next->data)) != ADDR_ITEM
3379 ; next = g_list_next(next))
3381 ao = next ? (AddressObject *) next->data : NULL;
3382 next = next ? g_list_next(next) : NULL;
3385 /* check the children (if level permits) */
3386 if (level == -1 || data->level < level) {
3387 current = GTK_CTREE_ROW(current)->children;
3390 addressbook_traverse(current, func, data, level);
3394 /* check the siblings */
3400 static GtkCTreeNode *addressbook_get_group_node(const gchar *name)
3402 FindGroup fg = { { FALSE, NULL, NULL }, NULL };
3403 fg.groupname = name;
3404 addressbook_traverse(NULL, (void *)traverse_find_group_by_name, (FindObject *)&fg, -1);
3405 return fg.ancestor.node_found;
3408 static void addressbook_free_item(AddressItem *item)
3411 if (item->name) g_free(item->name);
3412 if (item->address) g_free(item->address);
3413 if (item->remarks) g_free(item->remarks);
3418 static AddressItem *addressbook_alloc_item(const gchar *name, const gchar *address, const gchar *remarks)
3420 AddressItem *item = g_new0(AddressItem, 1);
3423 item->obj.type = ADDR_ITEM;
3424 if (item->name = g_strdup(name))
3425 if (item->address = g_strdup(address)) {
3427 item->remarks = g_strdup(remarks);
3432 addressbook_free_item(item);
3438 /* public provisional API */
3440 /* addressbook_access() - should be called before using any of the following apis. it
3441 * reloads the address book. */
3442 void addressbook_access(void)
3444 log_message("accessing address book\n");
3445 if (!addrbook.window) {
3446 addressbook_create(FALSE);
3447 addressbook_read_file();
3448 addrbook.open_folder = TRUE;
3449 gtk_ctree_select(GTK_CTREE(addrbook.ctree), GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
3453 /* addressbook_unaccess() - should only be called after changing the address book's
3455 void addressbook_unaccess(void)
3457 log_message("unaccessing address book\n");
3458 addressbook_export_to_file();
3459 invalidate_address_completion();
3462 const gchar *addressbook_get_personal_folder_name(void)
3464 return _("Personal addresses"); /* human readable */
3467 const gchar *addressbook_get_common_folder_name(void)
3469 return _("Common addresses"); /* human readable */
3472 /* addressbook_find_group_by_name() - finds a group (folder or group) by
3474 AddressObject *addressbook_find_group_by_name(const gchar *name)
3476 FindGroup fg = { { FALSE, NULL, NULL } };
3479 /* initialize obj members */
3480 fg.groupname = name;
3481 ao = addressbook_traverse(NULL,
3482 (ADDRESSBOOK_TRAVERSE_FUNC)traverse_find_group_by_name,
3483 (FindObject *)&fg, -1);
3487 /* addressbook_find_contact() - finds an address item by either name or address
3488 * or both. the comparison is done on the first few characters of the strings */
3489 AddressObject *addressbook_find_contact(const gchar *name, const gchar *address)
3491 FindAddress fa = { { FALSE, NULL, NULL } };
3495 fa.address = address;
3496 ao = addressbook_traverse(NULL, (ADDRESSBOOK_TRAVERSE_FUNC)traverse_find_name_email,
3497 (FindObject *)&fa, -1);
3501 /* addressbook_get_group_list() - returns a list of strings with group names (both
3502 * groups and folders). free the list using g_list_free(). note that another
3503 * call may invalidate the returned list */
3504 GList *addressbook_get_group_list(void)
3506 FindAllGroups fag = { { FALSE, NULL, NULL }, NULL };
3507 addressbook_traverse(NULL, (ADDRESSBOOK_TRAVERSE_FUNC)traverse_find_all_groups,
3508 (FindObject *)&fag, -1);
3509 return fag.grouplist;
3512 /* addressbook_add_contact() - adds a contact to the address book. returns 1
3513 * if succesful else error */
3514 gint addressbook_add_contact(const gchar *group, const gchar *name, const gchar *address,
3515 const gchar *remarks)
3519 FindAddress fa = { { FALSE, NULL, NULL } };
3521 /* a healthy mix of hiro's and my code */
3522 if (name == NULL || strlen(name) == 0
3523 || address == NULL || strlen(address) == 0
3524 || group == NULL || strlen(group) == 0) {
3527 node = addressbook_get_group_node(group);
3532 /* check if it's already in this group */
3534 fa.address = address;
3536 if (addressbook_traverse(node, (gpointer)traverse_find_name_email, (gpointer)&fa, 0)) {
3537 log_message("address <%s> already in %s\n", address, group);
3541 item = addressbook_alloc_item(name, address, remarks);
3546 if (!addressbook_add_object(node, (AddressObject *)item)) {
3547 addressbook_free_item(item);
3551 /* make sure it's updated if selected */
3552 log_message("updating addressbook widgets\n");
3553 addrbook.open_folder = TRUE;
3554 gtk_ctree_select(GTK_CTREE(addrbook.ctree), addrbook.opened);
3556 /* not saved yet. only after unaccessing the address book */
3560 static void group_object_data_destroy(gchar *group)
3575 static void addressbook_destroy_contact(ContactInfo *ci)
3577 g_return_if_fail(ci != NULL);
3578 if (ci->name) g_free(ci->name);
3579 if (ci->address) g_free(ci->address);
3580 if (ci->remarks) g_free(ci->remarks);
3584 static ContactInfo *addressbook_new_contact(const gchar *name, const gchar *address, const gchar *remarks)
3586 ContactInfo *ci = g_new0(ContactInfo, 1);
3588 g_return_val_if_fail(ci != NULL, NULL);
3589 g_return_val_if_fail(address != NULL, NULL); /* address should be valid */
3590 ci->name = name ? g_strdup(name) : NULL;
3591 ci->address = g_strdup(address);
3592 ci->remarks = remarks ? g_strdup(remarks) : NULL;
3593 if (NULL == ci->address) {
3594 addressbook_destroy_contact(ci);
3600 static void addressbook_group_menu_selected(GtkMenuItem *menuitem,
3603 const gchar *group_name = (const gchar *) gtk_object_get_data(GTK_OBJECT(menuitem),
3607 g_warning("%s(%d) - invalid group name\n", __FILE__, __LINE__);
3610 g_return_if_fail(group_name != NULL);
3612 g_message("selected group %s from menu\n", group_name);
3613 g_message("selected %s <%s>\n", data->name ? data->name : data->address, data->address);
3615 addressbook_access();
3616 addressbook_add_contact(group_name, data->name ? data->name : data->address,
3617 data->address, data->remarks ? data->remarks : data->address);
3618 addressbook_unaccess();
3623 /* addressbook_add_contact_by_meny() - launches menu with group items. submenu may be
3624 * the menu item in the parent menu, or NULL for a normal right-click context menu */
3625 gboolean addressbook_add_contact_by_menu(GtkWidget *submenu,
3627 const gchar *address,
3628 const gchar *remarks)
3630 GtkWidget *menu, *menuitem;
3631 GList *groups, *tmp;
3634 ci = addressbook_new_contact(name, address, remarks);
3635 g_return_val_if_fail(ci != NULL, FALSE);
3637 addressbook_access();
3638 groups = addressbook_get_group_list();
3639 g_return_val_if_fail(groups != NULL, (addressbook_destroy_contact(ci), FALSE));
3641 menu = gtk_menu_new();
3642 g_return_val_if_fail(menu != NULL, (g_list_free(groups), addressbook_destroy_contact(ci), FALSE));
3644 /* add groups to menu */
3645 for (tmp = g_list_first(groups); tmp != NULL; tmp = g_list_next(tmp)) {
3646 const gchar *display_name;
3647 gchar *original_name = (gchar *) tmp->data;
3648 gboolean addItem = TRUE;
3650 if (!g_strcasecmp(original_name, ADDRESS_TAG_PERSONAL)) {
3651 display_name = addressbook_get_personal_folder_name();
3653 else if (!g_strcasecmp(original_name, ADDRESS_TAG_COMMON)) {
3654 display_name = addressbook_get_common_folder_name();
3656 else if( ! g_strcasecmp( original_name, ADDRESS_TAG_VCARD ) ) {
3660 else if( ! g_strcasecmp( original_name, ADDRESS_TAG_JPILOT ) ) {
3665 else if( ! g_strcasecmp( original_name, ADDRESS_TAG_LDAP ) ) {
3670 display_name = original_name;
3674 original_name = g_strdup(original_name);
3675 menuitem = gtk_menu_item_new_with_label(display_name);
3676 /* register the duplicated string pointer as object data,
3677 * so we get the opportunity to free it */
3678 gtk_object_set_data_full(GTK_OBJECT(menuitem), "group_name",
3680 (GtkDestroyNotify) group_object_data_destroy);
3681 gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
3682 GTK_SIGNAL_FUNC(addressbook_group_menu_selected),
3684 gtk_menu_append(GTK_MENU(menu), menuitem);
3685 gtk_widget_show(menuitem);
3689 gtk_widget_show(menu);
3692 gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu), menu);
3693 gtk_widget_set_sensitive(GTK_WIDGET(submenu), TRUE);
3696 gtk_widget_grab_focus(GTK_WIDGET(menu));
3697 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME);
3700 if (groups) g_list_free(groups);