2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <gtk/gtkwindow.h>
30 #include <gtk/gtksignal.h>
31 #include <gtk/gtkvbox.h>
32 #include <gtk/gtkscrolledwindow.h>
33 #include <gtk/gtkhpaned.h>
34 #include <gtk/gtkhbox.h>
35 #include <gtk/gtklabel.h>
36 #include <gtk/gtkentry.h>
37 #include <gtk/gtkctree.h>
38 #include <gtk/gtkclist.h>
39 #include <gtk/gtktable.h>
40 #include <gtk/gtkhbbox.h>
41 #include <gtk/gtkbutton.h>
42 #include <gtk/gtkmenu.h>
43 #include <gtk/gtkmenuitem.h>
44 #include <gtk/gtkitemfactory.h>
47 #include <sys/types.h>
51 #include "addressbook.h"
52 #include "manage_window.h"
53 #include "prefs_common.h"
54 #include "alertpanel.h"
55 #include "inputdialog.h"
57 #include "stock_pixmap.h"
59 #include "prefs_gtk.h"
65 #include "addr_compl.h"
68 #include "addressitem.h"
70 #include "addrcache.h"
72 #include "addrindex.h"
73 #include "addressadd.h"
74 #include "addrduplicates.h"
75 #include "addressbook_foldersel.h"
77 #include "editvcard.h"
78 #include "editgroup.h"
79 #include "editaddress.h"
81 #include "importldif.h"
82 #include "importmutt.h"
83 #include "importpine.h"
88 #include "editjpilot.h"
93 #include "ldapserver.h"
95 #include "ldapupdate.h"
97 #define ADDRESSBOOK_LDAP_BUSYMSG "Busy"
100 #include "addrquery.h"
101 #include "addrselect.h"
102 #include "addrclip.h"
103 #include "addrgather.h"
104 #include "adbookbase.h"
105 #include "exphtmldlg.h"
106 #include "expldifdlg.h"
107 #include "browseldap.h"
108 #include "addrcustomattr.h"
114 } AddressIndexColumns;
122 } AddressListColumns;
125 AddressBookFile *book;
133 AddressDataSource *book;
137 static gchar *list_titles[] = { N_("Name"),
141 #define COL_NAME_WIDTH 164
142 #define COL_ADDRESS_WIDTH 156
144 #define COL_FOLDER_WIDTH 170
145 #define ADDRESSBOOK_WIDTH 640
146 #define ADDRESSBOOK_HEIGHT 360
148 #define ADDRESSBOOK_MSGBUF_SIZE 2048
150 static GdkPixmap *folderxpm;
151 static GdkBitmap *folderxpmmask;
152 static GdkPixmap *folderopenxpm;
153 static GdkBitmap *folderopenxpmmask;
154 static GdkPixmap *groupxpm;
155 static GdkBitmap *groupxpmmask;
156 static GdkPixmap *interfacexpm;
157 static GdkBitmap *interfacexpmmask;
158 static GdkPixmap *bookxpm;
159 static GdkBitmap *bookxpmmask;
160 static GdkPixmap *addressxpm;
161 static GdkBitmap *addressxpmmask;
162 static GdkPixmap *vcardxpm;
163 static GdkBitmap *vcardxpmmask;
164 static GdkPixmap *jpilotxpm;
165 static GdkBitmap *jpilotxpmmask;
166 static GdkPixmap *categoryxpm;
167 static GdkBitmap *categoryxpmmask;
168 static GdkPixmap *ldapxpm;
169 static GdkBitmap *ldapxpmmask;
170 static GdkPixmap *addrsearchxpm;
171 static GdkPixmap *addrsearchxpmmask;
174 static gchar addressbook_msgbuf[ ADDRESSBOOK_MSGBUF_SIZE ];
176 /* Address list selection */
177 static AddrSelectList *_addressSelect_ = NULL;
178 static AddressClipboard *_clipBoard_ = NULL;
180 /* Address index file and interfaces */
181 static AddressIndex *_addressIndex_ = NULL;
182 static GList *_addressInterfaceList_ = NULL;
183 static GList *_addressIFaceSelection_ = NULL;
184 #define ADDRESSBOOK_IFACE_SELECTION "1/y,3/y,4/y,2/n"
186 static AddressBook_win addrbook;
188 static GHashTable *_addressBookTypeHash_ = NULL;
189 static GList *_addressBookTypeList_ = NULL;
191 static void addressbook_new_address_from_book_post_cb( ItemPerson *person );
192 static void addressbook_new_address_from_folder_post_cb( ItemPerson *person );
193 static void addressbook_edit_address_post_cb( ItemPerson *person );
195 static void addressbook_create (void);
196 static gint addressbook_close (void);
197 static void addressbook_button_set_sensitive (void);
199 static gboolean address_index_has_focus = FALSE;
200 static gboolean address_list_has_focus = FALSE;
202 /* callback functions */
203 static void addressbook_del_clicked (GtkButton *button,
205 static void addressbook_reg_clicked (GtkButton *button,
207 static void addressbook_to_clicked (GtkButton *button,
209 static void addressbook_lup_clicked (GtkButton *button,
211 static void addressbook_close_clicked (GtkButton *button,
214 static void addressbook_tree_selected (GtkCTree *ctree,
218 static void addressbook_select_row_tree (GtkCTree *ctree,
222 static void addressbook_list_row_selected (GtkCTree *clist,
226 static void addressbook_list_row_unselected (GtkCTree *clist,
230 static void addressbook_person_expand_node (GtkCTree *ctree,
233 static void addressbook_person_collapse_node (GtkCTree *ctree,
237 static gboolean addressbook_list_button_pressed (GtkWidget *widget,
238 GdkEventButton *event,
240 static gboolean addressbook_list_button_released(GtkWidget *widget,
241 GdkEventButton *event,
243 static gboolean addressbook_tree_button_pressed (GtkWidget *ctree,
244 GdkEventButton *event,
246 static gboolean addressbook_tree_button_released(GtkWidget *ctree,
247 GdkEventButton *event,
250 static void addressbook_new_folder_cb (gpointer data,
253 static void addressbook_new_group_cb (gpointer data,
256 static void addressbook_treenode_edit_cb (gpointer data,
259 static void addressbook_treenode_delete_cb (gpointer data,
263 static void addressbook_change_node_name (GtkCTreeNode *node,
266 static void addressbook_new_address_cb (gpointer data,
269 static void addressbook_edit_address_cb (gpointer data,
272 static void addressbook_delete_address_cb (gpointer data,
276 static void close_cb (gpointer data,
279 static void addressbook_file_save_cb (gpointer data,
283 /* Data source edit stuff */
284 static void addressbook_new_book_cb (gpointer data,
287 static void addressbook_new_vcard_cb (gpointer data,
292 static void addressbook_new_jpilot_cb (gpointer data,
298 static void addressbook_new_ldap_cb (gpointer data,
303 static void addressbook_set_clist (AddressObject *obj,
306 static void addressbook_load_tree (void);
307 void addressbook_read_file (void);
309 static GtkCTreeNode *addressbook_add_object (GtkCTreeNode *node,
311 static void addressbook_treenode_remove_item ( void );
313 static AddressDataSource *addressbook_find_datasource
314 (GtkCTreeNode *node );
316 static AddressBookFile *addressbook_get_book_file(void);
318 static GtkCTreeNode *addressbook_node_add_folder
320 AddressDataSource *ds,
321 ItemFolder *itemFolder,
322 AddressObjectType otype);
323 static GtkCTreeNode *addressbook_node_add_group (GtkCTreeNode *node,
324 AddressDataSource *ds,
325 ItemGroup *itemGroup);
326 static void addressbook_tree_remove_children (GtkCTree *ctree,
327 GtkCTreeNode *parent);
328 static void addressbook_move_nodes_up (GtkCTree *ctree,
330 static GtkCTreeNode *addressbook_find_group_node (GtkCTreeNode *parent,
332 static gboolean addressbook_entry_key_pressed (GtkWidget *widget,
335 static gint addressbook_treenode_compare_func (GtkCList *clist,
338 static void addressbook_folder_load_one_person (GtkCTree *clist,
340 AddressTypeControlItem *atci,
341 AddressTypeControlItem *atciMail);
342 static void addressbook_folder_refresh_one_person(GtkCTree *clist,
344 static void addressbook_folder_remove_one_person(GtkCTree *clist,
346 static void addressbook_folder_remove_node (GtkCTree *clist,
349 static void addressbook_edit_address( gpointer data, guint action, GtkWidget *widget,
350 gboolean force_focus );
352 /* LUT's and IF stuff */
353 static void addressbook_free_treenode ( gpointer data );
354 static AddressTypeControlItem *addrbookctl_lookup (gint ot);
355 static AddressTypeControlItem *addrbookctl_lookup_iface(AddressIfType ifType);
357 static void addrbookctl_build_map (GtkWidget *window);
358 static void addrbookctl_build_iflist (void);
359 static AdapterInterface *addrbookctl_find_interface (AddressIfType ifType);
360 static void addrbookctl_build_ifselect (void);
362 static void addrbookctl_free_interface (AdapterInterface *adapter);
363 static void addrbookctl_free_datasource (AdapterDSource *adapter);
364 static void addrbookctl_free_folder (AdapterFolder *adapter);
365 static void addrbookctl_free_group (AdapterGroup *adapter);
367 static void addressbook_list_select_clear ( void );
368 static void addressbook_list_select_add ( AddrItemObject *aio,
369 AddressDataSource *ds );
370 static void addressbook_list_select_remove ( AddrItemObject *aio );
372 static void addressbook_import_ldif_cb ( void );
373 static void addressbook_find_duplicates_cb ( void );
374 static void addressbook_edit_custom_attr_cb ( void );
375 static void addressbook_import_mutt_cb ( void );
376 static void addressbook_import_pine_cb ( void );
377 static void addressbook_export_html_cb ( void );
378 static void addressbook_export_ldif_cb ( void );
379 static void addressbook_select_all_cb ( void );
380 static void addressbook_clip_cut_cb ( void );
381 static void addressbook_clip_copy_cb ( void );
382 static void addressbook_clip_paste_cb ( void );
383 static void addressbook_treenode_cut_cb ( void );
384 static void addressbook_treenode_copy_cb ( void );
385 static void addressbook_treenode_paste_cb ( void );
387 static void addressbook_mail_to_cb ( void );
390 static void addressbook_browse_entry_cb ( void );
392 static void addressbook_edit_clicked(GtkButton *button, gpointer data);
394 static void addressbook_start_drag(GtkWidget *widget, gint button,
397 static void addressbook_drag_data_get(GtkWidget *widget,
398 GdkDragContext *drag_context,
399 GtkSelectionData *selection_data,
403 static gboolean addressbook_drag_motion_cb(GtkWidget *widget,
404 GdkDragContext *context,
409 static void addressbook_drag_leave_cb(GtkWidget *widget,
410 GdkDragContext *context,
413 static void addressbook_drag_received_cb(GtkWidget *widget,
414 GdkDragContext *drag_context,
417 GtkSelectionData *data,
421 static void addressbook_list_menu_setup( void );
423 static GtkTargetEntry addressbook_drag_types[] =
425 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY}
428 static GtkTargetList *addressbook_target_list = NULL;
431 static GtkItemFactoryEntry addressbook_entries[] =
433 {N_("/_Book"), NULL, NULL, 0, "<Branch>", NULL},
434 {N_("/_Book/New _Book"), "<control>B", addressbook_new_book_cb, 0, NULL, NULL},
435 {N_("/_Book/New _Folder"), "<control>R", addressbook_new_folder_cb, 0, NULL, NULL},
436 {N_("/_Book/New _vCard"), "<control><shift>D", addressbook_new_vcard_cb, 0, NULL, NULL},
438 {N_("/_Book/New _JPilot"), "<control>J", addressbook_new_jpilot_cb, 0, NULL, NULL},
441 {N_("/_Book/New LDAP _Server"), "<control><shift>S", addressbook_new_ldap_cb, 0, NULL, NULL},
443 {N_("/_Book/---"), NULL, NULL, 0, "<Separator>", NULL},
444 {N_("/_Book/_Edit book"), NULL, addressbook_treenode_edit_cb, 0, NULL, NULL},
445 {N_("/_Book/_Delete book"), NULL, addressbook_treenode_delete_cb, 0, NULL, NULL},
446 {N_("/_Book/---"), NULL, NULL, 0, "<Separator>", NULL},
447 {N_("/_Book/_Save"), "<control>S", addressbook_file_save_cb, 0, NULL, NULL},
448 {N_("/_Book/_Close"), "<control>W", close_cb, 0, NULL, NULL},
449 {N_("/_Address"), NULL, NULL, 0, "<Branch>", NULL},
450 {N_("/_Address/_Select all"), "<control>A", addressbook_select_all_cb, 0, NULL, NULL},
451 {N_("/_Address/---"), NULL, NULL, 0, "<Separator>", NULL},
452 {N_("/_Address/C_ut"), "<control>X", addressbook_clip_cut_cb, 0, NULL, NULL},
453 {N_("/_Address/_Copy"), "<control>C", addressbook_clip_copy_cb, 0, NULL, NULL},
454 {N_("/_Address/_Paste"), "<control>V", addressbook_clip_paste_cb, 0, NULL, NULL},
455 {N_("/_Address/---"), NULL, NULL, 0, "<Separator>", NULL},
456 {N_("/_Address/_Edit"), "<control>Return",addressbook_edit_address_cb, 0, NULL, NULL},
457 {N_("/_Address/_Delete"), "<control>D", addressbook_delete_address_cb, 0, NULL, NULL},
458 {N_("/_Address/---"), NULL, NULL, 0, "<Separator>", NULL},
459 {N_("/_Address/New _Address"), "<control>N", addressbook_new_address_cb, 0, NULL, NULL},
460 {N_("/_Address/New _Group"), "<control>G", addressbook_new_group_cb, 0, NULL, NULL},
461 {N_("/_Address/---"), NULL, NULL, 0, "<Separator>", NULL},
462 {N_("/_Address/_Mail To"), NULL, addressbook_mail_to_cb, 0, NULL, NULL},
463 {N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL},
464 {N_("/_Tools/Import _LDIF file..."), NULL, addressbook_import_ldif_cb, 0, NULL, NULL},
465 {N_("/_Tools/Import M_utt file..."), NULL, addressbook_import_mutt_cb, 0, NULL, NULL},
466 {N_("/_Tools/Import _Pine file..."), NULL, addressbook_import_pine_cb, 0, NULL, NULL},
467 {N_("/_Tools/---"), NULL, NULL, 0, "<Separator>", NULL},
468 {N_("/_Tools/Export _HTML..."), NULL, addressbook_export_html_cb, 0, NULL, NULL},
469 {N_("/_Tools/Export LDI_F..."), NULL, addressbook_export_ldif_cb, 0, NULL, NULL},
470 {N_("/_Tools/---"), NULL, NULL, 0, "<Separator>", NULL},
471 {N_("/_Tools/Find duplicates..."), NULL, addressbook_find_duplicates_cb, 0, NULL, NULL},
472 {N_("/_Tools/Edit custom attributes..."), NULL, addressbook_edit_custom_attr_cb, 0, NULL, NULL},
473 {N_("/_Help"), NULL, NULL, 0, "<Branch>", NULL},
474 {N_("/_Help/_About"), NULL, about_show, 0, NULL, NULL}
477 static GtkItemFactoryEntry addressbook_tree_popup_entries[] =
479 {N_("/_Edit"), NULL, addressbook_treenode_edit_cb, 0, NULL, NULL},
480 {N_("/_Delete"), NULL, addressbook_treenode_delete_cb, 0, NULL, NULL},
481 {"/---", NULL, NULL, 0, "<Separator>", NULL},
482 {N_("/New _Book"), NULL, addressbook_new_book_cb, 0, NULL, NULL},
483 {N_("/New _Folder"), NULL, addressbook_new_folder_cb, 0, NULL, NULL},
484 {N_("/New _Group"), NULL, addressbook_new_group_cb, 0, NULL, NULL},
485 {"/---", NULL, NULL, 0, "<Separator>", NULL},
486 {N_("/C_ut"), NULL, addressbook_treenode_cut_cb, 0, NULL, NULL},
487 {N_("/_Copy"), NULL, addressbook_treenode_copy_cb, 0, NULL, NULL},
488 {N_("/_Paste"), NULL, addressbook_treenode_paste_cb, 0, NULL, NULL}
491 static GtkItemFactoryEntry addressbook_list_popup_entries[] =
493 {N_("/_Select all"), NULL, addressbook_select_all_cb, 0, NULL, NULL},
494 {"/---", NULL, NULL, 0, "<Separator>", NULL},
495 {N_("/_Edit"), NULL, addressbook_edit_address_cb, 0, NULL, NULL},
496 {N_("/_Delete"), NULL, addressbook_delete_address_cb, 0, NULL, NULL},
497 {"/---", NULL, NULL, 0, "<Separator>", NULL},
498 {N_("/New _Address"), NULL, addressbook_new_address_cb, 0, NULL, NULL},
499 {N_("/New _Group"), NULL, addressbook_new_group_cb, 0, NULL, NULL},
500 {"/---", NULL, NULL, 0, "<Separator>", NULL},
501 {N_("/C_ut"), NULL, addressbook_clip_cut_cb, 0, NULL, NULL},
502 {N_("/_Copy"), NULL, addressbook_clip_copy_cb, 0, NULL, NULL},
503 {N_("/_Paste"), NULL, addressbook_clip_paste_cb, 0, NULL, NULL},
504 {"/---", NULL, NULL, 0, "<Separator>", NULL},
505 /* {N_("/Pa_ste Address"), NULL, addressbook_clip_paste_address_cb, 0, NULL, NULL},*/
506 {N_("/_Mail To"), NULL, addressbook_mail_to_cb, 0, NULL, NULL},
508 {N_("/_Browse Entry"), NULL, addressbook_browse_entry_cb, 0, NULL, NULL},
513 * Structure of error message table.
515 typedef struct _ErrMsgTableEntry ErrMsgTableEntry;
516 struct _ErrMsgTableEntry {
521 static gchar *_errMsgUnknown_ = N_( "Unknown" );
524 * Lookup table of error messages for general errors. Note that a NULL
525 * description signifies the end of the table.
527 static ErrMsgTableEntry _lutErrorsGeneral_[] = {
528 { MGU_SUCCESS, N_("Success") },
529 { MGU_BAD_ARGS, N_("Bad arguments") },
530 { MGU_NO_FILE, N_("File not specified") },
531 { MGU_OPEN_FILE, N_("Error opening file") },
532 { MGU_ERROR_READ, N_("Error reading file") },
533 { MGU_EOF, N_("End of file encountered") },
534 { MGU_OO_MEMORY, N_("Error allocating memory") },
535 { MGU_BAD_FORMAT, N_("Bad file format") },
536 { MGU_ERROR_WRITE, N_("Error writing to file") },
537 { MGU_OPEN_DIRECTORY, N_("Error opening directory") },
538 { MGU_NO_PATH, N_("No path specified") },
544 * Lookup table of error messages for LDAP errors.
546 static ErrMsgTableEntry _lutErrorsLDAP_[] = {
547 { LDAPRC_SUCCESS, N_("Success") },
548 { LDAPRC_CONNECT, N_("Error connecting to LDAP server") },
549 { LDAPRC_INIT, N_("Error initializing LDAP") },
550 { LDAPRC_BIND, N_("Error binding to LDAP server") },
551 { LDAPRC_SEARCH, N_("Error searching LDAP database") },
552 { LDAPRC_TIMEOUT, N_("Timeout performing LDAP operation") },
553 { LDAPRC_CRITERIA, N_("Error in LDAP search criteria") },
554 { LDAPRC_NOENTRIES, N_("No LDAP entries found for search criteria") },
555 { LDAPRC_STOP_FLAG, N_("LDAP search terminated on request") },
556 { LDAPRC_TLS, N_("Error starting TLS connection") },
557 { LDAPRC_NODN, N_("Distinguished Name (dn) is missing") },
558 { LDAPRC_NAMING_VIOLATION, N_("Missing required information") },
559 { LDAPRC_ALREADY_EXIST, N_("Another contact exists with that key") },
560 { LDAPRC_STRONG_AUTH, N_("Strong(er) authentication required") },
566 * Lookup message for specified error code.
567 * \param lut Lookup table.
568 * \param code Code to lookup.
569 * \return Description associated to code.
571 static gchar *addressbook_err2string( ErrMsgTableEntry lut[], gint code ) {
573 ErrMsgTableEntry entry;
576 for( i = 0; ; i++ ) {
578 if( entry.description == NULL ) break;
579 if( entry.code == code ) {
580 desc = entry.description;
585 desc = _errMsgUnknown_;
590 static gboolean lastCanLookup = FALSE;
592 static void addressbook_show_buttons(gboolean add_and_delete, gboolean lookup, gboolean mail_ops)
594 if (add_and_delete) {
595 gtk_widget_show(addrbook.edit_btn);
596 gtk_widget_show(addrbook.del_btn);
597 gtk_widget_show(addrbook.reg_btn);
599 gtk_widget_hide(addrbook.edit_btn);
600 gtk_widget_hide(addrbook.del_btn);
601 gtk_widget_hide(addrbook.reg_btn);
605 gtk_widget_show(addrbook.lup_btn);
606 gtk_widget_show(addrbook.entry);
607 gtk_widget_show(addrbook.label);
609 gtk_widget_hide(addrbook.lup_btn);
610 gtk_widget_hide(addrbook.entry);
611 gtk_widget_hide(addrbook.label);
614 lastCanLookup = lookup;
617 gtk_widget_show(addrbook.to_btn);
618 gtk_widget_show(addrbook.cc_btn);
619 gtk_widget_show(addrbook.bcc_btn);
621 gtk_widget_hide(addrbook.to_btn);
622 gtk_widget_hide(addrbook.cc_btn);
623 gtk_widget_hide(addrbook.bcc_btn);
627 void addressbook_open(Compose *target)
629 /* Initialize all static members */
630 if( _clipBoard_ == NULL ) {
631 _clipBoard_ = addrclip_create();
633 if( _addressIndex_ != NULL ) {
634 addrclip_set_index( _clipBoard_, _addressIndex_ );
636 if( _addressSelect_ == NULL ) {
637 _addressSelect_ = addrselect_list_create();
639 if (!addrbook.window) {
640 addressbook_read_file();
641 addressbook_create();
642 addressbook_load_tree();
643 gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
644 GTK_CTREE_NODE(GTK_CLIST(addrbook.ctree)->row_list));
647 gtk_widget_hide(addrbook.window);
650 gtk_widget_show_all(addrbook.window);
652 maemo_window_full_screen_if_needed(GTK_WINDOW(addrbook.window));
653 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(addrbook.window));
655 if (!prefs_common.addressbook_use_editaddress_dialog)
656 addressbook_edit_person_widgetset_hide();
658 address_completion_start(addrbook.window);
660 addressbook_show_buttons(target == NULL, lastCanLookup, target != NULL);
661 addressbook_set_target_compose(target);
665 * Destroy addressbook.
667 void addressbook_destroy( void ) {
668 /* Free up address stuff */
669 if( _addressSelect_ != NULL ) {
670 addrselect_list_free( _addressSelect_ );
672 if( _clipBoard_ != NULL ) {
673 addrclip_free( _clipBoard_ );
675 if( _addressIndex_ != NULL ) {
676 addrindex_free_index( _addressIndex_ );
677 addrindex_teardown();
679 _addressSelect_ = NULL;
681 _addressIndex_ = NULL;
684 void addressbook_set_target_compose(Compose *target)
686 addrbook.target_compose = target;
687 addressbook_button_set_sensitive();
690 Compose *addressbook_get_target_compose(void)
692 return addrbook.target_compose;
696 * Refresh addressbook and save to file(s).
698 void addressbook_refresh( void )
700 if (addrbook.window) {
701 if (addrbook.treeSelected) {
702 gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
703 addrbook.treeSelected);
704 addressbook_set_clist(
705 gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
706 addrbook.treeSelected),
711 addressbook_export_to_file();
714 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
716 if (event && event->keyval == GDK_Escape)
718 else if (event && event->keyval == GDK_Delete) {
719 /* TODO: enable deletion when focus is in ctree (needs implementation in _del_clicked() */
720 if ( /* address_index_has_focus || */ address_list_has_focus )
721 addressbook_del_clicked(NULL, NULL);
727 *\brief Save Gtk object size to prefs dataset
729 static void addressbook_size_allocate_cb(GtkWidget *widget,
730 GtkAllocation *allocation)
732 g_return_if_fail(allocation != NULL);
734 prefs_common.addressbookwin_width = allocation->width;
735 prefs_common.addressbookwin_height = allocation->height;
738 static gint sort_column_number = 0;
739 static GtkSortType sort_column_type = GTK_SORT_ASCENDING;
741 static gint list_case_sort(
742 GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
744 GtkCListRow *row1 = (GtkCListRow *) ptr1;
745 GtkCListRow *row2 = (GtkCListRow *) ptr2;
746 gchar *name1 = NULL, *name2 = NULL;
747 AddrItemObject *aio1 = ((GtkCListRow *)ptr1)->data;
748 AddrItemObject *aio2 = ((GtkCListRow *)ptr2)->data;
750 if( aio1->type == aio2->type ) {
752 name1 = GTK_CELL_TEXT (row1->cell[sort_column_number])->text;
754 name2 = GTK_CELL_TEXT (row2->cell[sort_column_number])->text;
755 if( ! name1 ) return ( name2 != NULL );
756 if( ! name2 ) return -1;
757 return g_utf8_collate( name1, name2 );
759 /* Order groups before person */
760 if( aio1->type == ITEMTYPE_GROUP ) {
761 return (sort_column_type==GTK_SORT_ASCENDING) ? -1:+1;
762 } else if( aio2->type == ITEMTYPE_GROUP ) {
763 return (sort_column_type==GTK_SORT_ASCENDING) ? +1:-1;
769 static void addressbook_sort_list(GtkCList *clist, const gint col,
770 const GtkSortType sort_type)
773 GtkWidget *hbox, *label, *arrow;
775 sort_column_number = col;
776 sort_column_type = sort_type;
777 gtk_clist_set_compare_func(clist, list_case_sort);
778 gtk_clist_set_sort_type(clist, sort_type);
779 gtk_clist_set_sort_column(clist, col);
781 gtk_clist_freeze(clist);
782 gtk_clist_sort(clist);
784 for(pos = 0 ; pos < N_LIST_COLS ; pos++) {
785 hbox = gtk_hbox_new(FALSE, 4);
786 label = gtk_label_new(gettext(list_titles[pos]));
787 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
790 arrow = gtk_arrow_new(sort_type == GTK_SORT_ASCENDING ?
791 GTK_ARROW_DOWN : GTK_ARROW_UP, GTK_SHADOW_IN);
792 gtk_box_pack_end(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
795 gtk_widget_show_all(hbox);
796 gtk_clist_set_column_widget(clist, pos, hbox);
799 gtk_clist_thaw(clist);
802 static void addressbook_name_clicked(GtkWidget *button, GtkCList *clist)
804 static GtkSortType sort_type = GTK_SORT_ASCENDING;
806 sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
808 addressbook_sort_list(clist, COL_NAME, sort_type);
811 static void addressbook_address_clicked(GtkWidget *button, GtkCList *clist)
813 static GtkSortType sort_type = GTK_SORT_ASCENDING;
815 sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
817 addressbook_sort_list(clist, COL_ADDRESS, sort_type);
820 static void addressbook_remarks_clicked(GtkWidget *button, GtkCList *clist)
822 static GtkSortType sort_type = GTK_SORT_ASCENDING;
824 sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
826 addressbook_sort_list(clist, COL_REMARKS, sort_type);
829 static gboolean addressbook_address_index_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
832 address_index_has_focus = TRUE;
836 static gboolean addressbook_address_index_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
839 address_index_has_focus = FALSE;
840 if (!prefs_common.addressbook_use_editaddress_dialog
841 && !address_list_has_focus)
842 addressbook_address_list_disable_some_actions();
846 static gboolean addressbook_address_list_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
849 address_list_has_focus = TRUE;
853 static gboolean addressbook_address_list_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
856 address_list_has_focus = FALSE;
857 if (!prefs_common.addressbook_use_editaddress_dialog
858 && !address_index_has_focus)
859 addressbook_address_list_disable_some_actions();
863 /* save hpane and vpane's handle position when it moves */
864 static void addressbook_pane_save_position(void)
867 prefs_common.addressbook_hpaned_pos =
868 gtk_paned_get_position(GTK_PANED(addrbook.hpaned));
870 prefs_common.addressbook_vpaned_pos =
871 gtk_paned_get_position(GTK_PANED(addrbook.vpaned));
875 * Create the address book widgets. The address book contains two CTree widgets: the
876 * address index tree on the left and the address list on the right.
878 * The address index tree displays a hierarchy of interfaces and groups. Each node in
879 * this tree is linked to an address Adapter. Adapters have been created for interfaces,
880 * data sources and folder objects.
882 * The address list displays group, person and email objects. These items are linked
883 * directly to ItemGroup, ItemPerson and ItemEMail objects inside the address book data
886 * In the tradition of MVC architecture, the data stores have been separated from the
887 * GUI components. The addrindex.c file provides the interface to all data stores.
889 static void addressbook_create(void)
895 GtkWidget *ctree_swin;
897 GtkWidget *editaddress_vbox;
898 GtkWidget *clist_vbox;
899 GtkWidget *clist_swin;
906 GtkWidget *statusbar;
917 GtkWidget *close_btn;
918 GtkWidget *tree_popup;
919 GtkWidget *list_popup;
920 GtkItemFactory *tree_factory;
921 GtkItemFactory *list_factory;
922 GtkItemFactory *menu_factory;
926 gchar *index_titles[N_INDEX_COLS];
930 static GdkGeometry geometry;
932 debug_print("Creating addressbook window...\n");
934 index_titles[COL_SOURCES] = _("Sources");
936 /* Address book window */
937 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "addressbook");
938 gtk_window_set_title(GTK_WINDOW(window), _("Address book"));
939 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
940 gtk_widget_realize(window);
942 g_signal_connect(G_OBJECT(window), "delete_event",
943 G_CALLBACK(addressbook_close), NULL);
944 g_signal_connect(G_OBJECT(window), "size_allocate",
945 G_CALLBACK(addressbook_size_allocate_cb), NULL);
946 g_signal_connect(G_OBJECT(window), "key_press_event",
947 G_CALLBACK(key_pressed), NULL);
948 MANAGE_WINDOW_SIGNALS_CONNECT(window);
950 vbox = gtk_vbox_new(FALSE, 0);
951 gtk_container_add(GTK_CONTAINER(window), vbox);
954 n_entries = sizeof(addressbook_entries) /
955 sizeof(addressbook_entries[0]);
956 menubar = menubar_create(window, addressbook_entries, n_entries,
957 "<AddressBook>", NULL);
958 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
959 menu_factory = gtk_item_factory_from_widget(menubar);
961 vbox2 = gtk_vbox_new(FALSE, BORDER_WIDTH);
962 gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
963 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
965 ctree_swin = gtk_scrolled_window_new(NULL, NULL);
966 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ctree_swin),
967 GTK_POLICY_AUTOMATIC,
968 GTK_POLICY_AUTOMATIC);
969 gtk_widget_set_size_request(ctree_swin, COL_FOLDER_WIDTH + 20, -1);
972 ctree = gtk_sctree_new_with_titles(N_INDEX_COLS, 0, index_titles);
973 gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
974 gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
975 gtk_clist_set_column_width(GTK_CLIST(ctree), 0, COL_FOLDER_WIDTH);
976 if (prefs_common.enable_dotted_lines) {
977 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
978 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
979 GTK_CTREE_EXPANDER_SQUARE);
981 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
982 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
983 GTK_CTREE_EXPANDER_TRIANGLE);
985 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
986 gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
987 gtk_clist_set_compare_func(GTK_CLIST(ctree),
988 addressbook_treenode_compare_func);
990 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
991 G_CALLBACK(addressbook_tree_selected), NULL);
992 g_signal_connect(G_OBJECT(ctree), "button_press_event",
993 G_CALLBACK(addressbook_tree_button_pressed),
995 g_signal_connect(G_OBJECT(ctree), "button_release_event",
996 G_CALLBACK(addressbook_tree_button_released),
999 g_signal_connect(G_OBJECT(ctree), "select_row",
1000 G_CALLBACK(addressbook_select_row_tree), NULL);
1002 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
1003 addressbook_drag_types, 1,
1004 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
1005 g_signal_connect(G_OBJECT(ctree), "drag_motion",
1006 G_CALLBACK(addressbook_drag_motion_cb),
1008 g_signal_connect(G_OBJECT(ctree), "drag_leave",
1009 G_CALLBACK(addressbook_drag_leave_cb),
1011 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
1012 G_CALLBACK(addressbook_drag_received_cb),
1014 g_signal_connect(G_OBJECT(ctree), "focus_in_event",
1015 G_CALLBACK(addressbook_address_index_focus_evt_in), NULL);
1016 g_signal_connect(G_OBJECT(ctree), "focus_out_event",
1017 G_CALLBACK(addressbook_address_index_focus_evt_out), NULL);
1019 clist_vbox = gtk_vbox_new(FALSE, 4);
1021 clist_swin = gtk_scrolled_window_new(NULL, NULL);
1022 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
1023 GTK_POLICY_AUTOMATIC,
1024 GTK_POLICY_AUTOMATIC);
1025 gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
1028 clist = gtk_sctree_new_with_titles(N_LIST_COLS, 0, list_titles);
1029 gtk_container_add(GTK_CONTAINER(clist_swin), clist);
1030 gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_EXTENDED);
1031 if (prefs_common.enable_dotted_lines) {
1032 gtk_ctree_set_line_style(GTK_CTREE(clist), GTK_CTREE_LINES_DOTTED);
1033 gtk_ctree_set_expander_style(GTK_CTREE(clist),
1034 GTK_CTREE_EXPANDER_SQUARE);
1036 gtk_ctree_set_line_style(GTK_CTREE(clist), GTK_CTREE_LINES_NONE);
1037 gtk_ctree_set_expander_style(GTK_CTREE(clist),
1038 GTK_CTREE_EXPANDER_TRIANGLE);
1040 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
1041 gtk_ctree_set_indent(GTK_CTREE(clist), CTREE_INDENT);
1042 gtk_clist_set_column_width(GTK_CLIST(clist), COL_NAME,
1044 gtk_clist_set_column_width(GTK_CLIST(clist), COL_ADDRESS,
1046 gtk_widget_set_size_request(clist, -1, 80);
1048 addressbook_sort_list(GTK_CLIST(clist), COL_NAME, GTK_SORT_ASCENDING);
1049 g_signal_connect(G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
1050 "clicked", G_CALLBACK(addressbook_name_clicked), clist);
1051 g_signal_connect(G_OBJECT(GTK_CLIST(clist)->column[COL_ADDRESS].button),
1052 "clicked", G_CALLBACK(addressbook_address_clicked), clist);
1053 g_signal_connect(G_OBJECT(GTK_CLIST(clist)->column[COL_REMARKS].button),
1054 "clicked", G_CALLBACK(addressbook_remarks_clicked), clist);
1055 g_signal_connect(G_OBJECT(clist), "focus_in_event",
1056 G_CALLBACK(addressbook_address_list_focus_evt_in), NULL);
1057 g_signal_connect(G_OBJECT(clist), "focus_out_event",
1058 G_CALLBACK(addressbook_address_list_focus_evt_out), NULL);
1060 for (i = 0; i < N_LIST_COLS; i++)
1061 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button,
1064 g_signal_connect(G_OBJECT(clist), "tree_select_row",
1065 G_CALLBACK(addressbook_list_row_selected), NULL);
1066 g_signal_connect(G_OBJECT(clist), "tree_unselect_row",
1067 G_CALLBACK(addressbook_list_row_unselected), NULL);
1068 g_signal_connect(G_OBJECT(clist), "button_press_event",
1069 G_CALLBACK(addressbook_list_button_pressed),
1071 g_signal_connect(G_OBJECT(clist), "button_release_event",
1072 G_CALLBACK(addressbook_list_button_released),
1074 g_signal_connect(G_OBJECT(clist), "tree_expand",
1075 G_CALLBACK(addressbook_person_expand_node), NULL );
1076 g_signal_connect(G_OBJECT(clist), "tree_collapse",
1077 G_CALLBACK(addressbook_person_collapse_node), NULL );
1078 g_signal_connect(G_OBJECT(clist), "start_drag",
1079 G_CALLBACK(addressbook_start_drag), NULL);
1080 g_signal_connect(G_OBJECT(clist), "drag_data_get",
1081 G_CALLBACK(addressbook_drag_data_get), NULL);
1082 hbox = gtk_hbox_new(FALSE, 4);
1083 gtk_box_pack_start(GTK_BOX(clist_vbox), hbox, FALSE, FALSE, 0);
1085 label = gtk_label_new(_("Lookup name:"));
1086 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1088 entry = gtk_entry_new();
1089 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
1091 address_completion_register_entry(GTK_ENTRY(entry), FALSE);
1093 g_signal_connect(G_OBJECT(entry), "key_press_event",
1094 G_CALLBACK(addressbook_entry_key_pressed),
1097 if (!prefs_common.addressbook_use_editaddress_dialog) {
1098 editaddress_vbox = gtk_vbox_new(FALSE, 4);
1099 vpaned = gtk_vpaned_new();
1100 gtk_paned_pack1(GTK_PANED(vpaned), clist_vbox, FALSE, FALSE);
1101 gtk_paned_pack2(GTK_PANED(vpaned), editaddress_vbox, TRUE, FALSE);
1104 editaddress_vbox = NULL;
1106 hpaned = gtk_hpaned_new();
1107 gtk_box_pack_start(GTK_BOX(vbox2), hpaned, TRUE, TRUE, 0);
1108 gtk_paned_pack1(GTK_PANED(hpaned), ctree_swin, FALSE, FALSE);
1109 if (prefs_common.addressbook_use_editaddress_dialog)
1110 gtk_paned_pack2(GTK_PANED(hpaned), clist_vbox, TRUE, FALSE);
1112 gtk_paned_pack2(GTK_PANED(hpaned), vpaned, TRUE, FALSE);
1115 hsbox = gtk_hbox_new(FALSE, 0);
1116 gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
1117 statusbar = gtk_statusbar_new();
1118 gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
1121 hbbox = gtk_hbutton_box_new();
1122 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
1123 gtk_box_set_spacing(GTK_BOX(hbbox), 2);
1124 gtk_container_set_border_width(GTK_CONTAINER(hbbox), 4);
1125 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
1127 gtkut_stock_button_add_help(hbbox, &help_btn);
1129 edit_btn = gtk_button_new_from_stock(GTK_STOCK_EDIT);
1130 GTK_WIDGET_SET_FLAGS(edit_btn, GTK_CAN_DEFAULT);
1131 gtk_box_pack_start(GTK_BOX(hbbox), edit_btn, TRUE, TRUE, 0);
1132 del_btn = gtk_button_new_from_stock(GTK_STOCK_DELETE);
1133 GTK_WIDGET_SET_FLAGS(del_btn, GTK_CAN_DEFAULT);
1134 gtk_box_pack_start(GTK_BOX(hbbox), del_btn, TRUE, TRUE, 0);
1135 reg_btn = gtk_button_new_from_stock(GTK_STOCK_NEW);
1136 GTK_WIDGET_SET_FLAGS(reg_btn, GTK_CAN_DEFAULT);
1137 gtk_box_pack_start(GTK_BOX(hbbox), reg_btn, TRUE, TRUE, 0);
1140 lup_btn = gtk_button_new_from_stock(GTK_STOCK_FIND);
1141 GTK_WIDGET_SET_FLAGS(lup_btn, GTK_CAN_DEFAULT);
1142 gtk_box_pack_start(GTK_BOX(hbox), lup_btn, TRUE, TRUE, 0);
1144 g_signal_connect(G_OBJECT(help_btn), "clicked",
1145 G_CALLBACK(manual_open_with_anchor_cb),
1146 MANUAL_ANCHOR_ADDRBOOK);
1148 g_signal_connect(G_OBJECT(edit_btn), "clicked",
1149 G_CALLBACK(addressbook_edit_clicked), NULL);
1150 g_signal_connect(G_OBJECT(del_btn), "clicked",
1151 G_CALLBACK(addressbook_del_clicked), NULL);
1152 g_signal_connect(G_OBJECT(reg_btn), "clicked",
1153 G_CALLBACK(addressbook_reg_clicked), NULL);
1154 g_signal_connect(G_OBJECT(lup_btn), "clicked",
1155 G_CALLBACK(addressbook_lup_clicked), NULL);
1157 to_btn = gtk_button_new_with_label
1158 (prefs_common_translated_header_name("To:"));
1159 GTK_WIDGET_SET_FLAGS(to_btn, GTK_CAN_DEFAULT);
1160 gtk_box_pack_start(GTK_BOX(hbbox), to_btn, TRUE, TRUE, 0);
1161 cc_btn = gtk_button_new_with_label
1162 (prefs_common_translated_header_name("Cc:"));
1163 GTK_WIDGET_SET_FLAGS(cc_btn, GTK_CAN_DEFAULT);
1164 gtk_box_pack_start(GTK_BOX(hbbox), cc_btn, TRUE, TRUE, 0);
1165 bcc_btn = gtk_button_new_with_label
1166 (prefs_common_translated_header_name("Bcc:"));
1167 GTK_WIDGET_SET_FLAGS(bcc_btn, GTK_CAN_DEFAULT);
1168 gtk_box_pack_start(GTK_BOX(hbbox), bcc_btn, TRUE, TRUE, 0);
1170 close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
1171 GTK_WIDGET_SET_FLAGS(close_btn, GTK_CAN_DEFAULT);
1172 gtk_box_pack_start(GTK_BOX(hbbox), close_btn, TRUE, TRUE, 0);
1174 g_signal_connect(G_OBJECT(to_btn), "clicked",
1175 G_CALLBACK(addressbook_to_clicked),
1176 GINT_TO_POINTER(COMPOSE_TO));
1177 g_signal_connect(G_OBJECT(cc_btn), "clicked",
1178 G_CALLBACK(addressbook_to_clicked),
1179 GINT_TO_POINTER(COMPOSE_CC));
1180 g_signal_connect(G_OBJECT(bcc_btn), "clicked",
1181 G_CALLBACK(addressbook_to_clicked),
1182 GINT_TO_POINTER(COMPOSE_BCC));
1183 g_signal_connect(G_OBJECT(close_btn), "clicked",
1184 G_CALLBACK(addressbook_close_clicked), NULL);
1186 /* Build icons for interface */
1187 stock_pixmap_gdk( window, STOCK_PIXMAP_INTERFACE,
1188 &interfacexpm, &interfacexpmmask );
1190 /* Build control tables */
1191 addrbookctl_build_map(window);
1192 addrbookctl_build_iflist();
1193 addrbookctl_build_ifselect();
1195 addrbook.clist = NULL;
1197 /* Add each interface into the tree as a root level folder */
1198 nodeIf = _addressInterfaceList_;
1200 AdapterInterface *adapter = nodeIf->data;
1201 AddressInterface *iface = adapter->interface;
1202 nodeIf = g_list_next(nodeIf);
1204 if(iface->useInterface) {
1205 AddressTypeControlItem *atci = adapter->atci;
1206 text = atci->displayName;
1208 gtk_sctree_insert_node( GTK_CTREE(ctree),
1209 NULL, NULL, &text, FOLDER_SPACING,
1210 interfacexpm, interfacexpmmask,
1211 interfacexpm, interfacexpmmask,
1213 menu_set_sensitive( menu_factory, atci->menuCommand, adapter->haveLibrary );
1214 gtk_ctree_node_set_row_data_full(
1215 GTK_CTREE(ctree), adapter->treeNode, adapter,
1216 addressbook_free_treenode );
1221 n_entries = sizeof(addressbook_tree_popup_entries) /
1222 sizeof(addressbook_tree_popup_entries[0]);
1223 tree_popup = menu_create_items(addressbook_tree_popup_entries,
1225 "<AddressBookTree>", &tree_factory,
1227 n_entries = sizeof(addressbook_list_popup_entries) /
1228 sizeof(addressbook_list_popup_entries[0]);
1229 list_popup = menu_create_items(addressbook_list_popup_entries,
1231 "<AddressBookList>", &list_factory,
1234 addrbook.window = window;
1235 addrbook.hpaned = hpaned;
1236 addrbook.vpaned = vpaned;
1237 addrbook.menubar = menubar;
1238 addrbook.ctree = ctree;
1241 addrbook.editaddress_vbox = editaddress_vbox;
1242 addrbook.clist = clist;
1243 addrbook.label = label;
1244 addrbook.entry = entry;
1245 addrbook.statusbar = statusbar;
1246 addrbook.status_cid = gtk_statusbar_get_context_id(
1247 GTK_STATUSBAR(statusbar), "Addressbook Window" );
1249 addrbook.help_btn = help_btn;
1250 addrbook.edit_btn = edit_btn;
1251 addrbook.del_btn = del_btn;
1252 addrbook.reg_btn = reg_btn;
1253 addrbook.lup_btn = lup_btn;
1254 addrbook.to_btn = to_btn;
1255 addrbook.cc_btn = cc_btn;
1256 addrbook.bcc_btn = bcc_btn;
1258 addrbook.tree_popup = tree_popup;
1259 addrbook.list_popup = list_popup;
1260 addrbook.tree_factory = tree_factory;
1261 addrbook.list_factory = list_factory;
1262 addrbook.menu_factory = menu_factory;
1264 addrbook.listSelected = NULL;
1266 if (!geometry.min_height) {
1267 geometry.min_width = ADDRESSBOOK_WIDTH;
1268 geometry.min_height = ADDRESSBOOK_HEIGHT;
1271 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
1273 gtk_widget_set_size_request(window, prefs_common.addressbookwin_width,
1274 prefs_common.addressbookwin_height);
1276 if (!prefs_common.addressbook_use_editaddress_dialog) {
1277 if (prefs_common.addressbook_vpaned_pos > 0)
1278 gtk_paned_set_position(GTK_PANED(vpaned),
1279 prefs_common.addressbook_vpaned_pos);
1281 if (prefs_common.addressbook_hpaned_pos > 0)
1282 gtk_paned_set_position(GTK_PANED(hpaned),
1283 prefs_common.addressbook_hpaned_pos);
1286 gtk_widget_show_all(window);
1290 * Close address book window and save to file(s).
1292 static gint addressbook_close( void ) {
1293 address_completion_end(addrbook.window);
1294 if (!prefs_common.addressbook_use_editaddress_dialog)
1295 addressbook_edit_person_invalidate(NULL, NULL, NULL);
1297 addressbook_pane_save_position();
1299 gtk_widget_hide(addrbook.window);
1300 addressbook_export_to_file();
1305 * Display message in status line.
1306 * \param msg Message to display.
1308 static void addressbook_status_show( gchar *msg ) {
1309 if( addrbook.statusbar != NULL ) {
1311 GTK_STATUSBAR(addrbook.statusbar),
1312 addrbook.status_cid );
1315 GTK_STATUSBAR(addrbook.statusbar),
1316 addrbook.status_cid, msg );
1321 static void addressbook_ds_show_message( AddressDataSource *ds ) {
1325 *addressbook_msgbuf = '\0';
1327 name = addrindex_ds_get_name( ds );
1328 retVal = addrindex_ds_get_status_code( ds );
1329 if( retVal == MGU_SUCCESS ) {
1330 g_snprintf( addressbook_msgbuf,
1331 sizeof(addressbook_msgbuf), "%s", name );
1334 desc = addressbook_err2string( _lutErrorsGeneral_, retVal );
1335 g_snprintf( addressbook_msgbuf,
1336 sizeof(addressbook_msgbuf), "%s: %s", name, desc );
1339 addressbook_status_show( addressbook_msgbuf );
1342 static void addressbook_button_set_sensitive(void)
1344 gboolean to_sens = FALSE;
1345 gboolean cc_sens = FALSE;
1346 gboolean bcc_sens = FALSE;
1348 if (!addrbook.window) return;
1350 if (addrbook.target_compose) {
1356 gtk_widget_set_sensitive(addrbook.to_btn, to_sens);
1357 gtk_widget_set_sensitive(addrbook.cc_btn, cc_sens);
1358 gtk_widget_set_sensitive(addrbook.bcc_btn, bcc_sens);
1361 static void addressbook_edit_clicked(GtkButton *button, gpointer data)
1363 addressbook_edit_address_cb(NULL, 0, NULL);
1366 static gboolean find_person(AddrSelectItem *item_a, ItemPerson *person)
1368 return ((ItemPerson *)item_a->addressItem == person)?0:-1;
1372 * Delete one or more objects from address list.
1374 static void addressbook_del_clicked(GtkButton *button, gpointer data)
1376 GtkCTree *clist = GTK_CTREE(addrbook.clist);
1377 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
1378 AddressObject *pobj;
1379 AdapterDSource *ads = NULL;
1380 GtkCTreeNode *nodeList;
1383 AddressBookFile *abf = NULL;
1384 AddressDataSource *ds = NULL;
1385 AddressInterface *iface;
1386 AddrItemObject *aio;
1387 AddrSelectItem *item;
1389 gboolean refreshList = FALSE;
1391 pobj = gtk_ctree_node_get_row_data(ctree, addrbook.opened );
1392 g_return_if_fail(pobj != NULL);
1394 /* Test whether anything selected for deletion */
1395 nodeList = addrbook.listSelected;
1397 aio = gtk_ctree_node_get_row_data( clist, nodeList );
1398 if( aio == NULL) return;
1399 ds = addressbook_find_datasource( addrbook.treeSelected );
1400 if( ds == NULL ) return;
1402 /* Test for read only */
1403 iface = ds->interface;
1404 if( iface->readOnly ) {
1405 alertpanel( _("Delete address(es)"),
1406 _("This address data is readonly and cannot be deleted."),
1407 GTK_STOCK_CLOSE, NULL, NULL );
1411 /* Test whether Ok to proceed */
1413 if( pobj->type == ADDR_DATASOURCE ) {
1414 ads = ADAPTER_DSOURCE(pobj);
1415 if( ads->subType == ADDR_BOOK ) procFlag = TRUE;
1417 else if( pobj->type == ADDR_ITEM_FOLDER ) {
1420 else if( pobj->type == ADDR_ITEM_GROUP ) {
1423 if( ! procFlag ) return;
1424 abf = ds->rawDataSource;
1425 if( abf == NULL ) return;
1427 gtk_clist_freeze(GTK_CLIST(addrbook.clist));
1428 g_signal_handlers_block_by_func
1429 (G_OBJECT(addrbook.clist),
1430 G_CALLBACK(addressbook_list_row_unselected), NULL);
1432 /* Process deletions */
1433 if( pobj->type == ADDR_DATASOURCE || pobj->type == ADDR_ITEM_FOLDER ) {
1434 GList *groups = NULL, *persons = NULL, *emails = NULL;
1435 gboolean group_delete = TRUE;
1436 /* Items inside folders */
1437 list = addrselect_get_list( _addressSelect_ );
1438 /* Confirm deletion */
1442 node = g_list_next( node );
1443 aio = ( AddrItemObject * ) item->addressItem;
1444 if( aio->type == ADDR_ITEM_PERSON || aio->type == ADDR_ITEM_EMAIL ) {
1445 group_delete = FALSE;
1450 aval = alertpanel( _("Delete group"),
1451 _("Really delete the group(s)?\n"
1452 "The addresses it contains will not be lost."),
1453 GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL );
1454 if( aval != G_ALERTALTERNATE ) {
1458 aval = alertpanel( _("Delete address(es)"),
1459 _("Really delete the address(es)?"),
1460 GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL );
1461 if( aval != G_ALERTALTERNATE ) {
1466 /* first, set lists of groups and persons to remove */
1470 node = g_list_next( node );
1471 aio = ( AddrItemObject * ) item->addressItem;
1474 if( aio->type == ADDR_ITEM_GROUP ) {
1475 groups = g_list_prepend(groups, item);
1477 else if( aio->type == ADDR_ITEM_PERSON ) {
1478 persons = g_list_prepend(persons, item);
1481 /* then set list of emails to remove *if* they're not children of
1482 * persons to remove */
1486 node = g_list_next( node );
1487 aio = ( AddrItemObject * ) item->addressItem;
1490 if( aio->type == ADDR_ITEM_EMAIL ) {
1491 ItemEMail *sitem = ( ItemEMail * ) aio;
1492 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
1493 if (!g_list_find_custom(persons, person, (GCompareFunc)(find_person))) {
1494 emails = g_list_prepend(emails, item);
1496 /* else, the email will be removed via the parent person */
1499 /* then delete groups */
1503 node = g_list_next( node );
1504 aio = ( AddrItemObject * ) item->addressItem;
1507 if( aio->type == ADDR_ITEM_GROUP ) {
1508 ItemGroup *item = ( ItemGroup * ) aio;
1509 GtkCTreeNode *nd = NULL;
1510 nd = addressbook_find_group_node( addrbook.opened, item );
1511 item = addrbook_remove_group( abf, item );
1513 addritem_free_item_group( item );
1515 /* Remove group from parent node */
1516 gtk_ctree_remove_node( ctree, nd );
1520 /* then delete persons */
1524 node = g_list_next( node );
1525 aio = ( AddrItemObject * ) item->addressItem;
1528 if( aio->type == ADDR_ITEM_PERSON ) {
1529 ItemPerson *item = ( ItemPerson * ) aio;
1530 item->status = DELETE_ENTRY;
1531 addressbook_folder_remove_one_person( clist, item );
1532 if (pobj->type == ADDR_ITEM_FOLDER)
1533 addritem_folder_remove_person(ADAPTER_FOLDER(pobj)->itemFolder, item);
1534 item = addrbook_remove_person( abf, item );
1536 if (ds && ds->type == ADDR_IF_LDAP) {
1537 LdapServer *server = ds->rawDataSource;
1538 ldapsvr_set_modified(server, TRUE);
1539 ldapsvr_update_book(server, item);
1543 gchar *filename = addritem_person_get_picture(item);
1544 if (filename && is_file_exist(filename))
1545 claws_unlink(filename);
1547 addritem_free_item_person( item );
1551 /* then delete emails */
1555 node = g_list_next( node );
1556 aio = ( AddrItemObject * ) item->addressItem;
1560 if( aio->type == ADDR_ITEM_EMAIL ) {
1561 ItemEMail *sitem = ( ItemEMail * ) aio;
1562 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
1563 sitem = addrbook_person_remove_email( abf, person, sitem );
1565 addrcache_remove_email(abf->addressCache, sitem);
1566 addritem_free_item_email( sitem );
1568 addressbook_folder_refresh_one_person( clist, person );
1571 g_list_free( groups );
1572 g_list_free( persons );
1573 g_list_free( emails );
1574 g_list_free( list );
1575 addressbook_list_select_clear();
1577 gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened);
1578 addressbook_set_clist(
1579 gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
1583 addrbook_set_dirty(abf, TRUE);
1584 addressbook_export_to_file();
1585 addressbook_list_menu_setup();
1588 else if( pobj->type == ADDR_ITEM_GROUP ) {
1589 /* Items inside groups */
1590 list = addrselect_get_list( _addressSelect_ );
1594 node = g_list_next( node );
1595 aio = ( AddrItemObject * ) item->addressItem;
1596 if( aio->type == ADDR_ITEM_EMAIL ) {
1597 ItemEMail *item = ( ItemEMail * ) aio;
1598 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(item);
1599 item = addrbook_person_remove_email( abf, person, item );
1601 addritem_free_item_email( item );
1605 g_list_free( list );
1606 addressbook_list_select_clear();
1607 gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened);
1608 addressbook_set_clist(
1609 gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
1613 addrbook_set_dirty(abf, TRUE);
1614 addressbook_export_to_file();
1615 addressbook_list_menu_setup();
1619 gtk_ctree_node_set_row_data( clist, nodeList, NULL );
1620 gtk_ctree_remove_node( clist, nodeList );
1622 gtk_clist_thaw(GTK_CLIST(addrbook.clist));
1623 g_signal_handlers_unblock_by_func
1624 (G_OBJECT(addrbook.clist),
1625 G_CALLBACK(addressbook_list_row_unselected), NULL);
1628 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
1630 addressbook_new_address_cb( NULL, 0, NULL );
1633 static gchar *addressbook_format_address( AddrItemObject * aio ) {
1636 gchar *address = NULL;
1638 if( aio->type == ADDR_ITEM_EMAIL ) {
1639 ItemPerson *person = NULL;
1640 ItemEMail *email = ( ItemEMail * ) aio;
1642 person = ( ItemPerson * ) ADDRITEM_PARENT(email);
1643 if( email->address ) {
1644 if( ADDRITEM_NAME(email) ) {
1645 name = ADDRITEM_NAME(email);
1646 if( *name == '\0' ) {
1647 name = ADDRITEM_NAME(person);
1650 else if( ADDRITEM_NAME(person) ) {
1651 name = ADDRITEM_NAME(person);
1654 buf = g_strdup( email->address );
1656 address = email->address;
1659 else if( aio->type == ADDR_ITEM_PERSON ) {
1660 ItemPerson *person = ( ItemPerson * ) aio;
1661 GList *node = person->listEMail;
1663 name = ADDRITEM_NAME(person);
1665 ItemEMail *email = ( ItemEMail * ) node->data;
1666 address = email->address;
1670 if( name && name[0] != '\0' ) {
1671 if( strchr_with_skip_quote( name, '"', ',' ) )
1672 buf = g_strdup_printf( "\"%s\" <%s>", name, address );
1674 buf = g_strdup_printf( "%s <%s>", name, address );
1677 buf = g_strdup( address );
1684 static void addressbook_to_clicked(GtkButton *button, gpointer data)
1688 AddrSelectItem *item;
1689 AddrItemObject *aio;
1692 compose = addrbook.target_compose;
1693 if( ! compose ) return;
1695 /* Nothing selected, but maybe there is something in text entry */
1696 addr = (char *)gtk_entry_get_text( GTK_ENTRY( addrbook.entry) );
1698 compose_entry_append(
1699 compose, addr, (ComposeEntryType)data );
1702 /* Select from address list */
1703 list = addrselect_get_list( _addressSelect_ );
1708 node = g_list_next( node );
1709 aio = item->addressItem;
1710 if( aio->type == ADDR_ITEM_PERSON ||
1711 aio->type == ADDR_ITEM_EMAIL ) {
1712 addr = addressbook_format_address( aio );
1713 compose_entry_append(
1714 compose, addr, (ComposeEntryType) data );
1717 else if( aio->type == ADDR_ITEM_GROUP ) {
1718 ItemGroup *group = ( ItemGroup * ) aio;
1719 GList *nodeMail = group->listEMail;
1721 ItemEMail *email = nodeMail->data;
1723 addr = addressbook_format_address(
1724 ( AddrItemObject * ) email );
1725 compose_entry_append(
1726 compose, addr, (ComposeEntryType) data );
1728 nodeMail = g_list_next( nodeMail );
1733 AddressObject *obj = NULL;
1735 obj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), addrbook.treeSelected );
1737 if( obj && obj->type == ADDR_ITEM_GROUP ) {
1738 ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup;
1739 GList *nodeMail = itemGroup->listEMail;
1741 ItemEMail *email = nodeMail->data;
1743 addr = addressbook_format_address(
1744 ( AddrItemObject * ) email );
1745 compose_entry_append(
1746 compose, addr, (ComposeEntryType) data );
1748 nodeMail = g_list_next( nodeMail );
1752 g_list_free( list );
1755 static void addressbook_menubar_set_sensitive( gboolean sensitive ) {
1756 menu_set_sensitive( addrbook.menu_factory, "/Book/Edit book", sensitive );
1757 menu_set_sensitive( addrbook.menu_factory, "/Book/Delete book", sensitive );
1758 menu_set_sensitive( addrbook.menu_factory, "/Book/New Folder", sensitive );
1760 menu_set_sensitive( addrbook.menu_factory, "/Address/Select all", TRUE );
1761 menu_set_sensitive( addrbook.menu_factory, "/Address/Cut", sensitive );
1762 menu_set_sensitive( addrbook.menu_factory, "/Address/Copy", sensitive );
1763 menu_set_sensitive( addrbook.menu_factory, "/Address/Paste", sensitive );
1765 menu_set_sensitive( addrbook.menu_factory, "/Address/New Address", sensitive );
1766 menu_set_sensitive( addrbook.menu_factory, "/Address/New Group", sensitive );
1767 menu_set_sensitive( addrbook.menu_factory, "/Address/Mail To", sensitive );
1768 gtk_widget_set_sensitive( addrbook.edit_btn, sensitive );
1769 gtk_widget_set_sensitive( addrbook.del_btn, sensitive );
1772 static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCTreeNode *node ) {
1773 gboolean canEdit = FALSE;
1774 gboolean canDelete = TRUE;
1775 gboolean canAdd = FALSE;
1776 gboolean canEditTr = TRUE;
1777 gboolean editAddress = FALSE;
1778 gboolean canExport = TRUE;
1779 AddressTypeControlItem *atci = NULL;
1780 AddressDataSource *ds = NULL;
1781 AddressInterface *iface = NULL;
1783 if( obj == NULL ) return;
1784 if( obj->type == ADDR_INTERFACE ) {
1785 AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
1786 iface = adapter->interface;
1788 if( iface->haveLibrary ) {
1789 /* Enable appropriate File / New command */
1790 atci = adapter->atci;
1791 menu_set_sensitive( addrbook.menu_factory, atci->menuCommand, TRUE );
1794 canEditTr = canExport = FALSE;
1796 else if( obj->type == ADDR_DATASOURCE ) {
1797 AdapterDSource *ads = ADAPTER_DSOURCE(obj);
1798 ds = ads->dataSource;
1799 iface = ds->interface;
1800 if( ! iface->readOnly ) {
1801 canAdd = canEdit = editAddress = canDelete = TRUE;
1803 if( ! iface->haveLibrary ) {
1804 canAdd = canEdit = editAddress = canExport = canDelete = FALSE;
1807 else if( obj->type == ADDR_ITEM_FOLDER ) {
1808 ds = addressbook_find_datasource( addrbook.treeSelected );
1810 iface = ds->interface;
1811 if( iface->readOnly ) {
1816 canAdd = editAddress = TRUE;
1820 else if( obj->type == ADDR_ITEM_GROUP ) {
1821 ds = addressbook_find_datasource( addrbook.treeSelected );
1823 iface = ds->interface;
1824 if( ! iface->readOnly ) {
1830 if( addrbook.listSelected == NULL )
1834 menu_set_sensitive( addrbook.menu_factory, "/Address/New Address", editAddress );
1835 menu_set_sensitive( addrbook.menu_factory, "/Address/New Group", canAdd );
1836 menu_set_sensitive( addrbook.menu_factory, "/Book/New Folder", canAdd );
1837 gtk_widget_set_sensitive( addrbook.reg_btn, editAddress );
1840 menu_set_sensitive( addrbook.menu_factory, "/Address/Edit", canEdit );
1841 menu_set_sensitive( addrbook.menu_factory, "/Address/Delete", canDelete );
1842 gtk_widget_set_sensitive( addrbook.edit_btn, canEdit );
1843 gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
1845 menu_set_sensitive( addrbook.menu_factory, "/Book/Edit book", canEditTr );
1846 menu_set_sensitive( addrbook.menu_factory, "/Book/Delete book", canEditTr );
1849 menu_set_sensitive( addrbook.menu_factory, "/Tools/Export HTML...", canExport );
1850 menu_set_sensitive( addrbook.menu_factory, "/Tools/Export LDIF...", canExport );
1854 * Address book tree callback function that responds to selection of tree
1857 * \param ctree Tree widget.
1858 * \param node Node that was selected.
1859 * \param column Column number where selected occurred.
1860 * \param data Pointer to user data.
1862 static void addressbook_tree_selected(GtkCTree *ctree, GtkCTreeNode *node,
1863 gint column, gpointer data)
1865 AddressObject *obj = NULL;
1866 AdapterDSource *ads = NULL;
1867 AddressDataSource *ds = NULL;
1868 ItemFolder *rootFolder = NULL;
1869 AddressObjectType aot;
1871 addrbook.treeSelected = node;
1872 addrbook.listSelected = NULL;
1873 addressbook_status_show( "" );
1874 if( addrbook.entry != NULL ) gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
1876 if( node ) obj = gtk_ctree_node_get_row_data( ctree, node );
1878 addressbook_set_clist(NULL, TRUE);
1881 addrbook.opened = node;
1883 if( obj->type == ADDR_DATASOURCE ) {
1884 /* Read from file */
1885 static gboolean tVal = TRUE;
1887 ads = ADAPTER_DSOURCE(obj);
1888 if( ads == NULL ) return;
1889 ds = ads->dataSource;
1890 if( ds == NULL ) return;
1892 if( addrindex_ds_get_modify_flag( ds ) ) {
1893 addrindex_ds_read_data( ds );
1896 if( ! addrindex_ds_get_read_flag( ds ) ) {
1897 addrindex_ds_read_data( ds );
1899 addressbook_ds_show_message( ds );
1901 if( ! addrindex_ds_get_access_flag( ds ) ) {
1902 /* Remove existing folders and groups */
1903 gtk_clist_freeze( GTK_CLIST(ctree) );
1904 addressbook_tree_remove_children( ctree, node );
1905 gtk_clist_thaw( GTK_CLIST(ctree) );
1907 /* Load folders into the tree */
1908 rootFolder = addrindex_ds_get_root_folder( ds );
1909 if( ds && ds->type == ADDR_IF_JPILOT ) {
1910 aot = ADDR_CATEGORY;
1912 else if( ds && ds->type == ADDR_IF_LDAP ) {
1913 aot = ADDR_LDAP_QUERY;
1916 aot = ADDR_ITEM_FOLDER;
1918 addressbook_node_add_folder( node, ds, rootFolder, aot );
1919 addrindex_ds_set_access_flag( ds, &tVal );
1920 gtk_ctree_expand( ctree, node );
1923 addressbook_set_clist(NULL, TRUE);
1926 /* Update address list */
1927 g_signal_handlers_block_by_func
1929 G_CALLBACK(addressbook_tree_selected), NULL);
1930 addressbook_set_clist( obj, FALSE );
1931 g_signal_handlers_unblock_by_func
1933 G_CALLBACK(addressbook_tree_selected), NULL);
1934 if (!prefs_common.addressbook_use_editaddress_dialog)
1935 addressbook_edit_person_invalidate(NULL, NULL, NULL);
1937 /* Setup main menu selections */
1938 addressbook_menubar_set_sensitive( FALSE );
1939 addressbook_menuitem_set_sensitive( obj, node );
1940 addressbook_list_select_clear();
1941 addressbook_list_menu_setup();
1946 * Setup address list popup menu items. Items are enabled or disabled as
1949 static void addressbook_list_menu_setup( void ) {
1950 GtkCTree *clist = NULL;
1951 AddressObject *pobj = NULL;
1952 AddressObject *obj = NULL;
1953 AdapterDSource *ads = NULL;
1954 AddressInterface *iface = NULL;
1955 AddressDataSource *ds = NULL;
1956 gboolean canEdit = FALSE;
1957 gboolean canDelete = FALSE;
1958 gboolean canCut = FALSE;
1959 gboolean canCopy = FALSE;
1960 gboolean canPaste = FALSE;
1961 gboolean canBrowse = FALSE;
1963 pobj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), addrbook.treeSelected );
1964 if( pobj == NULL ) return;
1966 clist = GTK_CTREE(addrbook.clist);
1967 obj = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
1968 if( obj == NULL ) canEdit = FALSE;
1970 menu_set_insensitive_all( GTK_MENU_SHELL(addrbook.list_popup) );
1971 menu_set_sensitive( addrbook.list_factory, "/Select all", TRUE );
1973 if( pobj->type == ADDR_DATASOURCE ) {
1974 /* Parent object is a data source */
1975 ads = ADAPTER_DSOURCE(pobj);
1976 ds = ads->dataSource;
1979 iface = ds->interface;
1982 if( ! iface->readOnly ) {
1983 menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
1984 if (iface->type != ADDR_IF_LDAP)
1985 menu_set_sensitive( addrbook.list_factory, "/New Group", TRUE );
1986 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
1989 canDelete = canEdit;
1992 else if( pobj->type != ADDR_INTERFACE ) {
1993 /* Parent object is not an interface */
1994 ds = addressbook_find_datasource( addrbook.treeSelected );
1997 iface = ds->interface;
2000 if( ! iface->readOnly ) {
2001 /* Folder or group */
2002 if( pobj->type == ADDR_ITEM_FOLDER || pobj->type == ADDR_ITEM_GROUP ) {
2003 menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
2004 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2005 if( obj ) canEdit = TRUE;
2008 if( pobj->type == ADDR_ITEM_FOLDER ) {
2009 if (iface->type != ADDR_IF_LDAP)
2010 menu_set_sensitive( addrbook.list_factory, "/New Group", TRUE );
2011 if( obj ) canEdit = TRUE;
2013 canDelete = canEdit;
2015 if( iface->type == ADDR_IF_LDAP ) {
2016 if( obj ) canBrowse = TRUE;
2023 /* Enable cut and paste */
2024 if( ! addrclip_is_empty( _clipBoard_ ) )
2026 if( ! addrselect_test_empty( _addressSelect_ ) )
2028 /* Enable copy if something is selected */
2029 if( ! addrselect_test_empty( _addressSelect_ ) )
2033 /* Disable edit or browse if more than one row selected */
2034 if( GTK_CLIST(clist)->selection && GTK_CLIST(clist)->selection->next ) {
2039 /* Forbid write changes when read-only */
2040 if( iface && iface->readOnly ) {
2046 /* Now go finalize menu items */
2047 menu_set_sensitive( addrbook.list_factory, "/Edit", canEdit );
2048 menu_set_sensitive( addrbook.list_factory, "/Delete", canDelete );
2050 menu_set_sensitive( addrbook.list_factory, "/Cut", canCut );
2051 menu_set_sensitive( addrbook.list_factory, "/Copy", canCopy );
2052 menu_set_sensitive( addrbook.list_factory, "/Paste", canPaste );
2054 menu_set_sensitive( addrbook.list_factory, "/Mail To", canCopy );
2056 menu_set_sensitive( addrbook.menu_factory, "/Address/Cut", canCut );
2057 menu_set_sensitive( addrbook.menu_factory, "/Address/Copy", canCopy );
2058 menu_set_sensitive( addrbook.menu_factory, "/Address/Paste", canPaste );
2060 menu_set_sensitive( addrbook.menu_factory, "/Address/Edit", canEdit );
2061 menu_set_sensitive( addrbook.menu_factory, "/Address/Delete", canDelete );
2062 menu_set_sensitive( addrbook.menu_factory, "/Address/Mail To", canCopy );
2064 gtk_widget_set_sensitive( addrbook.edit_btn, canEdit );
2065 gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
2068 menu_set_sensitive( addrbook.list_factory, "/Browse Entry", canBrowse );
2072 static void addressbook_select_row_tree (GtkCTree *ctree,
2080 * Add list of items into tree node below specified tree node.
2081 * \param treeNode Tree node.
2082 * \param ds Data source.
2083 * \param listItems List of items.
2085 static void addressbook_treenode_add_list(
2086 GtkCTreeNode *treeNode, AddressDataSource *ds, GList *listItems )
2092 AddrItemObject *aio;
2096 if( ADDRESS_OBJECT_TYPE(aio) == ITEMTYPE_GROUP ) {
2099 group = ( ItemGroup * ) aio;
2100 nn = addressbook_node_add_group( treeNode, ds, group );
2102 else if( ADDRESS_OBJECT_TYPE(aio) == ITEMTYPE_FOLDER ) {
2105 folder = ( ItemFolder * ) aio;
2106 nn = addressbook_node_add_folder(
2107 treeNode, ds, folder, ADDR_ITEM_FOLDER );
2109 node = g_list_next( node );
2113 static void addressbook_select_all_cb( void ) {
2114 gtk_clist_select_all(GTK_CLIST(addrbook.clist));
2118 * Cut from address list widget.
2120 static void addressbook_clip_cut_cb( void ) {
2121 _clipBoard_->cutFlag = TRUE;
2122 addrclip_clear( _clipBoard_ );
2123 addrclip_add( _clipBoard_, _addressSelect_ );
2124 /* addrclip_list_show( _clipBoard_, stdout ); */
2128 * Copy from address list widget.
2130 static void addressbook_clip_copy_cb( void ) {
2131 _clipBoard_->cutFlag = FALSE;
2132 addrclip_clear( _clipBoard_ );
2133 addrclip_add( _clipBoard_, _addressSelect_ );
2134 /* addrclip_list_show( _clipBoard_, stdout ); */
2138 * Paste clipboard into address list widget.
2140 static void addressbook_clip_paste_cb( void ) {
2141 GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
2142 AddressObject *pobj = NULL;
2143 AddressDataSource *ds = NULL;
2144 AddressBookFile *abf = NULL;
2145 ItemFolder *folder = NULL;
2146 GList *folderGroup = NULL;
2148 ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
2149 if( ds == NULL ) return;
2150 if( addrindex_ds_get_readonly( ds ) ) {
2151 alertpanel_error( _("Cannot paste. Target address book is readonly.") );
2155 pobj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
2157 if( pobj->type == ADDR_ITEM_FOLDER ) {
2158 folder = ADAPTER_FOLDER(pobj)->itemFolder;
2160 else if( pobj->type == ADDR_ITEM_GROUP ) {
2161 alertpanel_error( _("Cannot paste into an address group.") );
2166 /* Get an address book */
2167 abf = addressbook_get_book_file();
2168 if( abf == NULL ) return;
2170 if( _clipBoard_->cutFlag ) {
2172 folderGroup = addrclip_paste_cut( _clipBoard_, abf, folder );
2174 /* Remove all groups and folders in clipboard from tree node */
2175 addressbook_treenode_remove_item();
2177 /* Remove all "cut" items */
2178 addrclip_delete_item( _clipBoard_ );
2180 /* Clear clipboard - cut items??? */
2181 addrclip_clear( _clipBoard_ );
2185 folderGroup = addrclip_paste_copy( _clipBoard_, abf, folder );
2188 /* addrclip_list_show( _clipBoard_, stdout ); */
2190 /* Update tree by inserting node for each folder or group */
2191 addressbook_treenode_add_list(
2192 addrbook.treeSelected, ds, folderGroup );
2193 gtk_ctree_expand( ctree, addrbook.treeSelected );
2194 g_list_free( folderGroup );
2198 /* Display items pasted */
2199 gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened );
2200 addressbook_set_clist(
2201 gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
2209 * Add current treenode object to clipboard. Note that widget only allows
2210 * one entry from the tree list to be selected.
2212 static void addressbook_treenode_to_clipboard( void ) {
2213 AddressObject *obj = NULL;
2214 AddressDataSource *ds = NULL;
2215 AddrSelectItem *item;
2216 GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
2219 node = addrbook.treeSelected;
2220 if( node == NULL ) return;
2221 obj = gtk_ctree_node_get_row_data( ctree, node );
2222 if( obj == NULL ) return;
2224 ds = addressbook_find_datasource( node );
2225 if( ds == NULL ) return;
2228 if( obj->type == ADDR_ITEM_FOLDER ) {
2229 AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2230 ItemFolder *folder = adapter->itemFolder;
2232 item = addrselect_create_node( obj );
2233 item->uid = g_strdup( ADDRITEM_ID(folder) );
2235 else if( obj->type == ADDR_ITEM_GROUP ) {
2236 AdapterGroup *adapter = ADAPTER_GROUP(obj);
2237 ItemGroup *group = adapter->itemGroup;
2239 item = addrselect_create_node( obj );
2240 item->uid = g_strdup( ADDRITEM_ID(group) );
2242 else if( obj->type == ADDR_DATASOURCE ) {
2244 item = addrselect_create_node( obj );
2249 /* Clear existing list and add item into list */
2252 addressbook_list_select_clear();
2253 cacheID = addrindex_get_cache_id( _addressIndex_, ds );
2254 addrselect_list_add( _addressSelect_, item, cacheID );
2260 * Cut from tree widget.
2262 static void addressbook_treenode_cut_cb( void ) {
2263 _clipBoard_->cutFlag = TRUE;
2264 addressbook_treenode_to_clipboard();
2265 addrclip_clear( _clipBoard_ );
2266 addrclip_add( _clipBoard_, _addressSelect_ );
2267 /* addrclip_list_show( _clipBoard_, stdout ); */
2271 * Copy from tree widget.
2273 static void addressbook_treenode_copy_cb( void ) {
2274 _clipBoard_->cutFlag = FALSE;
2275 addressbook_treenode_to_clipboard();
2276 addrclip_clear( _clipBoard_ );
2277 addrclip_add( _clipBoard_, _addressSelect_ );
2278 /* addrclip_list_show( _clipBoard_, stdout ); */
2282 * Paste clipboard into address tree widget.
2284 static void addressbook_treenode_paste_cb( void ) {
2285 addressbook_clip_paste_cb();
2289 * Clear selected entries in clipboard.
2291 static void addressbook_list_select_clear( void ) {
2292 addrselect_list_clear( _addressSelect_ );
2296 * Add specified address item to selected address list.
2297 * \param aio Address item object.
2298 * \param ds Datasource.
2300 static void addressbook_list_select_add( AddrItemObject *aio, AddressDataSource *ds ) {
2303 if( ds == NULL ) return;
2304 cacheID = addrindex_get_cache_id( _addressIndex_, ds );
2305 addrselect_list_add_obj( _addressSelect_, aio, cacheID );
2310 * Remove specified address item from selected address list.
2311 * \param aio Address item object.
2313 static void addressbook_list_select_remove( AddrItemObject *aio ) {
2314 addrselect_list_remove( _addressSelect_, aio );
2318 * Invoke EMail compose window with addresses in selected address list.
2320 static void addressbook_mail_to_cb( void ) {
2323 if( ! addrselect_test_empty( _addressSelect_ ) ) {
2324 listAddress = addrselect_build_list( _addressSelect_ );
2325 compose_new_with_list( NULL, listAddress );
2326 mgu_free_dlist( listAddress );
2331 static void addressbook_list_row_selected( GtkCTree *clist,
2336 GtkEntry *entry = GTK_ENTRY(addrbook.entry);
2337 AddrItemObject *aio = NULL;
2338 AddressObject *pobj = NULL;
2339 AdapterDSource *ads = NULL;
2340 AddressDataSource *ds = NULL;
2342 gtk_entry_set_text( entry, "" );
2343 addrbook.listSelected = node;
2345 pobj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), addrbook.treeSelected );
2346 if( pobj == NULL ) return;
2348 if( pobj->type == ADDR_DATASOURCE ) {
2349 ads = ADAPTER_DSOURCE(pobj);
2350 ds = ads->dataSource;
2352 else if( pobj->type != ADDR_INTERFACE ) {
2353 ds = addressbook_find_datasource( addrbook.treeSelected );
2356 aio = gtk_ctree_node_get_row_data( clist, node );
2358 /* g_print( "list select: %d : '%s'\n", aio->type, aio->name ); */
2359 addressbook_list_select_add( aio, ds );
2362 addressbook_list_menu_setup();
2364 if (!addrbook.target_compose && !prefs_common.addressbook_use_editaddress_dialog) {
2365 AddressObject *obj = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
2367 if (obj && obj->type != ADDR_ITEM_GROUP)
2368 addressbook_edit_address(NULL, 0, NULL, FALSE);
2372 static void addressbook_list_row_unselected( GtkCTree *ctree,
2377 AddrItemObject *aio;
2379 aio = gtk_ctree_node_get_row_data( ctree, node );
2381 /* g_print( "list unselect: %d : '%s'\n", aio->type, aio->name ); */
2382 addressbook_list_select_remove( aio );
2385 if (!prefs_common.addressbook_use_editaddress_dialog)
2386 addressbook_edit_person_invalidate(NULL, NULL, NULL);
2389 static gboolean addressbook_list_button_pressed(GtkWidget *widget,
2390 GdkEventButton *event,
2393 if( ! event ) return FALSE;
2395 addressbook_list_menu_setup();
2397 if( event->button == 3 ) {
2398 gtk_menu_popup( GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
2399 event->button, event->time );
2400 } else if (event->button == 1) {
2401 if (event->type == GDK_2BUTTON_PRESS) {
2402 if (prefs_common.add_address_by_click &&
2403 addrbook.target_compose)
2404 addressbook_to_clicked(NULL, GINT_TO_POINTER(COMPOSE_TO));
2406 if (prefs_common.addressbook_use_editaddress_dialog)
2407 addressbook_edit_address_cb(NULL, 0, NULL);
2409 GtkCTree *clist = GTK_CTREE(addrbook.clist);
2410 AddressObject *obj = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
2411 if( obj && obj->type == ADDR_ITEM_GROUP )
2412 addressbook_edit_address_cb(NULL, 0, NULL);
2420 static gboolean addressbook_list_button_released(GtkWidget *widget,
2421 GdkEventButton *event,
2427 static gboolean addressbook_tree_button_pressed(GtkWidget *ctree,
2428 GdkEventButton *event,
2431 GtkCList *clist = GTK_CLIST(ctree);
2433 AddressObject *obj = NULL;
2434 AdapterDSource *ads = NULL;
2435 AddressInterface *iface = NULL;
2436 AddressDataSource *ds = NULL;
2437 gboolean canEdit = FALSE;
2438 gboolean canDelete = FALSE;
2439 gboolean canCut = FALSE;
2440 gboolean canCopy = FALSE;
2441 gboolean canPaste = FALSE;
2442 gboolean canTreeCut = FALSE;
2443 gboolean canTreeCopy = FALSE;
2444 gboolean canTreePaste = FALSE;
2445 gboolean canLookup = FALSE;
2446 GtkCTreeNode *node = NULL;
2448 if( ! event ) return FALSE;
2449 /* if( ! event || event->type != GDK_BUTTON_PRESS) return FALSE;*/
2451 if (event->button == 1) {
2452 if (event->type == GDK_2BUTTON_PRESS) {
2453 if( gtk_clist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
2454 gtkut_clist_set_focus_row(clist, row);
2455 obj = gtk_clist_get_row_data( clist, row );
2460 if (obj->type == ADDR_ITEM_GROUP) {
2462 addressbook_treenode_edit_cb(NULL, 0, NULL);
2464 /* expand pr collapse */
2465 node = gtk_ctree_node_nth(GTK_CTREE(ctree), row);
2466 gtk_ctree_toggle_expansion(GTK_CTREE(ctree), node);
2472 addressbook_menubar_set_sensitive( FALSE );
2474 if( gtk_clist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
2475 gtkut_clist_set_focus_row(clist, row);
2476 obj = gtk_clist_get_row_data( clist, row );
2479 menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
2483 node = gtk_ctree_node_nth(GTK_CTREE(clist), row);
2485 if( ! addrclip_is_empty( _clipBoard_ ) )
2486 canTreePaste = TRUE;
2488 if (obj->type == ADDR_INTERFACE) {
2489 AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
2492 iface = adapter->interface;
2495 if( !iface->readOnly ) {
2496 menu_set_sensitive( addrbook.tree_factory, "/New Book", TRUE );
2497 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2499 if( iface->externalQuery )
2502 if (obj->type == ADDR_DATASOURCE) {
2503 ads = ADAPTER_DSOURCE(obj);
2504 ds = ads->dataSource;
2507 iface = ds->interface;
2510 if( !iface->readOnly ) {
2512 menu_set_sensitive( addrbook.tree_factory, "/New Folder", TRUE );
2513 menu_set_sensitive( addrbook.tree_factory, "/New Group", TRUE );
2514 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2518 if( iface->externalQuery )
2521 else if (obj->type == ADDR_ITEM_FOLDER) {
2522 ds = addressbook_find_datasource( node );
2525 iface = ds->interface;
2528 if( !iface->readOnly ) {
2532 menu_set_sensitive( addrbook.tree_factory, "/New Folder", TRUE );
2533 menu_set_sensitive( addrbook.tree_factory, "/New Group", TRUE );
2534 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2538 if( iface->externalQuery ) {
2539 /* Enable deletion of LDAP folder */
2544 else if (obj->type == ADDR_ITEM_GROUP) {
2545 ds = addressbook_find_datasource( node );
2548 iface = ds->interface;
2551 if( ! iface->readOnly ) {
2554 menu_set_sensitive( addrbook.list_factory, "/New Address", TRUE );
2555 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2559 if( canEdit && !addrselect_test_empty( _addressSelect_ ) )
2561 if( ! addrselect_test_empty( _addressSelect_ ) )
2563 if( ! addrclip_is_empty( _clipBoard_ ) )
2566 /* Forbid write changes when read-only */
2567 if( iface && iface->readOnly ) {
2569 canTreePaste = FALSE;
2577 menu_set_sensitive( addrbook.tree_factory, "/Edit", canEdit );
2578 menu_set_sensitive( addrbook.tree_factory, "/Delete", canDelete );
2579 menu_set_sensitive( addrbook.tree_factory, "/Cut", canTreeCut );
2580 menu_set_sensitive( addrbook.tree_factory, "/Copy", canTreeCopy );
2581 menu_set_sensitive( addrbook.tree_factory, "/Paste", canTreePaste );
2583 menu_set_sensitive( addrbook.menu_factory, "/Book/Edit book", canEdit );
2584 menu_set_sensitive( addrbook.menu_factory, "/Book/Delete book", canEdit );
2585 menu_set_sensitive( addrbook.menu_factory, "/Address/Cut", canCut );
2586 menu_set_sensitive( addrbook.menu_factory, "/Address/Copy", canCopy );
2587 menu_set_sensitive( addrbook.menu_factory, "/Address/Paste", canPaste );
2589 addressbook_show_buttons(addrbook.target_compose == NULL, canLookup,
2590 addrbook.target_compose != NULL);
2592 if( event->button == 3 )
2593 gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
2594 event->button, event->time);
2599 static gboolean addressbook_tree_button_released(GtkWidget *ctree,
2600 GdkEventButton *event,
2603 gtkut_ctree_set_focus_row(GTK_CTREE(addrbook.ctree), addrbook.opened);
2607 static void addressbook_new_folder_cb(gpointer data, guint action,
2610 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2611 AddressObject *obj = NULL;
2612 AddressDataSource *ds = NULL;
2613 AddressBookFile *abf = NULL;
2614 ItemFolder *parentFolder = NULL;
2615 ItemFolder *folder = NULL;
2617 if( ! addrbook.treeSelected ) return;
2618 obj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
2619 if( obj == NULL ) return;
2620 ds = addressbook_find_datasource( addrbook.treeSelected );
2621 if( ds == NULL ) return;
2623 if( obj->type == ADDR_DATASOURCE ) {
2624 if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
2626 else if( obj->type == ADDR_ITEM_FOLDER ) {
2627 parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
2633 abf = ds->rawDataSource;
2634 if( abf == NULL ) return;
2635 folder = addressbook_edit_folder( abf, parentFolder, NULL );
2638 nn = addressbook_node_add_folder(
2639 addrbook.treeSelected, ds, folder, ADDR_ITEM_FOLDER );
2640 gtk_ctree_expand( ctree, addrbook.treeSelected );
2641 if( addrbook.treeSelected == addrbook.opened )
2642 addressbook_set_clist(obj, TRUE);
2646 static void addressbook_new_group_cb(gpointer data, guint action,
2649 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2650 AddressObject *obj = NULL;
2651 AddressDataSource *ds = NULL;
2652 AddressBookFile *abf = NULL;
2653 ItemFolder *parentFolder = NULL;
2654 ItemGroup *group = NULL;
2656 if( ! addrbook.treeSelected ) return;
2657 obj = gtk_ctree_node_get_row_data(ctree, addrbook.treeSelected);
2658 if( obj == NULL ) return;
2659 ds = addressbook_find_datasource( addrbook.treeSelected );
2660 if( ds == NULL ) return;
2662 if( obj->type == ADDR_DATASOURCE ) {
2663 if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
2665 else if( obj->type == ADDR_ITEM_FOLDER ) {
2666 parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
2672 abf = ds->rawDataSource;
2673 if( abf == NULL ) return;
2674 group = addressbook_edit_group( abf, parentFolder, NULL );
2677 nn = addressbook_node_add_group( addrbook.treeSelected, ds, group );
2678 gtk_ctree_expand( ctree, addrbook.treeSelected );
2679 if( addrbook.treeSelected == addrbook.opened )
2680 addressbook_set_clist(obj, TRUE);
2684 static void addressbook_change_node_name(GtkCTreeNode *node, const gchar *name)
2686 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2689 GdkPixmap *pix_cl, *pix_op;
2690 GdkBitmap *mask_cl, *mask_op;
2691 gboolean is_leaf, expanded;
2693 gtk_ctree_get_node_info(ctree, node, text, &spacing,
2694 &pix_cl, &mask_cl, &pix_op, &mask_op,
2695 &is_leaf, &expanded);
2696 gtk_sctree_set_node_info(ctree, node, name, spacing,
2697 pix_cl, mask_cl, pix_op, mask_op,
2703 * \param obj Address object to edit.
2704 * \param node Node in tree.
2705 * \return New name of data source.
2707 static gchar *addressbook_edit_datasource( AddressObject *obj, GtkCTreeNode *node ) {
2708 gchar *newName = NULL;
2709 AddressDataSource *ds = NULL;
2710 AddressInterface *iface = NULL;
2711 AdapterDSource *ads = NULL;
2713 ds = addressbook_find_datasource( node );
2714 if( ds == NULL ) return NULL;
2715 iface = ds->interface;
2716 if( ! iface->haveLibrary ) return NULL;
2718 /* Read data from data source */
2719 if( addrindex_ds_get_modify_flag( ds ) ) {
2720 addrindex_ds_read_data( ds );
2723 if( ! addrindex_ds_get_read_flag( ds ) ) {
2724 addrindex_ds_read_data( ds );
2728 ads = ADAPTER_DSOURCE(obj);
2729 if( ads->subType == ADDR_BOOK ) {
2730 if( addressbook_edit_book( _addressIndex_, ads ) == NULL ) return NULL;
2732 else if( ads->subType == ADDR_VCARD ) {
2733 if( addressbook_edit_vcard( _addressIndex_, ads ) == NULL ) return NULL;
2736 else if( ads->subType == ADDR_JPILOT ) {
2737 if( addressbook_edit_jpilot( _addressIndex_, ads ) == NULL ) return NULL;
2741 else if( ads->subType == ADDR_LDAP ) {
2742 if( addressbook_edit_ldap( _addressIndex_, ads ) == NULL ) return NULL;
2748 newName = obj->name;
2753 * Edit an object that is in the address tree area.
2755 static void addressbook_treenode_edit_cb(gpointer data, guint action,
2758 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2760 AddressDataSource *ds = NULL;
2761 AddressBookFile *abf = NULL;
2762 GtkCTreeNode *node = NULL, *parentNode = NULL;
2765 if( ! addrbook.treeSelected ) return;
2766 node = addrbook.treeSelected;
2767 if( GTK_CTREE_ROW(node)->level == 1 ) return;
2768 obj = gtk_ctree_node_get_row_data( ctree, node );
2769 if( obj == NULL ) return;
2770 parentNode = GTK_CTREE_ROW(node)->parent;
2772 ds = addressbook_find_datasource( node );
2773 if( ds == NULL ) return;
2775 if( obj->type == ADDR_DATASOURCE ) {
2776 name = addressbook_edit_datasource( obj, node );
2777 if( name == NULL ) return;
2780 abf = ds->rawDataSource;
2781 if( abf == NULL ) return;
2782 if( obj->type == ADDR_ITEM_FOLDER ) {
2783 AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2784 ItemFolder *item = adapter->itemFolder;
2785 ItemFolder *parentFolder = NULL;
2786 parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
2787 if( addressbook_edit_folder( abf, parentFolder, item ) == NULL ) return;
2788 name = ADDRITEM_NAME(item);
2790 else if( obj->type == ADDR_ITEM_GROUP ) {
2791 AdapterGroup *adapter = ADAPTER_GROUP(obj);
2792 ItemGroup *item = adapter->itemGroup;
2793 ItemFolder *parentFolder = NULL;
2794 parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
2795 if( addressbook_edit_group( abf, parentFolder, item ) == NULL ) return;
2796 name = ADDRITEM_NAME(item);
2799 if( name && parentNode ) {
2800 /* Update node in tree view */
2801 addressbook_change_node_name( node, name );
2802 gtk_sctree_sort_node(ctree, parentNode);
2803 gtk_ctree_expand( ctree, node );
2804 gtk_sctree_select( GTK_SCTREE( ctree), node );
2811 ADDRTREE_DEL_FOLDER_ONLY,
2812 ADDRTREE_DEL_FOLDER_ADDR
2816 * Delete an item from the tree widget.
2817 * \param data Data passed in.
2818 * \param action Action.
2819 * \param widget Widget issuing callback.
2821 static void addressbook_treenode_delete_cb(
2822 gpointer data, guint action, GtkWidget *widget )
2824 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
2825 GtkCTreeNode *node = NULL;
2829 AddrBookBase *adbase;
2830 AddressCache *cache;
2831 AdapterDSource *ads = NULL;
2832 AddressInterface *iface = NULL;
2833 AddressDataSource *ds = NULL;
2834 gboolean remFlag = FALSE;
2835 TreeItemDelType delType;
2837 if( ! addrbook.treeSelected ) return;
2838 node = addrbook.treeSelected;
2839 if( GTK_CTREE_ROW(node)->level == 1 ) return;
2841 obj = gtk_ctree_node_get_row_data( ctree, node );
2842 g_return_if_fail(obj != NULL);
2844 if( obj->type == ADDR_DATASOURCE ) {
2845 ads = ADAPTER_DSOURCE(obj);
2846 if( ads == NULL ) return;
2847 ds = ads->dataSource;
2848 if( ds == NULL ) return;
2851 /* Must be folder or something else */
2852 ds = addressbook_find_datasource( node );
2853 if( ds == NULL ) return;
2855 /* Only allow deletion from non-readOnly */
2856 iface = ds->interface;
2857 if( iface->readOnly ) {
2858 /* Allow deletion of query results */
2859 if( ! iface->externalQuery ) return;
2863 /* Confirm deletion */
2864 delType = ADDRTREE_DEL_NONE;
2865 if( obj->type == ADDR_ITEM_FOLDER ) {
2866 if( iface->externalQuery ) {
2867 message = g_strdup_printf( _(
2868 "Do you want to delete the query " \
2869 "results and addresses in '%s' ?" ),
2871 aval = alertpanel( _("Delete"), message,
2872 GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL );
2874 if( aval == G_ALERTALTERNATE ) {
2875 delType = ADDRTREE_DEL_FOLDER_ADDR;
2879 message = g_strdup_printf
2880 ( _( "Do you want to delete '%s' ? "
2881 "If you delete the folder only, the addresses it contains will be moved into the parent folder." ),
2883 aval = alertpanel( _("Delete folder"), message,
2884 GTK_STOCK_CANCEL, _("+Delete _folder only"), _("Delete folder and _addresses"));
2886 if( aval == G_ALERTALTERNATE ) {
2887 delType = ADDRTREE_DEL_FOLDER_ONLY;
2889 else if( aval == G_ALERTOTHER ) {
2890 delType = ADDRTREE_DEL_FOLDER_ADDR;
2894 else if( obj->type == ADDR_ITEM_GROUP ) {
2895 message = g_strdup_printf(_("Do you want to delete '%s'?\n"
2896 "The addresses it contains will not be lost."), obj->name);
2897 aval = alertpanel(_("Delete"), message, GTK_STOCK_CANCEL,
2898 "+" GTK_STOCK_DELETE, NULL);
2900 if( aval == G_ALERTALTERNATE ) delType = ADDRTREE_DEL_FOLDER_ONLY;
2902 message = g_strdup_printf(_("Do you want to delete '%s'?\n"
2903 "The addresses it contains will be lost."), obj->name);
2904 aval = alertpanel(_("Delete"), message, GTK_STOCK_CANCEL,
2905 "+" GTK_STOCK_DELETE, NULL);
2907 if( aval == G_ALERTALTERNATE ) delType = ADDRTREE_DEL_DATA;
2909 if( delType == ADDRTREE_DEL_NONE ) return;
2911 /* Proceed with deletion */
2912 if( obj->type == ADDR_DATASOURCE ) {
2913 /* Remove node from tree */
2914 gtk_ctree_remove_node( ctree, node );
2916 /* Remove data source. */
2917 if( addrindex_index_remove_datasource( _addressIndex_, ds ) ) {
2918 addrindex_free_datasource( ds );
2923 /* Get reference to cache */
2924 adbase = ( AddrBookBase * ) ds->rawDataSource;
2925 if( adbase == NULL ) return;
2926 cache = adbase->addressCache;
2928 /* Remove query results folder */
2929 if( iface->externalQuery ) {
2930 AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2931 ItemFolder *folder = adapter->itemFolder;
2933 adapter->itemFolder = NULL;
2935 g_print( "remove folder for ::%s::\n", obj->name );
2936 g_print( " folder name ::%s::\n", ADDRITEM_NAME(folder) );
2937 g_print( "-------------- remove results\n" );
2939 addrindex_remove_results( ds, folder );
2940 /* g_print( "-------------- remove node\n" ); */
2941 gtk_ctree_remove_node( ctree, node );
2945 /* Code below is valid for regular address book deletion */
2946 if( obj->type == ADDR_ITEM_FOLDER ) {
2947 AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2948 ItemFolder *item = adapter->itemFolder;
2950 if( delType == ADDRTREE_DEL_FOLDER_ONLY ) {
2951 /* Remove folder only */
2952 item = addrcache_remove_folder( cache, item );
2954 addritem_free_item_folder( item );
2955 addressbook_move_nodes_up( ctree, node );
2959 else if( delType == ADDRTREE_DEL_FOLDER_ADDR ) {
2960 /* Remove folder and addresses */
2961 item = addrcache_remove_folder_delete( cache, item );
2963 addritem_free_item_folder( item );
2968 else if( obj->type == ADDR_ITEM_GROUP ) {
2969 AdapterGroup *adapter = ADAPTER_GROUP(obj);
2970 ItemGroup *item = adapter->itemGroup;
2972 item = addrcache_remove_group( cache, item );
2974 addritem_free_item_group( item );
2981 gtk_ctree_remove_node(ctree, node );
2985 static void addressbook_new_address_from_book_post_cb( ItemPerson *person )
2987 if( person && addrbook.treeSelected == addrbook.opened ) {
2988 person->status = ADD_ENTRY;
2989 gtk_clist_unselect_all( GTK_CLIST(addrbook.clist) );
2990 addressbook_folder_refresh_one_person(
2991 GTK_CTREE(addrbook.clist), person );
2993 addressbook_address_list_set_focus();
2996 static void addressbook_new_address_from_folder_post_cb( ItemPerson *person )
2998 if( person && addrbook.treeSelected == addrbook.opened) {
2999 person->status = ADD_ENTRY;
3000 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), addrbook.opened );
3001 addressbook_set_clist(
3002 gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
3006 addressbook_address_list_set_focus();
3010 * Label (a format string) that is used to name each folder.
3012 static gchar *_queryFolderLabel_ = N_( "Search '%s'" );
3015 * Search ctree widget callback function.
3016 * \param pA Pointer to node.
3017 * \param pB Pointer to data item being sought.
3018 * \return Zero (0) if folder found.
3020 static int addressbook_treenode_find_folder_cb( gconstpointer pA, gconstpointer pB ) {
3023 aoA = ( AddressObject * ) pA;
3024 if( aoA->type == ADDR_ITEM_FOLDER ) {
3025 ItemFolder *folder, *fld;
3027 fld = ADAPTER_FOLDER(aoA)->itemFolder;
3028 folder = ( ItemFolder * ) pB;
3029 if( fld == folder ) return 0; /* Found folder */
3034 static ItemFolder * addressbook_setup_subf(
3035 AddressDataSource *ds, gchar *title,
3036 GtkCTreeNode *pNode )
3038 AddrBookBase *adbase;
3039 AddressCache *cache;
3042 GtkCTreeNode *nNode;
3044 AddressObjectType aoType = ADDR_NONE;
3047 if( *title == '\0' || strlen( title ) < 1 ) return NULL;
3049 if( ds && ds->type == ADDR_IF_LDAP ) {
3051 aoType = ADDR_LDAP_QUERY;
3058 ctree = GTK_CTREE(addrbook.ctree);
3059 /* Get reference to address cache */
3060 adbase = ( AddrBookBase * ) ds->rawDataSource;
3061 cache = adbase->addressCache;
3063 if ((children = addrcache_get_list_folder(cache)) != NULL) {
3064 GList *cur = children;
3065 for (; cur; cur = cur->next) {
3066 ItemFolder *child = (ItemFolder *) cur->data;
3067 if (!strcmp2(ADDRITEM_NAME(child), title)) {
3068 nNode = gtk_ctree_find_by_row_data_custom(
3070 addressbook_treenode_find_folder_cb );
3072 addrindex_remove_results( ds, child );
3073 while( child->listPerson ) {
3074 ItemPerson *item = ( ItemPerson * ) child->listPerson->data;
3075 item = addrcache_remove_person( cache, item );
3077 addritem_free_item_person( item );
3081 gtk_sctree_select( GTK_SCTREE(ctree), nNode );
3082 addrbook.treeSelected = nNode;
3089 /* Create a folder */
3090 folder = addrcache_add_new_folder( cache, NULL );
3091 name = g_strdup_printf( "%s", title );
3092 addritem_folder_set_name( folder, name );
3093 addritem_folder_set_remarks( folder, "" );
3096 /* Now let's see the folder */
3097 nNode = addressbook_node_add_folder( pNode, ds, folder, aoType );
3098 gtk_ctree_expand( ctree, pNode );
3100 gtk_sctree_select( GTK_SCTREE(ctree), nNode );
3101 addrbook.treeSelected = nNode;
3107 static void addressbook_new_address_cb( gpointer data, guint action, GtkWidget *widget ) {
3108 AddressObject *pobj = NULL;
3109 AddressDataSource *ds = NULL;
3110 AddressBookFile *abf = NULL;
3111 debug_print("adding address\n");
3112 pobj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), addrbook.treeSelected);
3113 if( pobj == NULL ) {
3114 debug_print("no row data\n");
3117 ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
3119 debug_print("no datasource\n");
3123 abf = ds->rawDataSource;
3125 g_print("no addressbook file\n");
3129 if( pobj->type == ADDR_DATASOURCE ) {
3130 if (ADAPTER_DSOURCE(pobj)->subType == ADDR_BOOK ||
3131 ADAPTER_DSOURCE(pobj)->subType == ADDR_LDAP) {
3133 ItemFolder *folder = NULL;
3135 if (abf && abf->type == ADDR_IF_LDAP) {
3136 GtkCTreeNode *parentNode;
3137 ds = addressbook_find_datasource( GTK_CTREE_NODE( addrbook.treeSelected ) );
3138 if( ds == NULL ) return;
3140 /* We must have a datasource that is an external interface */
3141 if( ! ds->interface->haveLibrary ) return;
3142 if( ! ds->interface->externalQuery ) return;
3144 if( pobj->type == ADDR_ITEM_FOLDER ) {
3145 parentNode = GTK_CTREE_ROW(GTK_CTREE_NODE( addrbook.treeSelected ) )->parent;
3148 parentNode = GTK_CTREE_NODE( addrbook.treeSelected );
3150 folder = addressbook_setup_subf( ds, _("New Contacts"), parentNode );
3152 pobj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), addrbook.treeSelected);
3153 ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
3154 abf = ds->rawDataSource;
3157 person = addressbook_edit_person( abf, folder, NULL, FALSE,
3158 addrbook.editaddress_vbox,
3159 addressbook_new_address_from_book_post_cb,
3162 if (abf && abf->type == ADDR_IF_LDAP) {
3163 LdapServer *server = ds->rawDataSource;
3164 ldapsvr_set_modified(server, TRUE);
3165 ldapsvr_update_book(server, NULL);
3166 if (server->retVal != LDAPRC_SUCCESS) {
3167 alertpanel( _("Add address(es)"),
3168 addressbook_err2string(_lutErrorsLDAP_, server->retVal),
3169 GTK_STOCK_CLOSE, NULL, NULL );
3170 server->retVal = LDAPRC_SUCCESS;
3175 if (prefs_common.addressbook_use_editaddress_dialog)
3176 addressbook_new_address_from_book_post_cb( person );
3179 else if( pobj->type == ADDR_ITEM_FOLDER ) {
3181 ItemFolder *folder = ADAPTER_FOLDER(pobj)->itemFolder;
3184 if (abf && abf->type == ADDR_IF_LDAP) {
3185 GtkCTreeNode *parentNode;
3186 ds = addressbook_find_datasource( GTK_CTREE_NODE( addrbook.treeSelected ) );
3187 if( ds == NULL ) return;
3189 /* We must have a datasource that is an external interface */
3190 if( ! ds->interface->haveLibrary ) return;
3191 if( ! ds->interface->externalQuery ) return;
3193 if( pobj->type == ADDR_ITEM_FOLDER ) {
3194 parentNode = GTK_CTREE_ROW(GTK_CTREE_NODE( addrbook.treeSelected ) )->parent;
3197 parentNode = GTK_CTREE_NODE( addrbook.treeSelected );
3199 folder = addressbook_setup_subf( ds, _("New Contacts"), parentNode );
3202 pobj = gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), addrbook.treeSelected);
3203 ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
3204 abf = ds->rawDataSource;
3207 person = addressbook_edit_person( abf, folder, NULL, FALSE,
3208 addrbook.editaddress_vbox,
3209 addressbook_new_address_from_folder_post_cb,
3212 if (abf && abf->type == ADDR_IF_LDAP) {
3213 LdapServer *server = ds->rawDataSource;
3214 ldapsvr_set_modified(server, TRUE);
3215 ldapsvr_update_book(server, NULL);
3216 if (server->retVal != LDAPRC_SUCCESS) {
3217 alertpanel( _("Add address(es)"),
3218 addressbook_err2string(_lutErrorsLDAP_, server->retVal),
3219 GTK_STOCK_CLOSE, NULL, NULL );
3224 if (prefs_common.addressbook_use_editaddress_dialog)
3225 addressbook_new_address_from_folder_post_cb( person );
3227 else if( pobj->type == ADDR_ITEM_GROUP ) {
3228 /* New address in group */
3229 ItemGroup *group = ADAPTER_GROUP(pobj)->itemGroup;
3230 if( addressbook_edit_group( abf, NULL, group ) == NULL ) return;
3231 if (addrbook.treeSelected == addrbook.opened) {
3232 /* Change node name in tree. */
3233 addressbook_change_node_name( addrbook.treeSelected, ADDRITEM_NAME(group) );
3234 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), addrbook.opened );
3235 addressbook_set_clist(
3236 gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
3244 * Search for specified child group node in address index tree.
3245 * \param parent Parent node.
3246 * \param group Group to find.
3248 static GtkCTreeNode *addressbook_find_group_node( GtkCTreeNode *parent, ItemGroup *group ) {
3249 GtkCTreeNode *node = NULL;
3250 GtkCTreeRow *currRow;
3252 currRow = GTK_CTREE_ROW( parent );
3254 node = currRow->children;
3258 obj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), node );
3259 if( obj->type == ADDR_ITEM_GROUP ) {
3260 ItemGroup *g = ADAPTER_GROUP(obj)->itemGroup;
3261 if( g == group ) return node;
3263 currRow = GTK_CTREE_ROW(node);
3264 node = currRow->sibling;
3270 static AddressBookFile *addressbook_get_book_file() {
3271 AddressBookFile *abf = NULL;
3272 AddressDataSource *ds = NULL;
3274 ds = addressbook_find_datasource( addrbook.treeSelected );
3275 if( ds == NULL ) return NULL;
3276 if( ds->type == ADDR_IF_BOOK || ds->type == ADDR_IF_LDAP ) abf = ds->rawDataSource;
3280 static void addressbook_tree_remove_children( GtkCTree *ctree, GtkCTreeNode *parent ) {
3284 /* Remove existing folders and groups */
3285 row = GTK_CTREE_ROW( parent );
3287 while( (node = row->children) ) {
3288 gtk_ctree_remove_node( ctree, node );
3293 static void addressbook_move_nodes_up( GtkCTree *ctree, GtkCTreeNode *node ) {
3294 GtkCTreeNode *parent, *child;
3295 GtkCTreeRow *currRow;
3296 currRow = GTK_CTREE_ROW( node );
3298 parent = currRow->parent;
3299 while( (child = currRow->children) ) {
3300 gtk_ctree_move( ctree, child, parent, node );
3302 gtk_sctree_sort_node( ctree, parent );
3306 static void addressbook_edit_address_post_cb( ItemPerson *person )
3310 if (strcmp2(person->nickName, ADDRITEM_NAME(person)))
3311 addritem_person_set_nick_name( person, ADDRITEM_NAME(person));
3313 addressbook_folder_refresh_one_person( GTK_CTREE(addrbook.clist), person );
3314 invalidate_address_completion();
3316 addressbook_address_list_set_focus();
3319 void addressbook_address_list_set_focus( void )
3321 if (!prefs_common.addressbook_use_editaddress_dialog) {
3322 gtk_window_set_focus(GTK_WINDOW(addrbook.window), addrbook.clist);
3323 addressbook_list_menu_setup();
3327 void addressbook_address_list_disable_some_actions(void)
3329 /* disable address copy/pasting when editing contact's detail (embedded form) */
3330 menu_set_sensitive( addrbook.menu_factory, "/Address/Cut", FALSE );
3331 menu_set_sensitive( addrbook.menu_factory, "/Address/Copy", FALSE );
3332 menu_set_sensitive( addrbook.menu_factory, "/Address/Paste", FALSE );
3335 static void addressbook_edit_address_cb( gpointer data, guint action, GtkWidget *widget ) {
3336 addressbook_edit_address(data, action, widget, TRUE);
3339 static void addressbook_edit_address( gpointer data, guint action, GtkWidget *widget,
3340 gboolean force_focus ) {
3341 GtkCTree *clist = GTK_CTREE(addrbook.clist);
3343 AddressObject *obj = NULL, *pobj = NULL;
3344 AddressDataSource *ds = NULL;
3345 GtkCTreeNode *node = NULL, *parentNode = NULL;
3347 AddressBookFile *abf = NULL;
3349 if( addrbook.listSelected == NULL ) return;
3350 obj = gtk_ctree_node_get_row_data( clist, addrbook.listSelected );
3351 g_return_if_fail(obj != NULL);
3353 ctree = GTK_CTREE( addrbook.ctree );
3354 pobj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
3355 node = gtk_ctree_find_by_row_data( ctree, addrbook.treeSelected, obj );
3357 ds = addressbook_find_datasource( GTK_CTREE_NODE(addrbook.treeSelected) );
3358 if( ds == NULL ) return;
3360 abf = addressbook_get_book_file();
3362 if( obj->type == ADDR_ITEM_EMAIL ) {
3363 ItemEMail *email = ( ItemEMail * ) obj;
3364 if( email == NULL ) return;
3365 if( pobj && pobj->type == ADDR_ITEM_GROUP ) {
3366 /* Edit parent group */
3367 AdapterGroup *adapter = ADAPTER_GROUP(pobj);
3368 ItemGroup *itemGrp = adapter->itemGroup;
3369 if( abf == NULL ) return;
3370 if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
3371 name = ADDRITEM_NAME(itemGrp);
3372 node = addrbook.treeSelected;
3373 parentNode = GTK_CTREE_ROW(node)->parent;
3376 /* Edit person - email page */
3378 person = ( ItemPerson * ) ADDRITEM_PARENT(email);
3379 if ( addressbook_edit_person( abf, NULL, person, TRUE, addrbook.editaddress_vbox,
3380 addressbook_edit_address_post_cb,
3381 (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
3384 if (abf && abf->type == ADDR_IF_LDAP) {
3385 ldapsvr_set_modified( (LdapServer *) abf, TRUE );
3386 person->status = UPDATE_ENTRY;
3389 if (prefs_common.addressbook_use_editaddress_dialog)
3390 addressbook_edit_address_post_cb( person );
3395 else if( obj->type == ADDR_ITEM_PERSON ) {
3396 /* Edit person - basic page */
3397 ItemPerson *person = ( ItemPerson * ) obj;
3398 if( addressbook_edit_person( abf, NULL, person, FALSE, addrbook.editaddress_vbox,
3399 addressbook_edit_address_post_cb,
3400 (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
3403 if (abf && abf->type == ADDR_IF_LDAP) {
3404 ldapsvr_set_modified( (LdapServer *) abf, TRUE );
3405 person->status = UPDATE_ENTRY;
3408 if (prefs_common.addressbook_use_editaddress_dialog)
3409 addressbook_edit_address_post_cb( person );
3413 else if( obj->type == ADDR_ITEM_GROUP ) {
3414 ItemGroup *itemGrp = ( ItemGroup * ) obj;
3415 if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
3416 parentNode = addrbook.treeSelected;
3417 node = addressbook_find_group_node( parentNode, itemGrp );
3418 name = ADDRITEM_NAME(itemGrp);
3419 invalidate_address_completion();
3425 /* Update tree node with node name */
3426 if( node == NULL ) return;
3427 addressbook_change_node_name( node, name );
3428 gtk_sctree_sort_node( ctree, parentNode );
3429 gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened );
3430 addressbook_set_clist(
3431 gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree),
3436 static void addressbook_delete_address_cb(gpointer data, guint action,
3439 addressbook_del_clicked(NULL, NULL);
3442 static void close_cb(gpointer data, guint action, GtkWidget *widget)
3444 addressbook_close();
3447 static void addressbook_file_save_cb( gpointer data, guint action, GtkWidget *widget ) {
3448 addressbook_export_to_file();
3451 static void addressbook_person_expand_node( GtkCTree *ctree, GList *node, gpointer *data ) {
3453 ItemPerson *person = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
3454 if( person ) addritem_person_set_opened( person, TRUE );
3458 static void addressbook_person_collapse_node( GtkCTree *ctree, GList *node, gpointer *data ) {
3460 ItemPerson *person = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
3461 if( person ) addritem_person_set_opened( person, FALSE );
3465 static gchar *addressbook_format_item_clist( ItemPerson *person, ItemEMail *email ) {
3467 gchar *eMailAlias = ADDRITEM_NAME(email);
3468 if( eMailAlias && *eMailAlias != '\0' ) {
3470 str = g_strdup_printf( "%s - %s", ADDRITEM_NAME(person), eMailAlias );
3473 str = g_strdup( eMailAlias );
3479 static void addressbook_load_group( GtkCTree *clist, ItemGroup *itemGroup ) {
3480 GList *items = itemGroup->listEMail;
3481 AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3482 for( ; items != NULL; items = g_list_next( items ) ) {
3483 GtkCTreeNode *nodeEMail = NULL;
3484 gchar *text[N_LIST_COLS];
3485 ItemEMail *email = items->data;
3489 if( ! email ) continue;
3491 person = ( ItemPerson * ) ADDRITEM_PARENT(email);
3492 str = addressbook_format_item_clist( person, email );
3494 text[COL_NAME] = addressbook_set_col_name_guard(str);
3497 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3499 text[COL_ADDRESS] = email->address;
3500 text[COL_REMARKS] = email->remarks;
3501 nodeEMail = gtk_sctree_insert_node(
3503 text, FOLDER_SPACING,
3504 atci->iconXpm, atci->maskXpm,
3505 atci->iconXpmOpen, atci->maskXpmOpen,
3507 gtk_ctree_node_set_row_data( clist, nodeEMail, email );
3513 gchar *addressbook_set_col_name_guard(gchar *value)
3515 gchar *ret = "<not set>";
3516 gchar *tmp = g_strdup(value);
3518 if (tmp !=NULL && *tmp != '\0')
3524 static void addressbook_folder_load_one_person(
3525 GtkCTree *clist, ItemPerson *person,
3526 AddressTypeControlItem *atci,
3527 AddressTypeControlItem *atciMail )
3529 GtkCTreeNode *nodePerson = NULL;
3530 GtkCTreeNode *nodeEMail = NULL;
3531 gchar *text[N_LIST_COLS];
3532 gboolean flgFirst = TRUE, haveAddr = FALSE;
3535 AddressBookFile *abf = addressbook_get_book_file();
3538 if( person == NULL ) return;
3540 text[COL_NAME] = "";
3541 node = person->listEMail;
3543 ItemEMail *email = node->data;
3544 gchar *eMailAddr = NULL;
3545 node = g_list_next( node );
3547 text[COL_ADDRESS] = email->address;
3548 text[COL_REMARKS] = email->remarks;
3549 eMailAddr = ADDRITEM_NAME(email);
3550 if( eMailAddr && *eMailAddr == '\0' ) eMailAddr = NULL;
3552 /* First email belongs with person */
3553 gchar *str = addressbook_format_item_clist( person, email );
3555 text[COL_NAME] = addressbook_set_col_name_guard(str);
3558 else if( abf && abf->type == ADDR_IF_LDAP &&
3559 person && person->nickName ) {
3560 if (person->nickName) {
3561 if (strcmp(person->nickName, "") != 0) {
3562 text[COL_NAME] = addressbook_set_col_name_guard(person->nickName);
3565 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3571 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3573 nodePerson = gtk_sctree_insert_node(
3575 text, FOLDER_SPACING,
3576 atci->iconXpm, atci->maskXpm,
3577 atci->iconXpmOpen, atci->maskXpmOpen,
3578 FALSE, person->isOpened );
3581 gtk_ctree_node_set_row_data(clist, nodePerson, person );
3584 /* Subsequent email is a child node of person */
3585 text[COL_NAME] = ADDRITEM_NAME(email);
3586 nodeEMail = gtk_sctree_insert_node(
3587 clist, nodePerson, NULL,
3588 text, FOLDER_SPACING,
3589 atciMail->iconXpm, atciMail->maskXpm,
3590 atciMail->iconXpmOpen, atciMail->maskXpmOpen,
3592 gtk_ctree_node_set_row_data(clist, nodeEMail, email );
3598 /* Have name without EMail */
3599 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3600 text[COL_ADDRESS] = "";
3601 text[COL_REMARKS] = "";
3602 nodePerson = gtk_sctree_insert_node(
3604 text, FOLDER_SPACING,
3605 atci->iconXpm, atci->maskXpm,
3606 atci->iconXpmOpen, atci->maskXpmOpen,
3607 FALSE, person->isOpened );
3608 gtk_ctree_node_set_row_data(clist, nodePerson, person );
3613 static void addressbook_folder_load_person( GtkCTree *clist, ItemFolder *itemFolder ) {
3615 AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
3616 AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3618 if( atci == NULL ) return;
3619 if( atciMail == NULL ) return;
3621 /* Load email addresses */
3622 items = addritem_folder_get_person_list( itemFolder );
3623 for( ; items != NULL; items = g_list_next( items ) ) {
3624 addressbook_folder_load_one_person( clist, items->data, atci, atciMail );
3626 /* Free up the list */
3627 mgu_clear_list( items );
3628 g_list_free( items );
3631 static void addressbook_folder_remove_node( GtkCTree *clist, GtkCTreeNode *node ) {
3632 addrbook.listSelected = NULL;
3633 gtk_ctree_remove_node( clist, node );
3634 addressbook_menubar_set_sensitive( FALSE );
3635 addressbook_menuitem_set_sensitive(
3636 gtk_ctree_node_get_row_data(
3637 GTK_CTREE(clist), addrbook.treeSelected ),
3638 addrbook.treeSelected );
3641 static void addressbook_folder_refresh_one_person( GtkCTree *clist, ItemPerson *person ) {
3642 AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
3643 AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3645 if( atci == NULL ) return;
3646 if( atciMail == NULL ) return;
3647 if( person == NULL ) return;
3648 /* unload the person */
3650 node = gtk_ctree_find_by_row_data( clist, NULL, person );
3652 addressbook_folder_remove_node( clist, node );
3653 addressbook_folder_load_one_person( clist, person, atci, atciMail );
3654 gtk_sctree_sort_node( clist, NULL );
3655 node = gtk_ctree_find_by_row_data( clist, NULL, person );
3657 gtk_sctree_select( GTK_SCTREE(clist), node );
3658 if (!gtk_ctree_node_is_visible( clist, node ) )
3659 gtk_ctree_node_moveto( clist, node, 0, 0, 0 );
3663 static void addressbook_folder_remove_one_person( GtkCTree *clist, ItemPerson *person ) {
3667 if( person == NULL ) return;
3668 node = gtk_ctree_find_by_row_data( clist, NULL, person );
3669 row = gtk_clist_find_row_from_data( GTK_CLIST(clist), person );
3671 addressbook_folder_remove_node( clist, node );
3675 static void addressbook_folder_load_group( GtkCTree *clist, ItemFolder *itemFolder ) {
3677 AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_GROUP );
3679 /* Load any groups */
3680 if( ! atci ) return;
3681 items = addritem_folder_get_group_list( itemFolder );
3682 for( ; items != NULL; items = g_list_next( items ) ) {
3683 GtkCTreeNode *nodeGroup = NULL;
3684 gchar *text[N_LIST_COLS];
3685 ItemGroup *group = items->data;
3686 if( group == NULL ) continue;
3687 text[COL_NAME] = ADDRITEM_NAME(group);
3688 text[COL_ADDRESS] = "";
3689 text[COL_REMARKS] = "";
3690 nodeGroup = gtk_sctree_insert_node(clist, NULL, NULL,
3691 text, FOLDER_SPACING,
3692 atci->iconXpm, atci->maskXpm,
3693 atci->iconXpmOpen, atci->maskXpmOpen,
3695 gtk_ctree_node_set_row_data(clist, nodeGroup, group );
3696 gtk_sctree_sort_node(clist, NULL);
3698 /* Free up the list */
3699 mgu_clear_list( items );
3700 g_list_free( items );
3704 * Search ctree widget callback function.
3705 * \param pA Pointer to node.
3706 * \param pB Pointer to data item being sought.
3707 * \return Zero (0) if group found.
3709 static int addressbook_treenode_find_group_cb( gconstpointer pA, gconstpointer pB ) {
3712 aoA = ( AddressObject * ) pA;
3713 if( aoA->type == ADDR_ITEM_GROUP ) {
3714 ItemGroup *group, *grp;
3716 grp = ADAPTER_GROUP(aoA)->itemGroup;
3717 group = ( ItemGroup * ) pB;
3718 if( grp == group ) return 0; /* Found group */
3724 * Remove folder and group nodes from tree widget for items contained ("cut")
3727 static void addressbook_treenode_remove_item( void ) {
3729 AddrSelectItem *cutItem;
3730 AddressCache *cache;
3731 AddrItemObject *aio;
3732 GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
3735 node = _clipBoard_->objectList;
3737 cutItem = node->data;
3738 node = g_list_next( node );
3739 cache = addrindex_get_cache(
3740 _clipBoard_->addressIndex, cutItem->cacheID );
3741 if( cache == NULL ) continue;
3742 aio = addrcache_get_object( cache, cutItem->uid );
3745 if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
3748 folder = ( ItemFolder * ) aio;
3749 tn = gtk_ctree_find_by_row_data_custom(
3750 ctree, NULL, folder,
3751 addressbook_treenode_find_folder_cb );
3753 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
3756 group = ( ItemGroup * ) aio;
3757 tn = gtk_ctree_find_by_row_data_custom(
3759 addressbook_treenode_find_group_cb );
3763 /* Free up adapter and remove node. */
3764 gtk_ctree_remove_node( ctree, tn );
3771 * Find parent datasource for specified tree node.
3772 * \param node Node to test.
3773 * \return Data source, or NULL if not found.
3775 static AddressDataSource *addressbook_find_datasource( GtkCTreeNode *node ) {
3776 AddressDataSource *ds = NULL;
3779 g_return_val_if_fail(addrbook.ctree != NULL, NULL);
3782 if( GTK_CTREE_ROW(node)->level < 2 ) return NULL;
3783 ao = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), node );
3785 /* g_print( "ao->type = %d\n", ao->type ); */
3786 if( ao->type == ADDR_DATASOURCE ) {
3787 AdapterDSource *ads = ADAPTER_DSOURCE(ao);
3788 /* g_print( "found it\n" ); */
3789 ds = ads->dataSource;
3793 node = GTK_CTREE_ROW(node)->parent;
3799 * Load address list widget with children of specified object.
3800 * \param obj Parent object to be loaded.
3802 static void addressbook_set_clist( AddressObject *obj, gboolean refresh ) {
3803 GtkCTree *ctreelist = GTK_CTREE(addrbook.clist);
3804 GtkCList *clist = GTK_CLIST(addrbook.clist);
3805 AddressDataSource *ds = NULL;
3806 AdapterDSource *ads = NULL;
3807 static AddressObject *last_obj = NULL;
3809 if (addrbook.clist == NULL) {
3812 if (obj == last_obj && !refresh)
3817 gtk_clist_clear(clist);
3821 if( obj->type == ADDR_INTERFACE ) {
3822 /* g_print( "set_clist: loading datasource...\n" ); */
3823 /* addressbook_node_load_datasource( GTK_CTREE(clist), obj ); */
3827 gtk_clist_freeze(clist);
3828 gtk_clist_clear(clist);
3830 if( obj->type == ADDR_DATASOURCE ) {
3831 ads = ADAPTER_DSOURCE(obj);
3832 ds = ADAPTER_DSOURCE(obj)->dataSource;
3834 /* Load root folder */
3835 ItemFolder *rootFolder = NULL;
3836 rootFolder = addrindex_ds_get_root_folder( ds );
3837 addressbook_folder_load_person(
3838 ctreelist, addrindex_ds_get_root_folder( ds ) );
3839 addressbook_folder_load_group(
3840 ctreelist, addrindex_ds_get_root_folder( ds ) );
3844 if( obj->type == ADDR_ITEM_GROUP ) {
3846 ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup;
3847 addressbook_load_group( ctreelist, itemGroup );
3849 else if( obj->type == ADDR_ITEM_FOLDER ) {
3851 ItemFolder *itemFolder = ADAPTER_FOLDER(obj)->itemFolder;
3852 addressbook_folder_load_person( ctreelist, itemFolder );
3853 addressbook_folder_load_group( ctreelist, itemFolder );
3856 gtk_sctree_sort_recursive(GTK_CTREE(clist), NULL);
3857 clist->focus_row = -1;
3858 gtk_clist_thaw(clist);
3862 * Call back function to free adaptor. Call back is setup by function
3863 * gtk_ctree_node_set_row_data_full() when node is populated. This function is
3864 * called when the address book tree widget node is removed by calling
3865 * function gtk_ctree_remove_node().
3867 * \param data Tree node's row data.
3869 static void addressbook_free_treenode( gpointer data ) {
3872 ao = ( AddressObject * ) data;
3873 if( ao == NULL ) return;
3874 if( ao->type == ADDR_INTERFACE ) {
3875 AdapterInterface *ai = ADAPTER_INTERFACE(ao);
3876 addrbookctl_free_interface( ai );
3878 else if( ao->type == ADDR_DATASOURCE ) {
3879 AdapterDSource *ads = ADAPTER_DSOURCE(ao);
3880 addrbookctl_free_datasource( ads );
3882 else if( ao->type == ADDR_ITEM_FOLDER ) {
3883 AdapterFolder *af = ADAPTER_FOLDER(ao);
3884 addrbookctl_free_folder( af );
3886 else if( ao->type == ADDR_ITEM_GROUP ) {
3887 AdapterGroup *ag = ADAPTER_GROUP(ao);
3888 addrbookctl_free_group( ag );
3893 * Create new adaptor for specified data source.
3895 AdapterDSource *addressbook_create_ds_adapter( AddressDataSource *ds,
3896 AddressObjectType otype, gchar *name )
3898 AdapterDSource *adapter = g_new0( AdapterDSource, 1 );
3899 ADDRESS_OBJECT(adapter)->type = ADDR_DATASOURCE;
3900 ADDRESS_OBJECT_NAME(adapter) = g_strdup( name );
3901 adapter->dataSource = ds;
3902 adapter->subType = otype;
3906 void addressbook_ads_set_name( AdapterDSource *adapter, gchar *value ) {
3907 ADDRESS_OBJECT_NAME(adapter) =
3908 mgu_replace_string( ADDRESS_OBJECT_NAME(adapter), value );
3912 * Load tree from address index with the initial data.
3914 static void addressbook_load_tree( void ) {
3915 GtkCTree *ctree = GTK_CTREE( addrbook.ctree );
3916 GList *nodeIf, *nodeDS;
3917 AdapterInterface *adapter;
3918 AddressInterface *iface;
3919 AddressTypeControlItem *atci;
3920 AddressDataSource *ds;
3921 AdapterDSource *ads;
3922 GtkCTreeNode *node, *newNode;
3925 nodeIf = _addressInterfaceList_;
3927 adapter = nodeIf->data;
3928 node = adapter->treeNode;
3929 iface = adapter->interface;
3930 atci = adapter->atci;
3932 if( iface->useInterface ) {
3933 /* Load data sources below interface node */
3934 nodeDS = iface->listSource;
3938 name = addrindex_ds_get_name( ds );
3939 ads = addressbook_create_ds_adapter(
3940 ds, atci->objectType, name );
3941 newNode = addressbook_add_object(
3942 node, ADDRESS_OBJECT(ads) );
3943 nodeDS = g_list_next( nodeDS );
3945 gtk_ctree_expand( ctree, node );
3948 nodeIf = g_list_next( nodeIf );
3953 * Convert the old address book to new format.
3955 static gboolean addressbook_convert( AddressIndex *addrIndex ) {
3956 gboolean retVal = FALSE;
3957 gboolean errFlag = TRUE;
3960 /* Read old address book, performing conversion */
3961 debug_print( "Reading and converting old address book...\n" );
3962 addrindex_set_file_name( addrIndex, ADDRESSBOOK_OLD_FILE );
3963 addrindex_read_data( addrIndex );
3964 if( addrIndex->retVal == MGU_NO_FILE ) {
3965 /* We do not have a file - new user */
3966 debug_print( "New user... create new books...\n" );
3967 addrindex_create_new_books( addrIndex );
3968 if( addrIndex->retVal == MGU_SUCCESS ) {
3969 /* Save index file */
3970 addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
3971 addrindex_save_data( addrIndex );
3972 if( addrIndex->retVal == MGU_SUCCESS ) {
3977 msg = _( "New user, could not save index file." );
3981 msg = _( "New user, could not save address book files." );
3985 /* We have an old file */
3986 if( addrIndex->wasConverted ) {
3987 /* Converted successfully - save address index */
3988 addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
3989 addrindex_save_data( addrIndex );
3990 if( addrIndex->retVal == MGU_SUCCESS ) {
3991 msg = _( "Old address book converted successfully." );
3996 msg = _("Old address book converted,\n"
3997 "could not save new address index file." );
4001 /* File conversion failed - just create new books */
4002 debug_print( "File conversion failed... just create new books...\n" );
4003 addrindex_create_new_books( addrIndex );
4004 if( addrIndex->retVal == MGU_SUCCESS ) {
4006 addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4007 addrindex_save_data( addrIndex );
4008 if( addrIndex->retVal == MGU_SUCCESS ) {
4009 msg = _("Could not convert address book,\n"
4010 "but created empty new address book files." );
4015 msg = _("Could not convert address book,\n"
4016 "could not save new address index file." );
4020 msg = _("Could not convert address book\n"
4021 "and could not create new address book files." );
4026 debug_print( "Error\n%s\n", msg );
4027 alertpanel_full(_("Addressbook conversion error"), msg,
4028 GTK_STOCK_CLOSE, NULL, NULL, FALSE,
4029 NULL, ALERT_ERROR, G_ALERTDEFAULT);
4032 debug_print( "Warning\n%s\n", msg );
4033 alertpanel_full(_("Addressbook conversion error"), msg,
4034 GTK_STOCK_CLOSE, NULL, NULL, FALSE,
4035 NULL, ALERT_WARNING, G_ALERTDEFAULT);
4041 static gboolean migrate_addrbook(const gchar *origdir, const gchar *destdir)
4045 gboolean failed = FALSE;
4047 if( ( dp = opendir( origdir ) ) == NULL ) {
4051 while( ( d = readdir( dp ) ) != NULL ) {
4052 if (strncmp(d->d_name, "addrbook-", strlen("addrbook-")))
4055 gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S,
4057 gchar *dest_file = g_strconcat(destdir, G_DIR_SEPARATOR_S,
4059 if (copy_file(orig_file, dest_file, FALSE) < 0) {
4072 /* all copies succeeded, we can remove source files */
4073 if( ( dp = opendir( origdir ) ) == NULL ) {
4076 while( ( d = readdir( dp ) ) != NULL ) {
4077 if (strncmp(d->d_name, "addrbook-", strlen("addrbook-")))
4080 gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S,
4082 claws_unlink(orig_file);
4092 void addressbook_read_file( void ) {
4093 AddressIndex *addrIndex = NULL;
4094 gchar *indexdir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRBOOK_DIR, NULL);
4096 debug_print( "Reading address index...\n" );
4097 if( _addressIndex_ ) {
4098 debug_print( "address book already read!!!\n" );
4102 addrIndex = addrindex_create_index();
4103 addrindex_initialize();
4105 /* Use new address book index. */
4107 if ( !is_dir_exist(indexdir) ) {
4108 if ( make_dir(indexdir) < 0 ) {
4109 addrindex_set_file_path( addrIndex, get_rc_dir() );
4110 g_warning( "couldn't create dir %s\n", indexdir);
4112 if (!migrate_addrbook(get_rc_dir(), indexdir)) {
4113 remove_dir_recursive(indexdir);
4114 addrindex_set_file_path( addrIndex, get_rc_dir() );
4115 g_error("couldn't migrate dir %s", indexdir);
4117 addrindex_set_file_path( addrIndex, indexdir);
4121 addrindex_set_file_path( addrIndex, indexdir);
4124 addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4125 addrindex_read_data( addrIndex );
4126 if( addrIndex->retVal == MGU_NO_FILE ) {
4127 /* Conversion required */
4128 debug_print( "Converting...\n" );
4129 if( addressbook_convert( addrIndex ) ) {
4130 _addressIndex_ = addrIndex;
4133 else if( addrIndex->retVal == MGU_SUCCESS ) {
4134 _addressIndex_ = addrIndex;
4137 /* Error reading address book */
4138 debug_print( "Could not read address index.\n" );
4139 addrindex_print_index( addrIndex, stdout );
4140 alertpanel_full(_("Addressbook Error"),
4141 _("Could not read address index"),
4142 GTK_STOCK_CLOSE, NULL, NULL, FALSE,
4143 NULL, ALERT_ERROR, G_ALERTDEFAULT);
4145 debug_print( "done.\n" );
4149 * Add object into the address index tree widget.
4150 * Enter: node Parent node.
4151 * obj Object to add.
4152 * Return: Node that was added, or NULL if object not added.
4154 static GtkCTreeNode *addressbook_add_object(GtkCTreeNode *node,
4157 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
4158 GtkCTreeNode *added;
4159 AddressObject *pobj;
4160 AddressObjectType otype;
4161 AddressTypeControlItem *atci = NULL;
4163 g_return_val_if_fail(node != NULL, NULL);
4164 g_return_val_if_fail(obj != NULL, NULL);
4166 pobj = gtk_ctree_node_get_row_data(ctree, node);
4167 g_return_val_if_fail(pobj != NULL, NULL);
4169 /* Determine object type to be displayed */
4170 if( obj->type == ADDR_DATASOURCE ) {
4171 otype = ADAPTER_DSOURCE(obj)->subType;
4177 /* Handle any special conditions. */
4179 atci = addrbookctl_lookup( otype );
4181 if( atci->showInTree ) {
4182 /* Add object to tree */
4185 added = gtk_sctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
4186 atci->iconXpm, atci->maskXpm, atci->iconXpmOpen, atci->maskXpmOpen,
4187 atci->treeLeaf, atci->treeExpand );
4188 gtk_ctree_node_set_row_data_full( ctree, added, obj,
4189 addressbook_free_treenode );
4193 gtk_sctree_sort_node(ctree, node);
4199 * Add group into the address index tree.
4200 * \param node Parent node.
4201 * \param ds Data source.
4202 * \param itemGroup Group to add.
4203 * \return Inserted node.
4205 static GtkCTreeNode *addressbook_node_add_group(
4206 GtkCTreeNode *node, AddressDataSource *ds,
4207 ItemGroup *itemGroup )
4209 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
4210 GtkCTreeNode *newNode;
4211 AdapterGroup *adapter;
4212 AddressTypeControlItem *atci = NULL;
4215 if( ds == NULL ) return NULL;
4216 if( node == NULL || itemGroup == NULL ) return NULL;
4218 name = &itemGroup->obj.name;
4220 atci = addrbookctl_lookup( ADDR_ITEM_GROUP );
4222 adapter = g_new0( AdapterGroup, 1 );
4223 ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_GROUP;
4224 ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemGroup) );
4225 adapter->itemGroup = itemGroup;
4227 newNode = gtk_sctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
4228 atci->iconXpm, atci->maskXpm, atci->iconXpm, atci->maskXpm,
4229 atci->treeLeaf, atci->treeExpand );
4230 gtk_ctree_node_set_row_data_full( ctree, newNode, adapter,
4231 addressbook_free_treenode );
4232 gtk_sctree_sort_node( ctree, node );
4237 * Add folder into the address index tree. Only visible folders are loaded into
4238 * the address index tree. Note that the root folder is not inserted into the
4241 * \param node Parent node.
4242 * \param ds Data source.
4243 * \param itemFolder Folder to add.
4244 * \param otype Object type to display.
4245 * \return Inserted node for the folder.
4247 static GtkCTreeNode *addressbook_node_add_folder(
4248 GtkCTreeNode *node, AddressDataSource *ds,
4249 ItemFolder *itemFolder, AddressObjectType otype )
4251 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
4252 GtkCTreeNode *newNode = NULL;
4253 AdapterFolder *adapter;
4254 AddressTypeControlItem *atci = NULL;
4255 GList *listItems = NULL;
4257 ItemFolder *rootFolder;
4259 /* Only visible folders */
4260 if( itemFolder->isHidden ) return NULL;
4262 if( ds == NULL ) return NULL;
4263 if( node == NULL || itemFolder == NULL ) return NULL;
4265 /* Determine object type */
4266 atci = addrbookctl_lookup( otype );
4267 if( atci == NULL ) return NULL;
4269 rootFolder = addrindex_ds_get_root_folder( ds );
4270 if( itemFolder == rootFolder ) {
4274 adapter = g_new0( AdapterFolder, 1 );
4275 ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_FOLDER;
4276 ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemFolder) );
4277 adapter->itemFolder = itemFolder;
4279 name = ADDRITEM_NAME(itemFolder);
4280 newNode = gtk_sctree_insert_node( ctree, node, NULL, &name, FOLDER_SPACING,
4281 atci->iconXpm, atci->maskXpm, atci->iconXpm, atci->maskXpm,
4282 atci->treeLeaf, atci->treeExpand );
4284 gtk_ctree_node_set_row_data_full( ctree, newNode, adapter,
4285 addressbook_free_treenode );
4289 listItems = itemFolder->listFolder;
4290 while( listItems ) {
4291 ItemFolder *item = listItems->data;
4292 addressbook_node_add_folder( newNode, ds, item, otype );
4293 listItems = g_list_next( listItems );
4295 listItems = itemFolder->listGroup;
4296 while( listItems ) {
4297 ItemGroup *item = listItems->data;
4298 addressbook_node_add_group( newNode, ds, item );
4299 listItems = g_list_next( listItems );
4301 gtk_sctree_sort_node( ctree, node );
4305 void addressbook_export_to_file( void ) {
4306 if( _addressIndex_ ) {
4307 /* Save all new address book data */
4308 debug_print( "Saving address books...\n" );
4309 addrindex_save_all_books( _addressIndex_ );
4311 debug_print( "Exporting addressbook to file...\n" );
4312 addrindex_save_data( _addressIndex_ );
4313 if( _addressIndex_->retVal != MGU_SUCCESS ) {
4314 addrindex_print_index( _addressIndex_, stdout );
4317 /* Notify address completion of new data */
4318 invalidate_address_completion();
4322 static gboolean addressbook_entry_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
4324 if (event && (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter))
4325 addressbook_lup_clicked(NULL, NULL);
4330 * Comparison using cell contents (text in first column). Used for sort
4331 * address index widget.
4333 static gint addressbook_treenode_compare_func(
4334 GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
4336 GtkCell *cell1 = ((GtkCListRow *)ptr1)->cell;
4337 GtkCell *cell2 = ((GtkCListRow *)ptr2)->cell;
4338 gchar *name1 = NULL, *name2 = NULL;
4339 if( cell1 ) name1 = cell1->u.text;
4340 if( cell2 ) name2 = cell2->u.text;
4341 if( ! name1 ) return ( name2 != NULL );
4342 if( ! name2 ) return -1;
4343 return g_utf8_collate( name1, name2 );
4346 static void addressbook_new_book_cb( gpointer data, guint action, GtkWidget *widget ) {
4347 AdapterDSource *ads;
4348 AdapterInterface *adapter;
4349 GtkCTreeNode *newNode;
4351 adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
4352 if( adapter == NULL ) return;
4353 ads = addressbook_edit_book( _addressIndex_, NULL );
4355 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4357 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4358 addrbook.treeSelected = newNode;
4363 static void addressbook_new_vcard_cb( gpointer data, guint action, GtkWidget *widget ) {
4364 AdapterDSource *ads;
4365 AdapterInterface *adapter;
4366 GtkCTreeNode *newNode;
4368 adapter = addrbookctl_find_interface( ADDR_IF_VCARD );
4369 if( adapter == NULL ) return;
4370 ads = addressbook_edit_vcard( _addressIndex_, NULL );
4372 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4374 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4375 addrbook.treeSelected = newNode;
4381 static void addressbook_new_jpilot_cb( gpointer data, guint action, GtkWidget *widget ) {
4382 AdapterDSource *ads;
4383 AdapterInterface *adapter;
4384 AddressInterface *iface;
4385 GtkCTreeNode *newNode;
4387 adapter = addrbookctl_find_interface( ADDR_IF_JPILOT );
4388 if( adapter == NULL ) return;
4389 iface = adapter->interface;
4390 if( ! iface->haveLibrary ) return;
4391 ads = addressbook_edit_jpilot( _addressIndex_, NULL );
4393 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4395 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4396 addrbook.treeSelected = newNode;
4403 static void addressbook_new_ldap_cb( gpointer data, guint action, GtkWidget *widget ) {
4404 AdapterDSource *ads;
4405 AdapterInterface *adapter;
4406 AddressInterface *iface;
4407 GtkCTreeNode *newNode;
4409 adapter = addrbookctl_find_interface( ADDR_IF_LDAP );
4410 if( adapter == NULL ) return;
4411 iface = adapter->interface;
4412 if( ! iface->haveLibrary ) return;
4413 ads = addressbook_edit_ldap( _addressIndex_, NULL );
4415 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4417 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4418 addrbook.treeSelected = newNode;
4425 * Display address search status message.
4426 * \param queryType Query type.
4427 * \param status Status/Error code.
4429 static void addressbook_search_message( gint queryType, gint sts ) {
4431 *addressbook_msgbuf = '\0';
4433 if( sts != MGU_SUCCESS ) {
4434 if( queryType == ADDRQUERY_LDAP ) {
4436 desc = addressbook_err2string( _lutErrorsLDAP_, sts );
4441 g_snprintf( addressbook_msgbuf,
4442 sizeof(addressbook_msgbuf), "%s", desc );
4443 addressbook_status_show( addressbook_msgbuf );
4446 addressbook_status_show( "" );
4451 * Refresh addressbook by forcing refresh of current selected object in
4454 static void addressbook_refresh_current( void ) {
4458 ctree = GTK_CTREE(addrbook.ctree);
4459 obj = gtk_ctree_node_get_row_data( ctree, addrbook.treeSelected );
4460 if( obj == NULL ) return;
4461 addressbook_set_clist( obj, TRUE );
4465 * Message that is displayed whilst a query is executing in a background
4468 static gchar *_tempMessage_ = N_( "Busy searching..." );
4471 * Address search idle function. This function is called during UI idle time
4472 * while a search is in progress.
4474 * \param data Idler data.
4476 static void addressbook_search_idle( gpointer data ) {
4480 queryID = GPOINTER_TO_INT( data );
4481 g_print( "addressbook_ldap_idle... queryID=%d\n", queryID );
4486 * Search completion callback function. This removes the query from the idle
4489 * \param sender Sender of query.
4490 * \param queryID Query ID of search request.
4491 * \param status Search status.
4492 * \param data Query data.
4494 static void addressbook_search_callback_end(
4495 gpointer sender, gint queryID, gint status, gpointer data )
4499 AddrQueryObject *aqo;
4501 /* Remove idler function */
4502 ptrQID = GINT_TO_POINTER( queryID );
4504 g_idle_remove_by_data( ptrQID );
4507 /* Refresh addressbook contents */
4508 addressbook_refresh_current();
4509 req = qrymgr_find_request( queryID );
4511 aqo = ( AddrQueryObject * ) req->queryList->data;
4512 addressbook_search_message( aqo->queryType, status );
4515 /* Stop the search */
4516 addrindex_stop_search( queryID );
4522 * \param ds Data source to search.
4523 * \param searchTerm String to lookup.
4524 * \param pNode Parent data source node.
4526 static void addressbook_perform_search(
4527 AddressDataSource *ds, gchar *searchTerm,
4528 GtkCTreeNode *pNode )
4530 AddrBookBase *adbase;
4531 AddressCache *cache;
4537 AddressObjectType aoType = ADDR_NONE;
4541 if( *searchTerm == '\0' || strlen( searchTerm ) < 1 ) return;
4543 if( ds && ds->type == ADDR_IF_LDAP ) {
4545 aoType = ADDR_LDAP_QUERY;
4551 /* Get reference to address cache */
4552 adbase = ( AddrBookBase * ) ds->rawDataSource;
4553 cache = adbase->addressCache;
4555 /* Create a folder for the search results */
4556 name = g_strdup_printf( _queryFolderLabel_, searchTerm );
4557 folder = addressbook_setup_subf(ds, name, pNode);
4560 /* Setup the search */
4561 queryID = addrindex_setup_explicit_search(
4562 ds, searchTerm, folder, addressbook_search_callback_end, NULL );
4563 if( queryID == 0 ) return;
4565 /* Set up idler function */
4566 idleID = g_idle_add(
4567 ( GtkFunction ) addressbook_search_idle,
4568 GINT_TO_POINTER( queryID ) );
4570 /* Start search, sit back and wait for something to happen */
4571 addrindex_start_search( queryID );
4573 addressbook_status_show( _tempMessage_ );
4577 * Lookup button handler. Address search is only performed against
4578 * address interfaces for external queries.
4580 * \param button Lookup button widget.
4581 * \param data Data object.
4583 static void addressbook_lup_clicked( GtkButton *button, gpointer data ) {
4586 AddressDataSource *ds;
4587 AddressInterface *iface;
4589 GtkCTreeNode *node, *parentNode;
4591 node = addrbook.treeSelected;
4592 if( ! node ) return;
4593 if( GTK_CTREE_ROW(node)->level == 1 ) return;
4595 ctree = GTK_CTREE(addrbook.ctree);
4596 obj = gtk_ctree_node_get_row_data( ctree, node );
4597 if( obj == NULL ) return;
4599 ds = addressbook_find_datasource( node );
4600 if( ds == NULL ) return;
4602 /* We must have a datasource that is an external interface */
4603 iface = ds->interface;
4604 if( ! iface->haveLibrary ) return;
4605 if( ! iface->externalQuery ) return;
4608 gtk_editable_get_chars( GTK_EDITABLE(addrbook.entry), 0, -1 );
4609 g_strchomp( searchTerm );
4611 if( obj->type == ADDR_ITEM_FOLDER ) {
4612 parentNode = GTK_CTREE_ROW(node)->parent;
4617 addressbook_perform_search( ds, searchTerm, parentNode );
4619 gtk_widget_grab_focus( addrbook.entry );
4621 g_free( searchTerm );
4624 static void addressbook_close_clicked( GtkButton *button, gpointer data ) {
4625 addressbook_close();
4630 * Browse address entry for highlighted entry.
4632 static void addressbook_browse_entry_cb(void)
4634 GtkCTree *clist = GTK_CTREE(addrbook.clist);
4636 AddressDataSource *ds;
4637 AddressInterface *iface;
4641 if(addrbook.listSelected == NULL)
4644 obj = gtk_ctree_node_get_row_data(clist, addrbook.listSelected);
4648 ds = addressbook_find_datasource(GTK_CTREE_NODE(addrbook.treeSelected));
4652 iface = ds->interface;
4653 if(! iface->haveLibrary )
4657 if (obj->type == ADDR_ITEM_EMAIL) {
4658 email = ( ItemEMail * ) obj;
4662 person = (ItemPerson *) ADDRITEM_PARENT(email);
4664 else if (obj->type == ADDR_ITEM_PERSON) {
4665 person = (ItemPerson *) obj;
4672 if( iface && iface->type == ADDR_IF_LDAP ) {
4673 browseldap_entry(ds, person->externalID);
4678 /* **********************************************************************
4679 * Build lookup tables.
4680 * ***********************************************************************
4684 * Remap object types.
4685 * Enter: abType AddressObjectType (used in tree node).
4686 * Return: ItemObjectType (used in address cache data).
4688 ItemObjectType addressbook_type2item( AddressObjectType abType ) {
4689 ItemObjectType ioType;
4692 case ADDR_ITEM_PERSON: ioType = ITEMTYPE_PERSON; break;
4693 case ADDR_ITEM_EMAIL: ioType = ITEMTYPE_EMAIL; break;
4694 case ADDR_ITEM_FOLDER: ioType = ITEMTYPE_FOLDER; break;
4695 case ADDR_ITEM_GROUP: ioType = ITEMTYPE_GROUP; break;
4696 case ADDR_DATASOURCE: ioType = ITEMTYPE_DATASOURCE; break;
4697 default: ioType = ITEMTYPE_NONE; break;
4703 * Build table that controls the rendering of object types.
4705 static void addrbookctl_build_map( GtkWidget *window ) {
4706 AddressTypeControlItem *atci;
4709 stock_pixmap_gdk(window, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
4710 stock_pixmap_gdk(window, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
4711 stock_pixmap_gdk(window, STOCK_PIXMAP_GROUP, &groupxpm, &groupxpmmask);
4712 stock_pixmap_gdk(window, STOCK_PIXMAP_VCARD, &vcardxpm, &vcardxpmmask);
4713 stock_pixmap_gdk(window, STOCK_PIXMAP_BOOK, &bookxpm, &bookxpmmask);
4714 stock_pixmap_gdk(window, STOCK_PIXMAP_ADDRESS, &addressxpm, &addressxpmmask);
4715 stock_pixmap_gdk(window, STOCK_PIXMAP_JPILOT, &jpilotxpm, &jpilotxpmmask);
4716 stock_pixmap_gdk(window, STOCK_PIXMAP_CATEGORY, &categoryxpm, &categoryxpmmask);
4717 stock_pixmap_gdk(window, STOCK_PIXMAP_LDAP, &ldapxpm, &ldapxpmmask);
4718 stock_pixmap_gdk(window, STOCK_PIXMAP_ADDRESS_SEARCH, &addrsearchxpm, &addrsearchxpmmask);
4720 _addressBookTypeHash_ = g_hash_table_new( g_int_hash, g_int_equal );
4721 _addressBookTypeList_ = NULL;
4724 atci = g_new0( AddressTypeControlItem, 1 );
4725 atci->objectType = ADDR_INTERFACE;
4726 atci->interfaceType = ADDR_IF_NONE;
4727 atci->showInTree = TRUE;
4728 atci->treeExpand = TRUE;
4729 atci->treeLeaf = FALSE;
4730 atci->displayName = _( "Interface" );
4731 atci->iconXpm = folderxpm;
4732 atci->maskXpm = folderxpmmask;
4733 atci->iconXpmOpen = folderopenxpm;
4734 atci->maskXpmOpen = folderopenxpmmask;
4735 atci->menuCommand = NULL;
4736 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4737 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4740 atci = g_new0( AddressTypeControlItem, 1 );
4741 atci->objectType = ADDR_BOOK;
4742 atci->interfaceType = ADDR_IF_BOOK;
4743 atci->showInTree = TRUE;
4744 atci->treeExpand = TRUE;
4745 atci->treeLeaf = FALSE;
4746 atci->displayName = _( "Address Book" );
4747 atci->iconXpm = bookxpm;
4748 atci->maskXpm = bookxpmmask;
4749 atci->iconXpmOpen = bookxpm;
4750 atci->maskXpmOpen = bookxpmmask;
4751 atci->menuCommand = "/Book/New Book";
4752 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4753 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4756 atci = g_new0( AddressTypeControlItem, 1 );
4757 atci->objectType = ADDR_ITEM_PERSON;
4758 atci->interfaceType = ADDR_IF_NONE;
4759 atci->showInTree = FALSE;
4760 atci->treeExpand = FALSE;
4761 atci->treeLeaf = FALSE;
4762 atci->displayName = _( "Person" );
4763 atci->iconXpm = NULL;
4764 atci->maskXpm = NULL;
4765 atci->iconXpmOpen = NULL;
4766 atci->maskXpmOpen = NULL;
4767 atci->menuCommand = NULL;
4768 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4769 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4772 atci = g_new0( AddressTypeControlItem, 1 );
4773 atci->objectType = ADDR_ITEM_EMAIL;
4774 atci->interfaceType = ADDR_IF_NONE;
4775 atci->showInTree = FALSE;
4776 atci->treeExpand = FALSE;
4777 atci->treeLeaf = TRUE;
4778 atci->displayName = _( "Email Address" );
4779 atci->iconXpm = addressxpm;
4780 atci->maskXpm = addressxpmmask;
4781 atci->iconXpmOpen = addressxpm;
4782 atci->maskXpmOpen = addressxpmmask;
4783 atci->menuCommand = NULL;
4784 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4785 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4788 atci = g_new0( AddressTypeControlItem, 1 );
4789 atci->objectType = ADDR_ITEM_GROUP;
4790 atci->interfaceType = ADDR_IF_BOOK;
4791 atci->showInTree = TRUE;
4792 atci->treeExpand = FALSE;
4793 atci->treeLeaf = FALSE;
4794 atci->displayName = _( "Group" );
4795 atci->iconXpm = groupxpm;
4796 atci->maskXpm = groupxpmmask;
4797 atci->iconXpmOpen = groupxpm;
4798 atci->maskXpmOpen = groupxpmmask;
4799 atci->menuCommand = NULL;
4800 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4801 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4804 atci = g_new0( AddressTypeControlItem, 1 );
4805 atci->objectType = ADDR_ITEM_FOLDER;
4806 atci->interfaceType = ADDR_IF_BOOK;
4807 atci->showInTree = TRUE;
4808 atci->treeExpand = FALSE;
4809 atci->treeLeaf = FALSE;
4810 atci->displayName = _( "Folder" );
4811 atci->iconXpm = folderxpm;
4812 atci->maskXpm = folderxpmmask;
4813 atci->iconXpmOpen = folderopenxpm;
4814 atci->maskXpmOpen = folderopenxpmmask;
4815 atci->menuCommand = NULL;
4816 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4817 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4820 atci = g_new0( AddressTypeControlItem, 1 );
4821 atci->objectType = ADDR_VCARD;
4822 atci->interfaceType = ADDR_IF_VCARD;
4823 atci->showInTree = TRUE;
4824 atci->treeExpand = TRUE;
4825 atci->treeLeaf = TRUE;
4826 atci->displayName = _( "vCard" );
4827 atci->iconXpm = vcardxpm;
4828 atci->maskXpm = vcardxpmmask;
4829 atci->iconXpmOpen = vcardxpm;
4830 atci->maskXpmOpen = vcardxpmmask;
4831 atci->menuCommand = "/Book/New vCard";
4832 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4833 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4836 atci = g_new0( AddressTypeControlItem, 1 );
4837 atci->objectType = ADDR_JPILOT;
4838 atci->interfaceType = ADDR_IF_JPILOT;
4839 atci->showInTree = TRUE;
4840 atci->treeExpand = TRUE;
4841 atci->treeLeaf = FALSE;
4842 atci->displayName = _( "JPilot" );
4843 atci->iconXpm = jpilotxpm;
4844 atci->maskXpm = jpilotxpmmask;
4845 atci->iconXpmOpen = jpilotxpm;
4846 atci->maskXpmOpen = jpilotxpmmask;
4847 atci->menuCommand = "/Book/New JPilot";
4848 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4849 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4852 atci = g_new0( AddressTypeControlItem, 1 );
4853 atci->objectType = ADDR_CATEGORY;
4854 atci->interfaceType = ADDR_IF_JPILOT;
4855 atci->showInTree = TRUE;
4856 atci->treeExpand = TRUE;
4857 atci->treeLeaf = TRUE;
4858 atci->displayName = _( "JPilot" );
4859 atci->iconXpm = categoryxpm;
4860 atci->maskXpm = categoryxpmmask;
4861 atci->iconXpmOpen = categoryxpm;
4862 atci->maskXpmOpen = categoryxpmmask;
4863 atci->menuCommand = NULL;
4864 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4865 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4868 atci = g_new0( AddressTypeControlItem, 1 );
4869 atci->objectType = ADDR_LDAP;
4870 atci->interfaceType = ADDR_IF_LDAP;
4871 atci->showInTree = TRUE;
4872 atci->treeExpand = TRUE;
4873 atci->treeLeaf = FALSE;
4874 atci->displayName = _( "LDAP servers" );
4875 atci->iconXpm = ldapxpm;
4876 atci->maskXpm = ldapxpmmask;
4877 atci->iconXpmOpen = ldapxpm;
4878 atci->maskXpmOpen = ldapxpmmask;
4879 atci->menuCommand = "/Book/New LDAP Server";
4880 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4881 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4884 atci = g_new0( AddressTypeControlItem, 1 );
4885 atci->objectType = ADDR_LDAP_QUERY;
4886 atci->interfaceType = ADDR_IF_LDAP;
4887 atci->showInTree = TRUE;
4888 atci->treeExpand = FALSE;
4889 atci->treeLeaf = TRUE;
4890 atci->displayName = _( "LDAP Query" );
4891 atci->iconXpm = addrsearchxpm;
4892 atci->maskXpm = addrsearchxpmmask;
4893 atci->iconXpmOpen = addrsearchxpm;
4894 atci->maskXpmOpen = addrsearchxpmmask;
4895 atci->menuCommand = NULL;
4896 g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4897 _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4902 * Search for specified object type.
4904 static AddressTypeControlItem *addrbookctl_lookup( gint ot ) {
4906 return ( AddressTypeControlItem * ) g_hash_table_lookup( _addressBookTypeHash_, &objType );
4910 * Search for specified interface type.
4912 static AddressTypeControlItem *addrbookctl_lookup_iface( AddressIfType ifType ) {
4913 GList *node = _addressBookTypeList_;
4915 AddressTypeControlItem *atci = node->data;
4916 if( atci->interfaceType == ifType ) return atci;
4917 node = g_list_next( node );
4922 static void addrbookctl_free_address( AddressObject *obj ) {
4923 g_free( obj->name );
4924 obj->type = ADDR_NONE;
4928 static void addrbookctl_free_interface( AdapterInterface *adapter ) {
4929 addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
4930 adapter->interface = NULL;
4931 adapter->interfaceType = ADDR_IF_NONE;
4932 adapter->atci = NULL;
4933 adapter->enabled = FALSE;
4934 adapter->haveLibrary = FALSE;
4935 adapter->treeNode = NULL;
4939 static void addrbookctl_free_datasource( AdapterDSource *adapter ) {
4940 addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
4941 adapter->dataSource = NULL;
4942 adapter->subType = ADDR_NONE;
4946 static void addrbookctl_free_folder( AdapterFolder *adapter ) {
4947 addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
4948 adapter->itemFolder = NULL;
4952 static void addrbookctl_free_group( AdapterGroup *adapter ) {
4953 addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
4954 adapter->itemGroup = NULL;
4959 * Build GUI interface list.
4961 static void addrbookctl_build_iflist( void ) {
4962 AddressTypeControlItem *atci;
4963 AdapterInterface *adapter;
4966 if( _addressIndex_ == NULL ) {
4967 _addressIndex_ = addrindex_create_index();
4968 if( _clipBoard_ == NULL ) {
4969 _clipBoard_ = addrclip_create();
4971 addrclip_set_index( _clipBoard_, _addressIndex_ );
4973 _addressInterfaceList_ = NULL;
4974 list = addrindex_get_interface_list( _addressIndex_ );
4976 AddressInterface *interface = list->data;
4977 atci = addrbookctl_lookup_iface( interface->type );
4979 adapter = g_new0( AdapterInterface, 1 );
4980 adapter->interfaceType = interface->type;
4981 adapter->atci = atci;
4982 adapter->interface = interface;
4983 adapter->treeNode = NULL;
4984 adapter->enabled = TRUE;
4985 adapter->haveLibrary = interface->haveLibrary;
4986 ADDRESS_OBJECT(adapter)->type = ADDR_INTERFACE;
4987 ADDRESS_OBJECT_NAME(adapter) = g_strdup( atci->displayName );
4988 _addressInterfaceList_ =
4989 g_list_append( _addressInterfaceList_, adapter );
4991 list = g_list_next( list );
4996 * Find GUI interface type specified interface type.
4997 * \param ifType Interface type.
4998 * \return Interface item, or NULL if not found.
5000 static AdapterInterface *addrbookctl_find_interface( AddressIfType ifType ) {
5001 GList *node = _addressInterfaceList_;
5003 AdapterInterface *adapter = node->data;
5004 if( adapter->interfaceType == ifType ) return adapter;
5005 node = g_list_next( node );
5011 * Build interface list selection.
5013 static void addrbookctl_build_ifselect( void ) {
5014 GList *newList = NULL;
5019 gchar *endptr = NULL;
5021 AdapterInterface *adapter;
5023 selectStr = g_strdup( ADDRESSBOOK_IFACE_SELECTION );
5026 splitStr = g_strsplit( selectStr, ",", -1 );
5027 for( i = 0; i < ADDRESSBOOK_MAX_IFACE; i++ ) {
5029 /* g_print( "%d : %s\n", i, splitStr[i] ); */
5030 ifType = strtol( splitStr[i], &endptr, 10 );
5033 if( strcmp( endptr, "/n" ) == 0 ) {
5037 /* g_print( "\t%d : %s\n", ifType, enabled ? "yes" : "no" ); */
5038 adapter = addrbookctl_find_interface( ifType );
5040 newList = g_list_append( newList, adapter );
5047 /* g_print( "i=%d\n", i ); */
5048 g_strfreev( splitStr );
5049 g_free( selectStr );
5051 /* Replace existing list */
5052 mgu_clear_list( _addressIFaceSelection_ );
5053 g_list_free( _addressIFaceSelection_ );
5054 _addressIFaceSelection_ = newList;
5058 /* ***********************************************************************
5059 * Add sender to address book.
5060 * ***********************************************************************
5064 * This function is used by the Add sender to address book function.
5066 gboolean addressbook_add_contact(
5067 const gchar *name, const gchar *address, const gchar *remarks,
5068 GdkPixbuf *picture )
5070 debug_print( "addressbook_add_contact: name/address: %s - %s\n", name, address );
5071 if( addressadd_selection( _addressIndex_, name, address, remarks, picture ) ) {
5072 debug_print( "addressbook_add_contact - added\n" );
5073 addressbook_refresh();
5078 /* ***********************************************************************
5079 * Book/folder selection.
5080 * ***********************************************************************
5084 * This function is used by the matcher dialog to select a book/folder.
5086 gchar *addressbook_folder_selection( const gchar *folderpath)
5088 AddressBookFile *book = NULL;
5089 ItemFolder *folder = NULL;
5092 g_return_val_if_fail( folderpath != NULL, NULL);
5094 if ( addressbook_foldersel_selection( _addressIndex_, &book, &folder, folderpath )
5096 if ( folder != NULL) {
5098 gchar *oldtmp = NULL;
5099 AddrItemObject *obj = NULL;
5101 /* walk thru folder->parent to build the full folder path */
5102 /* TODO: wwp: optimize this */
5104 tmp = g_strdup(obj->uid);
5105 while ( obj->parent ) {
5107 if ( obj->name != NULL ) {
5108 oldtmp = g_strdup(tmp);
5110 tmp = g_strdup_printf("%s/%s", obj->uid, oldtmp);
5114 path = g_strdup_printf("%s/%s", book->fileName, tmp);
5117 path = g_strdup_printf("%s", book->fileName);
5119 debug_print( "addressbook_foldersel: %s\n", path?path:"(null)");
5125 /* ***********************************************************************
5126 * Book/folder checking.
5127 * ***********************************************************************
5130 static FolderInfo *addressbook_peek_subfolder_exists_create_folderinfo( AddressBookFile *abf, ItemFolder *folder )
5132 FolderInfo *fi = g_new0( FolderInfo, 1 );
5134 fi->folder = folder;
5138 static void addressbook_peek_subfolder_exists_load_folder( ItemFolder *parentFolder,
5139 FolderInfo *fiParent, FolderPathMatch *match )
5145 FolderPathMatch *nextmatch = NULL;
5150 list = parentFolder->listFolder;
5152 folder = list->data;
5153 fName = g_strdup( ADDRITEM_NAME(folder) );
5155 /* match folder name, match pointer will be set to NULL if next recursive call
5156 doesn't need to match subfolder name */
5157 if ( match != NULL &&
5158 match->matched == FALSE ) {
5159 if ( strcmp(match->folder_path[match->index], folder->obj.uid) == 0 ) {
5160 /* folder name matches, prepare next subfolder match */
5161 debug_print("matched folder name '%s'\n", fName);
5163 if ( match->folder_path[match->index] == NULL ) {
5164 /* we've matched all elements */
5165 match->matched = TRUE;
5166 match->folder = folder;
5167 debug_print("book/folder path matched!\n");
5169 /* keep on matching */
5177 fi = addressbook_peek_subfolder_exists_create_folderinfo( fiParent->book, folder );
5178 addressbook_peek_subfolder_exists_load_folder( folder, fi, nextmatch );
5180 list = g_list_next( list );
5185 * This function is used by to check if a matcher book/folder path corresponds to an
5186 existing addressbook book/folder ("" or "Any" are considered as valid, NULL invalid).
5187 Caution: returned book and folder pointers can be NULL even when returning TRUE:
5188 if book AND folder are NULL this means that folderpath was empty or Any.
5189 If folderpath is a simple book name (without folder), book will not be NULL and folder
5190 will be NULL. It's not expected to return book as NULL and folder as non NULL.
5193 gboolean addressbook_peek_folder_exists( gchar *folderpath,
5194 AddressDataSource **book,
5195 ItemFolder **folder )
5197 AddressDataSource *ds;
5198 GList *list, *nodeDS;
5199 ItemFolder *rootFolder;
5200 AddressBookFile *abf;
5202 FolderPathMatch folder_path_match = { NULL, FALSE, 0, NULL, NULL };
5209 if ( folderpath == NULL )
5212 if ( strcasecmp(folderpath, "Any") == 0 || *folderpath == '\0' )
5215 /* split the folder path we've received, we'll try to match this path, subpath by
5216 subpath against the book/folder structure in order */
5217 folder_path_match.folder_path = g_strsplit( folderpath, "/", 256 );
5218 if (!folder_path_match.folder_path)
5221 list = addrindex_get_interface_list( _addressIndex_ );
5222 while ( list && !folder_path_match.matched ) {
5223 AddressInterface *interface = list->data;
5224 if ( interface && interface->type == ADDR_IF_BOOK ) {
5225 nodeDS = interface->listSource;
5226 while ( nodeDS && !folder_path_match.matched ) {
5229 /* Read address book */
5230 if( ! addrindex_ds_get_read_flag( ds ) ) {
5231 addrindex_ds_read_data( ds );
5234 /* Add node for address book */
5235 abf = ds->rawDataSource;
5237 /* match book name */
5238 if ( abf && abf->fileName &&
5239 strcmp(folder_path_match.folder_path[0], abf->fileName) == 0 ) {
5241 debug_print("matched book name '%s'\n", abf->fileName);
5242 folder_path_match.book = ds;
5244 if ( folder_path_match.folder_path[1] == NULL ) {
5245 /* no folder part to match */
5247 folder_path_match.matched = TRUE;
5248 folder_path_match.folder = NULL;
5249 debug_print("book path matched!\n");
5252 /* match folder part */
5254 fi = addressbook_peek_subfolder_exists_create_folderinfo( abf, NULL );
5255 rootFolder = addrindex_ds_get_root_folder( ds );
5257 /* prepare for recursive call */
5258 folder_path_match.index = 1;
5259 /* this call will set folder_path_match.matched and folder_path_match.folder */
5260 addressbook_peek_subfolder_exists_load_folder( rootFolder, fi, &folder_path_match );
5265 nodeDS = g_list_next( nodeDS );
5268 list = g_list_next( list );
5271 g_strfreev( folder_path_match.folder_path );
5274 *book = folder_path_match.book;
5276 *folder = folder_path_match.folder;
5277 return folder_path_match.matched;
5281 /* **********************************************************************
5283 * ***********************************************************************
5289 static void addressbook_import_ldif_cb( void ) {
5290 AddressDataSource *ds = NULL;
5291 AdapterDSource *ads = NULL;
5292 AddressBookFile *abf = NULL;
5293 AdapterInterface *adapter;
5294 GtkCTreeNode *newNode;
5296 adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5298 if( adapter->treeNode ) {
5299 abf = addressbook_imp_ldif( _addressIndex_ );
5301 ds = addrindex_index_add_datasource(
5302 _addressIndex_, ADDR_IF_BOOK, abf );
5303 ads = addressbook_create_ds_adapter(
5304 ds, ADDR_BOOK, NULL );
5305 addressbook_ads_set_name(
5306 ads, addrbook_get_name( abf ) );
5307 newNode = addressbook_add_object(
5309 ADDRESS_OBJECT(ads) );
5311 gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
5313 addrbook.treeSelected = newNode;
5316 /* Notify address completion */
5317 invalidate_address_completion();
5326 static void addressbook_import_mutt_cb( void ) {
5327 AddressDataSource *ds = NULL;
5328 AdapterDSource *ads = NULL;
5329 AddressBookFile *abf = NULL;
5330 AdapterInterface *adapter;
5331 GtkCTreeNode *newNode;
5333 adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5335 if( adapter->treeNode ) {
5336 abf = addressbook_imp_mutt( _addressIndex_ );
5338 ds = addrindex_index_add_datasource(
5339 _addressIndex_, ADDR_IF_BOOK, abf );
5340 ads = addressbook_create_ds_adapter(
5341 ds, ADDR_BOOK, NULL );
5342 addressbook_ads_set_name(
5343 ads, addrbook_get_name( abf ) );
5344 newNode = addressbook_add_object(
5346 ADDRESS_OBJECT(ads) );
5348 gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
5350 addrbook.treeSelected = newNode;
5353 /* Notify address completion */
5354 invalidate_address_completion();
5363 static void addressbook_import_pine_cb( void ) {
5364 AddressDataSource *ds = NULL;
5365 AdapterDSource *ads = NULL;
5366 AddressBookFile *abf = NULL;
5367 AdapterInterface *adapter;
5368 GtkCTreeNode *newNode;
5370 adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5372 if( adapter->treeNode ) {
5373 abf = addressbook_imp_pine( _addressIndex_ );
5375 ds = addrindex_index_add_datasource(
5376 _addressIndex_, ADDR_IF_BOOK, abf );
5377 ads = addressbook_create_ds_adapter(
5378 ds, ADDR_BOOK, NULL );
5379 addressbook_ads_set_name(
5380 ads, addrbook_get_name( abf ) );
5381 newNode = addressbook_add_object(
5383 ADDRESS_OBJECT(ads) );
5385 gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
5387 addrbook.treeSelected = newNode;
5390 /* Notify address completion */
5391 invalidate_address_completion();
5398 * Harvest addresses.
5399 * \param folderItem Folder to import.
5400 * \param sourceInd Source indicator: FALSE - Folder, TRUE - Messages.
5401 * \param msgList List of message numbers, or NULL to process folder.
5403 void addressbook_harvest(
5404 FolderItem *folderItem, gboolean sourceInd, GList *msgList )
5406 AddressDataSource *ds = NULL;
5407 AdapterDSource *ads = NULL;
5408 AddressBookFile *abf = NULL;
5409 AdapterInterface *adapter;
5410 GtkCTreeNode *newNode;
5412 abf = addrgather_dlg_execute(
5413 folderItem, _addressIndex_, sourceInd, msgList );
5415 ds = addrindex_index_add_datasource(
5416 _addressIndex_, ADDR_IF_BOOK, abf );
5418 adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5420 if( adapter->treeNode ) {
5421 ads = addressbook_create_ds_adapter(
5422 ds, ADDR_BOOK, addrbook_get_name( abf ) );
5423 newNode = addressbook_add_object(
5425 ADDRESS_OBJECT(ads) );
5429 /* Notify address completion */
5430 invalidate_address_completion();
5437 static void addressbook_export_html_cb( void ) {
5438 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
5440 AddressDataSource *ds = NULL;
5441 AddrBookBase *adbase;
5442 AddressCache *cache;
5443 GtkCTreeNode *node = NULL;
5445 if( ! addrbook.treeSelected ) return;
5446 node = addrbook.treeSelected;
5447 if( GTK_CTREE_ROW(node)->level == 1 ) return;
5448 obj = gtk_ctree_node_get_row_data( ctree, node );
5449 if( obj == NULL ) return;
5451 ds = addressbook_find_datasource( node );
5452 if( ds == NULL ) return;
5453 adbase = ( AddrBookBase * ) ds->rawDataSource;
5454 cache = adbase->addressCache;
5455 addressbook_exp_html( cache );
5461 static void addressbook_export_ldif_cb( void ) {
5462 GtkCTree *ctree = GTK_CTREE(addrbook.ctree);
5464 AddressDataSource *ds = NULL;
5465 AddrBookBase *adbase;
5466 AddressCache *cache;
5467 GtkCTreeNode *node = NULL;
5469 if( ! addrbook.treeSelected ) return;
5470 node = addrbook.treeSelected;
5471 if( GTK_CTREE_ROW(node)->level == 1 ) return;
5472 obj = gtk_ctree_node_get_row_data( ctree, node );
5473 if( obj == NULL ) return;
5475 ds = addressbook_find_datasource( node );
5476 if( ds == NULL ) return;
5477 adbase = ( AddrBookBase * ) ds->rawDataSource;
5478 cache = adbase->addressCache;
5479 addressbook_exp_ldif( cache );
5482 static void addressbook_find_duplicates_cb(void)
5484 addrduplicates_find(GTK_WINDOW(addrbook.window));
5487 static void addressbook_edit_custom_attr_cb(void)
5489 addressbook_custom_attr_edit();
5492 static void addressbook_start_drag(GtkWidget *widget, gint button,
5496 GdkDragContext *context;
5497 if (addressbook_target_list == NULL)
5498 addressbook_target_list = gtk_target_list_new(
5499 addressbook_drag_types, 1);
5500 context = gtk_drag_begin(widget, addressbook_target_list,
5501 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
5502 gtk_drag_set_icon_default(context);
5505 static void addressbook_drag_data_get(GtkWidget *widget,
5506 GdkDragContext *drag_context,
5507 GtkSelectionData *selection_data,
5512 AddrItemObject *aio = NULL;
5513 AddressObject *pobj = NULL;
5514 AdapterDSource *ads = NULL;
5515 AddressDataSource *ds = NULL;
5518 pobj = gtk_ctree_node_get_row_data( GTK_CTREE(addrbook.ctree), addrbook.treeSelected );
5520 if( pobj == NULL ) return;
5522 if( pobj->type == ADDR_DATASOURCE ) {
5523 ads = ADAPTER_DSOURCE(pobj);
5524 ds = ads->dataSource;
5525 } else if (pobj->type == ADDR_ITEM_GROUP) {
5530 else if( pobj->type != ADDR_INTERFACE ) {
5531 ds = addressbook_find_datasource( addrbook.treeSelected );
5537 for(cur = GTK_CLIST(addrbook.clist)->selection; cur; cur = cur->next) {
5538 aio = (AddrItemObject *)gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.clist),
5539 GTK_CTREE_NODE(cur->data));
5540 while (aio && aio->type != ADDR_ITEM_PERSON) {
5545 if (aio && aio->type == ADDR_ITEM_PERSON) {
5546 if( ds && ds->interface && ds->interface->readOnly)
5547 gtk_selection_data_set(selection_data,
5548 selection_data->target, 8,
5549 (const guchar *)"Dummy_addr_copy", 15);
5551 gtk_selection_data_set(selection_data,
5552 selection_data->target, 8,
5553 (const guchar *)"Dummy_addr_move", 15);
5557 static gboolean addressbook_drag_motion_cb(GtkWidget *widget,
5558 GdkDragContext *context,
5565 GtkCTreeNode *node = NULL;
5566 gboolean acceptable = FALSE;
5567 gint height = addrbook.ctree->allocation.height;
5568 gint total_height = addrbook.ctree->requisition.height;
5569 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
5570 GTK_SCROLLED_WINDOW(addrbook.ctree_swin));
5571 gfloat vpos = pos->value;
5573 if (gtk_clist_get_selection_info
5574 (GTK_CLIST(widget), x - 24, y - 24, &row, &column)) {
5576 if (y > height - 24 && height + vpos < total_height) {
5577 gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
5578 gtk_adjustment_changed(pos);
5580 if (y < 24 && y > 0) {
5581 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
5582 gtk_adjustment_changed(pos);
5584 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
5587 AddressObject *obj = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node );
5588 if( obj->type == ADDR_ITEM_FOLDER
5589 || obj->type == ADDR_ITEM_GROUP)
5592 AdapterDSource *ads = NULL;
5593 AddressDataSource *ds = NULL;
5594 ads = ADAPTER_DSOURCE(obj);
5595 if (ads == NULL ){ return FALSE;}
5596 ds = ads->dataSource;
5597 if (ds == NULL ) { return FALSE;}
5605 g_signal_handlers_block_by_func
5607 G_CALLBACK(addressbook_tree_selected), NULL);
5608 gtk_sctree_select( GTK_SCTREE(widget), node);
5609 g_signal_handlers_unblock_by_func
5611 G_CALLBACK(addressbook_tree_selected), NULL);
5612 gdk_drag_status(context,
5613 (context->actions == GDK_ACTION_COPY ?
5614 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
5616 gdk_drag_status(context, 0, time);
5621 static void addressbook_drag_leave_cb(GtkWidget *widget,
5622 GdkDragContext *context,
5626 if (addrbook.treeSelected) {
5627 g_signal_handlers_block_by_func
5629 G_CALLBACK(addressbook_tree_selected), NULL);
5630 gtk_sctree_select( GTK_SCTREE(widget), addrbook.opened);
5631 g_signal_handlers_unblock_by_func
5633 G_CALLBACK(addressbook_tree_selected), NULL);
5638 static void addressbook_drag_received_cb(GtkWidget *widget,
5639 GdkDragContext *drag_context,
5642 GtkSelectionData *data,
5649 GtkCTreeNode *lastopened = addrbook.opened;
5651 if (!strncmp(data->data, "Dummy_addr", 10)) {
5652 if (gtk_clist_get_selection_info
5653 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0) {
5657 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
5658 if( !node || !gtk_ctree_node_get_row_data(GTK_CTREE(addrbook.ctree), node))
5661 gtk_clist_freeze(GTK_CLIST(addrbook.clist));
5662 if (drag_context->action == GDK_ACTION_COPY ||
5663 !strcmp(data->data, "Dummy_addr_copy"))
5664 addressbook_clip_copy_cb();
5666 addressbook_clip_cut_cb();
5667 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), node);
5668 addressbook_clip_paste_cb();
5669 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), lastopened);
5670 gtk_clist_thaw(GTK_CLIST(addrbook.clist));
5671 gtk_drag_finish(drag_context, TRUE, TRUE, time);