1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2020 the Claws Mail team and Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24 
25 #include "defs.h"
26 
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <gtk/gtk.h>
31 #include <string.h>
32 #include <setjmp.h>
33 #include <sys/types.h>
34 #include <dirent.h>
35 
36 #include "main.h"
37 #include "addressbook.h"
38 #include "manage_window.h"
39 #include "prefs_common.h"
40 #include "alertpanel.h"
41 #include "inputdialog.h"
42 #include "menu.h"
43 #include "stock_pixmap.h"
44 #include "xml.h"
45 #include "prefs_gtk.h"
46 #include "procmime.h"
47 #include "file-utils.h"
48 #include "utils.h"
49 #include "gtkutils.h"
50 #include "codeconv.h"
51 #include "about.h"
52 #include "addr_compl.h"
53 #include "password.h"
54 
55 #include "mgutils.h"
56 #include "addressitem.h"
57 #include "addritem.h"
58 #include "addrcache.h"
59 #include "addrbook.h"
60 #include "addrindex.h"
61 #include "addrmerge.h"
62 #include "addressadd.h"
63 #include "addrduplicates.h"
64 #include "addressbook_foldersel.h"
65 #include "vcard.h"
66 #include "editvcard.h"
67 #include "editgroup.h"
68 #include "editaddress.h"
69 #include "editbook.h"
70 #include "importldif.h"
71 #include "importmutt.h"
72 #include "importpine.h"
73 #include "manual.h"
74 
75 #ifdef USE_JPILOT
76 #include "jpilot.h"
77 #include "editjpilot.h"
78 #endif
79 
80 #ifdef USE_LDAP
81 #include <pthread.h>
82 #include "ldapserver.h"
83 #include "editldap.h"
84 #include "ldapupdate.h"
85 
86 #define ADDRESSBOOK_LDAP_BUSYMSG "Busy"
87 #endif
88 
89 #include "addrquery.h"
90 #include "addrselect.h"
91 #include "addrclip.h"
92 #include "addrgather.h"
93 #include "adbookbase.h"
94 #include "exphtmldlg.h"
95 #include "expldifdlg.h"
96 #include "browseldap.h"
97 #include "addrcustomattr.h"
98 #ifdef G_OS_WIN32
99 #undef interface
100 #endif
101 typedef enum
102 {
103 	COL_SOURCES	= 0,
104 	N_INDEX_COLS	= 1
105 } AddressIndexColumns;
106 
107 typedef enum
108 {
109 	COL_NAME	= 0,
110 	COL_ADDRESS	= 1,
111 	COL_REMARKS	= 2,
112 	N_LIST_COLS	= 3
113 } AddressListColumns;
114 
115 typedef struct {
116 	AddressBookFile	*book;
117 	ItemFolder	*folder;
118 } FolderInfo;
119 
120 typedef struct {
121 	gchar **folder_path;
122 	gboolean matched;
123 	gint index;
124 	AddressDataSource *book;
125 	ItemFolder *folder;
126 } FolderPathMatch;
127 
128 static gchar *list_titles[] = { N_("Name"),
129                                 N_("Email Address"),
130                                 N_("Remarks") };
131 
132 #define COL_NAME_WIDTH		164
133 #define COL_ADDRESS_WIDTH	156
134 
135 #define COL_FOLDER_WIDTH	170
136 #define ADDRESSBOOK_WIDTH	640
137 #define ADDRESSBOOK_HEIGHT	360
138 
139 #define ADDRESSBOOK_MSGBUF_SIZE 2048
140 
141 static GdkPixbuf *folderxpm = NULL;
142 static GdkPixbuf *folderopenxpm = NULL;
143 static GdkPixbuf *groupxpm = NULL;
144 static GdkPixbuf *interfacexpm = NULL;
145 static GdkPixbuf *bookxpm = NULL;
146 static GdkPixbuf *addressxpm = NULL;
147 static GdkPixbuf *vcardxpm = NULL;
148 static GdkPixbuf *jpilotxpm = NULL;
149 static GdkPixbuf *categoryxpm = NULL;
150 static GdkPixbuf *ldapxpm = NULL;
151 static GdkPixbuf *addrsearchxpm = NULL;
152 
153 /* Message buffer */
154 static gchar addressbook_msgbuf[ ADDRESSBOOK_MSGBUF_SIZE ];
155 
156 /* Address list selection */
157 static AddrSelectList *_addressSelect_ = NULL;
158 static AddressClipboard *_clipBoard_ = NULL;
159 
160 /* Address index file and interfaces */
161 static AddressIndex *_addressIndex_ = NULL;
162 static GList *_addressInterfaceList_ = NULL;
163 static GList *_addressIFaceSelection_ = NULL;
164 #define ADDRESSBOOK_IFACE_SELECTION "1/y,3/y,4/y,2/n"
165 
166 static AddressBook_win addrbook;
167 
168 static GHashTable *_addressBookTypeHash_ = NULL;
169 static GList *_addressBookTypeList_ = NULL;
170 
171 static void addressbook_new_address_from_book_post_cb( ItemPerson *person );
172 static void addressbook_new_address_from_folder_post_cb( ItemPerson *person );
173 static void addressbook_edit_address_post_cb( ItemPerson *person );
174 
175 static void addressbook_create			(void);
176 static gint addressbook_close			(void);
177 
178 static gboolean address_index_has_focus = FALSE;
179 static gboolean address_list_has_focus = FALSE;
180 
181 /* callback functions */
182 static void addressbook_del_clicked		(GtkButton	*button,
183 						 gpointer	 data);
184 static void addressbook_reg_clicked		(GtkButton	*button,
185 						 gpointer	 data);
186 static void addressbook_to_clicked		(GtkButton	*button,
187 						 gpointer	 data);
188 static void addressbook_lup_clicked		(GtkButton	*button,
189 						 gpointer	data);
190 static void addressbook_close_clicked		(GtkButton	*button,
191 						 gpointer	data);
192 
193 static void addressbook_tree_selected		(GtkCMCTree	*ctree,
194 						 GtkCMCTreeNode	*node,
195 						 gint		 column,
196 						 gpointer	 data);
197 static void addressbook_select_row_tree		(GtkCMCTree	*ctree,
198 						 GtkCMCTreeNode	*node,
199 						 gint		 column,
200 						 gpointer	 data);
201 static void addressbook_list_row_selected	(GtkCMCTree	*clist,
202 						 GtkCMCTreeNode	*node,
203 						 gint		 column,
204 						 gpointer	 data);
205 static void addressbook_list_row_unselected	(GtkCMCTree	*clist,
206 						 GtkCMCTreeNode	*node,
207 						 gint		 column,
208 						 gpointer	 data);
209 static void addressbook_person_expand_node	(GtkCMCTree	*ctree,
210 						 GList		*node,
211 						 gpointer	*data );
212 static void addressbook_person_collapse_node	(GtkCMCTree	*ctree,
213 						 GList		*node,
214 						 gpointer	*data );
215 
216 static void addressbook_entry_activated		(GtkWidget	*widget,
217 						 gpointer	 data);
218 
219 static gboolean addressbook_list_button_pressed	(GtkWidget	*widget,
220 						 GdkEventButton	*event,
221 						 gpointer	 data);
222 static gboolean addressbook_list_button_released(GtkWidget	*widget,
223 						 GdkEventButton	*event,
224 						 gpointer	 data);
225 static gboolean addressbook_tree_button_pressed	(GtkWidget	*ctree,
226 						 GdkEventButton	*event,
227 						 gpointer	 data);
228 static gboolean addressbook_tree_button_released(GtkWidget	*ctree,
229 						 GdkEventButton	*event,
230 						 gpointer	 data);
231 
232 static void addressbook_new_folder_cb		(GtkAction	*action,
233 						 gpointer	 data);
234 static void addressbook_new_group_cb		(GtkAction	*action,
235 						 gpointer	 data);
236 static void addressbook_treenode_edit_cb	(GtkAction	*action,
237 						 gpointer	 data);
238 static void addressbook_treenode_delete_cb	(GtkAction	*action,
239 						 gpointer	 data);
240 
241 static void addressbook_change_node_name	(GtkCMCTreeNode	*node,
242 						 const gchar	*name);
243 
244 static void addressbook_new_address_cb		(GtkAction	*action,
245 						 gpointer	 data);
246 static void addressbook_edit_address_cb		(GtkAction	*action,
247 						 gpointer	 data);
248 static void addressbook_delete_address_cb	(GtkAction	*action,
249 						 gpointer	 data);
250 
251 static void close_cb				(GtkAction	*action,
252 						 gpointer	 data);
253 static void addressbook_file_save_cb		(GtkAction	*action,
254 						 gpointer	 data);
255 
256 /* Data source edit stuff */
257 static void addressbook_new_book_cb		(GtkAction	*action,
258 						 gpointer	 data);
259 static void addressbook_new_vcard_cb		(GtkAction	*action,
260 						 gpointer	 data);
261 
262 #ifdef USE_JPILOT
263 static void addressbook_new_jpilot_cb		(GtkAction	*action,
264 						 gpointer	 data);
265 #endif
266 
267 #ifdef USE_LDAP
268 static void addressbook_new_ldap_cb		(GtkAction	*action,
269 						 gpointer	 data);
270 #endif
271 
272 static void addressbook_set_clist		(AddressObject	*obj,
273 						 gboolean 	 refresh);
274 
275 static void addressbook_load_tree		(void);
276 void addressbook_read_file			(void);
277 
278 static GtkCMCTreeNode *addressbook_add_object	(GtkCMCTreeNode	*node,
279 						 AddressObject	*obj);
280 static void addressbook_treenode_remove_item	( void );
281 
282 static AddressDataSource *addressbook_find_datasource
283 						(GtkCMCTreeNode	*node );
284 
285 static AddressBookFile *addressbook_get_book_file(void);
286 
287 static GtkCMCTreeNode *addressbook_node_add_folder
288 						(GtkCMCTreeNode	*node,
289 						AddressDataSource *ds,
290 						ItemFolder	*itemFolder,
291 						AddressObjectType otype);
292 static GtkCMCTreeNode *addressbook_node_add_group (GtkCMCTreeNode	*node,
293 						AddressDataSource *ds,
294 						ItemGroup	*itemGroup);
295 static void addressbook_tree_remove_children	(GtkCMCTree	*ctree,
296 						GtkCMCTreeNode	*parent);
297 static void addressbook_move_nodes_up		(GtkCMCTree	*ctree,
298 						GtkCMCTreeNode	*node);
299 static GtkCMCTreeNode *addressbook_find_group_node (GtkCMCTreeNode	*parent,
300 						   ItemGroup	*group);
301 static gboolean addressbook_entry_key_pressed	(GtkWidget	*widget,
302 						 GdkEventKey	*event,
303 						 gpointer	 data);
304 static gint addressbook_treenode_compare_func	(GtkCMCList	*clist,
305 						 gconstpointer	 ptr1,
306 						 gconstpointer	 ptr2);
307 static void addressbook_folder_load_one_person	(GtkCMCTree *clist,
308 						 ItemPerson *person,
309 						 AddressTypeControlItem *atci,
310 						 AddressTypeControlItem *atciMail);
311 static void addressbook_folder_remove_node	(GtkCMCTree *clist,
312 						 GtkCMCTreeNode *node);
313 
314 static void addressbook_edit_address( gpointer data, guint action, GtkWidget *widget,
315 									  gboolean force_focus );
316 
317 /* LUT's and IF stuff */
318 static void addressbook_free_treenode		( gpointer data );
319 static AddressTypeControlItem *addrbookctl_lookup	(gint		 ot);
320 static AddressTypeControlItem *addrbookctl_lookup_iface(AddressIfType	 ifType);
321 
322 static void addrbookctl_build_map			(GtkWidget	*window);
323 static void addrbookctl_build_iflist			(void);
324 static AdapterInterface *addrbookctl_find_interface	(AddressIfType	 ifType);
325 static void addrbookctl_build_ifselect			(void);
326 
327 static void addrbookctl_free_interface		(AdapterInterface *adapter);
328 static void addrbookctl_free_datasource		(AdapterDSource	  *adapter);
329 static void addrbookctl_free_folder		(AdapterFolder	  *adapter);
330 static void addrbookctl_free_group		(AdapterGroup	  *adapter);
331 
332 static void addressbook_list_select_clear	( void );
333 static void addressbook_list_select_add		( AddrItemObject    *aio,
334 						  AddressDataSource *ds );
335 static void addressbook_list_select_remove	( AddrItemObject    *aio );
336 
337 static void addressbook_import_ldif_cb		( GtkAction *action, gpointer data );
338 static void addressbook_find_duplicates_cb	( GtkAction *action, gpointer data );
339 static void addressbook_edit_custom_attr_cb	( GtkAction *action, gpointer data );
340 static void addressbook_import_mutt_cb		( GtkAction *action, gpointer data );
341 static void addressbook_import_pine_cb		( GtkAction *action, gpointer data );
342 static void addressbook_export_html_cb		( GtkAction *action, gpointer data );
343 static void addressbook_export_ldif_cb		( GtkAction *action, gpointer data );
344 static void addressbook_select_all_cb		( GtkAction *action, gpointer data );
345 static void addressbook_clip_cut_cb		( GtkAction *action, gpointer data );
346 static void addressbook_clip_copy_cb		( GtkAction *action, gpointer data );
347 static void addressbook_clip_paste_cb		( GtkAction *action, gpointer data );
348 static void addressbook_treenode_cut_cb		( GtkAction *action, gpointer data );
349 static void addressbook_treenode_copy_cb	( GtkAction *action, gpointer data );
350 static void addressbook_treenode_paste_cb	( GtkAction *action, gpointer data );
351 
352 static void addressbook_mail_to_cb		( GtkAction *action, gpointer data );
353 static void addressbook_merge_cb		( GtkAction *action, gpointer data );
354 
355 #ifdef USE_LDAP
356 static void addressbook_browse_entry_cb		( GtkAction *action, gpointer data );
357 #endif
358 static void addressbook_edit_clicked(GtkButton *button, gpointer data);
359 
360 static void addressbook_start_drag(GtkWidget *widget, gint button,
361 				   GdkEvent *event,
362 			           void *data);
363 static void addressbook_drag_data_get(GtkWidget        *widget,
364 				     GdkDragContext   *drag_context,
365 				     GtkSelectionData *selection_data,
366 				     guint             info,
367 				     guint             time,
368 				     void	      *data);
369 static gboolean addressbook_drag_motion_cb(GtkWidget      *widget,
370 					  GdkDragContext *context,
371 					  gint            x,
372 					  gint            y,
373 					  guint           time,
374 					  void           *data);
375 static void addressbook_drag_leave_cb(GtkWidget      *widget,
376 				     GdkDragContext *context,
377 				     guint           time,
378 				     void           *data);
379 static void addressbook_drag_received_cb(GtkWidget        *widget,
380 					GdkDragContext   *drag_context,
381 					gint              x,
382 					gint              y,
383 					GtkSelectionData *data,
384 					guint             info,
385 					guint             time,
386 					void             *pdata);
387 static void addressbook_list_menu_setup( void );
388 
389 static GtkTargetEntry addressbook_drag_types[] =
390 {
391 	{"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY}
392 };
393 
394 static GtkTargetList *addressbook_target_list = NULL;
395 
about_show_cb(GtkAction * action,gpointer data)396 static void about_show_cb(GtkAction *action, gpointer data)
397 {
398 	about_show();
399 }
400 
401 static GtkActionEntry addressbook_entries[] =
402 {
403 	{"Menu",				NULL, "Menu", NULL, NULL, NULL },
404 /* menus */
405 	{"Book",			NULL, N_("_Book"), NULL, NULL, NULL },
406 	{"Edit",			NULL, N_("_Edit"), NULL, NULL, NULL },
407 	{"Tools",			NULL, N_("_Tools"), NULL, NULL, NULL },
408 
409 /* Book menu */
410 	{"Book/NewBook",		NULL, N_("New _Book"), "<control>B", NULL, G_CALLBACK(addressbook_new_book_cb) },
411 	{"Book/NewFolder",		NULL, N_("New _Folder"), "<control>R", NULL, G_CALLBACK(addressbook_new_folder_cb) },
412 	{"Book/NewVCard",		NULL, N_("New _vCard"), "<control><shift>D", NULL, G_CALLBACK(addressbook_new_vcard_cb) },
413 
414 
415 #ifdef USE_JPILOT
416 	{"Book/NewJPilot",		NULL, N_("New _JPilot"), "<control>J", NULL, G_CALLBACK(addressbook_new_jpilot_cb) },
417 #endif
418 #ifdef USE_LDAP
419 	{"Book/NewLDAPServer",		NULL, N_("New LDAP _Server"), "<control><shift>S", NULL, G_CALLBACK(addressbook_new_ldap_cb) },
420 #endif
421 	{"Book/---",			NULL, "---", NULL, NULL, NULL },
422 
423 	{"Book/EditBook",		NULL, N_("_Edit book"), NULL, NULL, G_CALLBACK(addressbook_treenode_edit_cb) },
424 	{"Book/DeleteBook",		NULL, N_("_Delete book"), NULL, NULL, G_CALLBACK(addressbook_treenode_delete_cb) },
425 	/* {"Book/---",			NULL, "---", NULL, NULL, NULL }, */
426 	{"Book/Save",			NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(addressbook_file_save_cb) },
427 	{"Book/Close",			NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(close_cb) },
428 
429 /* Adress menu */
430 	{"Address/SelectAll",		NULL, N_("_Select all"), "<control>A", NULL, G_CALLBACK(addressbook_select_all_cb) },
431 	{"Address/---",			NULL, "---", NULL, NULL, NULL },
432 	{"Address/Cut",			NULL, N_("C_ut"), "<control>X", NULL, G_CALLBACK(addressbook_clip_cut_cb) },
433 	{"Address/Copy",		NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(addressbook_clip_copy_cb) },
434 	{"Address/Paste",		NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(addressbook_clip_paste_cb) },
435 	/* {"Address/---",			NULL, "---", NULL, NULL, NULL }, */
436 	{"Address/Edit",		NULL, N_("_Edit"), "<control>Return", NULL, G_CALLBACK(addressbook_edit_address_cb) },
437 	{"Address/Delete",		NULL, N_("_Delete"), "<control>D", NULL, G_CALLBACK(addressbook_delete_address_cb) },
438 	/* {"Address/---",			NULL, "---", NULL, NULL, NULL }, */
439 	{"Address/NewAddress",		NULL, N_("New _Address"), "<control>N", NULL, G_CALLBACK(addressbook_new_address_cb) },
440 	{"Address/NewGroup",		NULL, N_("New _Group"), "<control>G", NULL, G_CALLBACK(addressbook_new_group_cb) },
441 	/* {"Address/---",			NULL, "---", NULL, NULL, NULL }, */
442 	{"Address/Mailto",		NULL, N_("_Mail To"), "<control>M", NULL, G_CALLBACK(addressbook_mail_to_cb) },
443 	{"Address/Merge",		NULL, N_("_Merge"), "<control>E", NULL, G_CALLBACK(addressbook_merge_cb) },
444 
445 
446 /* Tools menu */
447 	{"Tools/ImportLDIF",		NULL, N_("Import _LDIF file..."), NULL, NULL, G_CALLBACK(addressbook_import_ldif_cb) },
448 	{"Tools/ImportMutt",		NULL, N_("Import M_utt file..."), NULL, NULL, G_CALLBACK(addressbook_import_mutt_cb) },
449 	{"Tools/ImportPine",		NULL, N_("Import _Pine file..."), NULL, NULL, G_CALLBACK(addressbook_import_pine_cb) },
450 	{"Tools/---",			NULL, "---", NULL, NULL, NULL },
451 	{"Tools/ExportHTML",		NULL, N_("Export _HTML..."), NULL, NULL, G_CALLBACK(addressbook_export_html_cb) },
452 	{"Tools/ExportLDIF",		NULL, N_("Export LDI_F..."), NULL, NULL, G_CALLBACK(addressbook_export_ldif_cb) },
453 	/* {"Tools/---",			NULL, "---", NULL, NULL, NULL },*/
454 	{"Tools/FindDuplicates",	NULL, N_("Find duplicates..."), NULL, NULL, G_CALLBACK(addressbook_find_duplicates_cb) },
455 	{"Tools/EditAttrs",		NULL, N_("Edit custom attributes..."), NULL, NULL, G_CALLBACK(addressbook_edit_custom_attr_cb) },
456 };
457 
458 static GtkActionEntry addressbook_tree_popup_entries[] =
459 {
460 	{"ABTreePopup",			NULL, "ABTreePopup", NULL, NULL, NULL },
461 	{"ABTreePopup/EditBook",	NULL, N_("_Edit"), NULL, NULL, G_CALLBACK(addressbook_treenode_edit_cb) },
462 	{"ABTreePopup/DeleteBook",	NULL, N_("_Delete"), NULL, NULL, G_CALLBACK(addressbook_treenode_delete_cb) },
463 	{"ABTreePopup/---",		NULL, "---", NULL, NULL, NULL },
464 	{"ABTreePopup/NewBook",		NULL, N_("New _Book"), NULL, NULL, G_CALLBACK(addressbook_new_book_cb) },
465 	{"ABTreePopup/NewFolder",	NULL, N_("New _Folder"), NULL, NULL, G_CALLBACK(addressbook_new_folder_cb) },
466 	{"ABTreePopup/NewGroup",	NULL, N_("New _Group"), NULL, NULL, G_CALLBACK(addressbook_new_group_cb) },
467 	/* {"ABTreePopup/---",		NULL, "---", NULL, NULL, NULL }, */
468 	{"ABTreePopup/Cut",		NULL, N_("C_ut"), NULL, NULL, G_CALLBACK(addressbook_treenode_cut_cb) },
469 	{"ABTreePopup/Copy",		NULL, N_("_Copy"), NULL, NULL, G_CALLBACK(addressbook_treenode_copy_cb) },
470 	{"ABTreePopup/Paste",		NULL, N_("_Paste"), NULL, NULL, G_CALLBACK(addressbook_treenode_paste_cb) },
471 };
472 
473 static GtkActionEntry addressbook_list_popup_entries[] =
474 {
475 	{"ABListPopup",			NULL, "ABListPopup", NULL, NULL, NULL },
476 	{"ABListPopup/SelectAll",	NULL, N_("_Select all"), NULL, NULL, G_CALLBACK(addressbook_select_all_cb) },
477 	{"ABListPopup/---",		NULL, "---", NULL, NULL, NULL },
478 	{"ABListPopup/Edit",		NULL, N_("_Edit"), NULL, NULL, G_CALLBACK(addressbook_edit_address_cb) },
479 	{"ABListPopup/Delete",		NULL, N_("_Delete"), NULL, NULL, G_CALLBACK(addressbook_delete_address_cb) },
480 	/* {"ABListPopup/---",		NULL, "---", NULL, NULL, NULL }, */
481 	{"ABListPopup/NewAddress",	NULL, N_("New _Address"), NULL, NULL, G_CALLBACK(addressbook_new_address_cb) },
482 	{"ABListPopup/NewGroup",	NULL, N_("New _Group"), NULL, NULL, G_CALLBACK(addressbook_new_group_cb) },
483 	/* {"ABListPopup/---",		NULL, "---", NULL, NULL, NULL }, */
484 	{"ABListPopup/Cut",		NULL, N_("C_ut"), NULL, NULL, G_CALLBACK(addressbook_clip_cut_cb) },
485 	{"ABListPopup/Copy",		NULL, N_("_Copy"), NULL, NULL, G_CALLBACK(addressbook_clip_copy_cb) },
486 	{"ABListPopup/Paste",		NULL, N_("_Paste"), NULL, NULL, G_CALLBACK(addressbook_clip_paste_cb) },
487 	/* {"ABListPopup/---",		NULL, "---", NULL, NULL, NULL }, */
488 	{"ABListPopup/Mailto",		NULL, N_("_Mail To"), NULL, NULL, G_CALLBACK(addressbook_mail_to_cb) },
489 #ifdef USE_LDAP
490 	{"ABListPopup/BrowseEntry",	NULL, N_("_Browse Entry"), NULL, NULL, G_CALLBACK(addressbook_browse_entry_cb) },
491 #endif
492 	{"ABListPopup/Merge",		NULL, N_("_Merge"), NULL, NULL, G_CALLBACK(addressbook_merge_cb) },
493 };
494 
495 /**
496  * Structure of error message table.
497  */
498 typedef struct _ErrMsgTableEntry ErrMsgTableEntry;
499 struct _ErrMsgTableEntry {
500 	gint	code;
501 	gchar	*description;
502 };
503 
504 static gchar *_errMsgUnknown_ = N_( "Unknown" );
505 
506 /**
507  * Lookup table of error messages for general errors. Note that a NULL
508  * description signifies the end of the table.
509  */
510 static ErrMsgTableEntry _lutErrorsGeneral_[] = {
511 	{ MGU_SUCCESS,		N_("Success") },
512 	{ MGU_BAD_ARGS,		N_("Bad arguments") },
513 	{ MGU_NO_FILE,		N_("File not specified") },
514 	{ MGU_OPEN_FILE,	N_("Error opening file") },
515 	{ MGU_ERROR_READ,	N_("Error reading file") },
516 	{ MGU_EOF,		N_("End of file encountered") },
517 	{ MGU_OO_MEMORY,	N_("Error allocating memory") },
518 	{ MGU_BAD_FORMAT,	N_("Bad file format") },
519 	{ MGU_ERROR_WRITE,	N_("Error writing to file") },
520 	{ MGU_OPEN_DIRECTORY,	N_("Error opening directory") },
521 	{ MGU_NO_PATH,      	N_("No path specified") },
522 	{ 0,			NULL }
523 };
524 
525 #ifdef USE_LDAP
526 /**
527  * Lookup table of error messages for LDAP errors.
528  */
529 static ErrMsgTableEntry _lutErrorsLDAP_[] = {
530 	{ LDAPRC_SUCCESS,			N_("Success") },
531 	{ LDAPRC_CONNECT,			N_("Error connecting to LDAP server") },
532 	{ LDAPRC_INIT,				N_("Error initializing LDAP") },
533 	{ LDAPRC_BIND,				N_("Error binding to LDAP server") },
534 	{ LDAPRC_SEARCH,			N_("Error searching LDAP database") },
535 	{ LDAPRC_TIMEOUT,			N_("Timeout performing LDAP operation") },
536 	{ LDAPRC_CRITERIA,			N_("Error in LDAP search criteria") },
537 	{ LDAPRC_NOENTRIES,			N_("No LDAP entries found for search criteria") },
538 	{ LDAPRC_STOP_FLAG,			N_("LDAP search terminated on request") },
539 	{ LDAPRC_TLS,				N_("Error starting STARTTLS connection") },
540 	{ LDAPRC_NODN,				N_("Distinguished Name (dn) is missing") },
541 	{ LDAPRC_NAMING_VIOLATION,		N_("Missing required information") },
542 	{ LDAPRC_ALREADY_EXIST,			N_("Another contact exists with that key") },
543 	{ LDAPRC_STRONG_AUTH,			N_("Strong(er) authentication required") },
544 	{ 0,					NULL }
545 };
546 #endif
547 
548 /**
549  * Lookup message for specified error code.
550  * \param lut  Lookup table.
551  * \param code Code to lookup.
552  * \return Description associated to code.
553  */
addressbook_err2string(ErrMsgTableEntry lut[],gint code)554 static gchar *addressbook_err2string( ErrMsgTableEntry lut[], gint code ) {
555         gchar *desc = NULL;
556         ErrMsgTableEntry entry;
557         gint i;
558 
559         for( i = 0; ; i++ ) {
560                 entry = lut[ i ];
561                 if( entry.description == NULL ) break;
562                 if( entry.code == code ) {
563                         desc = entry.description;
564                         break;
565                 }
566         }
567         if( ! desc ) {
568 		desc = _errMsgUnknown_;
569         }
570         return desc;
571 }
572 
573 static gboolean lastCanLookup = FALSE;
574 
addressbook_show_buttons(gboolean add_and_delete,gboolean lookup,gboolean mail_ops)575 static void addressbook_show_buttons(gboolean add_and_delete, gboolean lookup, gboolean mail_ops)
576 {
577 	if (add_and_delete) {
578 		gtk_widget_show(addrbook.edit_btn);
579 		gtk_widget_show(addrbook.del_btn);
580 		gtk_widget_show(addrbook.reg_btn);
581 	} else {
582 		gtk_widget_hide(addrbook.edit_btn);
583 		gtk_widget_hide(addrbook.del_btn);
584 		gtk_widget_hide(addrbook.reg_btn);
585 	}
586 
587 	if (lookup) {
588 		gtk_widget_show(addrbook.lup_btn);
589 		gtk_widget_show(addrbook.entry);
590 		gtk_widget_show(addrbook.label);
591 	} else {
592 		gtk_widget_hide(addrbook.lup_btn);
593 		gtk_widget_hide(addrbook.entry);
594 		gtk_widget_hide(addrbook.label);
595 	}
596 
597 	lastCanLookup = lookup;
598 
599 	if (mail_ops) {
600 		gtk_widget_show(addrbook.to_btn);
601 		gtk_widget_show(addrbook.cc_btn);
602 		gtk_widget_show(addrbook.bcc_btn);
603 	} else {
604 		gtk_widget_hide(addrbook.to_btn);
605 		gtk_widget_hide(addrbook.cc_btn);
606 		gtk_widget_hide(addrbook.bcc_btn);
607 	}
608 }
609 
addressbook_open(Compose * target)610 void addressbook_open(Compose *target)
611 {
612 	/* Initialize all static members */
613 	if( _clipBoard_ == NULL ) {
614 		_clipBoard_ = addrclip_create();
615 	}
616 	if( _addressIndex_ != NULL ) {
617 		addrclip_set_index( _clipBoard_, _addressIndex_ );
618 	}
619 	if( _addressSelect_ == NULL ) {
620 		_addressSelect_ = addrselect_list_create();
621 	}
622 	if (!addrbook.window) {
623 		addressbook_read_file();
624 		addressbook_create();
625 		addressbook_load_tree();
626 		gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
627 				 GTK_CMCTREE_NODE(GTK_CMCLIST(addrbook.ctree)->row_list));
628 	}
629 	else {
630 		gtk_widget_hide(addrbook.window);
631 	}
632 
633 	gtk_widget_show_all(addrbook.window);
634 
635 	if (!prefs_common.addressbook_use_editaddress_dialog)
636 		addressbook_edit_person_widgetset_hide();
637 
638 	address_completion_start(addrbook.window);
639 
640 	addressbook_show_buttons(target == NULL, lastCanLookup, target != NULL);
641 	addressbook_set_target_compose(target);
642 }
643 
644 /**
645  * Destroy addressbook.
646  */
addressbook_destroy(void)647 void addressbook_destroy( void ) {
648 	/* Free up address stuff */
649 	if( _addressSelect_ != NULL ) {
650 		addrselect_list_free( _addressSelect_ );
651 	}
652 	if( _clipBoard_ != NULL ) {
653 		addrclip_free( _clipBoard_ );
654 		_clipBoard_ = NULL;
655 	}
656 	if( _addressIndex_ != NULL ) {
657 		addrindex_free_index( _addressIndex_ );
658 		addrindex_teardown();
659 	}
660 	_addressSelect_ = NULL;
661 	_clipBoard_ = NULL;
662 	_addressIndex_ = NULL;
663 }
664 
addressbook_set_target_compose(Compose * target)665 void addressbook_set_target_compose(Compose *target)
666 {
667 	addrbook.target_compose = target;
668 }
669 
addressbook_get_target_compose(void)670 Compose *addressbook_get_target_compose(void)
671 {
672 	return addrbook.target_compose;
673 }
674 
675 /**
676  * Refresh addressbook and save to file(s).
677  */
addressbook_refresh(void)678 void addressbook_refresh( void )
679 {
680 	if (addrbook.window) {
681 		if (addrbook.treeSelected) {
682 			gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
683 					 addrbook.treeSelected);
684 			addressbook_set_clist(
685 				gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
686 					addrbook.treeSelected),
687 				TRUE);
688 
689 		}
690 	}
691 	addressbook_export_to_file();
692 }
693 
key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer data)694 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
695 {
696 	if (event && event->keyval == GDK_KEY_Escape)
697 		addressbook_close();
698 	else if (event && event->keyval == GDK_KEY_Delete) {
699 		/* TODO: enable deletion when focus is in ctree (needs implementation in _del_clicked() */
700 		if ( /* address_index_has_focus || */ address_list_has_focus )
701 			addressbook_del_clicked(NULL, NULL);
702 	}
703 	return FALSE;
704 }
705 
706 /*!
707  *\brief	Save Gtk object size to prefs dataset
708  */
addressbook_size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation)709 static void addressbook_size_allocate_cb(GtkWidget *widget,
710 					 GtkAllocation *allocation)
711 {
712 	cm_return_if_fail(allocation != NULL);
713 
714 	prefs_common.addressbookwin_width = allocation->width;
715 	prefs_common.addressbookwin_height = allocation->height;
716 }
717 
718 static gint sort_column_number = 0;
719 static GtkSortType sort_column_type = GTK_SORT_ASCENDING;
720 
list_case_sort(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)721 static gint list_case_sort(
722 	GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
723 {
724 	GtkCMCListRow *row1 = (GtkCMCListRow *) ptr1;
725 	GtkCMCListRow *row2 = (GtkCMCListRow *) ptr2;
726 	gchar *name1 = NULL, *name2 = NULL;
727 	AddrItemObject *aio1 = ((GtkCMCListRow *)ptr1)->data;
728 	AddrItemObject *aio2 = ((GtkCMCListRow *)ptr2)->data;
729 
730 	if( aio1->type == aio2->type ) {
731 		if( row1 )
732 			name1 = GTK_CMCELL_TEXT (row1->cell[sort_column_number])->text;
733 		if( row2 )
734 			name2 = GTK_CMCELL_TEXT (row2->cell[sort_column_number])->text;
735 		if( ! name1 ) return ( name2 != NULL );
736 		if( ! name2 ) return -1;
737 		return g_utf8_collate( name1, name2 );
738 	} else {
739 		/* Order groups before person */
740 		if( aio1->type == ITEMTYPE_GROUP ) {
741 			return (sort_column_type==GTK_SORT_ASCENDING) ? -1:+1;
742 		} else if( aio2->type == ITEMTYPE_GROUP ) {
743 			return (sort_column_type==GTK_SORT_ASCENDING) ? +1:-1;
744 		}
745 		return 0;
746 	}
747 }
748 
addressbook_sort_list(GtkCMCList * clist,const gint col,const GtkSortType sort_type)749 static void addressbook_sort_list(GtkCMCList *clist, const gint col,
750 		const GtkSortType sort_type)
751 {
752 	gint pos;
753 	GtkWidget *hbox, *label, *arrow;
754 
755 	sort_column_number = col;
756 	sort_column_type = sort_type;
757 	gtk_cmclist_set_compare_func(clist, list_case_sort);
758 	gtk_cmclist_set_sort_type(clist, sort_type);
759 	gtk_cmclist_set_sort_column(clist, col);
760 
761 	gtk_cmclist_freeze(clist);
762 	gtk_cmclist_sort(clist);
763 
764 	for(pos = 0 ; pos < N_LIST_COLS ; pos++) {
765 		hbox = gtk_hbox_new(FALSE, 4);
766 		label = gtk_label_new(gettext(list_titles[pos]));
767 		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
768 
769 		if(pos == col) {
770 			arrow = gtk_arrow_new(sort_type == GTK_SORT_ASCENDING ?
771 				GTK_ARROW_DOWN : GTK_ARROW_UP, GTK_SHADOW_IN);
772 			gtk_box_pack_end(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
773 		}
774 
775 		gtk_widget_show_all(hbox);
776 		gtk_cmclist_set_column_widget(clist, pos, hbox);
777 	}
778 
779 	gtk_cmclist_thaw(clist);
780 }
781 
addressbook_name_clicked(GtkWidget * button,GtkCMCList * clist)782 static void addressbook_name_clicked(GtkWidget *button, GtkCMCList *clist)
783 {
784 	static GtkSortType sort_type = GTK_SORT_ASCENDING;
785 
786 	sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
787 			GTK_SORT_ASCENDING;
788 	addressbook_sort_list(clist, COL_NAME, sort_type);
789 }
790 
addressbook_address_clicked(GtkWidget * button,GtkCMCList * clist)791 static void addressbook_address_clicked(GtkWidget *button, GtkCMCList *clist)
792 {
793 	static GtkSortType sort_type = GTK_SORT_ASCENDING;
794 
795 	sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
796 			GTK_SORT_ASCENDING;
797 	addressbook_sort_list(clist, COL_ADDRESS, sort_type);
798 }
799 
addressbook_remarks_clicked(GtkWidget * button,GtkCMCList * clist)800 static void addressbook_remarks_clicked(GtkWidget *button, GtkCMCList *clist)
801 {
802 	static GtkSortType sort_type = GTK_SORT_ASCENDING;
803 
804 	sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
805 			GTK_SORT_ASCENDING;
806 	addressbook_sort_list(clist, COL_REMARKS, sort_type);
807 }
808 
addressbook_address_index_focus_evt_in(GtkWidget * widget,GdkEventFocus * event,gpointer data)809 static gboolean addressbook_address_index_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
810 											 gpointer data)
811 {
812 	address_index_has_focus = TRUE;
813 	return FALSE;
814 }
815 
addressbook_address_index_focus_evt_out(GtkWidget * widget,GdkEventFocus * event,gpointer data)816 static gboolean addressbook_address_index_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
817 											 gpointer data)
818 {
819 	address_index_has_focus = FALSE;
820 	if (!prefs_common.addressbook_use_editaddress_dialog
821 			&& !address_list_has_focus)
822 		addressbook_address_list_disable_some_actions();
823 	return FALSE;
824 }
825 
addressbook_address_list_focus_evt_in(GtkWidget * widget,GdkEventFocus * event,gpointer data)826 static gboolean addressbook_address_list_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
827 											 gpointer data)
828 {
829 	address_list_has_focus = TRUE;
830 	return FALSE;
831 }
832 
addressbook_address_list_focus_evt_out(GtkWidget * widget,GdkEventFocus * event,gpointer data)833 static gboolean addressbook_address_list_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
834 											 gpointer data)
835 {
836 	address_list_has_focus = FALSE;
837 	if (!prefs_common.addressbook_use_editaddress_dialog
838 			&& !address_index_has_focus)
839 		addressbook_address_list_disable_some_actions();
840 	return FALSE;
841 }
842 
843 /* save hpane and vpane's handle position when it moves */
addressbook_pane_save_position(void)844 static void addressbook_pane_save_position(void)
845 {
846 	if (addrbook.hpaned)
847 		prefs_common.addressbook_hpaned_pos =
848 			gtk_paned_get_position(GTK_PANED(addrbook.hpaned));
849 	if (addrbook.vpaned)
850 		prefs_common.addressbook_vpaned_pos =
851 			gtk_paned_get_position(GTK_PANED(addrbook.vpaned));
852 }
853 
854 /*
855 * Create the address book widgets. The address book contains two CTree widgets: the
856 * address index tree on the left and the address list on the right.
857 *
858 * The address index tree displays a hierarchy of interfaces and groups. Each node in
859 * this tree is linked to an address Adapter. Adapters have been created for interfaces,
860 * data sources and folder objects.
861 *
862 * The address list displays group, person and email objects. These items are linked
863 * directly to ItemGroup, ItemPerson and ItemEMail objects inside the address book data
864 * sources.
865 *
866 * In the tradition of MVC architecture, the data stores have been separated from the
867 * GUI components. The addrindex.c file provides the interface to all data stores.
868 */
addressbook_create(void)869 static void addressbook_create(void)
870 {
871 	GtkWidget *window;
872 	GtkWidget *vbox;
873 	GtkWidget *menubar;
874 	GtkWidget *vbox2;
875 	GtkWidget *ctree_swin;
876 	GtkWidget *ctree;
877 	GtkWidget *editaddress_vbox;
878 	GtkWidget *clist_vbox;
879 	GtkWidget *clist_swin;
880 	GtkWidget *clist;
881 	GtkWidget *hpaned;
882 	GtkWidget *vpaned;
883 	GtkWidget *hbox;
884 	GtkWidget *label;
885 	GtkWidget *entry;
886 	GtkWidget *statusbar;
887 	GtkWidget *hbbox;
888 	GtkWidget *hsbox;
889 	GtkWidget *help_btn;
890 	GtkWidget *del_btn;
891 	GtkWidget *edit_btn;
892 	GtkWidget *reg_btn;
893 	GtkWidget *lup_btn;
894 	GtkWidget *to_btn;
895 	GtkWidget *cc_btn;
896 	GtkWidget *bcc_btn;
897 	GtkWidget *close_btn;
898 	GtkWidget *tree_popup;
899 	GtkWidget *list_popup;
900 	GList *nodeIf;
901 	GtkUIManager *ui_manager;
902 	GtkActionGroup *action_group;
903 	gchar *index_titles[N_INDEX_COLS];
904 	gchar *text;
905 	gint i;
906 
907 	static GdkGeometry geometry;
908 
909 	debug_print("Creating addressbook window...\n");
910 
911 	index_titles[COL_SOURCES] = _("Sources");
912 
913 	/* Address book window */
914 	window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "addressbook");
915 	gtk_window_set_title(GTK_WINDOW(window), _("Address book"));
916 	gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
917 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
918 	gtk_widget_realize(window);
919 
920 	g_signal_connect(G_OBJECT(window), "delete_event",
921 			 G_CALLBACK(addressbook_close), NULL);
922 	g_signal_connect(G_OBJECT(window), "size_allocate",
923 			 G_CALLBACK(addressbook_size_allocate_cb), NULL);
924 	g_signal_connect(G_OBJECT(window), "key_press_event",
925 			 G_CALLBACK(key_pressed), NULL);
926 	MANAGE_WINDOW_SIGNALS_CONNECT(window);
927 
928 	vbox = gtk_vbox_new(FALSE, 0);
929 	gtk_container_add(GTK_CONTAINER(window), vbox);
930 
931 	/* Menu bar */
932 	ui_manager = gtk_ui_manager_new();
933 	action_group = cm_menu_create_action_group_full(ui_manager,"Menu", addressbook_entries,
934 			G_N_ELEMENTS(addressbook_entries), NULL);
935 	gtk_action_group_add_actions(action_group, addressbook_tree_popup_entries,
936 			G_N_ELEMENTS(addressbook_tree_popup_entries), NULL);
937 	gtk_action_group_add_actions(action_group, addressbook_list_popup_entries,
938 			G_N_ELEMENTS(addressbook_list_popup_entries), NULL);
939 
940 	MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
941 
942 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Book", "Book", GTK_UI_MANAGER_MENU)
943 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
944 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
945 
946 /* Book menu */
947 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewBook", "Book/NewBook", GTK_UI_MANAGER_MENUITEM)
948 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewFolder", "Book/NewFolder", GTK_UI_MANAGER_MENUITEM)
949 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewVCard", "Book/NewVCard", GTK_UI_MANAGER_MENUITEM)
950 #ifdef USE_JPILOT
951 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewJPilot", "Book/NewJPilot", GTK_UI_MANAGER_MENUITEM)
952 #endif
953 #ifdef USE_LDAP
954 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewLDAPServer", "Book/NewLDAPServer", GTK_UI_MANAGER_MENUITEM)
955 #endif
956 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Separator1", "Book/---", GTK_UI_MANAGER_SEPARATOR)
957 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "EditBook", "Book/EditBook", GTK_UI_MANAGER_MENUITEM)
958 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "DeleteBook", "Book/DeleteBook", GTK_UI_MANAGER_MENUITEM)
959 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Separator2", "Book/---", GTK_UI_MANAGER_SEPARATOR)
960 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Save", "Book/Save", GTK_UI_MANAGER_MENUITEM)
961 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Close", "Book/Close", GTK_UI_MANAGER_MENUITEM)
962 
963 /* Address menu */
964 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "SelectAll", "Address/SelectAll", GTK_UI_MANAGER_MENUITEM)
965 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator1", "Address/---", GTK_UI_MANAGER_SEPARATOR)
966 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Cut", "Address/Cut", GTK_UI_MANAGER_MENUITEM)
967 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Copy", "Address/Copy", GTK_UI_MANAGER_MENUITEM)
968 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Paste", "Address/Paste", GTK_UI_MANAGER_MENUITEM)
969 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator2", "Address/---", GTK_UI_MANAGER_SEPARATOR)
970 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Edit", "Address/Edit", GTK_UI_MANAGER_MENUITEM)
971 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Delete", "Address/Delete", GTK_UI_MANAGER_MENUITEM)
972 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator3", "Address/---", GTK_UI_MANAGER_SEPARATOR)
973 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "NewAddress", "Address/NewAddress", GTK_UI_MANAGER_MENUITEM)
974 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "NewGroup", "Address/NewGroup", GTK_UI_MANAGER_MENUITEM)
975 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator4", "Address/---", GTK_UI_MANAGER_SEPARATOR)
976 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Mailto", "Address/Mailto", GTK_UI_MANAGER_MENUITEM)
977 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Merge", "Address/Merge", GTK_UI_MANAGER_MENUITEM)
978 
979 /* Tools menu */
980 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ImportLDIF", "Tools/ImportLDIF", GTK_UI_MANAGER_MENUITEM)
981 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ImportMutt", "Tools/ImportMutt", GTK_UI_MANAGER_MENUITEM)
982 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ImportPine", "Tools/ImportPine", GTK_UI_MANAGER_MENUITEM)
983 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "Separator1", "Tools/---", GTK_UI_MANAGER_SEPARATOR)
984 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ExportHTML", "Tools/ExportHTML", GTK_UI_MANAGER_MENUITEM)
985 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ExportLDIF", "Tools/ExportLDIF", GTK_UI_MANAGER_MENUITEM)
986 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "Separator2", "Tools/---", GTK_UI_MANAGER_SEPARATOR)
987 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "FindDuplicates", "Tools/FindDuplicates", GTK_UI_MANAGER_MENUITEM)
988 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "EditAttrs", "Tools/EditAttrs", GTK_UI_MANAGER_MENUITEM)
989 
990 	gtk_window_add_accel_group(GTK_WINDOW(window),
991 			gtk_ui_manager_get_accel_group(ui_manager));
992 
993 	menubar = gtk_ui_manager_get_widget(ui_manager, "/Menu");
994 
995 	gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
996 
997 	vbox2 = gtk_vbox_new(FALSE, BORDER_WIDTH);
998 	gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
999 	gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
1000 
1001 	ctree_swin = gtk_scrolled_window_new(NULL, NULL);
1002 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ctree_swin),
1003 				       GTK_POLICY_AUTOMATIC,
1004 				       GTK_POLICY_AUTOMATIC);
1005 	gtk_widget_set_size_request(ctree_swin, COL_FOLDER_WIDTH + 20, -1);
1006 
1007 	/* Address index */
1008 	ctree = gtk_sctree_new_with_titles(N_INDEX_COLS, 0, index_titles);
1009 	gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[0].button, FALSE);
1010 
1011 	gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
1012 	gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
1013 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), 0, COL_FOLDER_WIDTH);
1014 	gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
1015 			     GTK_CMCTREE_EXPANDER_TRIANGLE);
1016 	gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
1017 	gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
1018 	gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree),
1019 				   addressbook_treenode_compare_func);
1020 
1021 	g_signal_connect(G_OBJECT(ctree), "tree_select_row",
1022 			 G_CALLBACK(addressbook_tree_selected), NULL);
1023 	g_signal_connect(G_OBJECT(ctree), "button_press_event",
1024 			 G_CALLBACK(addressbook_tree_button_pressed),
1025 			 NULL);
1026 	g_signal_connect(G_OBJECT(ctree), "button_release_event",
1027 			 G_CALLBACK(addressbook_tree_button_released),
1028 			 NULL);
1029 	/* TEMPORARY */
1030 	g_signal_connect(G_OBJECT(ctree), "select_row",
1031 			 G_CALLBACK(addressbook_select_row_tree), NULL);
1032 
1033 	gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
1034 			  addressbook_drag_types, 1,
1035 			  GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
1036 	g_signal_connect(G_OBJECT(ctree), "drag_motion",
1037 			 G_CALLBACK(addressbook_drag_motion_cb),
1038 			 ctree);
1039 	g_signal_connect(G_OBJECT(ctree), "drag_leave",
1040 			 G_CALLBACK(addressbook_drag_leave_cb),
1041 			 ctree);
1042 	g_signal_connect(G_OBJECT(ctree), "drag_data_received",
1043 			 G_CALLBACK(addressbook_drag_received_cb),
1044 			 ctree);
1045 	g_signal_connect(G_OBJECT(ctree), "focus_in_event",
1046 		G_CALLBACK(addressbook_address_index_focus_evt_in), NULL);
1047 	g_signal_connect(G_OBJECT(ctree), "focus_out_event",
1048 		G_CALLBACK(addressbook_address_index_focus_evt_out), NULL);
1049 
1050 	clist_vbox = gtk_vbox_new(FALSE, 4);
1051 
1052 	clist_swin = gtk_scrolled_window_new(NULL, NULL);
1053 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
1054 				       GTK_POLICY_AUTOMATIC,
1055 				       GTK_POLICY_AUTOMATIC);
1056 	gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
1057 
1058 	/* Address list */
1059 	clist = gtk_sctree_new_with_titles(N_LIST_COLS, 0, list_titles);
1060 	gtk_container_add(GTK_CONTAINER(clist_swin), clist);
1061 	gtk_cmclist_set_selection_mode(GTK_CMCLIST(clist), GTK_SELECTION_MULTIPLE);
1062 	gtk_cmctree_set_expander_style(GTK_CMCTREE(clist),
1063 			     GTK_CMCTREE_EXPANDER_TRIANGLE);
1064 	gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
1065 	gtk_cmctree_set_indent(GTK_CMCTREE(clist), CTREE_INDENT);
1066 	gtk_cmclist_set_column_width(GTK_CMCLIST(clist), COL_NAME,
1067 				   COL_NAME_WIDTH);
1068 	gtk_cmclist_set_column_width(GTK_CMCLIST(clist), COL_ADDRESS,
1069 				   COL_ADDRESS_WIDTH);
1070 	gtk_widget_set_size_request(clist, -1, 80);
1071 
1072 	addressbook_sort_list(GTK_CMCLIST(clist), COL_NAME, GTK_SORT_ASCENDING);
1073 	g_signal_connect(G_OBJECT(GTK_CMCLIST(clist)->column[COL_NAME].button),
1074 		"clicked", G_CALLBACK(addressbook_name_clicked), clist);
1075 	g_signal_connect(G_OBJECT(GTK_CMCLIST(clist)->column[COL_ADDRESS].button),
1076 		"clicked", G_CALLBACK(addressbook_address_clicked), clist);
1077 	g_signal_connect(G_OBJECT(GTK_CMCLIST(clist)->column[COL_REMARKS].button),
1078 		"clicked", G_CALLBACK(addressbook_remarks_clicked), clist);
1079 	g_signal_connect(G_OBJECT(clist), "focus_in_event",
1080 		G_CALLBACK(addressbook_address_list_focus_evt_in), NULL);
1081 	g_signal_connect(G_OBJECT(clist), "focus_out_event",
1082 		G_CALLBACK(addressbook_address_list_focus_evt_out), NULL);
1083 
1084 	for (i = 0; i < N_LIST_COLS; i++)
1085 		gtk_widget_set_can_focus(GTK_CMCLIST(clist)->column[i].button,
1086 					 FALSE);
1087 
1088 	g_signal_connect(G_OBJECT(clist), "tree_select_row",
1089 			 G_CALLBACK(addressbook_list_row_selected), NULL);
1090 	g_signal_connect(G_OBJECT(clist), "tree_unselect_row",
1091 			 G_CALLBACK(addressbook_list_row_unselected), NULL);
1092 	g_signal_connect(G_OBJECT(clist), "button_press_event",
1093 			 G_CALLBACK(addressbook_list_button_pressed),
1094 			 NULL);
1095 	g_signal_connect(G_OBJECT(clist), "button_release_event",
1096 			 G_CALLBACK(addressbook_list_button_released),
1097 			 NULL);
1098 	g_signal_connect(G_OBJECT(clist), "tree_expand",
1099 			 G_CALLBACK(addressbook_person_expand_node), NULL );
1100 	g_signal_connect(G_OBJECT(clist), "tree_collapse",
1101 			 G_CALLBACK(addressbook_person_collapse_node), NULL );
1102 	g_signal_connect(G_OBJECT(clist), "start_drag",
1103 			 G_CALLBACK(addressbook_start_drag), NULL);
1104 	g_signal_connect(G_OBJECT(clist), "drag_data_get",
1105 			 G_CALLBACK(addressbook_drag_data_get), NULL);
1106 	hbox = gtk_hbox_new(FALSE, 4);
1107 	gtk_box_pack_start(GTK_BOX(clist_vbox), hbox, FALSE, FALSE, 0);
1108 
1109 	label = gtk_label_new(_("Search"));
1110 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1111 
1112 	entry = gtk_entry_new();
1113 	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
1114 
1115 	address_completion_register_entry(GTK_ENTRY(entry), FALSE);
1116 
1117 	g_signal_connect(G_OBJECT(entry), "key_press_event",
1118 			 G_CALLBACK(addressbook_entry_key_pressed),
1119 			 NULL);
1120 	g_signal_connect(G_OBJECT(entry), "activate",
1121 			 G_CALLBACK(addressbook_entry_activated), NULL);
1122 
1123 	if (!prefs_common.addressbook_use_editaddress_dialog) {
1124 		editaddress_vbox = gtk_vbox_new(FALSE, 4);
1125 		vpaned = gtk_vpaned_new();
1126 		gtk_paned_pack1(GTK_PANED(vpaned), clist_vbox, FALSE, FALSE);
1127 		gtk_paned_pack2(GTK_PANED(vpaned), editaddress_vbox, TRUE, FALSE);
1128 	} else {
1129 		vpaned = NULL;
1130 		editaddress_vbox = NULL;
1131 	}
1132 	hpaned = gtk_hpaned_new();
1133 	gtk_box_pack_start(GTK_BOX(vbox2), hpaned, TRUE, TRUE, 0);
1134 	gtk_paned_pack1(GTK_PANED(hpaned), ctree_swin, FALSE, FALSE);
1135 	if (prefs_common.addressbook_use_editaddress_dialog)
1136 		gtk_paned_pack2(GTK_PANED(hpaned), clist_vbox, TRUE, FALSE);
1137 	else
1138 		gtk_paned_pack2(GTK_PANED(hpaned), vpaned, TRUE, FALSE);
1139 
1140 	/* Status bar */
1141 	hsbox = gtk_hbox_new(FALSE, 0);
1142 	gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
1143 	statusbar = gtk_statusbar_new();
1144 	gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
1145 
1146 	/* Button panel */
1147 	hbbox = gtk_hbutton_box_new();
1148 	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
1149 	gtk_box_set_spacing(GTK_BOX(hbbox), 2);
1150 	gtk_container_set_border_width(GTK_CONTAINER(hbbox), 4);
1151 	gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
1152 
1153 	gtkut_stock_button_add_help(hbbox, &help_btn);
1154 
1155 	edit_btn = gtk_button_new_from_stock(GTK_STOCK_EDIT);
1156 	gtk_widget_set_can_default(edit_btn, TRUE);
1157 	gtk_box_pack_start(GTK_BOX(hbbox), edit_btn, TRUE, TRUE, 0);
1158 	del_btn = gtk_button_new_from_stock(GTK_STOCK_DELETE);
1159 	gtk_widget_set_can_default(del_btn, TRUE);
1160 	gtk_box_pack_start(GTK_BOX(hbbox), del_btn, TRUE, TRUE, 0);
1161 	reg_btn = gtk_button_new_from_stock(GTK_STOCK_NEW);
1162 	gtk_widget_set_can_default(reg_btn, TRUE);
1163 	gtk_box_pack_start(GTK_BOX(hbbox), reg_btn, TRUE, TRUE, 0);
1164 
1165 
1166 	lup_btn = gtk_button_new_from_stock(GTK_STOCK_FIND);
1167 	gtk_widget_set_can_default(lup_btn, TRUE);
1168 	gtk_box_pack_start(GTK_BOX(hbox), lup_btn, TRUE, TRUE, 0);
1169 
1170 	g_signal_connect(G_OBJECT(help_btn), "clicked",
1171 			 G_CALLBACK(manual_open_with_anchor_cb),
1172 			 MANUAL_ANCHOR_ADDRBOOK);
1173 
1174 	g_signal_connect(G_OBJECT(edit_btn), "clicked",
1175 			 G_CALLBACK(addressbook_edit_clicked), NULL);
1176 	g_signal_connect(G_OBJECT(del_btn), "clicked",
1177 			 G_CALLBACK(addressbook_del_clicked), NULL);
1178 	g_signal_connect(G_OBJECT(reg_btn), "clicked",
1179 			 G_CALLBACK(addressbook_reg_clicked), NULL);
1180 	g_signal_connect(G_OBJECT(lup_btn), "clicked",
1181 			 G_CALLBACK(addressbook_lup_clicked), NULL);
1182 
1183 	to_btn = gtk_button_new_with_label
1184 		(prefs_common_translated_header_name("To:"));
1185 	gtk_widget_set_can_default(to_btn, TRUE);
1186 	gtk_box_pack_start(GTK_BOX(hbbox), to_btn, TRUE, TRUE, 0);
1187 	cc_btn = gtk_button_new_with_label
1188 		(prefs_common_translated_header_name("Cc:"));
1189 	gtk_widget_set_can_default(cc_btn, TRUE);
1190 	gtk_box_pack_start(GTK_BOX(hbbox), cc_btn, TRUE, TRUE, 0);
1191 	bcc_btn = gtk_button_new_with_label
1192 		(prefs_common_translated_header_name("Bcc:"));
1193 	gtk_widget_set_can_default(bcc_btn, TRUE);
1194 	gtk_box_pack_start(GTK_BOX(hbbox), bcc_btn, TRUE, TRUE, 0);
1195 
1196 	close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
1197 	gtk_widget_set_can_default(close_btn, TRUE);
1198 	gtk_box_pack_start(GTK_BOX(hbbox), close_btn, TRUE, TRUE, 0);
1199 
1200 	g_signal_connect(G_OBJECT(to_btn), "clicked",
1201 			 G_CALLBACK(addressbook_to_clicked),
1202 			 GINT_TO_POINTER(COMPOSE_TO));
1203 	g_signal_connect(G_OBJECT(cc_btn), "clicked",
1204 			 G_CALLBACK(addressbook_to_clicked),
1205 			 GINT_TO_POINTER(COMPOSE_CC));
1206 	g_signal_connect(G_OBJECT(bcc_btn), "clicked",
1207 			 G_CALLBACK(addressbook_to_clicked),
1208 			 GINT_TO_POINTER(COMPOSE_BCC));
1209 	g_signal_connect(G_OBJECT(close_btn), "clicked",
1210 			 G_CALLBACK(addressbook_close_clicked), NULL);
1211 
1212 	/* Build icons for interface */
1213 
1214 	/* Build control tables */
1215 	addrbookctl_build_map(window);
1216 	addrbookctl_build_iflist();
1217 	addrbookctl_build_ifselect();
1218 
1219 	addrbook.clist   = NULL;
1220 
1221 	/* Add each interface into the tree as a root level folder */
1222 	nodeIf = _addressInterfaceList_;
1223 	while( nodeIf ) {
1224 		AdapterInterface *adapter = nodeIf->data;
1225 		AddressInterface *iface = adapter->interface;
1226 		nodeIf = g_list_next(nodeIf);
1227 
1228 		if(iface->useInterface) {
1229 			AddressTypeControlItem *atci = adapter->atci;
1230 			text = atci->displayName;
1231 			adapter->treeNode =
1232 				gtk_sctree_insert_node( GTK_CMCTREE(ctree),
1233 					NULL, NULL, &text, FOLDER_SPACING,
1234 					interfacexpm,
1235 					interfacexpm,
1236 					FALSE, FALSE );
1237 			cm_menu_set_sensitive_full(ui_manager, atci->menuCommand, adapter->haveLibrary );
1238 			gtk_cmctree_node_set_row_data_full(
1239 				GTK_CMCTREE(ctree), adapter->treeNode, adapter,
1240 				addressbook_free_treenode );
1241 		}
1242 	}
1243 
1244 	/* Popup menu */
1245 
1246 	MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popups", NULL, GTK_UI_MANAGER_MENUBAR);
1247 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups", "ABTreePopup", "ABTreePopup", GTK_UI_MANAGER_MENU)
1248 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "EditBook", "ABTreePopup/EditBook", GTK_UI_MANAGER_MENUITEM)
1249 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "DeleteBook", "ABTreePopup/DeleteBook", GTK_UI_MANAGER_MENUITEM)
1250 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Separator1", "ABTreePopup/---", GTK_UI_MANAGER_SEPARATOR)
1251 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "NewBook", "ABTreePopup/NewBook", GTK_UI_MANAGER_MENUITEM)
1252 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "NewFolder", "ABTreePopup/NewFolder", GTK_UI_MANAGER_MENUITEM)
1253 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "NewGroup", "ABTreePopup/NewGroup", GTK_UI_MANAGER_MENUITEM)
1254 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Separator2", "ABTreePopup/---", GTK_UI_MANAGER_SEPARATOR)
1255 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Cut", "ABTreePopup/Cut", GTK_UI_MANAGER_MENUITEM)
1256 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Copy", "ABTreePopup/Copy", GTK_UI_MANAGER_MENUITEM)
1257 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Paste", "ABTreePopup/Paste", GTK_UI_MANAGER_MENUITEM)
1258 
1259 	tree_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1260 				gtk_ui_manager_get_widget(ui_manager, "/Popups/ABTreePopup")));
1261 
1262 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups", "ABListPopup", "ABListPopup", GTK_UI_MANAGER_MENU)
1263 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "SelectAll", "ABListPopup/SelectAll", GTK_UI_MANAGER_MENUITEM)
1264 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator1", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
1265 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Edit", "ABListPopup/Edit", GTK_UI_MANAGER_MENUITEM)
1266 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Delete", "ABListPopup/Delete", GTK_UI_MANAGER_MENUITEM)
1267 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator2", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
1268 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "NewAddress", "ABListPopup/NewAddress", GTK_UI_MANAGER_MENUITEM)
1269 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "NewGroup", "ABListPopup/NewGroup", GTK_UI_MANAGER_MENUITEM)
1270 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator3", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
1271 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Cut", "ABListPopup/Cut", GTK_UI_MANAGER_MENUITEM)
1272 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Copy", "ABListPopup/Copy", GTK_UI_MANAGER_MENUITEM)
1273 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Paste", "ABListPopup/Paste", GTK_UI_MANAGER_MENUITEM)
1274 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator4", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
1275 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Mailto", "ABListPopup/Mailto", GTK_UI_MANAGER_MENUITEM)
1276 #ifdef USE_LDAP
1277 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "BrowseEntry", "ABListPopup/BrowseEntry", GTK_UI_MANAGER_MENUITEM)
1278 #endif
1279 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Merge", "ABListPopup/Merge", GTK_UI_MANAGER_MENUITEM)
1280 	list_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1281 				gtk_ui_manager_get_widget(ui_manager, "/Popups/ABListPopup")));
1282 
1283 	addrbook.window  = window;
1284 	addrbook.hpaned  = hpaned;
1285 	addrbook.vpaned  = vpaned;
1286 	addrbook.menubar = menubar;
1287 	addrbook.ctree   = ctree;
1288 	addrbook.ctree_swin
1289 			 = ctree_swin;
1290 	addrbook.editaddress_vbox = editaddress_vbox;
1291 	addrbook.clist   = clist;
1292 	addrbook.label	 = label;
1293 	addrbook.entry   = entry;
1294 	addrbook.statusbar = statusbar;
1295 	addrbook.status_cid = gtk_statusbar_get_context_id(
1296 			GTK_STATUSBAR(statusbar), "Addressbook Window" );
1297 
1298 	addrbook.help_btn = help_btn;
1299 	addrbook.edit_btn = edit_btn;
1300 	addrbook.del_btn = del_btn;
1301 	addrbook.reg_btn = reg_btn;
1302 	addrbook.lup_btn = lup_btn;
1303 	addrbook.to_btn  = to_btn;
1304 	addrbook.cc_btn  = cc_btn;
1305 	addrbook.bcc_btn = bcc_btn;
1306 
1307 	addrbook.tree_popup   = tree_popup;
1308 	addrbook.list_popup   = list_popup;
1309 	addrbook.ui_manager   = ui_manager;
1310 
1311 	addrbook.listSelected = NULL;
1312 
1313 	if (!geometry.min_height) {
1314 		geometry.min_width = ADDRESSBOOK_WIDTH;
1315 		geometry.min_height = ADDRESSBOOK_HEIGHT;
1316 	}
1317 
1318 	gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
1319 				      GDK_HINT_MIN_SIZE);
1320 	gtk_widget_set_size_request(window, prefs_common.addressbookwin_width,
1321 				    prefs_common.addressbookwin_height);
1322 #ifdef G_OS_WIN32
1323 	gtk_window_move(GTK_WINDOW(window), 48, 48);
1324 #endif
1325 
1326 	if (!prefs_common.addressbook_use_editaddress_dialog) {
1327 		if (prefs_common.addressbook_vpaned_pos > 0)
1328 			gtk_paned_set_position(GTK_PANED(vpaned),
1329 				prefs_common.addressbook_vpaned_pos);
1330 	}
1331 	if (prefs_common.addressbook_hpaned_pos > 0)
1332 		gtk_paned_set_position(GTK_PANED(hpaned),
1333 			prefs_common.addressbook_hpaned_pos);
1334 
1335 
1336 	gtk_widget_show_all(window);
1337 }
1338 
1339 /**
1340  * Close address book window and save to file(s).
1341  */
addressbook_close(void)1342 static gint addressbook_close( void ) {
1343 	address_completion_end(addrbook.window);
1344 	if (!prefs_common.addressbook_use_editaddress_dialog)
1345 		addressbook_edit_person_invalidate(NULL, NULL, NULL);
1346 
1347 	addressbook_pane_save_position();
1348 
1349 	gtk_widget_hide(addrbook.window);
1350 	addressbook_export_to_file();
1351 	return TRUE;
1352 }
1353 
1354 /**
1355  * Display message in status line.
1356  * \param msg Message to display.
1357  */
addressbook_status_show(gchar * msg)1358 static void addressbook_status_show( gchar *msg ) {
1359 	if( addrbook.statusbar != NULL ) {
1360 		gtk_statusbar_pop(
1361 			GTK_STATUSBAR(addrbook.statusbar),
1362 			addrbook.status_cid );
1363 		if( msg ) {
1364 			gtk_statusbar_push(
1365 				GTK_STATUSBAR(addrbook.statusbar),
1366 				addrbook.status_cid, msg );
1367 		}
1368 	}
1369 }
1370 
addressbook_ds_show_message(AddressDataSource * ds)1371 static void addressbook_ds_show_message( AddressDataSource *ds ) {
1372 	gint retVal;
1373 	gchar *name;
1374 	gchar *desc;
1375 	*addressbook_msgbuf = '\0';
1376 	if( ds ) {
1377 		name = addrindex_ds_get_name( ds );
1378 		retVal = addrindex_ds_get_status_code( ds );
1379 		if( retVal == MGU_SUCCESS ) {
1380 			g_snprintf( addressbook_msgbuf,
1381 				    sizeof(addressbook_msgbuf), "%s", name );
1382 		}
1383 		else {
1384 			desc = addressbook_err2string( _lutErrorsGeneral_, retVal );
1385 			g_snprintf( addressbook_msgbuf,
1386 			    sizeof(addressbook_msgbuf), "%s: %s", name, desc );
1387 		}
1388 	}
1389 	addressbook_status_show( addressbook_msgbuf );
1390 }
1391 
addressbook_edit_clicked(GtkButton * button,gpointer data)1392 static void addressbook_edit_clicked(GtkButton *button, gpointer data)
1393 {
1394 	addressbook_edit_address_cb(NULL, NULL);
1395 }
1396 
find_person(AddrSelectItem * item_a,ItemPerson * person)1397 static gboolean find_person(AddrSelectItem *item_a, ItemPerson *person)
1398 {
1399 	return ((ItemPerson *)item_a->addressItem == person)?0:-1;
1400 }
1401 
1402 /*
1403 * Delete one or more objects from address list.
1404 */
addressbook_del_clicked(GtkButton * button,gpointer data)1405 static void addressbook_del_clicked(GtkButton *button, gpointer data)
1406 {
1407 	GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
1408 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
1409 	AddressObject *pobj;
1410 	AdapterDSource *ads = NULL;
1411 	GtkCMCTreeNode *nodeList;
1412 	gboolean procFlag;
1413 	AlertValue aval;
1414 	AddressBookFile *abf = NULL;
1415 	AddressDataSource *ds = NULL;
1416 	AddressInterface *iface;
1417 	AddrItemObject *aio;
1418 	AddrSelectItem *item;
1419 	GList *list, *node;
1420 	gboolean refreshList = FALSE;
1421 
1422 	pobj = gtk_cmctree_node_get_row_data(ctree, addrbook.opened );
1423 	cm_return_if_fail(pobj != NULL);
1424 
1425 	/* Test whether anything selected for deletion */
1426 	nodeList = addrbook.listSelected;
1427 
1428 	aio = gtk_cmctree_node_get_row_data( clist, nodeList );
1429 	if( aio == NULL) return;
1430 	ds = addressbook_find_datasource( addrbook.treeSelected );
1431 	if( ds == NULL ) return;
1432 
1433 	/* Test for read only */
1434 	iface = ds->interface;
1435 	if( iface->readOnly ) {
1436 		alertpanel( _("Delete address(es)"),
1437 			_("This address data is readonly and cannot be deleted."),
1438 			GTK_STOCK_CLOSE, NULL, NULL, ALERTFOCUS_FIRST);
1439 		return;
1440 	}
1441 
1442 	/* Test whether Ok to proceed */
1443 	procFlag = FALSE;
1444 	if( pobj->type == ADDR_DATASOURCE ) {
1445 		ads = ADAPTER_DSOURCE(pobj);
1446 		if( ads->subType == ADDR_BOOK ) procFlag = TRUE;
1447 	}
1448 	else if( pobj->type == ADDR_ITEM_FOLDER ) {
1449 		procFlag = TRUE;
1450 	}
1451 	else if( pobj->type == ADDR_ITEM_GROUP ) {
1452 		procFlag = TRUE;
1453 	}
1454 	if( ! procFlag ) return;
1455 	abf = ds->rawDataSource;
1456 	if( abf == NULL ) return;
1457 
1458 	gtk_cmclist_freeze(GTK_CMCLIST(addrbook.clist));
1459 	g_signal_handlers_block_by_func
1460 		(G_OBJECT(addrbook.clist),
1461 		 G_CALLBACK(addressbook_list_row_unselected), NULL);
1462 
1463 	/* Process deletions */
1464 	if( pobj->type == ADDR_DATASOURCE || pobj->type == ADDR_ITEM_FOLDER ) {
1465 		GList *groups = NULL, *persons = NULL, *emails = NULL;
1466 		gboolean group_delete = TRUE;
1467 		/* Items inside folders */
1468 		list = addrselect_get_list( _addressSelect_ );
1469 		/* Confirm deletion */
1470 		node = list;
1471 		while( node ) {
1472 			item = node->data;
1473 			node = g_list_next( node );
1474 			aio = ( AddrItemObject * ) item->addressItem;
1475 			if( aio->type == ITEMTYPE_PERSON || aio->type == ITEMTYPE_EMAIL ) {
1476 				group_delete = FALSE;
1477 				break;
1478 			}
1479 		}
1480 		if (group_delete) {
1481 			aval = alertpanel( _("Delete group"),
1482 					_("Really delete the group(s)?\n"
1483 					  "The addresses it contains will not be lost."),
1484 					GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, ALERTFOCUS_SECOND );
1485 			if( aval != G_ALERTALTERNATE ) {
1486 				goto thaw_ret;
1487 			}
1488 		} else {
1489 			aval = alertpanel( _("Delete address(es)"),
1490 					_("Really delete the address(es)?"),
1491 					GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, ALERTFOCUS_SECOND );
1492 			if( aval != G_ALERTALTERNATE ) {
1493 				goto thaw_ret;
1494 			}
1495 		}
1496 
1497 	/* first, set lists of groups and persons to remove */
1498 		node = list;
1499 		while( node ) {
1500 			item = node->data;
1501 			node = g_list_next( node );
1502 			aio = ( AddrItemObject * ) item->addressItem;
1503 			if (!aio)
1504 				continue;
1505 			if( aio->type == ITEMTYPE_GROUP ) {
1506 				groups = g_list_prepend(groups, item);
1507 			}
1508 			else if( aio->type == ITEMTYPE_PERSON ) {
1509 				persons = g_list_prepend(persons, item);
1510 			}
1511 		}
1512 	/* then set list of emails to remove *if* they're not children of
1513 	 * persons to remove */
1514 		node = list;
1515 		while( node ) {
1516 			item = node->data;
1517 			node = g_list_next( node );
1518 			aio = ( AddrItemObject * ) item->addressItem;
1519 			if (!aio)
1520 				continue;
1521 			if( aio->type == ITEMTYPE_EMAIL ) {
1522 				ItemEMail *sitem = ( ItemEMail * ) aio;
1523 				ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
1524 				if (!g_list_find_custom(persons, person, (GCompareFunc)(find_person))) {
1525 					emails = g_list_prepend(emails, item);
1526 				}
1527 				/* else, the email will be removed via the parent person */
1528 			}
1529 		}
1530 	/* then delete groups */
1531 		node = groups;
1532 		while( node ) {
1533 			item = node->data;
1534 			node = g_list_next( node );
1535 			aio = ( AddrItemObject * ) item->addressItem;
1536 			if (!aio)
1537 				continue;
1538 			if( aio->type == ITEMTYPE_GROUP ) {
1539 				ItemGroup *item = ( ItemGroup * ) aio;
1540 				GtkCMCTreeNode *nd = NULL;
1541 				nd = addressbook_find_group_node( addrbook.opened, item );
1542 				item = addrbook_remove_group( abf, item );
1543 				if( item ) {
1544 					addritem_free_item_group( item );
1545 				}
1546 				/* Remove group from parent node */
1547 				gtk_cmctree_remove_node( ctree, nd );
1548 				refreshList = TRUE;
1549 			}
1550 		}
1551 	/* then delete persons */
1552 		node = persons;
1553 		while( node ) {
1554 			item = node->data;
1555 			node = g_list_next( node );
1556 			aio = ( AddrItemObject * ) item->addressItem;
1557 			if (!aio)
1558 				continue;
1559 			if( aio->type == ITEMTYPE_PERSON ) {
1560 				ItemPerson *item = ( ItemPerson * ) aio;
1561 				item->status = DELETE_ENTRY;
1562 				addressbook_folder_remove_one_person( clist, item );
1563 				if (pobj->type == ADDR_ITEM_FOLDER)
1564 					addritem_folder_remove_person(ADAPTER_FOLDER(pobj)->itemFolder, item);
1565 				item = addrbook_remove_person( abf, item );
1566 #ifdef USE_LDAP
1567 				if (ds && ds->type == ADDR_IF_LDAP) {
1568 					LdapServer *server = ds->rawDataSource;
1569 					ldapsvr_set_modified(server, TRUE);
1570 					ldapsvr_update_book(server, item);
1571 				}
1572 #endif
1573 				if( item ) {
1574 					addritem_person_remove_picture(item);
1575 					addritem_free_item_person( item );
1576 				}
1577 			}
1578 		}
1579 	/* then delete emails */
1580 		node = emails;
1581 		while( node ) {
1582 			item = node->data;
1583 			node = g_list_next( node );
1584 			aio = ( AddrItemObject * ) item->addressItem;
1585 			if (!aio)
1586 				continue;
1587 
1588 			if( aio->type == ITEMTYPE_EMAIL ) {
1589 				ItemEMail *sitem = ( ItemEMail * ) aio;
1590 				ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
1591 				sitem = addrbook_person_remove_email( abf, person, sitem );
1592 				if( sitem ) {
1593 					addrcache_remove_email(abf->addressCache, sitem);
1594 					addritem_free_item_email( sitem );
1595 				}
1596 				addressbook_folder_refresh_one_person( clist, person );
1597 			}
1598 		}
1599 		g_list_free( groups );
1600 		g_list_free( persons );
1601 		g_list_free( emails );
1602 		g_list_free( list );
1603 		addressbook_list_select_clear();
1604 		if( refreshList ) {
1605 			gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened);
1606 			addressbook_set_clist(
1607 				gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
1608 					addrbook.opened),
1609 				TRUE);
1610 		}
1611 		addrbook_set_dirty(abf, TRUE);
1612 		addressbook_export_to_file();
1613 		addressbook_list_menu_setup();
1614 		goto thaw_ret;
1615 	}
1616 	else if( pobj->type == ADDR_ITEM_GROUP ) {
1617 		/* Items inside groups */
1618 		list = addrselect_get_list( _addressSelect_ );
1619 		node = list;
1620 		while( node ) {
1621 			item = node->data;
1622 			node = g_list_next( node );
1623 			aio = ( AddrItemObject * ) item->addressItem;
1624 			if( aio->type == ITEMTYPE_EMAIL ) {
1625 				ItemEMail *item = ( ItemEMail * ) aio;
1626 				ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(item);
1627 				item = addrbook_person_remove_email( abf, person, item );
1628 				if( item ) {
1629 					addritem_free_item_email( item );
1630 				}
1631 			}
1632 		}
1633 		g_list_free( list );
1634 		addressbook_list_select_clear();
1635 		gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened);
1636 		addressbook_set_clist(
1637 			gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
1638 				addrbook.opened),
1639 			TRUE);
1640 
1641 		addrbook_set_dirty(abf, TRUE);
1642 		addressbook_export_to_file();
1643 		addressbook_list_menu_setup();
1644 		goto thaw_ret;
1645 	}
1646 
1647 	gtk_cmctree_node_set_row_data( clist, nodeList, NULL );
1648 	gtk_cmctree_remove_node( clist, nodeList );
1649 thaw_ret:
1650 	gtk_cmclist_thaw(GTK_CMCLIST(addrbook.clist));
1651 	g_signal_handlers_unblock_by_func
1652 		(G_OBJECT(addrbook.clist),
1653 		 G_CALLBACK(addressbook_list_row_unselected), NULL);
1654 }
1655 
addressbook_reg_clicked(GtkButton * button,gpointer data)1656 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
1657 {
1658 	addressbook_new_address_cb( NULL, NULL );
1659 }
1660 
addressbook_format_address(AddrItemObject * aio)1661 static gchar *addressbook_format_address( AddrItemObject * aio ) {
1662 	gchar *buf = NULL;
1663 	gchar *name = NULL;
1664 	gchar *address = NULL;
1665 
1666 	if( aio->type == ITEMTYPE_EMAIL ) {
1667 		ItemPerson *person = NULL;
1668 		ItemEMail *email = ( ItemEMail * ) aio;
1669 
1670 		person = ( ItemPerson * ) ADDRITEM_PARENT(email);
1671 		if( email->address ) {
1672 			if( ADDRITEM_NAME(email) ) {
1673 				name = ADDRITEM_NAME(email);
1674 				if( *name == '\0' ) {
1675 					name = ADDRITEM_NAME(person);
1676 				}
1677 			}
1678 			else if( ADDRITEM_NAME(person) ) {
1679 				name = ADDRITEM_NAME(person);
1680 			}
1681 			else {
1682 				buf = g_strdup( email->address );
1683 			}
1684 			address = email->address;
1685 		}
1686 	}
1687 	else if( aio->type == ITEMTYPE_PERSON ) {
1688 		ItemPerson *person = ( ItemPerson * ) aio;
1689 		GList *node = person->listEMail;
1690 
1691 		name = ADDRITEM_NAME(person);
1692 		if( node ) {
1693 			ItemEMail *email = ( ItemEMail * ) node->data;
1694 			address = email->address;
1695 		}
1696 	}
1697 	if( address ) {
1698 		if( name && name[0] != '\0' ) {
1699 			if( strchr_with_skip_quote( name, '"', ',' ) )
1700 				buf = g_strdup_printf( "\"%s\" <%s>", name, address );
1701 			else
1702 				buf = g_strdup_printf( "%s <%s>", name, address );
1703 		}
1704 		else {
1705 			buf = g_strdup( address );
1706 		}
1707 	}
1708 
1709 	return buf;
1710 }
1711 
addressbook_to_clicked(GtkButton * button,gpointer data)1712 static void addressbook_to_clicked(GtkButton *button, gpointer data)
1713 {
1714 	GList *list, *node;
1715 	Compose *compose;
1716 	AddrSelectItem *item;
1717 	AddrItemObject *aio;
1718 	gchar *addr;
1719 
1720 	compose = addrbook.target_compose;
1721 	if( ! compose ) return;
1722 
1723 	/* Nothing selected, but maybe there is something in text entry */
1724 	addr = (char *)gtk_entry_get_text( GTK_ENTRY( addrbook.entry) );
1725 	if ( addr ) {
1726 		compose_entry_append(
1727 			compose, addr, (ComposeEntryType)data , PREF_NONE);
1728 	}
1729 
1730 	/* Select from address list */
1731 	list = addrselect_get_list( _addressSelect_ );
1732 	node = list;
1733 	if (node) {
1734 		while( node ) {
1735 			item = node->data;
1736 			node = g_list_next( node );
1737 			aio = item->addressItem;
1738 			if( aio->type == ITEMTYPE_PERSON ||
1739 			    aio->type == ITEMTYPE_EMAIL ) {
1740 				addr = addressbook_format_address( aio );
1741 				compose_entry_append(
1742 					compose, addr, (ComposeEntryType) data, PREF_NONE );
1743 				g_free( addr );
1744 			}
1745 			else if( aio->type == ITEMTYPE_GROUP ) {
1746 				ItemGroup *group = ( ItemGroup * ) aio;
1747 				GList *nodeMail = group->listEMail;
1748 				while( nodeMail ) {
1749 					ItemEMail *email = nodeMail->data;
1750 
1751 					addr = addressbook_format_address(
1752 							( AddrItemObject * ) email );
1753 					compose_entry_append(
1754 						compose, addr, (ComposeEntryType) data, PREF_NONE );
1755 					g_free( addr );
1756 					nodeMail = g_list_next( nodeMail );
1757 				}
1758 			}
1759 		}
1760 	} else {
1761 		AddressObject *obj = NULL;
1762 
1763 		obj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
1764 
1765 		if( obj && obj->type == ADDR_ITEM_GROUP ) {
1766 			ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup;
1767 			GList *nodeMail = itemGroup->listEMail;
1768 			while( nodeMail ) {
1769 				ItemEMail *email = nodeMail->data;
1770 
1771 				addr = addressbook_format_address(
1772 						( AddrItemObject * ) email );
1773 				compose_entry_append(
1774 					compose, addr, (ComposeEntryType) data, PREF_NONE );
1775 				g_free( addr );
1776 				nodeMail = g_list_next( nodeMail );
1777 			}
1778 		}
1779 	}
1780 	g_list_free( list );
1781 }
1782 
addressbook_menubar_set_sensitive(gboolean sensitive)1783 static void addressbook_menubar_set_sensitive( gboolean sensitive ) {
1784 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook",   sensitive );
1785 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook", sensitive );
1786 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/NewFolder",  sensitive );
1787 
1788 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/SelectAll",    TRUE );
1789 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Cut",    sensitive );
1790 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Copy",   sensitive );
1791 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Paste",  sensitive );
1792 
1793 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/NewAddress", sensitive );
1794 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/NewGroup",   sensitive );
1795 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Mailto",     sensitive );
1796 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Merge",      sensitive );
1797 	gtk_widget_set_sensitive( addrbook.edit_btn, sensitive );
1798 	gtk_widget_set_sensitive( addrbook.del_btn, sensitive );
1799 }
1800 
addressbook_menuitem_set_sensitive(AddressObject * obj,GtkCMCTreeNode * node)1801 static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCMCTreeNode *node ) {
1802 	gboolean canEdit = FALSE;
1803 	gboolean canDelete = TRUE;
1804 	gboolean canAdd = FALSE;
1805 	gboolean canEditTr = TRUE;
1806 	gboolean editAddress = FALSE;
1807 	gboolean canExport = TRUE;
1808 	AddressTypeControlItem *atci = NULL;
1809 	AddressDataSource *ds = NULL;
1810 	AddressInterface *iface = NULL;
1811 
1812 	if( obj == NULL ) return;
1813 	if( obj->type == ADDR_INTERFACE ) {
1814 		AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
1815 		iface = adapter->interface;
1816 		if( iface ) {
1817 			if( iface->haveLibrary ) {
1818 				/* Enable appropriate File / New command */
1819 				atci = adapter->atci;
1820 				cm_menu_set_sensitive_full(addrbook.ui_manager, atci->menuCommand, TRUE );
1821 			}
1822 		}
1823 		canEditTr = canExport = FALSE;
1824 	}
1825 	else if( obj->type == ADDR_DATASOURCE ) {
1826 		AdapterDSource *ads = ADAPTER_DSOURCE(obj);
1827 		ds = ads->dataSource;
1828 		iface = ds->interface;
1829 		if( ! iface->readOnly ) {
1830 			canAdd = canEdit = editAddress = canDelete = TRUE;
1831 		}
1832 		if( ! iface->haveLibrary ) {
1833 			canAdd = canEdit = editAddress = canExport = canDelete = FALSE;
1834 		}
1835 	}
1836 	else if( obj->type == ADDR_ITEM_FOLDER ) {
1837 		ds = addressbook_find_datasource( addrbook.treeSelected );
1838 		if( ds ) {
1839 			iface = ds->interface;
1840 			if( iface->readOnly ) {
1841 				canEditTr = FALSE;
1842 				canDelete = FALSE;
1843 			}
1844 			else {
1845 				canAdd = editAddress = TRUE;
1846 			}
1847 		}
1848 	}
1849 	else if( obj->type == ADDR_ITEM_GROUP ) {
1850 		ds = addressbook_find_datasource( addrbook.treeSelected );
1851 		if( ds ) {
1852 			iface = ds->interface;
1853 			if( ! iface->readOnly ) {
1854 				editAddress = TRUE;
1855 			}
1856 		}
1857 	}
1858 
1859 	if( addrbook.listSelected == NULL )
1860 		canEdit = FALSE;
1861 
1862 	/* Enable add */
1863 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/NewAddress", editAddress );
1864 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/NewGroup",   canAdd );
1865 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/NewFolder",  canAdd );
1866 	gtk_widget_set_sensitive( addrbook.reg_btn, editAddress );
1867 
1868 	/* Enable edit */
1869 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Edit",   canEdit );
1870 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Delete", canDelete );
1871 	gtk_widget_set_sensitive( addrbook.edit_btn, canEdit );
1872 	gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
1873 
1874 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook",      canEditTr );
1875 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook",    canEditTr );
1876 
1877 	/* Export data */
1878 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Tools/ExportHTML", canExport );
1879 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Tools/ExportLDIF", canExport );
1880 }
1881 
1882 /**
1883  * Address book tree callback function that responds to selection of tree
1884  * items.
1885  *
1886  * \param ctree  Tree widget.
1887  * \param node   Node that was selected.
1888  * \param column Column number where selected occurred.
1889  * \param data   Pointer to user data.
1890  */
addressbook_tree_selected(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,gpointer data)1891 static void addressbook_tree_selected(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1892 				      gint column, gpointer data)
1893 {
1894 	AddressObject *obj = NULL;
1895 	AdapterDSource *ads = NULL;
1896 	AddressDataSource *ds = NULL;
1897 	ItemFolder *rootFolder = NULL;
1898 	AddressObjectType aot;
1899 
1900 	addrbook.treeSelected = node;
1901 	addrbook.listSelected = NULL;
1902 	addressbook_status_show( "" );
1903 	if( addrbook.entry != NULL ) gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
1904 
1905 	if( node ) obj = gtk_cmctree_node_get_row_data( ctree, node );
1906 	if( obj == NULL ) {
1907 		addressbook_set_clist(NULL, TRUE);
1908 		return;
1909 	}
1910 	addrbook.opened = node;
1911 
1912 	if( obj->type == ADDR_DATASOURCE ) {
1913 		/* Read from file */
1914 		static gboolean tVal = TRUE;
1915 
1916 		ads = ADAPTER_DSOURCE(obj);
1917 
1918 		ds = ads->dataSource;
1919 		if( ds == NULL ) return;
1920 
1921 		if( addrindex_ds_get_modify_flag( ds ) ) {
1922 			addrindex_ds_read_data( ds );
1923 		}
1924 
1925 		if( ! addrindex_ds_get_read_flag( ds ) ) {
1926 			addrindex_ds_read_data( ds );
1927 		}
1928 		addressbook_ds_show_message( ds );
1929 
1930 		if( ! addrindex_ds_get_access_flag( ds ) ) {
1931 			/* Remove existing folders and groups */
1932 			gtk_cmclist_freeze( GTK_CMCLIST(ctree) );
1933 			addressbook_tree_remove_children( ctree, node );
1934 			gtk_cmclist_thaw( GTK_CMCLIST(ctree) );
1935 
1936 			/* Load folders into the tree */
1937 			rootFolder = addrindex_ds_get_root_folder( ds );
1938 			if( ds && ds->type == ADDR_IF_JPILOT ) {
1939 				aot = ADDR_CATEGORY;
1940 			}
1941 			else if( ds && ds->type == ADDR_IF_LDAP ) {
1942 				aot = ADDR_LDAP_QUERY;
1943 			}
1944 			else {
1945 				aot = ADDR_ITEM_FOLDER;
1946 			}
1947 			addressbook_node_add_folder( node, ds, rootFolder, aot );
1948 			addrindex_ds_set_access_flag( ds, &tVal );
1949 			gtk_cmctree_expand( ctree, node );
1950 		}
1951 	} else {
1952 		addressbook_set_clist(NULL, TRUE);
1953 	}
1954 
1955 	/* Update address list */
1956 	g_signal_handlers_block_by_func
1957 		(G_OBJECT(ctree),
1958 		 G_CALLBACK(addressbook_tree_selected), NULL);
1959 	addressbook_set_clist( obj, FALSE );
1960 	g_signal_handlers_unblock_by_func
1961 		(G_OBJECT(ctree),
1962 		 G_CALLBACK(addressbook_tree_selected), NULL);
1963 	if (!prefs_common.addressbook_use_editaddress_dialog)
1964 		addressbook_edit_person_invalidate(NULL, NULL, NULL);
1965 
1966 	/* Setup main menu selections */
1967 	addressbook_menubar_set_sensitive( FALSE );
1968 	addressbook_menuitem_set_sensitive( obj, node );
1969 	addressbook_list_select_clear();
1970 	addressbook_list_menu_setup();
1971 	return;
1972 }
1973 
1974 /**
1975  * Setup address list popup menu items. Items are enabled or disabled as
1976  * required.
1977  */
addressbook_list_menu_setup(void)1978 static void addressbook_list_menu_setup( void ) {
1979 	GtkCMCTree *clist = NULL;
1980 	AddressObject *pobj = NULL;
1981 	AddressObject *obj = NULL;
1982 	AdapterDSource *ads = NULL;
1983 	AddressInterface *iface = NULL;
1984 	AddressDataSource *ds = NULL;
1985 	GList *list;
1986 	AddrItemObject *aio;
1987 	AddrSelectItem *item;
1988 	gboolean canEdit = FALSE;
1989 	gboolean canDelete = FALSE;
1990 	gboolean canCut = FALSE;
1991 	gboolean canCopy = FALSE;
1992 	gboolean canPaste = FALSE;
1993 	gboolean canBrowse = FALSE;
1994 	gboolean canMerge = FALSE;
1995 
1996 	pobj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
1997 	if( pobj == NULL ) return;
1998 
1999 	clist = GTK_CMCTREE(addrbook.clist);
2000 	obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
2001 	if( obj == NULL ) canEdit = FALSE;
2002 
2003 	menu_set_insensitive_all( GTK_MENU_SHELL(addrbook.list_popup) );
2004 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/SelectAll", TRUE );
2005 
2006 	if( pobj->type == ADDR_DATASOURCE ) {
2007 		/* Parent object is a data source */
2008 		ads = ADAPTER_DSOURCE(pobj);
2009 		ds = ads->dataSource;
2010 		if (!ds)
2011 			return;
2012 		iface = ds->interface;
2013 		if (!iface)
2014 			return;
2015 		if( ! iface->readOnly ) {
2016 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewAddress", TRUE );
2017 			if (iface->type != ADDR_IF_LDAP)
2018 				cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewGroup", TRUE );
2019 			gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2020 			if( obj )
2021 				canEdit = TRUE;
2022 			canDelete = canEdit;
2023 		}
2024 	}
2025 	else if( pobj->type != ADDR_INTERFACE ) {
2026 		/* Parent object is not an interface */
2027 		ds = addressbook_find_datasource( addrbook.treeSelected );
2028 		if (!ds)
2029 			return;
2030 		iface = ds->interface;
2031 		if (!iface)
2032 			return;
2033 		if( ! iface->readOnly ) {
2034 			/* Folder or group */
2035 			if( pobj->type == ADDR_ITEM_FOLDER || pobj->type == ADDR_ITEM_GROUP ) {
2036 				cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewAddress", TRUE );
2037 				gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2038 				if( obj ) canEdit = TRUE;
2039 			}
2040 			/* Folder */
2041 			if( pobj->type == ADDR_ITEM_FOLDER ) {
2042 				if (iface->type != ADDR_IF_LDAP)
2043 					cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewGroup", TRUE );
2044 				if( obj ) canEdit = TRUE;
2045 			}
2046 			canDelete = canEdit;
2047 		}
2048 		if( iface->type == ADDR_IF_LDAP ) {
2049 			if( obj ) canBrowse = TRUE;
2050 			canEdit = TRUE;
2051 			canDelete = TRUE;
2052 		}
2053 	}
2054 
2055 	if( iface ) {
2056 		/* Enable cut and paste */
2057 		if( ! addrclip_is_empty( _clipBoard_ ) )
2058 			canPaste = TRUE;
2059 		if( ! addrselect_test_empty( _addressSelect_ ) )
2060 			canCut = TRUE;
2061 		/* Enable copy if something is selected */
2062 		if( ! addrselect_test_empty( _addressSelect_ ) )
2063 			canCopy = TRUE;
2064 	}
2065 
2066 	/* Disable edit or browse if more than one row selected */
2067 	if( GTK_CMCLIST(clist)->selection && GTK_CMCLIST(clist)->selection->next ) {
2068 		canEdit = FALSE;
2069 		canBrowse = FALSE;
2070 	}
2071 
2072 	/* Allow merging persons or emails are selected */
2073 	list = _addressSelect_->listSelect;
2074 	if (list && list->next ) {
2075 		item = list->data;
2076 		aio = ( AddrItemObject * ) item->addressItem;
2077 		if( aio->type == ITEMTYPE_EMAIL ||
2078 				aio->type == ITEMTYPE_PERSON ) {
2079 			canMerge = TRUE;
2080 		}
2081 	}
2082 
2083 	/* Forbid write changes when read-only */
2084 	if( iface && iface->readOnly ) {
2085 		canCut = FALSE;
2086 		canDelete = FALSE;
2087 		canPaste = FALSE;
2088 		canMerge = FALSE;
2089 	}
2090 
2091 	/* Now go finalize menu items */
2092 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Edit",   canEdit );
2093 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Delete", canDelete );
2094 
2095 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Cut",           canCut );
2096 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Copy",          canCopy );
2097 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Paste",         canPaste );
2098 
2099 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Mailto",       canCopy );
2100 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Merge",        canMerge );
2101 
2102 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Cut",           canCut );
2103 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Copy",          canCopy );
2104 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Paste",         canPaste );
2105 
2106 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Edit",    canEdit );
2107 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Delete",  canDelete );
2108 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Mailto", canCopy );
2109 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Merge",  canMerge );
2110 
2111 	gtk_widget_set_sensitive( addrbook.edit_btn, canEdit );
2112 	gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
2113 
2114 	if (addrbook.target_compose) {
2115 		gtk_widget_set_sensitive(addrbook.to_btn, obj ? TRUE : FALSE);
2116 		gtk_widget_set_sensitive(addrbook.cc_btn, obj ? TRUE : FALSE);
2117 		gtk_widget_set_sensitive(addrbook.bcc_btn, obj ? TRUE : FALSE);
2118 	}
2119 #ifdef USE_LDAP
2120 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/BrowseEntry",    canBrowse );
2121 #endif
2122 }
2123 
addressbook_select_row_tree(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,gpointer data)2124 static void addressbook_select_row_tree	(GtkCMCTree	*ctree,
2125 					 GtkCMCTreeNode	*node,
2126 					 gint		 column,
2127 					 gpointer	 data)
2128 {
2129 }
2130 
2131 /**
2132  * Add list of items into tree node below specified tree node.
2133  * \param treeNode  Tree node.
2134  * \param ds        Data source.
2135  * \param listItems List of items.
2136  */
addressbook_treenode_add_list(GtkCMCTreeNode * treeNode,AddressDataSource * ds,GList * listItems)2137 static void addressbook_treenode_add_list(
2138 	GtkCMCTreeNode *treeNode, AddressDataSource *ds, GList *listItems )
2139 {
2140 	GList *node;
2141 
2142 	node = listItems;
2143 	while( node ) {
2144 		AddrItemObject *aio;
2145 		GtkCMCTreeNode *nn;
2146 
2147 		aio = node->data;
2148 		if( ADDRESS_OBJECT_TYPE(aio) == ADDR_ITEM_GROUP ) {
2149 			ItemGroup *group;
2150 
2151 			group = ( ItemGroup * ) aio;
2152 			nn = addressbook_node_add_group( treeNode, ds, group );
2153 			if (nn == NULL) {
2154 				g_message("error adding addressbook group\n");
2155 			}
2156 		}
2157 		else if( ADDRESS_OBJECT_TYPE(aio) == ADDR_ITEM_FOLDER ) {
2158 			ItemFolder *folder;
2159 
2160 			folder = ( ItemFolder * ) aio;
2161 			nn = addressbook_node_add_folder(
2162 				treeNode, ds, folder, ADDR_ITEM_FOLDER );
2163 			if (nn == NULL) {
2164 				g_message("error adding addressbook folder\n");
2165 			}
2166 		}
2167 		node = g_list_next( node );
2168 	}
2169 }
2170 
addressbook_select_all_cb(GtkAction * action,gpointer data)2171 static void addressbook_select_all_cb( GtkAction *action, gpointer data ) {
2172 	gtk_cmclist_select_all(GTK_CMCLIST(addrbook.clist));
2173 }
2174 
2175 /**
2176  * Cut from address list widget.
2177  */
addressbook_clip_cut_cb(GtkAction * action,gpointer data)2178 static void addressbook_clip_cut_cb( GtkAction *action, gpointer data ) {
2179 	_clipBoard_->cutFlag = TRUE;
2180 	addrclip_clear( _clipBoard_ );
2181 	addrclip_add( _clipBoard_, _addressSelect_ );
2182 	/* addrclip_list_show( _clipBoard_, stdout ); */
2183 }
2184 
2185 /**
2186  * Copy from address list widget.
2187  */
addressbook_clip_copy_cb(GtkAction * action,gpointer data)2188 static void addressbook_clip_copy_cb(GtkAction *action, gpointer data) {
2189 	_clipBoard_->cutFlag = FALSE;
2190 	addrclip_clear( _clipBoard_ );
2191 	addrclip_add( _clipBoard_, _addressSelect_ );
2192 	/* addrclip_list_show( _clipBoard_, stdout ); */
2193 }
2194 
2195 /**
2196  * Paste clipboard into address list widget.
2197  */
addressbook_clip_paste_cb(GtkAction * action,gpointer data)2198 static void addressbook_clip_paste_cb( GtkAction *action, gpointer data ) {
2199 	GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
2200 	AddressObject *pobj = NULL;
2201 	AddressDataSource *ds = NULL;
2202 	AddressBookFile *abf = NULL;
2203 	ItemFolder *folder = NULL;
2204 	GList *folderGroup = NULL;
2205 
2206 	ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
2207 	if( ds == NULL ) return;
2208 	if( addrindex_ds_get_readonly( ds ) ) {
2209 		alertpanel_error( _("Cannot paste. Target address book is readonly.") );
2210 		return;
2211 	}
2212 
2213 	pobj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
2214 	if( pobj ) {
2215 		if( pobj->type == ADDR_ITEM_FOLDER ) {
2216 			folder = ADAPTER_FOLDER(pobj)->itemFolder;
2217 		}
2218 		else if( pobj->type == ADDR_ITEM_GROUP ) {
2219 			alertpanel_error( _("Cannot paste into an address group.") );
2220 			return;
2221 		}
2222 	}
2223 
2224 	/* Get an address book */
2225 	abf = addressbook_get_book_file();
2226 	if( abf == NULL ) return;
2227 
2228 	if( _clipBoard_->cutFlag ) {
2229 		/* Paste/Cut */
2230 		folderGroup = addrclip_paste_cut( _clipBoard_, abf, folder );
2231 
2232 		/* Remove all groups and folders in clipboard from tree node */
2233 		addressbook_treenode_remove_item();
2234 
2235 		/* Remove all "cut" items */
2236 		addrclip_delete_item( _clipBoard_ );
2237 
2238 		/* Clear clipboard - cut items??? */
2239 		addrclip_clear( _clipBoard_ );
2240 	}
2241 	else {
2242 		/* Paste/Copy */
2243 		folderGroup = addrclip_paste_copy( _clipBoard_, abf, folder );
2244 	}
2245 
2246 	/* addrclip_list_show( _clipBoard_, stdout ); */
2247 	if( folderGroup ) {
2248 		/* Update tree by inserting node for each folder or group */
2249 		addressbook_treenode_add_list(
2250 			addrbook.treeSelected, ds, folderGroup );
2251 		gtk_cmctree_expand( ctree, addrbook.treeSelected );
2252 		g_list_free( folderGroup );
2253 		folderGroup = NULL;
2254 	}
2255 
2256 	/* Display items pasted */
2257 	gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened );
2258 	addressbook_set_clist(
2259 		gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
2260 			addrbook.opened),
2261 		TRUE);
2262 
2263 
2264 }
2265 
2266 /**
2267  * Add current treenode object to clipboard. Note that widget only allows
2268  * one entry from the tree list to be selected.
2269  */
addressbook_treenode_to_clipboard(void)2270 static void addressbook_treenode_to_clipboard( void ) {
2271 	AddressObject *obj = NULL;
2272 	AddressDataSource *ds = NULL;
2273 	AddrSelectItem *item;
2274 	GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
2275 	GtkCMCTreeNode *node;
2276 
2277 	node = addrbook.treeSelected;
2278 	if( node == NULL ) return;
2279 	obj = gtk_cmctree_node_get_row_data( ctree, node );
2280 	if( obj == NULL ) return;
2281 
2282 	ds = addressbook_find_datasource( node );
2283 	if( ds == NULL ) return;
2284 
2285 	item = NULL;
2286 	if( obj->type == ADDR_ITEM_FOLDER ) {
2287 		AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2288 		ItemFolder *folder = adapter->itemFolder;
2289 
2290 		item = addrselect_create_node( obj );
2291 		item->uid = g_strdup( ADDRITEM_ID(folder) );
2292 	}
2293 	else if( obj->type == ADDR_ITEM_GROUP ) {
2294 		AdapterGroup *adapter = ADAPTER_GROUP(obj);
2295 		ItemGroup *group = adapter->itemGroup;
2296 
2297 		item = addrselect_create_node( obj );
2298 		item->uid = g_strdup( ADDRITEM_ID(group) );
2299 	}
2300 	else if( obj->type == ADDR_DATASOURCE ) {
2301 		/* Data source */
2302 		item = addrselect_create_node( obj );
2303 		item->uid = NULL;
2304 	}
2305 
2306 	if( item ) {
2307 		/* Clear existing list and add item into list */
2308 		gchar *cacheID;
2309 
2310 		addressbook_list_select_clear();
2311 		cacheID = addrindex_get_cache_id( _addressIndex_, ds );
2312 		addrselect_list_add( _addressSelect_, item, cacheID );
2313 		g_free( cacheID );
2314 	}
2315 }
2316 
2317 /**
2318  * Cut from tree widget.
2319  */
addressbook_treenode_cut_cb(GtkAction * action,gpointer data)2320 static void addressbook_treenode_cut_cb( GtkAction *action, gpointer data ) {
2321 	_clipBoard_->cutFlag = TRUE;
2322 	addressbook_treenode_to_clipboard();
2323 	addrclip_clear( _clipBoard_ );
2324 	addrclip_add( _clipBoard_, _addressSelect_ );
2325 	/* addrclip_list_show( _clipBoard_, stdout ); */
2326 }
2327 
2328 /**
2329  * Copy from tree widget.
2330  */
addressbook_treenode_copy_cb(GtkAction * action,gpointer data)2331 static void addressbook_treenode_copy_cb( GtkAction *action, gpointer data ) {
2332 	_clipBoard_->cutFlag = FALSE;
2333 	addressbook_treenode_to_clipboard();
2334 	addrclip_clear( _clipBoard_ );
2335 	addrclip_add( _clipBoard_, _addressSelect_ );
2336 	/* addrclip_list_show( _clipBoard_, stdout ); */
2337 }
2338 
2339 /**
2340  * Paste clipboard into address tree widget.
2341  */
addressbook_treenode_paste_cb(GtkAction * action,gpointer data)2342 static void addressbook_treenode_paste_cb( GtkAction *action, gpointer data ) {
2343 	addressbook_clip_paste_cb(NULL,NULL);
2344 }
2345 
2346 /**
2347  * Clear selected entries in clipboard.
2348  */
addressbook_list_select_clear(void)2349 static void addressbook_list_select_clear( void ) {
2350 	addrselect_list_clear( _addressSelect_ );
2351 }
2352 
2353 /**
2354  * Add specified address item to selected address list.
2355  * \param aio Address item object.
2356  * \param ds  Datasource.
2357  */
addressbook_list_select_add(AddrItemObject * aio,AddressDataSource * ds)2358 static void addressbook_list_select_add( AddrItemObject *aio, AddressDataSource *ds ) {
2359 	gchar *cacheID;
2360 
2361 	if( ds == NULL ) return;
2362 	cacheID = addrindex_get_cache_id( _addressIndex_, ds );
2363 	addrselect_list_add_obj( _addressSelect_, aio, cacheID );
2364 	g_free( cacheID );
2365 }
2366 
2367 /**
2368  * Remove specified address item from selected address list.
2369  * \param aio Address item object.
2370  */
addressbook_list_select_remove(AddrItemObject * aio)2371 static void addressbook_list_select_remove( AddrItemObject *aio ) {
2372 	addrselect_list_remove( _addressSelect_, aio );
2373 }
2374 
2375 /**
2376  * Invoke EMail compose window with addresses in selected address list.
2377  */
addressbook_mail_to_cb(GtkAction * action,gpointer data)2378 static void addressbook_mail_to_cb( GtkAction *action, gpointer data ) {
2379 	GList *listAddress;
2380 
2381 	if( ! addrselect_test_empty( _addressSelect_ ) ) {
2382 		listAddress = addrselect_build_list( _addressSelect_ );
2383 		compose_new_with_list( NULL, listAddress );
2384 		g_list_free_full( listAddress, g_free );
2385 		listAddress = NULL;
2386 	}
2387 }
2388 
addressbook_merge_list(AddrSelectList * list)2389 static void addressbook_merge_list( AddrSelectList *list ) {
2390 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2391 	GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
2392 	AddressObject *pobj;
2393 	AddressDataSource *ds = NULL;
2394 
2395 	pobj = gtk_cmctree_node_get_row_data(ctree, addrbook.opened );
2396 	cm_return_if_fail(pobj != NULL);
2397 
2398 	ds = addressbook_find_datasource( addrbook.treeSelected );
2399 	if( ds == NULL ) return;
2400 
2401 	addrmerge_merge(clist, pobj, ds, list);
2402 }
2403 
2404 /**
2405  * Merge selected entries in the address list
2406  */
addressbook_merge_cb(GtkAction * action,gpointer data)2407 static void addressbook_merge_cb( GtkAction *action, gpointer data ) {
2408 	if( addrselect_test_empty( _addressSelect_ ) )
2409 		return;
2410 
2411 	addressbook_merge_list( _addressSelect_ );
2412 }
2413 
addressbook_list_row_selected(GtkCMCTree * clist,GtkCMCTreeNode * node,gint column,gpointer data)2414 static void addressbook_list_row_selected( GtkCMCTree *clist,
2415 					   GtkCMCTreeNode *node,
2416 					   gint column,
2417 					   gpointer data )
2418 {
2419 	AddrItemObject *aio = NULL;
2420 	AddressObject *pobj = NULL;
2421 	AdapterDSource *ads = NULL;
2422 	AddressDataSource *ds = NULL;
2423 
2424 	addrbook.listSelected = node;
2425 
2426 	pobj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
2427 	if( pobj == NULL ) return;
2428 
2429 	if( pobj->type == ADDR_DATASOURCE ) {
2430 		ads = ADAPTER_DSOURCE(pobj);
2431 		ds = ads->dataSource;
2432 	}
2433 	else if( pobj->type != ADDR_INTERFACE ) {
2434 		ds = addressbook_find_datasource( addrbook.treeSelected );
2435 	}
2436 
2437 	aio = gtk_cmctree_node_get_row_data( clist, node );
2438 	if( aio ) {
2439 		/* g_print( "list select: %d : '%s'\n", aio->type, aio->name ); */
2440 		addressbook_list_select_add( aio, ds );
2441 	}
2442 
2443 	addressbook_list_menu_setup();
2444 
2445 	if (!addrbook.target_compose && !prefs_common.addressbook_use_editaddress_dialog) {
2446 		AddressObject *obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
2447 
2448 		if (obj && obj->type != ADDR_ITEM_GROUP)
2449 			addressbook_edit_address(NULL, 0, NULL, FALSE);
2450 	}
2451 }
2452 
addressbook_list_row_unselected(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,gpointer data)2453 static void addressbook_list_row_unselected( GtkCMCTree *ctree,
2454 					     GtkCMCTreeNode *node,
2455 					     gint column,
2456 					     gpointer data )
2457 {
2458 	AddrItemObject *aio;
2459 
2460 	aio = gtk_cmctree_node_get_row_data( ctree, node );
2461 	if( aio != NULL ) {
2462 		/* g_print( "list unselect: %d : '%s'\n", aio->type, aio->name ); */
2463 		addressbook_list_select_remove( aio );
2464 	}
2465 
2466 	if (!prefs_common.addressbook_use_editaddress_dialog)
2467 		addressbook_edit_person_invalidate(NULL, NULL, NULL);
2468 }
2469 
addressbook_entry_activated(GtkWidget * widget,gpointer data)2470 static void addressbook_entry_activated(GtkWidget *widget, gpointer data)
2471 {
2472 	addressbook_lup_clicked(NULL, NULL);
2473 }
2474 
addressbook_list_button_pressed(GtkWidget * widget,GdkEventButton * event,gpointer data)2475 static gboolean addressbook_list_button_pressed(GtkWidget *widget,
2476 						GdkEventButton *event,
2477 						gpointer data)
2478 {
2479 	if( ! event ) return FALSE;
2480 	if( event->window != GTK_CMCLIST(widget)->clist_window ) return FALSE;
2481 
2482 	addressbook_list_menu_setup();
2483 
2484 	if( event->button == 3 ) {
2485 		gtk_menu_popup( GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
2486 		       event->button, event->time );
2487 	} else if (event->button == 1) {
2488 		if (event->type == GDK_2BUTTON_PRESS) {
2489 			if (prefs_common.add_address_by_click &&
2490 			    addrbook.target_compose)
2491 				addressbook_to_clicked(NULL, GINT_TO_POINTER(COMPOSE_TO));
2492 			else
2493 				if (prefs_common.addressbook_use_editaddress_dialog)
2494 					addressbook_edit_address_cb(NULL, NULL);
2495 				else {
2496 					GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
2497 					AddressObject *obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
2498 					if( obj && obj->type == ADDR_ITEM_GROUP )
2499 						addressbook_edit_address_cb(NULL, NULL);
2500 				}
2501 		}
2502 	}
2503 
2504 	return FALSE;
2505 }
2506 
addressbook_list_button_released(GtkWidget * widget,GdkEventButton * event,gpointer data)2507 static gboolean addressbook_list_button_released(GtkWidget *widget,
2508 						 GdkEventButton *event,
2509 						 gpointer data)
2510 {
2511 	return FALSE;
2512 }
2513 
addressbook_tree_button_pressed(GtkWidget * ctree,GdkEventButton * event,gpointer data)2514 static gboolean addressbook_tree_button_pressed(GtkWidget *ctree,
2515 						GdkEventButton *event,
2516 						gpointer data)
2517 {
2518 	GtkCMCList *clist = GTK_CMCLIST(ctree);
2519 	gint row, column;
2520 	AddressObject *obj = NULL;
2521 	AdapterDSource *ads = NULL;
2522 	AddressInterface *iface = NULL;
2523 	AddressDataSource *ds = NULL;
2524 	gboolean canEdit = FALSE;
2525 	gboolean canDelete = FALSE;
2526 	gboolean canCut = FALSE;
2527 	gboolean canCopy = FALSE;
2528 	gboolean canPaste = FALSE;
2529 	gboolean canTreeCut = FALSE;
2530 	gboolean canTreeCopy = FALSE;
2531 	gboolean canTreePaste = FALSE;
2532 	gboolean canLookup = FALSE;
2533 	GtkCMCTreeNode *node = NULL;
2534 
2535 	if( ! event ) return FALSE;
2536 /*	if( ! event || event->type != GDK_BUTTON_PRESS) return FALSE;*/
2537 
2538 	if( event->window != clist->clist_window )
2539 		return FALSE;
2540 
2541 	if (event->button == 1) {
2542 		if (event->type == GDK_2BUTTON_PRESS) {
2543 			if( gtk_cmclist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
2544 				gtkut_clist_set_focus_row(clist, row);
2545 				obj = gtk_cmclist_get_row_data( clist, row );
2546 			}
2547 			if( obj == NULL )
2548 				return FALSE;
2549 
2550 			if (obj->type == ADDR_ITEM_GROUP ||
2551 					obj->type == ADDR_DATASOURCE) {
2552 				/* edit group */
2553 				addressbook_treenode_edit_cb(NULL, NULL);
2554 			} else {
2555 				/* expand pr collapse */
2556 				node = gtk_cmctree_node_nth(GTK_CMCTREE(ctree), row);
2557 				gtk_cmctree_toggle_expansion(GTK_CMCTREE(ctree), node);
2558 			}
2559 			return FALSE;
2560 		}
2561 	}
2562 
2563 	addressbook_menubar_set_sensitive( FALSE );
2564 
2565 	if( gtk_cmclist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
2566 		gtkut_clist_set_focus_row(clist, row);
2567 		obj = gtk_cmclist_get_row_data( clist, row );
2568 	}
2569 
2570 	menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
2571 
2572 	if( obj == NULL )
2573 		return FALSE;
2574 	node = gtk_cmctree_node_nth(GTK_CMCTREE(clist), row);
2575 
2576 	if( ! addrclip_is_empty( _clipBoard_ ) )
2577 		canTreePaste = TRUE;
2578 
2579 	if (obj->type == ADDR_INTERFACE) {
2580 		AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
2581 		iface = adapter->interface;
2582 		if( !iface )
2583 			goto just_set_sens;
2584 		if( !iface->readOnly && iface->type == ADDR_IF_BOOK) {
2585 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewBook", TRUE );
2586 		}
2587 		if( iface->externalQuery )
2588 			canLookup = TRUE;
2589 	}
2590 	if (obj->type == ADDR_DATASOURCE) {
2591 		canLookup = TRUE;
2592 		ads = ADAPTER_DSOURCE(obj);
2593 		ds = ads->dataSource;
2594 		if( !ds )
2595 			goto just_set_sens;
2596 		iface = ds->interface;
2597 		if( !iface )
2598 			goto just_set_sens;
2599 		if( !iface->readOnly ) {
2600 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewFolder", TRUE );
2601 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewGroup", TRUE );
2602 			gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2603 		}
2604 		canDelete = TRUE;
2605 		canEdit = TRUE;
2606 		canTreeCopy = TRUE;
2607 	}
2608 	else if (obj->type == ADDR_ITEM_FOLDER) {
2609 		canLookup = TRUE;
2610 		ds = addressbook_find_datasource( node );
2611 		if( !ds )
2612 			goto just_set_sens;
2613 		iface = ds->interface;
2614 		if( !iface )
2615 			goto just_set_sens;
2616 		if( !iface->readOnly ) {
2617 			canEdit = TRUE;
2618 			canDelete = TRUE;
2619 			canTreeCut = TRUE;
2620 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewFolder", TRUE );
2621 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewGroup", TRUE );
2622 			gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2623 		}
2624 		canTreeCopy = TRUE;
2625 
2626 		if( iface->externalQuery ) {
2627 			/* Enable deletion of LDAP folder */
2628 			canDelete = TRUE;
2629 		}
2630 	}
2631 	else if (obj->type == ADDR_ITEM_GROUP) {
2632 		canLookup = TRUE;
2633 		ds = addressbook_find_datasource( node );
2634 		if( !ds )
2635 			goto just_set_sens;
2636 		iface = ds->interface;
2637 		if( !iface )
2638 			goto just_set_sens;
2639 		if( ! iface->readOnly ) {
2640 			canEdit = TRUE;
2641 			canDelete = TRUE;
2642 			cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewAddress", TRUE );
2643 			gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2644 		}
2645 	}
2646 
2647 	if( canEdit && !addrselect_test_empty( _addressSelect_ ) )
2648 		canCut = TRUE;
2649 	if( ! addrselect_test_empty( _addressSelect_ ) )
2650 		canCopy = TRUE;
2651 	if( ! addrclip_is_empty( _clipBoard_ ) )
2652 		canPaste = TRUE;
2653 
2654 	/* Forbid write changes when read-only */
2655 	if( iface && iface->readOnly ) {
2656 		canTreeCut = FALSE;
2657 		canTreePaste = FALSE;
2658 		canCut = FALSE;
2659 		canPaste = FALSE;
2660 	}
2661 
2662 just_set_sens:
2663 	/* Enable edit */
2664 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/EditBook",   canEdit );
2665 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/DeleteBook", canDelete );
2666 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/Cut",    canTreeCut );
2667 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/Copy",   canTreeCopy );
2668 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/Paste",  canTreePaste );
2669 
2670 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook",          canEdit );
2671 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook",        canDelete );
2672 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Cut",           canCut );
2673 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Copy",          canCopy );
2674 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Paste",         canPaste );
2675 
2676 	addressbook_show_buttons(addrbook.target_compose == NULL, canLookup,
2677 			addrbook.target_compose != NULL);
2678 
2679 	if( event->button == 3 )
2680 		gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
2681 			       event->button, event->time);
2682 
2683 	return FALSE;
2684 }
2685 
addressbook_tree_button_released(GtkWidget * ctree,GdkEventButton * event,gpointer data)2686 static gboolean addressbook_tree_button_released(GtkWidget *ctree,
2687 						 GdkEventButton *event,
2688 						 gpointer data)
2689 {
2690 	gtkut_ctree_set_focus_row(GTK_CMCTREE(addrbook.ctree), addrbook.opened);
2691 	return FALSE;
2692 }
2693 
addressbook_new_folder_cb(GtkAction * action,gpointer data)2694 static void addressbook_new_folder_cb(GtkAction *action, gpointer data)
2695 {
2696 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2697 	AddressObject *obj = NULL;
2698 	AddressDataSource *ds = NULL;
2699 	AddressBookFile *abf = NULL;
2700 	ItemFolder *parentFolder = NULL;
2701 	ItemFolder *folder = NULL;
2702 
2703 	if( ! addrbook.treeSelected ) return;
2704 	obj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
2705 	if( obj == NULL ) return;
2706 	ds = addressbook_find_datasource( addrbook.treeSelected );
2707 	if( ds == NULL ) return;
2708 
2709 	if( obj->type == ADDR_DATASOURCE ) {
2710 		if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
2711 	}
2712 	else if( obj->type == ADDR_ITEM_FOLDER ) {
2713 		parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
2714 	}
2715 	else {
2716 		return;
2717 	}
2718 
2719 	abf = ds->rawDataSource;
2720 	if( abf == NULL ) return;
2721 	folder = addressbook_edit_folder( abf, parentFolder, NULL );
2722 	if( folder ) {
2723 		GtkCMCTreeNode *nn;
2724 		nn = addressbook_node_add_folder(
2725 			addrbook.treeSelected, ds, folder, ADDR_ITEM_FOLDER );
2726 		if (nn == NULL) {
2727 			g_message("error adding addressbook folder\n");
2728 		}
2729 		gtk_cmctree_expand( ctree, addrbook.treeSelected );
2730 		if( addrbook.treeSelected == addrbook.opened )
2731 			addressbook_set_clist(obj, TRUE);
2732 	}
2733 }
2734 
addressbook_new_group_cb(GtkAction * action,gpointer data)2735 static void addressbook_new_group_cb(GtkAction *action, gpointer data)
2736 {
2737 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2738 	AddressObject *obj = NULL;
2739 	AddressDataSource *ds = NULL;
2740 	AddressBookFile *abf = NULL;
2741 	ItemFolder *parentFolder = NULL;
2742 	ItemGroup *group = NULL;
2743 
2744 	if( ! addrbook.treeSelected ) return;
2745 	obj = gtk_cmctree_node_get_row_data(ctree, addrbook.treeSelected);
2746 	if( obj == NULL ) return;
2747 	ds = addressbook_find_datasource( addrbook.treeSelected );
2748 	if( ds == NULL ) return;
2749 
2750 	if( obj->type == ADDR_DATASOURCE ) {
2751 		if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
2752 	}
2753 	else if( obj->type == ADDR_ITEM_FOLDER ) {
2754 		parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
2755 	}
2756 	else {
2757 		return;
2758 	}
2759 
2760 	abf = ds->rawDataSource;
2761 	if( abf == NULL ) return;
2762 	group = addressbook_edit_group( abf, parentFolder, NULL );
2763 	if( group ) {
2764 		GtkCMCTreeNode *nn;
2765 		nn = addressbook_node_add_group( addrbook.treeSelected, ds, group );
2766 		if (nn == NULL) {
2767 			g_message("error adding addressbook group\n");
2768 		}
2769 		gtk_cmctree_expand( ctree, addrbook.treeSelected );
2770 		if( addrbook.treeSelected == addrbook.opened )
2771 			addressbook_set_clist(obj, TRUE);
2772 	}
2773 }
2774 
addressbook_change_node_name(GtkCMCTreeNode * node,const gchar * name)2775 static void addressbook_change_node_name(GtkCMCTreeNode *node, const gchar *name)
2776 {
2777 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2778 	gchar *text[1];
2779 	guint8 spacing;
2780 	GdkPixbuf *pix_cl, *pix_op;
2781 	gboolean is_leaf, expanded;
2782 
2783 	gtk_cmctree_get_node_info(ctree, node, text, &spacing,
2784 				&pix_cl, &pix_op,
2785 				&is_leaf, &expanded);
2786 	gtk_cmctree_set_node_info(ctree, node, name, spacing,
2787 				pix_cl, pix_op,
2788 				is_leaf, expanded);
2789 }
2790 
2791 /**
2792  * Edit data source.
2793  * \param obj  Address object to edit.
2794  * \param node Node in tree.
2795  * \return New name of data source.
2796  */
addressbook_edit_datasource(AddressObject * obj,GtkCMCTreeNode * node)2797 static gchar *addressbook_edit_datasource( AddressObject *obj, GtkCMCTreeNode *node ) {
2798 	gchar *newName = NULL;
2799 	AddressDataSource *ds = NULL;
2800 	AddressInterface *iface = NULL;
2801 	AdapterDSource *ads = NULL;
2802 
2803 	ds = addressbook_find_datasource( node );
2804 	if( ds == NULL ) return NULL;
2805 	iface = ds->interface;
2806 	if( ! iface->haveLibrary ) return NULL;
2807 
2808 	/* Read data from data source */
2809 	if( addrindex_ds_get_modify_flag( ds ) ) {
2810 		addrindex_ds_read_data( ds );
2811 	}
2812 
2813 	if( ! addrindex_ds_get_read_flag( ds ) ) {
2814 		addrindex_ds_read_data( ds );
2815 	}
2816 
2817 	/* Handle edit */
2818 	ads = ADAPTER_DSOURCE(obj);
2819 	if( ads->subType == ADDR_BOOK ) {
2820                 if( addressbook_edit_book( _addressIndex_, ads ) == NULL ) return NULL;
2821 	}
2822 	else if( ads->subType == ADDR_VCARD ) {
2823        	        if( addressbook_edit_vcard( _addressIndex_, ads ) == NULL ) return NULL;
2824 	}
2825 #ifdef USE_JPILOT
2826 	else if( ads->subType == ADDR_JPILOT ) {
2827                 if( addressbook_edit_jpilot( _addressIndex_, ads ) == NULL ) return NULL;
2828 	}
2829 #endif
2830 #ifdef USE_LDAP
2831 	else if( ads->subType == ADDR_LDAP ) {
2832 		if( addressbook_edit_ldap( _addressIndex_, ads ) == NULL ) return NULL;
2833 	}
2834 #endif
2835 	else {
2836 		return NULL;
2837 	}
2838 	newName = obj->name;
2839 	return newName;
2840 }
2841 
2842 /*
2843 * Edit an object that is in the address tree area.
2844 */
addressbook_treenode_edit_cb(GtkAction * action,gpointer data)2845 static void addressbook_treenode_edit_cb(GtkAction *action, gpointer data)
2846 {
2847 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2848 	AddressObject *obj;
2849 	AddressDataSource *ds = NULL;
2850 	AddressBookFile *abf = NULL;
2851 	GtkCMCTreeNode *node = NULL, *parentNode = NULL;
2852 	gchar *name = NULL;
2853 
2854 	if( ! addrbook.treeSelected ) return;
2855 	node = addrbook.treeSelected;
2856 	if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
2857 	obj = gtk_cmctree_node_get_row_data( ctree, node );
2858 	if( obj == NULL ) return;
2859 	parentNode = GTK_CMCTREE_ROW(node)->parent;
2860 
2861 	ds = addressbook_find_datasource( node );
2862 	if( ds == NULL ) return;
2863 
2864 	if( obj->type == ADDR_DATASOURCE ) {
2865 		name = addressbook_edit_datasource( obj, node );
2866 		if( name == NULL ) return;
2867 	}
2868 	else {
2869 		abf = ds->rawDataSource;
2870 		if( abf == NULL ) return;
2871 		if( obj->type == ADDR_ITEM_FOLDER ) {
2872 			AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2873 			ItemFolder *item = adapter->itemFolder;
2874 			ItemFolder *parentFolder = NULL;
2875 			parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
2876 			if( addressbook_edit_folder( abf, parentFolder, item ) == NULL ) return;
2877 			name = ADDRITEM_NAME(item);
2878 		}
2879 		else if( obj->type == ADDR_ITEM_GROUP ) {
2880 			AdapterGroup *adapter = ADAPTER_GROUP(obj);
2881 			ItemGroup *item = adapter->itemGroup;
2882 			ItemFolder *parentFolder = NULL;
2883 			parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
2884 			if( addressbook_edit_group( abf, parentFolder, item ) == NULL ) return;
2885 			name = ADDRITEM_NAME(item);
2886 		}
2887 	}
2888 	if( name && parentNode ) {
2889 		/* Update node in tree view */
2890 		addressbook_change_node_name( node, name );
2891 		gtk_sctree_sort_node(ctree, parentNode);
2892 		gtk_cmctree_expand( ctree, node );
2893 		gtk_sctree_select( GTK_SCTREE( ctree), node );
2894 	}
2895 }
2896 
2897 typedef enum {
2898 	ADDRTREE_DEL_NONE,
2899 	ADDRTREE_DEL_DATA,
2900 	ADDRTREE_DEL_FOLDER_ONLY,
2901 	ADDRTREE_DEL_FOLDER_ADDR
2902 } TreeItemDelType ;
2903 
2904 /**
2905  * Delete an item from the tree widget.
2906  * \param data   Data passed in.
2907  * \param action Action.
2908  * \param widget Widget issuing callback.
2909  */
addressbook_treenode_delete_cb(GtkAction * action,gpointer data)2910 static void addressbook_treenode_delete_cb(GtkAction *action, gpointer data)
2911 {
2912 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2913 	GtkCMCTreeNode *node = NULL;
2914 	AddressObject *obj;
2915 	gchar *message;
2916 	AlertValue aval;
2917 	AddrBookBase *adbase;
2918 	AddressCache *cache;
2919 	AdapterDSource *ads = NULL;
2920 	AddressInterface *iface = NULL;
2921 	AddressDataSource *ds = NULL;
2922 	gboolean remFlag = FALSE;
2923 	TreeItemDelType delType;
2924 
2925 	if( ! addrbook.treeSelected ) return;
2926 	node = addrbook.treeSelected;
2927 	if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
2928 
2929 	obj = gtk_cmctree_node_get_row_data( ctree, node );
2930 	cm_return_if_fail(obj != NULL);
2931 
2932 	if( obj->type == ADDR_DATASOURCE ) {
2933 		ads = ADAPTER_DSOURCE(obj);
2934 
2935 		ds = ads->dataSource;
2936 		if( ds == NULL ) return;
2937 	}
2938 	else {
2939 		/* Must be folder or something else */
2940 		ds = addressbook_find_datasource( node );
2941 		if( ds == NULL ) return;
2942 
2943 		/* Only allow deletion from non-readOnly */
2944 		iface = ds->interface;
2945 		if( iface->readOnly ) {
2946 			/* Allow deletion of query results */
2947 			if( ! iface->externalQuery ) return;
2948 		}
2949 	}
2950 
2951 	/* Confirm deletion */
2952 	delType = ADDRTREE_DEL_NONE;
2953 	if( obj->type == ADDR_ITEM_FOLDER ) {
2954 		if( iface && iface->externalQuery ) {
2955 			message = g_strdup_printf( _(
2956 				"Do you want to delete the query " \
2957 				"results and addresses in '%s'?" ),
2958 				obj->name );
2959 			aval = alertpanel( _("Delete"), message,
2960 				GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, ALERTFOCUS_SECOND );
2961 			g_free(message);
2962 			if( aval == G_ALERTALTERNATE ) {
2963 				delType = ADDRTREE_DEL_FOLDER_ADDR;
2964 			}
2965 		}
2966 		else {
2967 			message = g_strdup_printf
2968 				( _( "Do you want to delete '%s'? "
2969 			    	     "If you delete the folder only, the addresses it contains will be moved into the parent folder." ),
2970 			 	 obj->name );
2971 			aval = alertpanel( _("Delete folder"), message,
2972 				GTK_STOCK_CANCEL, _("Delete _folder only"), _("Delete folder and _addresses"), ALERTFOCUS_SECOND);
2973 			g_free(message);
2974 			if( aval == G_ALERTALTERNATE ) {
2975 				delType = ADDRTREE_DEL_FOLDER_ONLY;
2976 			}
2977 			else if( aval == G_ALERTOTHER ) {
2978 				delType = ADDRTREE_DEL_FOLDER_ADDR;
2979 			}
2980 		}
2981 	}
2982 	else if( obj->type == ADDR_ITEM_GROUP ) {
2983 		message = g_strdup_printf(_("Do you want to delete '%s'?\n"
2984 					    "The addresses it contains will not be lost."), obj->name);
2985 		aval = alertpanel(_("Delete"), message, GTK_STOCK_CANCEL,
2986 				GTK_STOCK_DELETE, NULL, ALERTFOCUS_SECOND);
2987 		g_free(message);
2988 		if( aval == G_ALERTALTERNATE ) delType = ADDRTREE_DEL_FOLDER_ONLY;
2989 	} else {
2990 		message = g_strdup_printf(_("Do you want to delete '%s'?\n"
2991 					    "The addresses it contains will be lost."), obj->name);
2992 		aval = alertpanel(_("Delete"), message, GTK_STOCK_CANCEL,
2993 				GTK_STOCK_DELETE, NULL, ALERTFOCUS_SECOND);
2994 		g_free(message);
2995 		if( aval == G_ALERTALTERNATE ) delType = ADDRTREE_DEL_DATA;
2996 	}
2997 	if( delType == ADDRTREE_DEL_NONE ) return;
2998 
2999 	/* Proceed with deletion */
3000 	if( obj->type == ADDR_DATASOURCE ) {
3001 		/* Remove node from tree */
3002 		gtk_cmctree_remove_node( ctree, node );
3003 
3004 		if (delType == ADDRTREE_DEL_DATA &&
3005 		    ds->interface && ds->interface->type == ADDR_IF_BOOK)
3006 			addrbook_delete_book_file((AddressBookFile *) ds->rawDataSource);
3007 
3008 		/* Remove data source. */
3009 		if( addrindex_index_remove_datasource( _addressIndex_, ds ) ) {
3010 			addrindex_free_datasource( ds );
3011 		}
3012 		return;
3013 	}
3014 
3015 	/* Get reference to cache */
3016 	adbase = ( AddrBookBase * ) ds->rawDataSource;
3017 	if( adbase == NULL ) return;
3018 	cache = adbase->addressCache;
3019 
3020 	/* Remove query results folder */
3021 	if( iface && iface->externalQuery ) {
3022 		AdapterFolder *adapter = ADAPTER_FOLDER(obj);
3023 		ItemFolder *folder = adapter->itemFolder;
3024 
3025 		adapter->itemFolder = NULL;
3026 		/*
3027 		g_print( "remove folder for ::%s::\n", obj->name );
3028 		g_print( "      folder name ::%s::\n", ADDRITEM_NAME(folder) );
3029 		g_print( "-------------- remove results\n" );
3030 		*/
3031 		addrindex_remove_results( ds, folder );
3032 		/* g_print( "-------------- remove node\n" ); */
3033 		gtk_cmctree_remove_node( ctree, node );
3034 		return;
3035 	}
3036 
3037 	/* Code below is valid for regular address book deletion */
3038 	if( obj->type == ADDR_ITEM_FOLDER ) {
3039 		AdapterFolder *adapter = ADAPTER_FOLDER(obj);
3040 		ItemFolder *item = adapter->itemFolder;
3041 
3042 		if( delType == ADDRTREE_DEL_FOLDER_ONLY ) {
3043 			/* Remove folder only */
3044 			item = addrcache_remove_folder( cache, item );
3045 			if( item ) {
3046 				addritem_free_item_folder( item );
3047 				addressbook_move_nodes_up( ctree, node );
3048 				remFlag = TRUE;
3049 			}
3050 		}
3051 		else if( delType == ADDRTREE_DEL_FOLDER_ADDR ) {
3052 			/* Remove folder and addresses */
3053 			item = addrcache_remove_folder_delete( cache, item );
3054 			if( item ) {
3055 				addritem_free_item_folder( item );
3056 				remFlag = TRUE;
3057 			}
3058 		}
3059 	}
3060 	else if( obj->type == ADDR_ITEM_GROUP ) {
3061 		AdapterGroup *adapter = ADAPTER_GROUP(obj);
3062 		ItemGroup *item = adapter->itemGroup;
3063 
3064 		item = addrcache_remove_group( cache, item );
3065 		if( item ) {
3066 			addritem_free_item_group( item );
3067 			remFlag = TRUE;
3068 		}
3069 	}
3070 
3071 	if( remFlag ) {
3072 		/* Remove node. */
3073 		gtk_cmctree_remove_node(ctree, node );
3074 	}
3075 }
3076 
addressbook_new_address_from_book_post_cb(ItemPerson * person)3077 static void addressbook_new_address_from_book_post_cb( ItemPerson *person )
3078 {
3079 	if( person && addrbook.treeSelected == addrbook.opened ) {
3080 		person->status = ADD_ENTRY;
3081 		gtk_cmclist_unselect_all( GTK_CMCLIST(addrbook.clist) );
3082 		addressbook_folder_refresh_one_person(
3083 			GTK_CMCTREE(addrbook.clist), person );
3084 	}
3085 	addressbook_address_list_set_focus();
3086 }
3087 
addressbook_new_address_from_folder_post_cb(ItemPerson * person)3088 static void addressbook_new_address_from_folder_post_cb( ItemPerson *person )
3089 {
3090 	if( person && addrbook.treeSelected == addrbook.opened) {
3091 		person->status = ADD_ENTRY;
3092 		gtk_sctree_select( GTK_SCTREE(addrbook.ctree), addrbook.opened );
3093 		addressbook_set_clist(
3094 			gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
3095 				addrbook.opened),
3096 			TRUE);
3097 	}
3098 	addressbook_address_list_set_focus();
3099 }
3100 
3101 /**
3102  * Label (a format string) that is used to name each folder.
3103  */
3104 static gchar *_queryFolderLabel_ = N_( "Search '%s'" );
3105 
3106 /**
3107  * Search ctree widget callback function.
3108  * \param  pA Pointer to node.
3109  * \param  pB Pointer to data item being sought.
3110  * \return Zero (0) if folder found.
3111  */
addressbook_treenode_find_folder_cb(gconstpointer pA,gconstpointer pB)3112 static int addressbook_treenode_find_folder_cb( gconstpointer pA, gconstpointer pB ) {
3113 	AddressObject *aoA;
3114 
3115 	aoA = ( AddressObject * ) pA;
3116 	if( aoA->type == ADDR_ITEM_FOLDER ) {
3117 		ItemFolder *folder, *fld;
3118 
3119 		fld = ADAPTER_FOLDER(aoA)->itemFolder;
3120 		folder = ( ItemFolder * ) pB;
3121 		if( fld == folder ) return 0;	/* Found folder */
3122 	}
3123 	return 1;
3124 }
3125 
addressbook_setup_subf(AddressDataSource * ds,gchar * title,GtkCMCTreeNode * pNode)3126 static ItemFolder * addressbook_setup_subf(
3127 		AddressDataSource *ds, gchar *title,
3128 		GtkCMCTreeNode *pNode )
3129 {
3130 	AddrBookBase *adbase;
3131 	AddressCache *cache;
3132 	ItemFolder *folder;
3133 	GtkCMCTree *ctree;
3134 	GtkCMCTreeNode *nNode;
3135 	gchar *name;
3136 	AddressObjectType aoType = ADDR_NONE;
3137 	GList *children;
3138 	/* Setup a query */
3139 	if( *title == '\0' || strlen( title ) < 1 ) return NULL;
3140 
3141 	if( ds && ds->type == ADDR_IF_LDAP ) {
3142 #if USE_LDAP
3143 		aoType = ADDR_LDAP_QUERY;
3144 #endif
3145 	}
3146 	else {
3147 		return NULL;
3148 	}
3149 
3150 	ctree = GTK_CMCTREE(addrbook.ctree);
3151 	/* Get reference to address cache */
3152 	adbase = ( AddrBookBase * ) ds->rawDataSource;
3153 	cache = adbase->addressCache;
3154 
3155 	if ((children = addrcache_get_list_folder(cache)) != NULL) {
3156 		GList *cur = children;
3157 		for (; cur; cur = cur->next) {
3158 			ItemFolder *child = (ItemFolder *) cur->data;
3159 			if (!g_strcmp0(ADDRITEM_NAME(child), title)) {
3160 				nNode = gtk_cmctree_find_by_row_data_custom(
3161 					ctree, NULL, child,
3162 					addressbook_treenode_find_folder_cb );
3163 				if( nNode ) {
3164 					addrindex_remove_results( ds, child );
3165 					while( child->listPerson ) {
3166 						ItemPerson *item = ( ItemPerson * ) child->listPerson->data;
3167 						item = addrcache_remove_person( cache, item );
3168 						if( item ) {
3169 							addritem_free_item_person( item );
3170 							item = NULL;
3171 						}
3172 					}
3173 					gtk_sctree_select( GTK_SCTREE(ctree), nNode );
3174 					addrbook.treeSelected = nNode;
3175 				}
3176 				return child;
3177 			}
3178 		}
3179 	}
3180 
3181 	/* Create a folder */
3182 	folder = addrcache_add_new_folder( cache, NULL );
3183 	name = g_strdup_printf( "%s", title );
3184 	addritem_folder_set_name( folder, name );
3185 	addritem_folder_set_remarks( folder, "" );
3186 	g_free( name );
3187 
3188 	/* Now let's see the folder */
3189 	nNode = addressbook_node_add_folder( pNode, ds, folder, aoType );
3190 	gtk_cmctree_expand( ctree, pNode );
3191 	if( nNode ) {
3192 		gtk_sctree_select( GTK_SCTREE(ctree), nNode );
3193 		addrbook.treeSelected = nNode;
3194 		return folder;
3195 	}
3196 	return NULL;
3197 }
3198 
addressbook_new_address_cb(GtkAction * action,gpointer data)3199 static void addressbook_new_address_cb( GtkAction *action, gpointer data ) {
3200 	AddressObject *pobj = NULL;
3201 	AddressDataSource *ds = NULL;
3202 	AddressBookFile *abf = NULL;
3203 	debug_print("adding address\n");
3204 	pobj = gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected);
3205 	if( pobj == NULL ) {
3206 		debug_print("no row data\n");
3207 		return;
3208 	}
3209 	ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
3210 	if( ds == NULL ) {
3211 		debug_print("no datasource\n");
3212 		return;
3213 	}
3214 
3215 	abf = ds->rawDataSource;
3216 	if( abf == NULL ) {
3217 		g_print("no addressbook file\n");
3218 		return;
3219 	}
3220 
3221 	if( pobj->type == ADDR_DATASOURCE ) {
3222 		if (ADAPTER_DSOURCE(pobj)->subType == ADDR_BOOK ||
3223 		    ADAPTER_DSOURCE(pobj)->subType == ADDR_LDAP) {
3224 			ItemPerson *person;
3225 			ItemFolder *folder = NULL;
3226 #ifdef USE_LDAP
3227 			if (abf && abf->type == ADBOOKTYPE_LDAP) {
3228 				GtkCMCTreeNode *parentNode;
3229 				ds = addressbook_find_datasource( GTK_CMCTREE_NODE( addrbook.treeSelected ) );
3230 				if( ds == NULL ) return;
3231 
3232 				/* We must have a datasource that is an external interface */
3233 				if( ! ds->interface->haveLibrary ) return;
3234 				if( ! ds->interface->externalQuery ) return;
3235 
3236 				if( pobj->type == ADDR_ITEM_FOLDER ) {
3237 					parentNode = GTK_CMCTREE_ROW(GTK_CMCTREE_NODE( addrbook.treeSelected ) )->parent;
3238 				}
3239 				else {
3240 					parentNode = GTK_CMCTREE_NODE( addrbook.treeSelected );
3241 				}
3242 				folder = addressbook_setup_subf( ds, _("New Contacts"), parentNode );
3243 
3244 				ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
3245 				if (ds)
3246 					abf = ds->rawDataSource;
3247 			}
3248 #endif
3249 			person = addressbook_edit_person( abf, folder, NULL, FALSE,
3250 								  addrbook.editaddress_vbox,
3251 								  addressbook_new_address_from_book_post_cb,
3252 								  TRUE );
3253 #ifdef USE_LDAP
3254 			if (ds && abf && abf->type == ADBOOKTYPE_LDAP) {
3255 				LdapServer *server = ds->rawDataSource;
3256 				ldapsvr_set_modified(server, TRUE);
3257 				ldapsvr_update_book(server, NULL);
3258 				if (server->retVal != LDAPRC_SUCCESS) {
3259 					alertpanel( _("Add address(es)"),
3260 						addressbook_err2string(_lutErrorsLDAP_, server->retVal),
3261 						GTK_STOCK_CLOSE, NULL, NULL, ALERTFOCUS_FIRST );
3262 					server->retVal = LDAPRC_SUCCESS;
3263 					return;
3264 				}
3265 			}
3266 #endif
3267 			if (prefs_common.addressbook_use_editaddress_dialog)
3268 				addressbook_new_address_from_book_post_cb( person );
3269 		}
3270 	}
3271 	else if( pobj->type == ADDR_ITEM_FOLDER ) {
3272 		/* New address */
3273 		ItemFolder *folder = ADAPTER_FOLDER(pobj)->itemFolder;
3274 		ItemPerson *person;
3275 #ifdef USE_LDAP
3276 		if (abf && abf->type == ADBOOKTYPE_LDAP) {
3277 			GtkCMCTreeNode *parentNode;
3278 			ds = addressbook_find_datasource( GTK_CMCTREE_NODE( addrbook.treeSelected ) );
3279 			if( ds == NULL ) return;
3280 
3281 			/* We must have a datasource that is an external interface */
3282 			if( ! ds->interface->haveLibrary ) return;
3283 			if( ! ds->interface->externalQuery ) return;
3284 
3285 			if( pobj->type == ADDR_ITEM_FOLDER ) {
3286 				parentNode = GTK_CMCTREE_ROW(GTK_CMCTREE_NODE( addrbook.treeSelected ) )->parent;
3287 			}
3288 			else {
3289 				parentNode = GTK_CMCTREE_NODE( addrbook.treeSelected );
3290 			}
3291 			folder = addressbook_setup_subf( ds, _("New Contacts"), parentNode );
3292 			if (!folder)
3293 				return;
3294 
3295 			ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
3296 			if (ds)
3297 				abf = ds->rawDataSource;
3298 		}
3299 #endif
3300 		person = addressbook_edit_person( abf, folder, NULL, FALSE,
3301 							  addrbook.editaddress_vbox,
3302 							  addressbook_new_address_from_folder_post_cb,
3303 							  TRUE );
3304 #ifdef USE_LDAP
3305 		if (ds && abf && abf->type == ADBOOKTYPE_LDAP) {
3306 			LdapServer *server = ds->rawDataSource;
3307 			ldapsvr_set_modified(server, TRUE);
3308 			ldapsvr_update_book(server, NULL);
3309 			if (server->retVal != LDAPRC_SUCCESS) {
3310 				alertpanel( _("Add address(es)"),
3311 						addressbook_err2string(_lutErrorsLDAP_, server->retVal),
3312 					GTK_STOCK_CLOSE, NULL, NULL, ALERTFOCUS_FIRST );
3313 				return;
3314 			}
3315 		}
3316 #endif
3317 		if (prefs_common.addressbook_use_editaddress_dialog)
3318 			addressbook_new_address_from_folder_post_cb( person );
3319 	}
3320 	else if( pobj->type == ADDR_ITEM_GROUP ) {
3321 		/* New address in group */
3322 		ItemGroup *group = ADAPTER_GROUP(pobj)->itemGroup;
3323 		if( addressbook_edit_group( abf, NULL, group ) == NULL ) return;
3324 		if (addrbook.treeSelected == addrbook.opened) {
3325 			/* Change node name in tree. */
3326 			addressbook_change_node_name( addrbook.treeSelected, ADDRITEM_NAME(group) );
3327 			gtk_sctree_select( GTK_SCTREE(addrbook.ctree), addrbook.opened );
3328 			addressbook_set_clist(
3329 				gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
3330 					addrbook.opened),
3331 				TRUE);
3332 		}
3333 	}
3334 }
3335 
3336 /**
3337  * Search for specified child group node in address index tree.
3338  * \param parent Parent node.
3339  * \param group  Group to find.
3340  */
addressbook_find_group_node(GtkCMCTreeNode * parent,ItemGroup * group)3341 static GtkCMCTreeNode *addressbook_find_group_node( GtkCMCTreeNode *parent, ItemGroup *group ) {
3342 	GtkCMCTreeNode *node = NULL;
3343 	GtkCMCTreeRow *currRow;
3344 
3345 	currRow = GTK_CMCTREE_ROW( parent );
3346 	if( currRow ) {
3347 		node = currRow->children;
3348 		while( node ) {
3349 			AddressObject *obj;
3350 
3351 			obj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), node );
3352 			if(obj && obj->type == ADDR_ITEM_GROUP ) {
3353 				ItemGroup *g = ADAPTER_GROUP(obj)->itemGroup;
3354 				if( g == group ) return node;
3355 			}
3356 			currRow = GTK_CMCTREE_ROW(node);
3357 			node = currRow->sibling;
3358 		}
3359 	}
3360 	return NULL;
3361 }
3362 
addressbook_get_book_file()3363 static AddressBookFile *addressbook_get_book_file() {
3364 	AddressBookFile *abf = NULL;
3365 	AddressDataSource *ds = NULL;
3366 
3367 	ds = addressbook_find_datasource( addrbook.treeSelected );
3368 	if( ds == NULL ) return NULL;
3369 	if( ds->type == ADDR_IF_BOOK || ds->type == ADDR_IF_LDAP ) abf = ds->rawDataSource;
3370 	return abf;
3371 }
3372 
addressbook_tree_remove_children(GtkCMCTree * ctree,GtkCMCTreeNode * parent)3373 static void addressbook_tree_remove_children( GtkCMCTree *ctree, GtkCMCTreeNode *parent ) {
3374 	GtkCMCTreeNode *node;
3375 	GtkCMCTreeRow *row;
3376 
3377 	/* Remove existing folders and groups */
3378 	row = GTK_CMCTREE_ROW( parent );
3379 	if( row ) {
3380 		while( (node = row->children) ) {
3381 			gtk_cmctree_remove_node( ctree, node );
3382 		}
3383 	}
3384 }
3385 
addressbook_move_nodes_up(GtkCMCTree * ctree,GtkCMCTreeNode * node)3386 static void addressbook_move_nodes_up( GtkCMCTree *ctree, GtkCMCTreeNode *node ) {
3387 	GtkCMCTreeNode *parent, *child;
3388 	GtkCMCTreeRow *currRow;
3389 	currRow = GTK_CMCTREE_ROW( node );
3390 	if( currRow ) {
3391 		parent = currRow->parent;
3392 		while( (child = currRow->children) ) {
3393 			gtk_cmctree_move( ctree, child, parent, node );
3394 		}
3395 		gtk_sctree_sort_node( ctree, parent );
3396 	}
3397 }
3398 
addressbook_edit_address_post_cb(ItemPerson * person)3399 static void addressbook_edit_address_post_cb( ItemPerson *person )
3400 {
3401 	if( person ) {
3402 #ifdef USE_LDAP
3403 		AddressBookFile *abf = addressbook_get_book_file();
3404 
3405 		if (abf && abf->type == ADBOOKTYPE_LDAP) {
3406 			if (g_strcmp0(person->nickName, ADDRITEM_NAME(person)))
3407 				addritem_person_set_nick_name( person, ADDRITEM_NAME(person));
3408 		}
3409 #endif
3410 		addressbook_folder_refresh_one_person( GTK_CMCTREE(addrbook.clist), person );
3411 		invalidate_address_completion();
3412 	}
3413 	addressbook_address_list_set_focus();
3414 }
3415 
addressbook_address_list_set_focus(void)3416 void addressbook_address_list_set_focus( void )
3417 {
3418 	if (!prefs_common.addressbook_use_editaddress_dialog) {
3419 		gtk_window_set_focus(GTK_WINDOW(addrbook.window), addrbook.clist);
3420 		addressbook_list_menu_setup();
3421 	}
3422 }
3423 
addressbook_address_list_disable_some_actions(void)3424 void addressbook_address_list_disable_some_actions(void)
3425 {
3426 	/* disable address copy/pasting when editing contact's detail (embedded form) */
3427 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Cut",   FALSE );
3428 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Copy",  FALSE );
3429 	cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Edit/Paste", FALSE );
3430 }
3431 
addressbook_edit_address_cb(GtkAction * action,gpointer data)3432 static void addressbook_edit_address_cb( GtkAction *action, gpointer data ) {
3433 	addressbook_edit_address(data, 0, NULL, TRUE);
3434 }
3435 
addressbook_edit_address(gpointer data,guint action,GtkWidget * widget,gboolean force_focus)3436 static void addressbook_edit_address( gpointer data, guint action, GtkWidget *widget,
3437 									  gboolean force_focus ) {
3438 	GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
3439 	GtkCMCTree *ctree;
3440 	AddressObject *obj = NULL, *pobj = NULL;
3441 	AddressDataSource *ds = NULL;
3442 	GtkCMCTreeNode *node = NULL, *parentNode = NULL;
3443 	gchar *name = NULL;
3444 	AddressBookFile *abf = NULL;
3445 
3446 	if( addrbook.listSelected == NULL ) return;
3447 	obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
3448 	cm_return_if_fail(obj != NULL);
3449 
3450        	ctree = GTK_CMCTREE( addrbook.ctree );
3451 	pobj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
3452 
3453 	ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
3454 	if( ds == NULL ) return;
3455 
3456 	abf = addressbook_get_book_file();
3457 
3458 	if( obj->type == ADDR_ITEM_EMAIL ) {
3459 		ItemEMail *email = ( ItemEMail * ) obj;
3460 
3461 		if( pobj && pobj->type == ADDR_ITEM_GROUP ) {
3462 			/* Edit parent group */
3463 			AdapterGroup *adapter = ADAPTER_GROUP(pobj);
3464 			ItemGroup *itemGrp = adapter->itemGroup;
3465 			if( abf == NULL ) return;
3466 			if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
3467 			name = ADDRITEM_NAME(itemGrp);
3468 			node = addrbook.treeSelected;
3469 			parentNode = GTK_CMCTREE_ROW(node)->parent;
3470 		}
3471 		else {
3472 			/* Edit person - email page */
3473 			ItemPerson *person;
3474 			person = ( ItemPerson * ) ADDRITEM_PARENT(email);
3475 			if  ( addressbook_edit_person( abf, NULL, person, TRUE, addrbook.editaddress_vbox,
3476 										   addressbook_edit_address_post_cb,
3477 										   (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
3478 				  != NULL ) {
3479 #ifdef USE_LDAP
3480 				if (abf && abf->type == ADBOOKTYPE_LDAP) {
3481 					ldapsvr_set_modified( (LdapServer *) abf, TRUE );
3482 					person->status = UPDATE_ENTRY;
3483 				}
3484 #endif
3485 				if (prefs_common.addressbook_use_editaddress_dialog)
3486 					addressbook_edit_address_post_cb( person );
3487 			}
3488 			return;
3489 		}
3490 	}
3491 	else if( obj->type == ADDR_ITEM_PERSON ) {
3492 		/* Edit person - basic page */
3493 		ItemPerson *person = ( ItemPerson * ) obj;
3494 		if( addressbook_edit_person( abf, NULL, person, FALSE, addrbook.editaddress_vbox,
3495 									  addressbook_edit_address_post_cb,
3496 									  (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
3497 			!= NULL ) {
3498 #ifdef USE_LDAP
3499 				if (abf && abf->type == ADBOOKTYPE_LDAP) {
3500 					ldapsvr_set_modified( (LdapServer *) abf, TRUE );
3501 					person->status = UPDATE_ENTRY;
3502 				}
3503 #endif
3504 				if (prefs_common.addressbook_use_editaddress_dialog)
3505 					addressbook_edit_address_post_cb( person );
3506 		}
3507 		return;
3508 	}
3509 	else if( obj->type == ADDR_ITEM_GROUP ) {
3510 		ItemGroup *itemGrp = ( ItemGroup * ) obj;
3511 		if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
3512 		parentNode = addrbook.treeSelected;
3513 		node = addressbook_find_group_node( parentNode, itemGrp );
3514 		name = ADDRITEM_NAME(itemGrp);
3515 		invalidate_address_completion();
3516 	}
3517 	else {
3518 		return;
3519 	}
3520 
3521 	/* Update tree node with node name */
3522 	if( node == NULL ) return;
3523 	addressbook_change_node_name( node, name );
3524 	gtk_sctree_sort_node( ctree, parentNode );
3525 	gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened );
3526 	addressbook_set_clist(
3527 		gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
3528 			addrbook.opened),
3529 		TRUE);
3530 }
3531 
addressbook_delete_address_cb(GtkAction * action,gpointer data)3532 static void addressbook_delete_address_cb(GtkAction *action, gpointer data)
3533 {
3534 	addressbook_del_clicked(NULL, NULL);
3535 }
3536 
close_cb(GtkAction * action,gpointer data)3537 static void close_cb(GtkAction *action, gpointer data)
3538 {
3539 	addressbook_close();
3540 }
3541 
addressbook_file_save_cb(GtkAction * action,gpointer data)3542 static void addressbook_file_save_cb( GtkAction *action, gpointer data ) {
3543 	addressbook_export_to_file();
3544 }
3545 
addressbook_person_expand_node(GtkCMCTree * ctree,GList * node,gpointer * data)3546 static void addressbook_person_expand_node( GtkCMCTree *ctree, GList *node, gpointer *data ) {
3547 	if( node ) {
3548 		ItemPerson *person = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(node) );
3549 		if( person ) addritem_person_set_opened( person, TRUE );
3550 	}
3551 }
3552 
addressbook_person_collapse_node(GtkCMCTree * ctree,GList * node,gpointer * data)3553 static void addressbook_person_collapse_node( GtkCMCTree *ctree, GList *node, gpointer *data ) {
3554 	if( node ) {
3555 		ItemPerson *person = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(node) );
3556 		if( person ) addritem_person_set_opened( person, FALSE );
3557 	}
3558 }
3559 
addressbook_format_item_clist(ItemPerson * person,ItemEMail * email)3560 static gchar *addressbook_format_item_clist( ItemPerson *person, ItemEMail *email ) {
3561 	gchar *str = NULL;
3562 	gchar *eMailAlias = ADDRITEM_NAME(email);
3563 	if( eMailAlias && *eMailAlias != '\0' ) {
3564 		if( person ) {
3565 			str = g_strdup_printf( "%s - %s", ADDRITEM_NAME(person), eMailAlias );
3566 		}
3567 		else {
3568 			str = g_strdup( eMailAlias );
3569 		}
3570 	}
3571 	return str;
3572 }
3573 
addressbook_match_item(const gchar * name,const gchar * email_alias,const gchar * addr,const gchar * remarks,const gchar * str)3574 static gboolean addressbook_match_item(const gchar *name,
3575 				       const gchar *email_alias,
3576 				       const gchar *addr,
3577 				       const gchar *remarks,
3578 				       const gchar *str)
3579 {
3580 	if (!name)
3581 		return FALSE;
3582 	if (!str || str[0] == '\0')
3583 		return TRUE;
3584 	if (strcasestr(name, str))
3585 		return TRUE;
3586 	else if (email_alias && strcasestr(email_alias, str))
3587 		return TRUE;
3588 	else if (addr && strcasestr(addr, str))
3589 		return TRUE;
3590 	else if (remarks && strcasestr(remarks, str))
3591 		return TRUE;
3592 
3593 	return FALSE;
3594 }
3595 
addressbook_load_group(GtkCMCTree * clist,ItemGroup * itemGroup)3596 static void addressbook_load_group( GtkCMCTree *clist, ItemGroup *itemGroup ) {
3597 	GList *items = itemGroup->listEMail;
3598 	AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3599 	const gchar *search_str = gtk_entry_get_text(GTK_ENTRY(addrbook.entry));
3600 	for( ; items != NULL; items = g_list_next( items ) ) {
3601 		GtkCMCTreeNode *nodeEMail = NULL;
3602 		gchar *text[N_LIST_COLS];
3603 		ItemEMail *email = items->data;
3604 		ItemPerson *person;
3605 		gchar *str = NULL;
3606 
3607 		if( ! email ) continue;
3608 
3609 		person = ( ItemPerson * ) ADDRITEM_PARENT(email);
3610 
3611 		if( !addressbook_match_item(ADDRITEM_NAME(person),
3612 					    ADDRITEM_NAME(email),
3613 					    email->address, email->remarks,
3614 					    search_str))
3615 			continue;
3616 
3617 		str = addressbook_format_item_clist( person, email );
3618 		if( str ) {
3619 			text[COL_NAME] = addressbook_set_col_name_guard(str);
3620 		}
3621 		else {
3622 			text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3623 		}
3624 		text[COL_ADDRESS] = email->address;
3625 		text[COL_REMARKS] = email->remarks;
3626 		nodeEMail = gtk_sctree_insert_node(
3627 				clist, NULL, NULL,
3628 				text, FOLDER_SPACING,
3629 				atci->iconXpm,
3630 				atci->iconXpmOpen,
3631 				FALSE, FALSE );
3632 		gtk_cmctree_node_set_row_data( clist, nodeEMail, email );
3633 		g_free( str );
3634 		str = NULL;
3635 	}
3636 }
3637 
addressbook_set_col_name_guard(gchar * value)3638 gchar *addressbook_set_col_name_guard(gchar *value)
3639 {
3640 	gchar *ret = "<not set>";
3641 	gchar *tmp = g_strdup(value);
3642 	g_strstrip(tmp);
3643 	if (tmp !=NULL && *tmp != '\0')
3644 		ret = value;
3645 	g_free(tmp);
3646 	return ret;
3647 }
3648 
addressbook_folder_load_one_person(GtkCMCTree * clist,ItemPerson * person,AddressTypeControlItem * atci,AddressTypeControlItem * atciMail)3649 static void addressbook_folder_load_one_person(
3650 		GtkCMCTree *clist, ItemPerson *person,
3651 		AddressTypeControlItem *atci,
3652 		AddressTypeControlItem *atciMail )
3653 {
3654 	GtkCMCTreeNode *nodePerson = NULL;
3655 	GtkCMCTreeNode *nodeEMail = NULL;
3656 	gchar *text[N_LIST_COLS];
3657 	gboolean flgFirst = TRUE, haveAddr = FALSE;
3658 	GList *node;
3659 #ifdef USE_LDAP
3660 	AddressBookFile *abf = addressbook_get_book_file();
3661 #endif
3662 
3663 	if( person == NULL ) return;
3664 
3665 	text[COL_NAME] = "";
3666 	node = person->listEMail;
3667 	while( node ) {
3668 		ItemEMail *email = node->data;
3669 		gchar *eMailAddr = NULL;
3670 		node = g_list_next( node );
3671 
3672 		text[COL_ADDRESS] = email->address;
3673 		text[COL_REMARKS] = email->remarks;
3674 		eMailAddr = ADDRITEM_NAME(email);
3675 		if( eMailAddr && *eMailAddr == '\0' ) eMailAddr = NULL;
3676 		if( flgFirst ) {
3677 			/* First email belongs with person */
3678 			gchar *str = addressbook_format_item_clist( person, email );
3679 			if( str ) {
3680 				text[COL_NAME] = addressbook_set_col_name_guard(str);
3681 			}
3682 #ifdef USE_LDAP
3683 			else if( abf && abf->type == ADBOOKTYPE_LDAP &&
3684 				 person && person->nickName ) {
3685 				if (person->nickName) {
3686 					if (strcmp(person->nickName, "") != 0) {
3687 						text[COL_NAME] = addressbook_set_col_name_guard(person->nickName);
3688 					}
3689 					else {
3690 						text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3691 					}
3692 				}
3693 			}
3694 #endif
3695 			else {
3696 				text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3697 			}
3698 			nodePerson = gtk_sctree_insert_node(
3699 					clist, NULL, NULL,
3700 					text, FOLDER_SPACING,
3701 					atci->iconXpm,
3702 					atci->iconXpmOpen,
3703 					FALSE, person->isOpened );
3704 			g_free( str );
3705 			str = NULL;
3706 			gtk_cmctree_node_set_row_data(clist, nodePerson, person );
3707 		}
3708 		else {
3709 			/* Subsequent email is a child node of person */
3710 			text[COL_NAME] = ADDRITEM_NAME(email);
3711 			nodeEMail = gtk_sctree_insert_node(
3712 					clist, nodePerson, NULL,
3713 					text, FOLDER_SPACING,
3714 					atciMail->iconXpm,
3715 					atciMail->iconXpmOpen,
3716 					FALSE, TRUE );
3717 			gtk_cmctree_node_set_row_data(clist, nodeEMail, email );
3718 		}
3719 		flgFirst = FALSE;
3720 		haveAddr = TRUE;
3721 	}
3722 	if( ! haveAddr ) {
3723 		/* Have name without EMail */
3724 		text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3725 		text[COL_ADDRESS] = "";
3726 		text[COL_REMARKS] = "";
3727 		nodePerson = gtk_sctree_insert_node(
3728 				clist, NULL, NULL,
3729 				text, FOLDER_SPACING,
3730 				atci->iconXpm,
3731 				atci->iconXpmOpen,
3732 				FALSE, person->isOpened );
3733 		gtk_cmctree_node_set_row_data(clist, nodePerson, person );
3734 	}
3735 	return;
3736 }
3737 
addressbook_folder_load_person(GtkCMCTree * clist,ItemFolder * itemFolder)3738 static void addressbook_folder_load_person( GtkCMCTree *clist, ItemFolder *itemFolder ) {
3739 	GList *items, *cur;
3740 	AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
3741 	AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3742 	const gchar *search_str;
3743 
3744 	if( atci == NULL ) return;
3745 	if( atciMail == NULL ) return;
3746 
3747 	search_str = gtk_entry_get_text(GTK_ENTRY(addrbook.entry));
3748 
3749 	/* Load email addresses */
3750 	items = addritem_folder_get_person_list( itemFolder );
3751 	for(cur = items ; cur != NULL; cur = g_list_next( cur ) ) {
3752 		ItemPerson *person;
3753 		GList *node;
3754 		ItemEMail *email;
3755 
3756 		person = (ItemPerson *)cur->data;
3757 		if (!person)
3758 			continue;
3759 		node = person->listEMail;
3760 		if (node && node->data) {
3761 			email = node->data;
3762 			if (!addressbook_match_item(ADDRITEM_NAME(person), ADDRITEM_NAME(email), email->address, email->remarks, search_str))
3763 				continue;
3764 		} else {
3765 			if (!addressbook_match_item(ADDRITEM_NAME(person), NULL, NULL, NULL, search_str))
3766 				continue;
3767 		}
3768 
3769 		addressbook_folder_load_one_person( clist, cur->data, atci, atciMail );
3770 	}
3771 	/* Free up the list */
3772 	g_list_free( items );
3773 }
3774 
addressbook_folder_remove_node(GtkCMCTree * clist,GtkCMCTreeNode * node)3775 static void addressbook_folder_remove_node( GtkCMCTree *clist, GtkCMCTreeNode *node ) {
3776 	addrbook.listSelected = NULL;
3777 	gtk_cmctree_remove_node( clist, node );
3778 	addressbook_menubar_set_sensitive( FALSE );
3779 	addressbook_menuitem_set_sensitive(
3780 		gtk_cmctree_node_get_row_data(
3781 			GTK_CMCTREE(clist), addrbook.treeSelected ),
3782 		addrbook.treeSelected );
3783 }
3784 
addressbook_folder_refresh_one_person(GtkCMCTree * clist,ItemPerson * person)3785 void addressbook_folder_refresh_one_person( GtkCMCTree *clist, ItemPerson *person ) {
3786 	AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
3787 	AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3788 	GtkCMCTreeNode *node;
3789 	if( atci == NULL ) return;
3790 	if( atciMail == NULL ) return;
3791 	if( person == NULL ) return;
3792 	/* unload the person */
3793 
3794 	node = gtk_cmctree_find_by_row_data( clist, NULL, person );
3795 	if( node )
3796 		addressbook_folder_remove_node( clist, node );
3797 	addressbook_folder_load_one_person( clist, person, atci, atciMail );
3798 	gtk_sctree_sort_node( clist, NULL );
3799 	node = gtk_cmctree_find_by_row_data( clist, NULL, person );
3800 	if( node ) {
3801 		gtk_sctree_select( GTK_SCTREE(clist), node );
3802 		if (!gtk_cmctree_node_is_visible( clist, node ) )
3803 			gtk_cmctree_node_moveto( clist, node, 0, 0, 0 );
3804 	}
3805 }
3806 
addressbook_folder_remove_one_person(GtkCMCTree * clist,ItemPerson * person)3807 void addressbook_folder_remove_one_person( GtkCMCTree *clist, ItemPerson *person ) {
3808 	GtkCMCTreeNode *node;
3809 
3810 	if( person == NULL ) return;
3811 	node = gtk_cmctree_find_by_row_data( clist, NULL, person );
3812 	if( node ) {
3813 		addressbook_folder_remove_node( clist, node );
3814 	}
3815 }
3816 
addressbook_folder_load_group(GtkCMCTree * clist,ItemFolder * itemFolder)3817 static void addressbook_folder_load_group( GtkCMCTree *clist, ItemFolder *itemFolder ) {
3818 	GList *items;
3819 	AddressTypeControlItem *atci =  addrbookctl_lookup( ADDR_ITEM_GROUP );
3820 	const gchar *search_str;
3821 
3822 	/* Load any groups */
3823 	if( ! atci ) return;
3824 
3825 	search_str = gtk_entry_get_text(GTK_ENTRY(addrbook.entry));
3826 
3827 	items = addritem_folder_get_group_list( itemFolder );
3828 	for( ; items != NULL; items = g_list_next( items ) ) {
3829 		GtkCMCTreeNode *nodeGroup = NULL;
3830 		gchar *text[N_LIST_COLS];
3831 		ItemGroup *group = items->data;
3832 		if( group == NULL ) continue;
3833 		if( !addressbook_match_item(ADDRITEM_NAME(group),
3834 					    NULL, NULL, NULL, search_str) )
3835 			continue;
3836 
3837 		text[COL_NAME] = ADDRITEM_NAME(group);
3838 		text[COL_ADDRESS] = "";
3839 		text[COL_REMARKS] = "";
3840 		nodeGroup = gtk_sctree_insert_node(clist, NULL, NULL,
3841 				      text, FOLDER_SPACING,
3842 				      atci->iconXpm,
3843 				      atci->iconXpmOpen,
3844 				      FALSE, FALSE);
3845 		gtk_cmctree_node_set_row_data(clist, nodeGroup, group );
3846 		gtk_sctree_sort_node(clist, NULL);
3847 	}
3848 	/* Free up the list */
3849 	g_list_free( items );
3850 }
3851 
3852 /**
3853  * Search ctree widget callback function.
3854  * \param  pA Pointer to node.
3855  * \param  pB Pointer to data item being sought.
3856  * \return Zero (0) if group found.
3857  */
addressbook_treenode_find_group_cb(gconstpointer pA,gconstpointer pB)3858 static int addressbook_treenode_find_group_cb( gconstpointer pA, gconstpointer pB ) {
3859 	AddressObject *aoA;
3860 
3861 	aoA = ( AddressObject * ) pA;
3862 	if( aoA->type == ADDR_ITEM_GROUP ) {
3863 		ItemGroup *group, *grp;
3864 
3865 		grp = ADAPTER_GROUP(aoA)->itemGroup;
3866 		group = ( ItemGroup * ) pB;
3867 		if( grp == group ) return 0;	/* Found group */
3868 	}
3869 	return 1;
3870 }
3871 
3872 /*
3873 * Remove folder and group nodes from tree widget for items contained ("cut")
3874 * in clipboard.
3875 */
addressbook_treenode_remove_item(void)3876 static void addressbook_treenode_remove_item( void ) {
3877 	GList *node;
3878 	AddrSelectItem *cutItem;
3879 	AddressCache *cache;
3880 	AddrItemObject *aio;
3881 	GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
3882 	GtkCMCTreeNode *tn;
3883 
3884 	node = _clipBoard_->objectList;
3885 	while( node ) {
3886 		cutItem = node->data;
3887 		node = g_list_next( node );
3888 		cache = addrindex_get_cache(
3889 			_clipBoard_->addressIndex, cutItem->cacheID );
3890 		if( cache == NULL ) continue;
3891 		aio = addrcache_get_object( cache, cutItem->uid );
3892 		if( aio ) {
3893 			tn = NULL;
3894 			if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
3895 				ItemFolder *folder;
3896 
3897 				folder = ( ItemFolder * ) aio;
3898 				tn = gtk_cmctree_find_by_row_data_custom(
3899 					ctree, NULL, folder,
3900 					addressbook_treenode_find_folder_cb );
3901 			}
3902 			else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
3903 				ItemGroup *group;
3904 
3905 				group = ( ItemGroup * ) aio;
3906 				tn = gtk_cmctree_find_by_row_data_custom(
3907 					ctree, NULL, group,
3908 					addressbook_treenode_find_group_cb );
3909 			}
3910 
3911 			if( tn ) {
3912 				/* Free up adapter and remove node. */
3913 				gtk_cmctree_remove_node( ctree, tn );
3914 			}
3915 		}
3916 	}
3917 }
3918 
3919 /**
3920  * Find parent datasource for specified tree node.
3921  * \param  node Node to test.
3922  * \return Data source, or NULL if not found.
3923  */
addressbook_find_datasource(GtkCMCTreeNode * node)3924 static AddressDataSource *addressbook_find_datasource( GtkCMCTreeNode *node ) {
3925 	AddressDataSource *ds = NULL;
3926 	AddressObject *ao;
3927 
3928 	cm_return_val_if_fail(addrbook.ctree != NULL, NULL);
3929 
3930 	while( node ) {
3931 		if( GTK_CMCTREE_ROW(node)->level < 2 ) return NULL;
3932 		ao = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), node );
3933 		if( ao ) {
3934 			/* g_print( "ao->type = %d\n", ao->type ); */
3935 			if( ao->type == ADDR_DATASOURCE ) {
3936 				AdapterDSource *ads = ADAPTER_DSOURCE(ao);
3937 				/* g_print( "found it\n" ); */
3938 				ds = ads->dataSource;
3939 				break;
3940 			}
3941 		}
3942 		node = GTK_CMCTREE_ROW(node)->parent;
3943 	}
3944 	return ds;
3945 }
3946 
3947 /**
3948  * Load address list widget with children of specified object.
3949  * \param obj Parent object to be loaded.
3950  */
addressbook_set_clist(AddressObject * obj,gboolean refresh)3951 static void addressbook_set_clist( AddressObject *obj, gboolean refresh ) {
3952 	GtkCMCTree *ctreelist = GTK_CMCTREE(addrbook.clist);
3953 	GtkCMCList *clist = GTK_CMCLIST(addrbook.clist);
3954 	AddressDataSource *ds = NULL;
3955 	AdapterDSource *ads = NULL;
3956 	static AddressObject *last_obj = NULL;
3957 
3958 	if (addrbook.clist == NULL) {
3959 		return;
3960 	}
3961 	if (obj == last_obj && !refresh)
3962 		return;
3963 
3964 	last_obj = obj;
3965 	if( obj == NULL ) {
3966 		gtk_cmclist_clear(clist);
3967 		return;
3968 	}
3969 
3970 	if( obj->type == ADDR_INTERFACE ) {
3971 		/* g_print( "set_clist: loading datasource...\n" ); */
3972 		/* addressbook_node_load_datasource( GTK_CMCTREE(clist), obj ); */
3973 		return;
3974 	}
3975 
3976 	gtk_cmclist_freeze(clist);
3977 	gtk_cmclist_clear(clist);
3978 
3979 	if( obj->type == ADDR_DATASOURCE ) {
3980 		ads = ADAPTER_DSOURCE(obj);
3981 		ds = ads->dataSource;
3982 		if( ds ) {
3983 			/* Load root folder */
3984 			ItemFolder *rootFolder = NULL;
3985 			rootFolder = addrindex_ds_get_root_folder( ds );
3986 			addressbook_folder_load_person(
3987 				ctreelist, rootFolder );
3988 			addressbook_folder_load_group(
3989 				ctreelist, rootFolder );
3990 		}
3991 	}
3992 	else {
3993 		if( obj->type == ADDR_ITEM_GROUP ) {
3994 			/* Load groups */
3995 			ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup;
3996 			addressbook_load_group( ctreelist, itemGroup );
3997 		}
3998 		else if( obj->type == ADDR_ITEM_FOLDER ) {
3999 			/* Load folders */
4000 			ItemFolder *itemFolder = ADAPTER_FOLDER(obj)->itemFolder;
4001 			addressbook_folder_load_person( ctreelist, itemFolder );
4002 			addressbook_folder_load_group( ctreelist, itemFolder );
4003 		}
4004 	}
4005 	gtk_sctree_sort_recursive(GTK_CMCTREE(clist), NULL);
4006 	clist->focus_row = -1;
4007 	gtk_cmclist_thaw(clist);
4008 }
4009 
4010 /**
4011  * Call back function to free adaptor. Call back is setup by function
4012  * gtk_cmctree_node_set_row_data_full() when node is populated. This function is
4013  * called when the address book tree widget node is removed by calling
4014  * function gtk_cmctree_remove_node().
4015  *
4016  * \param data Tree node's row data.
4017  */
addressbook_free_treenode(gpointer data)4018 static void addressbook_free_treenode( gpointer data ) {
4019 	AddressObject *ao;
4020 
4021 	ao = ( AddressObject * ) data;
4022 	if( ao == NULL ) return;
4023 	if( ao->type == ADDR_INTERFACE ) {
4024 		AdapterInterface *ai = ADAPTER_INTERFACE(ao);
4025 		addrbookctl_free_interface( ai );
4026 	}
4027 	else if( ao->type == ADDR_DATASOURCE ) {
4028 		AdapterDSource *ads = ADAPTER_DSOURCE(ao);
4029 		addrbookctl_free_datasource( ads );
4030 	}
4031 	else if( ao->type == ADDR_ITEM_FOLDER ) {
4032 		AdapterFolder *af = ADAPTER_FOLDER(ao);
4033 		addrbookctl_free_folder( af );
4034 	}
4035 	else if( ao->type == ADDR_ITEM_GROUP ) {
4036 		AdapterGroup *ag = ADAPTER_GROUP(ao);
4037 		addrbookctl_free_group( ag );
4038 	}
4039 }
4040 
4041 /*
4042 * Create new adaptor for specified data source.
4043 */
addressbook_create_ds_adapter(AddressDataSource * ds,AddressObjectType otype,gchar * name)4044 AdapterDSource *addressbook_create_ds_adapter( AddressDataSource *ds,
4045 				AddressObjectType otype, gchar *name )
4046 {
4047 	AdapterDSource *adapter = g_new0( AdapterDSource, 1 );
4048 	ADDRESS_OBJECT(adapter)->type = ADDR_DATASOURCE;
4049 	ADDRESS_OBJECT_NAME(adapter) = g_strdup( name );
4050 	adapter->dataSource = ds;
4051 	adapter->subType = otype;
4052 	return adapter;
4053 }
4054 
addressbook_ads_set_name(AdapterDSource * adapter,gchar * value)4055 void addressbook_ads_set_name( AdapterDSource *adapter, gchar *value ) {
4056 	ADDRESS_OBJECT_NAME(adapter) =
4057 		mgu_replace_string( ADDRESS_OBJECT_NAME(adapter), value );
4058 }
4059 
4060 /*
4061  * Load tree from address index with the initial data.
4062  */
addressbook_load_tree(void)4063 static void addressbook_load_tree( void ) {
4064 	GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
4065 	GList *nodeIf, *nodeDS;
4066 	AdapterInterface *adapter;
4067 	AddressInterface *iface;
4068 	AddressTypeControlItem *atci;
4069 	AddressDataSource *ds;
4070 	AdapterDSource *ads;
4071 	GtkCMCTreeNode *node, *newNode;
4072 	gchar *name;
4073 
4074 	nodeIf = _addressInterfaceList_;
4075 	while( nodeIf ) {
4076 		adapter = nodeIf->data;
4077 		node = adapter->treeNode;
4078 		iface = adapter->interface;
4079 		atci = adapter->atci;
4080 		if( iface ) {
4081 			if( iface->useInterface ) {
4082 				/* Load data sources below interface node */
4083 				nodeDS = iface->listSource;
4084 				while( nodeDS ) {
4085 					ds = nodeDS->data;
4086 					name = addrindex_ds_get_name( ds );
4087 					ads = addressbook_create_ds_adapter(
4088 							ds, atci->objectType, name );
4089 					newNode = addressbook_add_object(
4090 							node, ADDRESS_OBJECT(ads) );
4091 					if (newNode == NULL) {
4092 						g_message("error adding addressbook object\n");
4093 					}
4094 					nodeDS = g_list_next( nodeDS );
4095 				}
4096 				gtk_cmctree_expand( ctree, node );
4097 			}
4098 		}
4099 		nodeIf = g_list_next( nodeIf );
4100 	}
4101 }
4102 
4103 /*
4104  * Convert the old address book to new format.
4105  */
addressbook_convert(AddressIndex * addrIndex)4106 static gboolean addressbook_convert( AddressIndex *addrIndex ) {
4107 	gboolean retVal = FALSE;
4108 	gboolean errFlag = TRUE;
4109 	gchar *msg = NULL;
4110 
4111 	/* Read old address book, performing conversion */
4112 	debug_print( "Reading and converting old address book...\n" );
4113 	addrindex_set_file_name( addrIndex, ADDRESSBOOK_OLD_FILE );
4114 	addrindex_read_data( addrIndex );
4115 	if( addrIndex->retVal == MGU_NO_FILE ) {
4116 		/* We do not have a file - new user */
4117 		debug_print( "New user... create new books...\n" );
4118 		addrindex_create_new_books( addrIndex );
4119 		if( addrIndex->retVal == MGU_SUCCESS ) {
4120 			/* Save index file */
4121 			addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4122 			addrindex_save_data( addrIndex );
4123 			if( addrIndex->retVal == MGU_SUCCESS ) {
4124 				retVal = TRUE;
4125 				errFlag = FALSE;
4126 			}
4127 			else {
4128 				msg = _( "New user, could not save index file." );
4129 			}
4130 		}
4131 		else {
4132 			msg = _( "New user, could not save address book files." );
4133 		}
4134 	}
4135 	else {
4136 		/* We have an old file */
4137 		if( addrIndex->wasConverted ) {
4138 			/* Converted successfully - save address index */
4139 			addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4140 			addrindex_save_data( addrIndex );
4141 			if( addrIndex->retVal == MGU_SUCCESS ) {
4142 				msg = _( "Old address book converted successfully." );
4143 				retVal = TRUE;
4144 				errFlag = FALSE;
4145 			}
4146 			else {
4147 				msg = _("Old address book converted,\n"
4148 					"could not save new address index file." );
4149 			}
4150 		}
4151 		else {
4152 			/* File conversion failed - just create new books */
4153 			debug_print( "File conversion failed... just create new books...\n" );
4154 			addrindex_create_new_books( addrIndex );
4155 			if( addrIndex->retVal == MGU_SUCCESS ) {
4156 				/* Save index */
4157 				addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4158 				addrindex_save_data( addrIndex );
4159 				if( addrIndex->retVal == MGU_SUCCESS ) {
4160 					msg = _("Could not convert address book,\n"
4161 						"but created empty new address book files." );
4162 					retVal = TRUE;
4163 					errFlag = FALSE;
4164 				}
4165 				else {
4166 					msg = _("Could not convert address book,\n"
4167 						"could not save new address index file." );
4168 				}
4169 			}
4170 			else {
4171 				msg = _("Could not convert address book\n"
4172 					"and could not create new address book files." );
4173 			}
4174 		}
4175 	}
4176 	if( errFlag ) {
4177 		debug_print( "Error\n%s\n", msg );
4178 		alertpanel_full(_("Addressbook conversion error"), msg,
4179 				GTK_STOCK_CLOSE, NULL, NULL, ALERTFOCUS_FIRST, FALSE,
4180 				NULL, ALERT_ERROR);
4181 	}
4182 	else if( msg ) {
4183 		debug_print( "Warning\n%s\n", msg );
4184 		alertpanel_full(_("Addressbook conversion error"), msg,
4185 				GTK_STOCK_CLOSE, NULL, NULL, ALERTFOCUS_FIRST, FALSE,
4186 				NULL, ALERT_WARNING);
4187 	}
4188 
4189 	return retVal;
4190 }
4191 
migrate_addrbook(const gchar * origdir,const gchar * destdir)4192 static gboolean migrate_addrbook(const gchar *origdir, const gchar *destdir)
4193 {
4194 	GDir *dp;
4195 	const gchar *d;
4196 	gboolean failed = FALSE;
4197 	GError *error = NULL;
4198 
4199 	if( ( dp = g_dir_open( origdir, 0, &error ) ) == NULL ) {
4200 		debug_print("opening '%s' failed: %d (%s)\n", origdir,
4201 				error->code, error->message);
4202 		g_error_free(error);
4203 		return FALSE;
4204 	}
4205 
4206 	while( ( d = g_dir_read_name( dp ) ) != NULL ) {
4207 		if (strncmp(d, "addrbook-", strlen("addrbook-")))
4208 			continue;
4209 		else {
4210 			gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S,
4211 					d, NULL);
4212 			gchar *dest_file = g_strconcat(destdir, G_DIR_SEPARATOR_S,
4213 					d, NULL);
4214 			if (copy_file(orig_file, dest_file, FALSE) < 0) {
4215 				failed = TRUE;
4216 			}
4217 			g_free(orig_file);
4218 			g_free(dest_file);
4219 			if (failed) {
4220 				break;
4221 			}
4222 		}
4223 	}
4224 	g_dir_close( dp );
4225 
4226 	if (!failed) {
4227 		/* all copies succeeded, we can remove source files */
4228 		if( ( dp = g_dir_open( origdir, 0, &error ) ) == NULL ) {
4229 			debug_print("opening '%s' failed: %d (%s)\n", origdir,
4230 					error->code, error->message);
4231 			g_error_free(error);
4232 			return FALSE;
4233 		}
4234 		while( ( d = g_dir_read_name( dp ) ) != NULL ) {
4235 			if (strncmp(d, "addrbook-", strlen("addrbook-")))
4236 				continue;
4237 			else {
4238 				gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S,
4239 						d, NULL);
4240 				claws_unlink(orig_file);
4241 				g_free(orig_file);
4242 			}
4243 		}
4244 		g_dir_close( dp );
4245 	}
4246 
4247 	return !failed;
4248 }
4249 
addressbook_read_file(void)4250 void addressbook_read_file( void ) {
4251 	AddressIndex *addrIndex = NULL;
4252 	gchar *indexdir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRBOOK_DIR, NULL);
4253 
4254 	debug_print( "Reading address index...\n" );
4255 	if( _addressIndex_ ) {
4256 		debug_print( "address book already read!!!\n" );
4257 		g_free(indexdir);
4258 		return;
4259 	}
4260 
4261 	addrIndex = addrindex_create_index();
4262 	addrindex_initialize();
4263 
4264 	/* Use new address book index. */
4265 
4266 	if ( !is_dir_exist(indexdir) ) {
4267 		if ( make_dir(indexdir) < 0 ) {
4268 			addrindex_set_file_path( addrIndex, get_rc_dir() );
4269 			g_warning( "couldn't create dir '%s'", indexdir);
4270 		} else {
4271 			if (!migrate_addrbook(get_rc_dir(), indexdir)) {
4272 				remove_dir_recursive(indexdir);
4273 				addrindex_set_file_path( addrIndex, get_rc_dir() );
4274 				g_error("couldn't migrate dir %s", indexdir);
4275 			} else {
4276 				addrindex_set_file_path( addrIndex, indexdir);
4277 			}
4278 		}
4279 	} else {
4280 		addrindex_set_file_path( addrIndex, indexdir);
4281 	}
4282 	g_free(indexdir);
4283 	addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4284 	addrindex_read_data( addrIndex );
4285 	if( addrIndex->retVal == MGU_NO_FILE ) {
4286 		/* Conversion required */
4287 		debug_print( "Converting...\n" );
4288 		if( addressbook_convert( addrIndex ) ) {
4289 			_addressIndex_ = addrIndex;
4290 		}
4291 	}
4292 	else if( addrIndex->retVal == MGU_SUCCESS ) {
4293 		_addressIndex_ = addrIndex;
4294 	}
4295 	else {
4296 		/* Error reading address book */
4297 		debug_print( "Could not read address index.\n" );
4298 		addrindex_print_index( addrIndex, stdout );
4299 		alertpanel_full(_("Addressbook Error"),
4300 				_("Could not read address index"),
4301 				GTK_STOCK_CLOSE, NULL, NULL, ALERTFOCUS_FIRST, FALSE,
4302 				NULL, ALERT_ERROR);
4303 	}
4304 	debug_print( "done.\n" );
4305 }
4306 
4307 /*
4308 * Add object into the address index tree widget.
4309 * Enter: node	Parent node.
4310 *        obj	Object to add.
4311 * Return: Node that was added, or NULL if object not added.
4312 */
addressbook_add_object(GtkCMCTreeNode * node,AddressObject * obj)4313 static GtkCMCTreeNode *addressbook_add_object(GtkCMCTreeNode *node,
4314 					    AddressObject *obj)
4315 {
4316 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
4317 	GtkCMCTreeNode *added;
4318 	AddressObject *pobj;
4319 	AddressObjectType otype;
4320 	AddressTypeControlItem *atci = NULL;
4321 
4322 	cm_return_val_if_fail(node != NULL, NULL);
4323 	cm_return_val_if_fail(obj  != NULL, NULL);
4324 
4325 	pobj = gtk_cmctree_node_get_row_data(ctree, node);
4326 	cm_return_val_if_fail(pobj != NULL, NULL);
4327 
4328 	/* Determine object type to be displayed */
4329 	if( obj->type == ADDR_DATASOURCE ) {
4330 		otype = ADAPTER_DSOURCE(obj)->subType;
4331 	}
4332 	else {
4333 		otype = obj->type;
4334 	}
4335 
4336 	/* Handle any special conditions. */
4337 	added = node;
4338 	atci = addrbookctl_lookup( otype );
4339 	if( atci ) {
4340 		if( atci->showInTree ) {
4341 			/* Add object to tree */
4342 			gchar **name;
4343 			name = &obj->name;
4344 			added = gtk_sctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
4345 				atci->iconXpm, atci->iconXpmOpen,
4346 				atci->treeLeaf, atci->treeExpand );
4347 			gtk_cmctree_node_set_row_data_full( ctree, added, obj,
4348 				addressbook_free_treenode );
4349 		}
4350 	}
4351 
4352 	gtk_sctree_sort_node(ctree, node);
4353 
4354 	return added;
4355 }
4356 
4357 /**
4358  * Add group into the address index tree.
4359  * \param  node      Parent node.
4360  * \param  ds        Data source.
4361  * \param  itemGroup Group to add.
4362  * \return Inserted node.
4363  */
addressbook_node_add_group(GtkCMCTreeNode * node,AddressDataSource * ds,ItemGroup * itemGroup)4364 static GtkCMCTreeNode *addressbook_node_add_group(
4365 		GtkCMCTreeNode *node, AddressDataSource *ds,
4366 		ItemGroup *itemGroup )
4367 {
4368 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
4369 	GtkCMCTreeNode *newNode;
4370 	AdapterGroup *adapter;
4371 	AddressTypeControlItem *atci = NULL;
4372 	gchar **name;
4373 
4374 	if( ds == NULL ) return NULL;
4375 	if( node == NULL || itemGroup == NULL ) return NULL;
4376 
4377 	name = &itemGroup->obj.name;
4378 
4379 	atci = addrbookctl_lookup( ADDR_ITEM_GROUP );
4380 
4381 	adapter = g_new0( AdapterGroup, 1 );
4382 	ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_GROUP;
4383 	ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemGroup) );
4384 	adapter->itemGroup = itemGroup;
4385 
4386 	newNode = gtk_sctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
4387 			atci->iconXpm, atci->iconXpm,
4388 			atci->treeLeaf, atci->treeExpand );
4389 	gtk_cmctree_node_set_row_data_full( ctree, newNode, adapter,
4390 		addressbook_free_treenode );
4391 	gtk_sctree_sort_node( ctree, node );
4392 	return newNode;
4393 }
4394 
4395 /**
4396  * Add folder into the address index tree. Only visible folders are loaded into
4397  * the address index tree. Note that the root folder is not inserted into the
4398  * tree.
4399  *
4400  * \param  node	      Parent node.
4401  * \param  ds         Data source.
4402  * \param  itemFolder Folder to add.
4403  * \param  otype      Object type to display.
4404  * \return Inserted node for the folder.
4405 */
addressbook_node_add_folder(GtkCMCTreeNode * node,AddressDataSource * ds,ItemFolder * itemFolder,AddressObjectType otype)4406 static GtkCMCTreeNode *addressbook_node_add_folder(
4407 		GtkCMCTreeNode *node, AddressDataSource *ds,
4408 		ItemFolder *itemFolder, AddressObjectType otype )
4409 {
4410 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
4411 	GtkCMCTreeNode *newNode = NULL;
4412 	AdapterFolder *adapter;
4413 	AddressTypeControlItem *atci = NULL;
4414 	GList *listItems = NULL;
4415 	gchar *name;
4416 	ItemFolder *rootFolder;
4417 
4418 	/* Only visible folders */
4419 	if( itemFolder == NULL || itemFolder->isHidden )
4420 		return NULL;
4421 
4422 	if( ds == NULL )
4423 		return NULL;
4424 	if( node == NULL || itemFolder == NULL )
4425 		return NULL;
4426 
4427 	/* Determine object type */
4428 	atci = addrbookctl_lookup( otype );
4429 	if( atci == NULL )
4430 		return NULL;
4431 
4432 	rootFolder = addrindex_ds_get_root_folder( ds );
4433 	if( itemFolder == rootFolder ) {
4434 		newNode = node;
4435 	}
4436 	else {
4437 		adapter = g_new0( AdapterFolder, 1 );
4438 		ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_FOLDER;
4439 		ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemFolder) );
4440 		adapter->itemFolder = itemFolder;
4441 
4442 		name = ADDRITEM_NAME(itemFolder);
4443 		newNode = gtk_sctree_insert_node( ctree, node, NULL, &name, FOLDER_SPACING,
4444 				atci->iconXpm, atci->iconXpm,
4445 				atci->treeLeaf, atci->treeExpand );
4446 		if( newNode ) {
4447 			gtk_cmctree_node_set_row_data_full( ctree, newNode, adapter,
4448 				addressbook_free_treenode );
4449 		}
4450 	}
4451 
4452 	listItems = itemFolder->listFolder;
4453 	while( listItems ) {
4454 		ItemFolder *item = listItems->data;
4455 		addressbook_node_add_folder( newNode, ds, item, otype );
4456 		listItems = g_list_next( listItems );
4457 	}
4458 	listItems = itemFolder->listGroup;
4459 	while( listItems ) {
4460 		ItemGroup *item = listItems->data;
4461 		addressbook_node_add_group( newNode, ds, item );
4462 		listItems = g_list_next( listItems );
4463 	}
4464 	gtk_sctree_sort_node( ctree, node );
4465 	return newNode;
4466 }
4467 
addressbook_export_to_file(void)4468 void addressbook_export_to_file( void ) {
4469 	if( _addressIndex_ ) {
4470 		/* Save all new address book data */
4471 		debug_print( "Saving address books...\n" );
4472 		addrindex_save_all_books( _addressIndex_ );
4473 
4474 		debug_print( "Exporting addressbook to file...\n" );
4475 		addrindex_save_data( _addressIndex_ );
4476 		if( _addressIndex_->retVal != MGU_SUCCESS ) {
4477 			addrindex_print_index( _addressIndex_, stdout );
4478 		}
4479 
4480 		/* Notify address completion of new data */
4481 		invalidate_address_completion();
4482 	}
4483 }
4484 
addressbook_entry_key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer data)4485 static gboolean addressbook_entry_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
4486 {
4487 	if (event && (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter))
4488 		addressbook_lup_clicked(NULL, NULL);
4489 	return FALSE;
4490 }
4491 
4492 /*
4493 * Comparison using cell contents (text in first column). Used for sort
4494 * address index widget.
4495 */
addressbook_treenode_compare_func(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)4496 static gint addressbook_treenode_compare_func(
4497 	GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
4498 {
4499 	GtkCMCell *cell1 = ((GtkCMCListRow *)ptr1)->cell;
4500 	GtkCMCell *cell2 = ((GtkCMCListRow *)ptr2)->cell;
4501 	gchar *name1 = NULL, *name2 = NULL;
4502 	if( cell1 ) name1 = cell1->u.text;
4503 	if( cell2 ) name2 = cell2->u.text;
4504 	if( ! name1 ) return ( name2 != NULL );
4505 	if( ! name2 ) return -1;
4506 	return g_utf8_collate( name1, name2 );
4507 }
4508 
addressbook_new_book_cb(GtkAction * action,gpointer data)4509 static void addressbook_new_book_cb( GtkAction *action, gpointer data ) {
4510 	AdapterDSource *ads;
4511 	AdapterInterface *adapter;
4512 	GtkCMCTreeNode *newNode;
4513 
4514 	adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
4515 	if( adapter == NULL ) return;
4516 	ads = addressbook_edit_book( _addressIndex_, NULL );
4517 	if( ads ) {
4518 		newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4519 		if( newNode ) {
4520 			gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4521 			addrbook.treeSelected = newNode;
4522 		}
4523 	}
4524 }
4525 
addressbook_new_vcard_cb(GtkAction * action,gpointer data)4526 static void addressbook_new_vcard_cb( GtkAction *action, gpointer data ) {
4527 	AdapterDSource *ads;
4528 	AdapterInterface *adapter;
4529 	GtkCMCTreeNode *newNode;
4530 
4531 	adapter = addrbookctl_find_interface( ADDR_IF_VCARD );
4532 	if( adapter == NULL ) return;
4533 	ads = addressbook_edit_vcard( _addressIndex_, NULL );
4534 	if( ads ) {
4535 		newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4536 		if( newNode ) {
4537 			gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4538 			addrbook.treeSelected = newNode;
4539 		}
4540 	}
4541 }
4542 
4543 #ifdef USE_JPILOT
addressbook_new_jpilot_cb(GtkAction * action,gpointer data)4544 static void addressbook_new_jpilot_cb( GtkAction *action, gpointer data ) {
4545 	AdapterDSource *ads;
4546 	AdapterInterface *adapter;
4547 	AddressInterface *iface;
4548 	GtkCMCTreeNode *newNode;
4549 
4550 	adapter = addrbookctl_find_interface( ADDR_IF_JPILOT );
4551 	if( adapter == NULL ) return;
4552 	iface = adapter->interface;
4553 	if( ! iface->haveLibrary ) return;
4554 	ads = addressbook_edit_jpilot( _addressIndex_, NULL );
4555 	if( ads ) {
4556 		newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4557 		if( newNode ) {
4558 			gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4559 			addrbook.treeSelected = newNode;
4560 		}
4561 	}
4562 }
4563 #endif
4564 
4565 #ifdef USE_LDAP
addressbook_new_ldap_cb(GtkAction * action,gpointer data)4566 static void addressbook_new_ldap_cb( GtkAction *action, gpointer data ) {
4567 	AdapterDSource *ads;
4568 	AdapterInterface *adapter;
4569 	AddressInterface *iface;
4570 	GtkCMCTreeNode *newNode;
4571 
4572 	adapter = addrbookctl_find_interface( ADDR_IF_LDAP );
4573 	if( adapter == NULL ) return;
4574 	iface = adapter->interface;
4575 	if( ! iface->haveLibrary ) return;
4576 	ads = addressbook_edit_ldap( _addressIndex_, NULL );
4577 	if( ads ) {
4578 		newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4579 		if( newNode ) {
4580 			gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4581 			addrbook.treeSelected = newNode;
4582 		}
4583 	}
4584 }
4585 #endif
4586 
4587 /**
4588  * Display address search status message.
4589  * \param queryType Query type.
4590  * \param status    Status/Error code.
4591  */
addressbook_search_message(gint queryType,gint sts)4592 static void addressbook_search_message( gint queryType, gint sts ) {
4593 	gchar *desc = NULL;
4594 	*addressbook_msgbuf = '\0';
4595 
4596 	if( sts != MGU_SUCCESS ) {
4597 		if( queryType == ADDRQUERY_LDAP ) {
4598 #ifdef USE_LDAP
4599 			desc = addressbook_err2string( _lutErrorsLDAP_, sts );
4600 #endif
4601 		}
4602 	}
4603 	if( desc ) {
4604 		g_snprintf( addressbook_msgbuf,
4605 			sizeof(addressbook_msgbuf), "%s", desc );
4606 		addressbook_status_show( addressbook_msgbuf );
4607 	}
4608 	else {
4609 		addressbook_status_show( "" );
4610 	}
4611 }
4612 
4613 /**
4614  * Refresh addressbook by forcing refresh of current selected object in
4615  * tree.
4616  */
addressbook_refresh_current(void)4617 static void addressbook_refresh_current( void ) {
4618 	AddressObject *obj;
4619 	GtkCMCTree *ctree;
4620 
4621 	ctree = GTK_CMCTREE(addrbook.ctree);
4622 	obj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
4623 	if( obj == NULL ) return;
4624 	addressbook_set_clist( obj, TRUE );
4625 }
4626 
4627 /**
4628  * Message that is displayed whilst a query is executing in a background
4629  * thread.
4630  */
4631 static gchar *_tempMessage_ = N_( "Busy searching..." );
4632 
4633 /**
4634  * Address search idle function. This function is called during UI idle time
4635  * while a search is in progress.
4636  *
4637  * \param data Idler data.
4638  */
addressbook_search_idle(gpointer data)4639 static void addressbook_search_idle( gpointer data ) {
4640 	/*
4641 	gint queryID;
4642 
4643 	queryID = GPOINTER_TO_INT( data );
4644 	g_print( "addressbook_ldap_idle... queryID=%d\n", queryID );
4645 	*/
4646 }
4647 
4648 /**
4649  * Search completion callback function. This removes the query from the idle
4650  * list.
4651  *
4652  * \param sender  Sender of query.
4653  * \param queryID Query ID of search request.
4654  * \param status  Search status.
4655  * \param data    Query data.
4656  */
addressbook_search_callback_end(gpointer sender,gint queryID,gint status,gpointer data)4657 static void addressbook_search_callback_end(
4658 		gpointer sender, gint queryID, gint status, gpointer data )
4659 {
4660 	gpointer ptrQID;
4661 	QueryRequest *req;
4662 	AddrQueryObject *aqo;
4663 
4664 	/* Remove idler function */
4665 	ptrQID = GINT_TO_POINTER( queryID );
4666 	if( ptrQID ) {
4667 		g_idle_remove_by_data( ptrQID );
4668 	}
4669 
4670 	/* Refresh addressbook contents */
4671 	addressbook_refresh_current();
4672 	req = qrymgr_find_request( queryID );
4673 	if( req != NULL ) {
4674 		aqo = ( AddrQueryObject * ) req->queryList->data;
4675 		addressbook_search_message( aqo->queryType, status );
4676 	}
4677 
4678 	/* Stop the search */
4679 	addrindex_stop_search( queryID );
4680 }
4681 
4682 /**
4683  * Perform search.
4684  *
4685  * \param ds         Data source to search.
4686  * \param searchTerm String to lookup.
4687  * \param pNode      Parent data source node.
4688  */
addressbook_perform_search(AddressDataSource * ds,gchar * searchTerm,GtkCMCTreeNode * pNode)4689 static void addressbook_perform_search(
4690 		AddressDataSource *ds, gchar *searchTerm,
4691 		GtkCMCTreeNode *pNode )
4692 {
4693 	ItemFolder *folder;
4694 	gchar *name;
4695 	gint queryID;
4696 	guint idleID;
4697 
4698 	/* Setup a query */
4699 	if( *searchTerm == '\0' || strlen( searchTerm ) < 1 ) return;
4700 
4701 	if( !ds || ds->type != ADDR_IF_LDAP ) return;
4702 
4703 	/* Create a folder for the search results */
4704 	name = g_strdup_printf( _queryFolderLabel_, searchTerm );
4705 	folder = addressbook_setup_subf(ds, name, pNode);
4706 	g_free( name );
4707 
4708 	/* Setup the search */
4709 	queryID = addrindex_setup_explicit_search(
4710 		ds, searchTerm, folder, addressbook_search_callback_end, NULL );
4711 	if( queryID == 0 ) return;
4712 
4713 	/* Set up idler function */
4714 	idleID = g_idle_add(
4715 			(GSourceFunc) addressbook_search_idle,
4716 			GINT_TO_POINTER( queryID ) );
4717 	if (idleID == 0) {
4718 		g_message("error adding addressbook_search_idle\n");
4719 	}
4720 
4721 	/* Start search, sit back and wait for something to happen */
4722 	addrindex_start_search( queryID );
4723 
4724 	addressbook_status_show( _tempMessage_ );
4725 }
4726 
4727 /**
4728  * Lookup button handler. Address search is only performed against
4729  * address interfaces for external queries.
4730  *
4731  * \param button Lookup button widget.
4732  * \param data   Data object.
4733  */
addressbook_lup_clicked(GtkButton * button,gpointer data)4734 static void addressbook_lup_clicked( GtkButton *button, gpointer data ) {
4735 	GtkCMCTree *ctree;
4736 	AddressObject *obj;
4737 	AddressDataSource *ds;
4738 	AddressInterface *iface;
4739 	gchar *searchTerm;
4740 	GtkCMCTreeNode *node, *parentNode;
4741 #ifdef USE_LDAP
4742 	LdapServer *ldap_server;
4743 	LdapControl *ldap_ctl;
4744 #endif
4745 
4746 	node = addrbook.treeSelected;
4747 	if( ! node ) return;
4748 	if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
4749 
4750 	ctree = GTK_CMCTREE(addrbook.ctree);
4751 	obj = gtk_cmctree_node_get_row_data( ctree, node );
4752 	if( obj == NULL ) return;
4753 
4754 	if (obj->type != ADDR_DATASOURCE ||
4755 			ADAPTER_DSOURCE(obj)->subType != ADDR_LDAP) {
4756 		addressbook_set_clist(
4757 				gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
4758 					addrbook.treeSelected),
4759 				TRUE);
4760 	}
4761 
4762 	ds = addressbook_find_datasource( node );
4763 	if( ds == NULL ) return;
4764 
4765 	/* We must have a datasource that is an external interface */
4766 	iface = ds->interface;
4767 	if( ! iface->haveLibrary ) return;
4768 	if( ! iface->externalQuery ) return;
4769 
4770 #ifdef USE_LDAP
4771 	if (iface->type == ADDR_IF_LDAP) {
4772 		ldap_server = ds->rawDataSource;
4773 		ldap_ctl = ldap_server->control;
4774 		if (ldap_ctl != NULL &&
4775 				ldap_ctl->bindDN != NULL && strlen(ldap_ctl->bindDN) > 0) {
4776 #ifndef PASSWORD_CRYPTO_OLD
4777 			/* LDAP server is password-protected. */
4778 			if (master_passphrase() == NULL) {
4779 				/* User did not enter master passphrase, do not start a search. */
4780 				return;
4781 			}
4782 #endif /* PASSWORD_CRYPTO_OLD */
4783 		}
4784 	}
4785 #endif /* USE_LDAP */
4786 
4787 	searchTerm =
4788 		gtk_editable_get_chars( GTK_EDITABLE(addrbook.entry), 0, -1 );
4789 	g_strchomp( searchTerm );
4790 
4791 	if( obj->type == ADDR_ITEM_FOLDER ) {
4792 		parentNode = GTK_CMCTREE_ROW(node)->parent;
4793 	}
4794 	else {
4795 		parentNode = node;
4796 	}
4797 	addressbook_perform_search( ds, searchTerm, parentNode );
4798 
4799 	gtk_widget_grab_focus( addrbook.entry );
4800 
4801 	g_free( searchTerm );
4802 }
4803 
addressbook_close_clicked(GtkButton * button,gpointer data)4804 static void addressbook_close_clicked( GtkButton *button, gpointer data ) {
4805 	addressbook_close();
4806 }
4807 
4808 #ifdef USE_LDAP
4809 /**
4810  * Browse address entry for highlighted entry.
4811  */
addressbook_browse_entry_cb(GtkAction * action,gpointer data)4812 static void addressbook_browse_entry_cb( GtkAction *action, gpointer data)
4813 {
4814 	GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
4815 	AddressObject *obj;
4816 	AddressDataSource *ds;
4817 	AddressInterface *iface;
4818 	ItemPerson *person;
4819 	ItemEMail *email;
4820 
4821 	if(addrbook.listSelected == NULL)
4822 		return;
4823 
4824 	obj = gtk_cmctree_node_get_row_data(clist, addrbook.listSelected);
4825 	if (obj == NULL)
4826 		return;
4827 
4828 	ds = addressbook_find_datasource(GTK_CMCTREE_NODE(addrbook.treeSelected));
4829 	if(ds == NULL)
4830 		return;
4831 
4832 	iface = ds->interface;
4833 	if(!iface || !iface->haveLibrary )
4834 		return;
4835 
4836 	person = NULL;
4837 	if (obj->type == ADDR_ITEM_EMAIL) {
4838 		email = ( ItemEMail * ) obj;
4839 
4840 		person = (ItemPerson *) ADDRITEM_PARENT(email);
4841 	}
4842 	else if (obj->type == ADDR_ITEM_PERSON) {
4843 		person = (ItemPerson *) obj;
4844 	}
4845 	else {
4846 		/* None of these */
4847 		return;
4848 	}
4849 
4850 	if( iface && iface->type == ADDR_IF_LDAP ) {
4851 		browseldap_entry(ds, person->externalID);
4852 	}
4853 }
4854 #endif
4855 
4856 /* **********************************************************************
4857 * Build lookup tables.
4858 * ***********************************************************************
4859 */
4860 
4861 /*
4862  * Remap object types.
4863  * Enter:  abType AddressObjectType (used in tree node).
4864  * Return: ItemObjectType (used in address cache data).
4865  */
addressbook_type2item(AddressObjectType abType)4866 ItemObjectType addressbook_type2item( AddressObjectType abType ) {
4867 	ItemObjectType ioType;
4868 
4869 	switch( abType ) {
4870 		case ADDR_ITEM_PERSON: ioType = ITEMTYPE_PERSON;     break;
4871 		case ADDR_ITEM_EMAIL:  ioType = ITEMTYPE_EMAIL;      break;
4872 		case ADDR_ITEM_FOLDER: ioType = ITEMTYPE_FOLDER;     break;
4873 		case ADDR_ITEM_GROUP:  ioType = ITEMTYPE_GROUP;      break;
4874 		case ADDR_DATASOURCE:  ioType = ITEMTYPE_DATASOURCE; break;
4875 		default:               ioType = ITEMTYPE_NONE;       break;
4876 	}
4877 	return ioType;
4878 }
4879 
4880 #define UPDATE_ICON_ATCI(id,icon,iconopen) {			\
4881 	atci = addrbookctl_lookup(id);				\
4882 	if (atci) {						\
4883 		atci->iconXpm = icon;				\
4884 		atci->iconXpmOpen = iconopen;			\
4885 	} else {						\
4886 		g_warning("can't get atci %d", id);		\
4887 	}							\
4888 }
4889 
4890 /*
4891 * Build table that controls the rendering of object types.
4892 */
addrbookctl_build_icons(GtkWidget * window)4893 static void addrbookctl_build_icons( GtkWidget *window ) {
4894 	AddressTypeControlItem *atci;
4895 
4896 	/* Build icons */
4897 	if (interfacexpm)
4898 		g_object_unref(interfacexpm);
4899 	if (folderxpm)
4900 		g_object_unref(folderxpm);
4901 	if (folderopenxpm)
4902 		g_object_unref(folderopenxpm);
4903 	if (groupxpm)
4904 		g_object_unref(groupxpm);
4905 	if (vcardxpm)
4906 		g_object_unref(vcardxpm);
4907 	if (bookxpm)
4908 		g_object_unref(bookxpm);
4909 	if (addressxpm)
4910 		g_object_unref(addressxpm);
4911 	if (jpilotxpm)
4912 		g_object_unref(jpilotxpm);
4913 	if (categoryxpm)
4914 		g_object_unref(categoryxpm);
4915 	if (ldapxpm)
4916 		g_object_unref(ldapxpm);
4917 	if (addrsearchxpm)
4918 		g_object_unref(addrsearchxpm);
4919 	stock_pixbuf_gdk(STOCK_PIXMAP_INTERFACE, &interfacexpm );
4920 	stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
4921 	stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
4922 	stock_pixbuf_gdk(STOCK_PIXMAP_GROUP, &groupxpm);
4923 	stock_pixbuf_gdk(STOCK_PIXMAP_VCARD, &vcardxpm);
4924 	stock_pixbuf_gdk(STOCK_PIXMAP_BOOK, &bookxpm);
4925 	stock_pixbuf_gdk(STOCK_PIXMAP_ADDRESS, &addressxpm);
4926 	stock_pixbuf_gdk(STOCK_PIXMAP_JPILOT, &jpilotxpm);
4927 	stock_pixbuf_gdk(STOCK_PIXMAP_CATEGORY, &categoryxpm);
4928 	stock_pixbuf_gdk(STOCK_PIXMAP_LDAP, &ldapxpm);
4929 	stock_pixbuf_gdk(STOCK_PIXMAP_ADDRESS_SEARCH, &addrsearchxpm);
4930 
4931 	UPDATE_ICON_ATCI(ADDR_INTERFACE,folderxpm,folderopenxpm);
4932 	UPDATE_ICON_ATCI(ADDR_BOOK,bookxpm,bookxpm);
4933 	UPDATE_ICON_ATCI(ADDR_ITEM_PERSON,NULL,NULL);
4934 	UPDATE_ICON_ATCI(ADDR_ITEM_EMAIL,addressxpm,addressxpm);
4935 	UPDATE_ICON_ATCI(ADDR_ITEM_GROUP,groupxpm,groupxpm);
4936 	UPDATE_ICON_ATCI(ADDR_ITEM_FOLDER,folderxpm,folderopenxpm);
4937 	UPDATE_ICON_ATCI(ADDR_VCARD,vcardxpm,vcardxpm);
4938 	UPDATE_ICON_ATCI(ADDR_JPILOT,jpilotxpm,jpilotxpm);
4939 	UPDATE_ICON_ATCI(ADDR_CATEGORY,categoryxpm,categoryxpm);
4940 	UPDATE_ICON_ATCI(ADDR_LDAP,ldapxpm,ldapxpm);
4941 	UPDATE_ICON_ATCI(ADDR_LDAP_QUERY,addrsearchxpm,addrsearchxpm);
4942 
4943 }
4944 
4945 /*
4946 * Build table that controls the rendering of object types.
4947 */
addrbookctl_build_map(GtkWidget * window)4948 static void addrbookctl_build_map( GtkWidget *window ) {
4949 	AddressTypeControlItem *atci;
4950 
4951 	_addressBookTypeHash_ = g_hash_table_new( g_int_hash, g_int_equal );
4952 	_addressBookTypeList_ = NULL;
4953 
4954 	/* Interface */
4955 	atci = g_new0( AddressTypeControlItem, 1 );
4956 	atci->objectType = ADDR_INTERFACE;
4957 	atci->interfaceType = ADDR_IF_NONE;
4958 	atci->showInTree = TRUE;
4959 	atci->treeExpand = TRUE;
4960 	atci->treeLeaf = FALSE;
4961 	atci->displayName = _( "Interface" );
4962 	atci->menuCommand = NULL;
4963 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4964 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4965 
4966 	/* Address book */
4967 	atci = g_new0( AddressTypeControlItem, 1 );
4968 	atci->objectType = ADDR_BOOK;
4969 	atci->interfaceType = ADDR_IF_BOOK;
4970 	atci->showInTree = TRUE;
4971 	atci->treeExpand = TRUE;
4972 	atci->treeLeaf = FALSE;
4973 	atci->displayName = _("Address Books");
4974 	atci->menuCommand = "Menu/Book/NewBook";
4975 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4976 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4977 
4978 	/* Item person */
4979 	atci = g_new0( AddressTypeControlItem, 1 );
4980 	atci->objectType = ADDR_ITEM_PERSON;
4981 	atci->interfaceType = ADDR_IF_NONE;
4982 	atci->showInTree = FALSE;
4983 	atci->treeExpand = FALSE;
4984 	atci->treeLeaf = FALSE;
4985 	atci->displayName = _( "Person" );
4986 	atci->menuCommand = NULL;
4987 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4988 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4989 
4990 	/* Item email */
4991 	atci = g_new0( AddressTypeControlItem, 1 );
4992 	atci->objectType = ADDR_ITEM_EMAIL;
4993 	atci->interfaceType = ADDR_IF_NONE;
4994 	atci->showInTree = FALSE;
4995 	atci->treeExpand = FALSE;
4996 	atci->treeLeaf = TRUE;
4997 	atci->displayName = _( "Email Address" );
4998 	atci->menuCommand = NULL;
4999 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
5000 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
5001 
5002 	/* Item group */
5003 	atci = g_new0( AddressTypeControlItem, 1 );
5004 	atci->objectType = ADDR_ITEM_GROUP;
5005 	atci->interfaceType = ADDR_IF_BOOK;
5006 	atci->showInTree = TRUE;
5007 	atci->treeExpand = FALSE;
5008 	atci->treeLeaf = FALSE;
5009 	atci->displayName = _( "Group" );
5010 	atci->menuCommand = NULL;
5011 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
5012 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
5013 
5014 	/* Item folder */
5015 	atci = g_new0( AddressTypeControlItem, 1 );
5016 	atci->objectType = ADDR_ITEM_FOLDER;
5017 	atci->interfaceType = ADDR_IF_BOOK;
5018 	atci->showInTree = TRUE;
5019 	atci->treeExpand = FALSE;
5020 	atci->treeLeaf = FALSE;
5021 	atci->displayName = _( "Folder" );
5022 	atci->menuCommand = NULL;
5023 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
5024 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
5025 
5026 	/* vCard */
5027 	atci = g_new0( AddressTypeControlItem, 1 );
5028 	atci->objectType = ADDR_VCARD;
5029 	atci->interfaceType = ADDR_IF_VCARD;
5030 	atci->showInTree = TRUE;
5031 	atci->treeExpand = TRUE;
5032 	atci->treeLeaf = TRUE;
5033 	atci->displayName = _( "vCard" );
5034 	atci->menuCommand = "Menu/Book/NewVCard";
5035 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
5036 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
5037 
5038 	/* J-Pilot */
5039 	atci = g_new0( AddressTypeControlItem, 1 );
5040 	atci->objectType = ADDR_JPILOT;
5041 	atci->interfaceType = ADDR_IF_JPILOT;
5042 	atci->showInTree = TRUE;
5043 	atci->treeExpand = TRUE;
5044 	atci->treeLeaf = FALSE;
5045 	atci->displayName = _( "JPilot" );
5046 	atci->menuCommand = "Menu/Book/NewJPilot";
5047 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
5048 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
5049 
5050 	/* Category */
5051 	atci = g_new0( AddressTypeControlItem, 1 );
5052 	atci->objectType = ADDR_CATEGORY;
5053 	atci->interfaceType = ADDR_IF_JPILOT;
5054 	atci->showInTree = TRUE;
5055 	atci->treeExpand = TRUE;
5056 	atci->treeLeaf = TRUE;
5057 	atci->displayName = _( "JPilot" );
5058 	atci->menuCommand = NULL;
5059 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
5060 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
5061 
5062 	/* LDAP Server */
5063 	atci = g_new0( AddressTypeControlItem, 1 );
5064 	atci->objectType = ADDR_LDAP;
5065 	atci->interfaceType = ADDR_IF_LDAP;
5066 	atci->showInTree = TRUE;
5067 	atci->treeExpand = TRUE;
5068 	atci->treeLeaf = FALSE;
5069 	atci->displayName = _( "LDAP servers" );
5070 	atci->menuCommand = "Menu/Book/NewLDAPServer";
5071 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
5072 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
5073 
5074 	/* LDAP Query  */
5075 	atci = g_new0( AddressTypeControlItem, 1 );
5076 	atci->objectType = ADDR_LDAP_QUERY;
5077 	atci->interfaceType = ADDR_IF_LDAP;
5078 	atci->showInTree = TRUE;
5079 	atci->treeExpand = FALSE;
5080 	atci->treeLeaf = TRUE;
5081 	atci->displayName = _( "LDAP Query" );
5082 	atci->menuCommand = NULL;
5083 	g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
5084 	_addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
5085 
5086 	addrbookctl_build_icons(window);
5087 }
5088 
addressbook_reflect_prefs_pixmap_theme(void)5089 void addressbook_reflect_prefs_pixmap_theme(void)
5090 {
5091 	if (addrbook.window)
5092 		addrbookctl_build_icons(addrbook.window);
5093 }
5094 
5095 /*
5096 * Search for specified object type.
5097 */
addrbookctl_lookup(gint ot)5098 static AddressTypeControlItem *addrbookctl_lookup( gint ot ) {
5099 	gint objType = ot;
5100 	return ( AddressTypeControlItem * ) g_hash_table_lookup( _addressBookTypeHash_, &objType );
5101 }
5102 
5103 /*
5104 * Search for specified interface type.
5105 */
addrbookctl_lookup_iface(AddressIfType ifType)5106 static AddressTypeControlItem *addrbookctl_lookup_iface( AddressIfType ifType ) {
5107 	GList *node = _addressBookTypeList_;
5108 	while( node ) {
5109 		AddressTypeControlItem *atci = node->data;
5110 		if( atci->interfaceType == ifType ) return atci;
5111 		node = g_list_next( node );
5112 	}
5113 	return NULL;
5114 }
5115 
addrbookctl_free_address(AddressObject * obj)5116 static void addrbookctl_free_address( AddressObject *obj ) {
5117 	g_free( obj->name );
5118 	obj->type = ADDR_NONE;
5119 	obj->name = NULL;
5120 }
5121 
addrbookctl_free_interface(AdapterInterface * adapter)5122 static void addrbookctl_free_interface( AdapterInterface *adapter ) {
5123 	addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
5124 	adapter->interface = NULL;
5125 	adapter->interfaceType = ADDR_IF_NONE;
5126 	adapter->atci = NULL;
5127 	adapter->enabled = FALSE;
5128 	adapter->haveLibrary = FALSE;
5129 	adapter->treeNode = NULL;
5130 	g_free( adapter );
5131 }
5132 
addrbookctl_free_datasource(AdapterDSource * adapter)5133 static void addrbookctl_free_datasource( AdapterDSource *adapter ) {
5134 	addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
5135 	adapter->dataSource = NULL;
5136 	adapter->subType = ADDR_NONE;
5137 	g_free( adapter );
5138 }
5139 
addrbookctl_free_folder(AdapterFolder * adapter)5140 static void addrbookctl_free_folder( AdapterFolder *adapter ) {
5141 	addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
5142 	adapter->itemFolder = NULL;
5143 	g_free( adapter );
5144 }
5145 
addrbookctl_free_group(AdapterGroup * adapter)5146 static void addrbookctl_free_group( AdapterGroup *adapter ) {
5147 	addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
5148 	adapter->itemGroup = NULL;
5149 	g_free( adapter );
5150 }
5151 
5152 /**
5153  * Build GUI interface list.
5154  */
addrbookctl_build_iflist(void)5155 static void addrbookctl_build_iflist( void ) {
5156 	AddressTypeControlItem *atci;
5157 	AdapterInterface *adapter;
5158 	GList *list = NULL;
5159 
5160 	if( _addressIndex_ == NULL ) {
5161 		_addressIndex_ = addrindex_create_index();
5162 		if( _clipBoard_ == NULL ) {
5163 			_clipBoard_ = addrclip_create();
5164 		}
5165 		addrclip_set_index( _clipBoard_, _addressIndex_ );
5166 	}
5167 	_addressInterfaceList_ = NULL;
5168 	list = addrindex_get_interface_list( _addressIndex_ );
5169 	while( list ) {
5170 		AddressInterface *interface = list->data;
5171 		atci = addrbookctl_lookup_iface( interface->type );
5172 		if( atci ) {
5173 			adapter = g_new0( AdapterInterface, 1 );
5174 			adapter->interfaceType = interface->type;
5175 			adapter->atci = atci;
5176 			adapter->interface = interface;
5177 			adapter->treeNode = NULL;
5178 			adapter->enabled = TRUE;
5179 			adapter->haveLibrary = interface->haveLibrary;
5180 			ADDRESS_OBJECT(adapter)->type = ADDR_INTERFACE;
5181 			ADDRESS_OBJECT_NAME(adapter) = g_strdup( atci->displayName );
5182 			_addressInterfaceList_ =
5183 				g_list_append( _addressInterfaceList_, adapter );
5184 		}
5185 		list = g_list_next( list );
5186 	}
5187 }
5188 
5189 /**
5190  * Find GUI interface type specified interface type.
5191  * \param  ifType Interface type.
5192  * \return Interface item, or NULL if not found.
5193  */
addrbookctl_find_interface(AddressIfType ifType)5194 static AdapterInterface *addrbookctl_find_interface( AddressIfType ifType ) {
5195 	GList *node = _addressInterfaceList_;
5196 	while( node ) {
5197 		AdapterInterface *adapter = node->data;
5198 		if( adapter->interfaceType == ifType ) return adapter;
5199 		node = g_list_next( node );
5200 	}
5201 	return NULL;
5202 }
5203 
5204 /**
5205  * Build interface list selection.
5206  */
addrbookctl_build_ifselect(void)5207 static void addrbookctl_build_ifselect( void ) {
5208 	GList *newList = NULL;
5209 	gchar *selectStr;
5210 	gchar **splitStr;
5211 	gint ifType;
5212 	gint i;
5213 	gchar *endptr = NULL;
5214 	/* gboolean enabled; */
5215 	AdapterInterface *adapter;
5216 
5217 	selectStr = g_strdup( ADDRESSBOOK_IFACE_SELECTION );
5218 
5219 	/* Parse string */
5220 	splitStr = g_strsplit( selectStr, ",", -1 );
5221 	for( i = 0; i < ADDRESSBOOK_MAX_IFACE; i++ ) {
5222 		if( splitStr[i] ) {
5223 			/* g_print( "%d : %s\n", i, splitStr[i] ); */
5224 			ifType = strtol( splitStr[i], &endptr, 10 );
5225 			/* enabled = TRUE;
5226 			if( *endptr ) {
5227 				if( strcmp( endptr, "/n" ) == 0 ) {
5228 					enabled = FALSE;
5229 				}
5230 			}
5231 			*/
5232 			/* g_print( "\t%d : %s\n", ifType, enabled ? "yes" : "no" ); */
5233 			adapter = addrbookctl_find_interface( ifType );
5234 			if( adapter ) {
5235 				newList = g_list_append( newList, adapter );
5236 			}
5237 		}
5238 		else {
5239 			break;
5240 		}
5241 	}
5242 	/* g_print( "i=%d\n", i ); */
5243 	g_strfreev( splitStr );
5244 	g_free( selectStr );
5245 
5246 	/* Replace existing list */
5247 	g_list_free( _addressIFaceSelection_ );
5248 	_addressIFaceSelection_ = newList;
5249 	newList = NULL;
5250 }
5251 
5252 /* ***********************************************************************
5253  * Add sender to address book.
5254  * ***********************************************************************
5255  */
5256 
5257 /*
5258  * This function is used by the Add sender to address book function.
5259  */
addressbook_add_contact(const gchar * name,const gchar * address,const gchar * remarks,GdkPixbuf * picture)5260 gboolean addressbook_add_contact(
5261 		const gchar *name, const gchar *address, const gchar *remarks,
5262 		GdkPixbuf *picture )
5263 {
5264 	debug_print( "addressbook_add_contact: name/address: %s - %s\n", name, address );
5265 	if( addressadd_selection( _addressIndex_, name, address, remarks, picture ) ) {
5266 		debug_print( "addressbook_add_contact - added\n" );
5267 		addressbook_refresh();
5268 	}
5269 	return TRUE;
5270 }
5271 
5272 /* ***********************************************************************
5273  * Book/folder selection.
5274  * ***********************************************************************
5275  */
5276 
5277 /*
5278  * This function is used by the matcher dialog to select a book/folder.
5279  */
addressbook_folder_selection(const gchar * folderpath)5280 gchar *addressbook_folder_selection( const gchar *folderpath)
5281 {
5282 	AddressBookFile *book = NULL;
5283 	ItemFolder *folder = NULL;
5284 	gchar *path = NULL;
5285 
5286 	cm_return_val_if_fail( folderpath != NULL, NULL);
5287 
5288 	if ( addressbook_foldersel_selection( _addressIndex_, &book, &folder, folderpath )
5289 		&& book != NULL ) {
5290 		if ( folder != NULL) {
5291 			gchar *tmp = NULL;
5292 			gchar *oldtmp = NULL;
5293 			AddrItemObject *obj = NULL;
5294 
5295 			/* walk thru folder->parent to build the full folder path */
5296 			/* TODO: wwp: optimize this */
5297 			obj = &folder->obj;
5298 			tmp = g_strdup(obj->uid);
5299 			while ( obj->parent ) {
5300 				obj = obj->parent;
5301 				if ( obj->name != NULL ) {
5302 					oldtmp = g_strdup(tmp);
5303 					g_free(tmp);
5304 					tmp = g_strdup_printf("%s/%s", obj->uid, oldtmp);
5305 					g_free(oldtmp);
5306 				}
5307 			}
5308 			path = g_strdup_printf("%s/%s", book->fileName, tmp);
5309 			g_free(tmp);
5310 		} else {
5311 			path = g_strdup_printf("%s", book->fileName);
5312 		}
5313 		debug_print( "addressbook_foldersel: %s\n", path?path:"(null)");
5314 		return path;
5315 	}
5316 	return NULL;
5317 }
5318 
5319 /* ***********************************************************************
5320  * Book/folder checking.
5321  * ***********************************************************************
5322  */
5323 
addressbook_peek_subfolder_exists_create_folderinfo(AddressBookFile * abf,ItemFolder * folder)5324 static FolderInfo *addressbook_peek_subfolder_exists_create_folderinfo( AddressBookFile *abf, ItemFolder *folder )
5325 {
5326 	FolderInfo *fi = g_new0( FolderInfo, 1 );
5327 	fi->book   = abf;
5328 	fi->folder = folder;
5329 	return fi;
5330 }
5331 
addressbook_peek_subfolder_exists_load_folder(ItemFolder * parentFolder,FolderInfo * fiParent,FolderPathMatch * match)5332 static void addressbook_peek_subfolder_exists_load_folder( ItemFolder *parentFolder,
5333 					FolderInfo *fiParent, FolderPathMatch *match )
5334 {
5335 	GList *list;
5336 	ItemFolder *folder;
5337 	gchar *fName;
5338 	FolderInfo *fi;
5339 	FolderPathMatch *nextmatch = NULL;
5340 
5341 	if (!parentFolder)
5342 		return;
5343 
5344 	list = parentFolder->listFolder;
5345 	while ( list ) {
5346 		folder = list->data;
5347 		fName = g_strdup( ADDRITEM_NAME(folder) );
5348 
5349 		/* match folder name, match pointer will be set to NULL if next recursive call
5350 		   doesn't need to match subfolder name */
5351 		if ( match != NULL &&
5352 			 match->matched == FALSE ) {
5353 			if ( strcmp(match->folder_path[match->index], folder->obj.uid) == 0 ) {
5354 				/* folder name matches, prepare next subfolder match */
5355 				debug_print("matched folder name '%s'\n", fName);
5356 				match->index++;
5357 				if ( match->folder_path[match->index] == NULL ) {
5358 					/* we've matched all elements */
5359 					match->matched = TRUE;
5360 					match->folder = folder;
5361 					debug_print("book/folder path matched!\n");
5362 				} else {
5363 					/* keep on matching */
5364 					nextmatch = match;
5365 				}
5366 			}
5367 		}
5368 
5369 		g_free( fName );
5370 
5371 		fi = addressbook_peek_subfolder_exists_create_folderinfo( fiParent->book, folder );
5372 		addressbook_peek_subfolder_exists_load_folder( folder, fi, nextmatch );
5373 		g_free(fi);
5374 		list = g_list_next( list );
5375 	}
5376 }
5377 
5378 /*
5379  * This function is used by to check if a matcher book/folder path corresponds to an
5380    existing addressbook book/folder ("" or "Any" are considered as valid, NULL invalid).
5381    Caution: returned book and folder pointers can be NULL even when returning TRUE:
5382    if book AND folder are NULL this means that folderpath was empty or Any.
5383    If folderpath is a simple book name (without folder), book will not be NULL and folder
5384    will be NULL. It's not expected to return book as NULL and folder as non NULL.
5385  */
5386 
addressbook_peek_folder_exists(gchar * folderpath,AddressDataSource ** book,ItemFolder ** folder)5387 gboolean addressbook_peek_folder_exists( gchar *folderpath,
5388 										 AddressDataSource **book,
5389 										 ItemFolder **folder )
5390 {
5391 	AddressDataSource *ds;
5392 	GList *list, *nodeDS;
5393 	ItemFolder *rootFolder;
5394 	AddressBookFile *abf;
5395 	FolderInfo *fi;
5396 	FolderPathMatch folder_path_match = { NULL, FALSE, 0, NULL, NULL };
5397 
5398 	if ( book )
5399 		*book = NULL;
5400 	if ( folder )
5401 		*folder = NULL;
5402 
5403 	if ( folderpath == NULL )
5404 		return FALSE;
5405 
5406 	if ( strcasecmp(folderpath, "Any") == 0 || *folderpath == '\0' )
5407 		return TRUE;
5408 
5409 	/* split the folder path we've received, we'll try to match this path, subpath by
5410 	   subpath against the book/folder structure in order */
5411 	folder_path_match.folder_path = g_strsplit( folderpath, "/", 256 );
5412 	if (!folder_path_match.folder_path)
5413 		return FALSE;
5414 
5415 	list = addrindex_get_interface_list( _addressIndex_ );
5416 	while ( list && !folder_path_match.matched ) {
5417 		AddressInterface *interface = list->data;
5418 		if ( interface && interface->type == ADDR_IF_BOOK ) {
5419 			nodeDS = interface->listSource;
5420 			while ( nodeDS && !folder_path_match.matched ) {
5421 				ds = nodeDS->data;
5422 
5423 				/* Read address book */
5424 				if( ! addrindex_ds_get_read_flag( ds ) ) {
5425 					addrindex_ds_read_data( ds );
5426 				}
5427 
5428 				/* Add node for address book */
5429 				abf = ds->rawDataSource;
5430 
5431 				/* match book name */
5432 				if ( abf && abf->fileName &&
5433 				    strcmp(folder_path_match.folder_path[0], abf->fileName) == 0 ) {
5434 
5435 					debug_print("matched book name '%s'\n", abf->fileName);
5436 					folder_path_match.book = ds;
5437 
5438 					if ( folder_path_match.folder_path[1] == NULL ) {
5439 						/* no folder part to match */
5440 
5441 						folder_path_match.matched = TRUE;
5442 						folder_path_match.folder = NULL;
5443 						debug_print("book path matched!\n");
5444 
5445 					} else {
5446 						/* match folder part */
5447 
5448 						fi = addressbook_peek_subfolder_exists_create_folderinfo( abf, NULL );
5449 						rootFolder = addrindex_ds_get_root_folder( ds );
5450 
5451 						/* prepare for recursive call */
5452 						folder_path_match.index = 1;
5453 						/* this call will set folder_path_match.matched and folder_path_match.folder */
5454 						addressbook_peek_subfolder_exists_load_folder( rootFolder, fi, &folder_path_match );
5455 						g_free(fi);
5456 					}
5457 				}
5458 
5459 				nodeDS = g_list_next( nodeDS );
5460 			}
5461 		}
5462 		list = g_list_next( list );
5463 	}
5464 
5465 	g_strfreev( folder_path_match.folder_path );
5466 
5467 	if ( book )
5468 		*book = folder_path_match.book;
5469 	if ( folder )
5470 		*folder = folder_path_match.folder;
5471 	return folder_path_match.matched;
5472 }
5473 
5474 
5475 /* **********************************************************************
5476  * Address Import.
5477  * ***********************************************************************
5478  */
5479 
5480 /**
5481  * Import LDIF file.
5482  */
addressbook_import_ldif_cb(GtkAction * action,gpointer data)5483 static void addressbook_import_ldif_cb( GtkAction *action, gpointer data ) {
5484 	AddressDataSource *ds = NULL;
5485 	AdapterDSource *ads = NULL;
5486 	AddressBookFile *abf = NULL;
5487 	AdapterInterface *adapter;
5488 	GtkCMCTreeNode *newNode;
5489 
5490 	adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5491 	if( adapter ) {
5492 		if( adapter->treeNode ) {
5493 			abf = addressbook_imp_ldif( _addressIndex_ );
5494 			if( abf ) {
5495 				ds = addrindex_index_add_datasource(
5496 					_addressIndex_, ADDR_IF_BOOK, abf );
5497 				ads = addressbook_create_ds_adapter(
5498 					ds, ADDR_BOOK, NULL );
5499 				addressbook_ads_set_name(
5500 					ads, addrbook_get_name( abf ) );
5501 				newNode = addressbook_add_object(
5502 					adapter->treeNode,
5503 					ADDRESS_OBJECT(ads) );
5504 				if( newNode ) {
5505 					gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
5506 						newNode );
5507 					addrbook.treeSelected = newNode;
5508 				}
5509 
5510 				/* Notify address completion */
5511 				invalidate_address_completion();
5512 			}
5513 		}
5514 	}
5515 }
5516 
5517 /**
5518  * Import MUTT file.
5519  */
addressbook_import_mutt_cb(GtkAction * action,gpointer data)5520 static void addressbook_import_mutt_cb( GtkAction *action, gpointer data ) {
5521 	AddressDataSource *ds = NULL;
5522 	AdapterDSource *ads = NULL;
5523 	AddressBookFile *abf = NULL;
5524 	AdapterInterface *adapter;
5525 	GtkCMCTreeNode *newNode;
5526 
5527 	adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5528 	if( adapter ) {
5529 		if( adapter->treeNode ) {
5530 			abf = addressbook_imp_mutt( _addressIndex_ );
5531 			if( abf ) {
5532 				ds = addrindex_index_add_datasource(
5533 					_addressIndex_, ADDR_IF_BOOK, abf );
5534 				ads = addressbook_create_ds_adapter(
5535 					ds, ADDR_BOOK, NULL );
5536 				addressbook_ads_set_name(
5537 					ads, addrbook_get_name( abf ) );
5538 				newNode = addressbook_add_object(
5539 					adapter->treeNode,
5540 					ADDRESS_OBJECT(ads) );
5541 				if( newNode ) {
5542 					gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
5543 						newNode );
5544 					addrbook.treeSelected = newNode;
5545 				}
5546 
5547 				/* Notify address completion */
5548 				invalidate_address_completion();
5549 			}
5550 		}
5551 	}
5552 }
5553 
5554 /**
5555  * Import Pine file.
5556  */
addressbook_import_pine_cb(GtkAction * action,gpointer data)5557 static void addressbook_import_pine_cb( GtkAction *action, gpointer data ) {
5558 	AddressDataSource *ds = NULL;
5559 	AdapterDSource *ads = NULL;
5560 	AddressBookFile *abf = NULL;
5561 	AdapterInterface *adapter;
5562 	GtkCMCTreeNode *newNode;
5563 
5564 	adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5565 	if( adapter ) {
5566 		if( adapter->treeNode ) {
5567 			abf = addressbook_imp_pine( _addressIndex_ );
5568 			if( abf ) {
5569 				ds = addrindex_index_add_datasource(
5570 					_addressIndex_, ADDR_IF_BOOK, abf );
5571 				ads = addressbook_create_ds_adapter(
5572 					ds, ADDR_BOOK, NULL );
5573 				addressbook_ads_set_name(
5574 					ads, addrbook_get_name( abf ) );
5575 				newNode = addressbook_add_object(
5576 					adapter->treeNode,
5577 					ADDRESS_OBJECT(ads) );
5578 				if( newNode ) {
5579 					gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
5580 						newNode );
5581 					addrbook.treeSelected = newNode;
5582 				}
5583 
5584 				/* Notify address completion */
5585 				invalidate_address_completion();
5586 			}
5587 		}
5588 	}
5589 }
5590 
5591 /**
5592  * Harvest addresses.
5593  * \param folderItem Folder to import.
5594  * \param sourceInd  Source indicator: FALSE - Folder, TRUE - Messages.
5595  * \param msgList    List of message numbers, or NULL to process folder.
5596  */
addressbook_harvest(FolderItem * folderItem,gboolean sourceInd,GList * msgList)5597 void addressbook_harvest(
5598 	FolderItem *folderItem, gboolean sourceInd, GList *msgList )
5599 {
5600 	AddressDataSource *ds = NULL;
5601 	AdapterDSource *ads = NULL;
5602 	AddressBookFile *abf = NULL;
5603 	AdapterInterface *adapter;
5604 	GtkCMCTreeNode *newNode;
5605 
5606 	abf = addrgather_dlg_execute(
5607 		folderItem, _addressIndex_, sourceInd, msgList );
5608 	if( abf ) {
5609 		ds = addrindex_index_add_datasource(
5610 			_addressIndex_, ADDR_IF_BOOK, abf );
5611 
5612 		adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5613 		if( adapter ) {
5614 			if( adapter->treeNode ) {
5615 				ads = addressbook_create_ds_adapter(
5616 					ds, ADDR_BOOK, addrbook_get_name( abf ) );
5617 				newNode = addressbook_add_object(
5618 						adapter->treeNode,
5619 						ADDRESS_OBJECT(ads) );
5620 				if (newNode == NULL) {
5621 					g_message("error adding addressbook object\n");
5622 				}
5623 			}
5624 		}
5625 
5626 		/* Notify address completion */
5627 		invalidate_address_completion();
5628 	}
5629 }
5630 
5631 /**
5632  * Export HTML file.
5633  */
addressbook_export_html_cb(GtkAction * action,gpointer data)5634 static void addressbook_export_html_cb( GtkAction *action, gpointer data ) {
5635 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
5636 	AddressObject *obj;
5637 	AddressDataSource *ds = NULL;
5638 	AddrBookBase *adbase;
5639 	AddressCache *cache;
5640 	GtkCMCTreeNode *node = NULL;
5641 
5642 	if( ! addrbook.treeSelected ) return;
5643 	node = addrbook.treeSelected;
5644 	if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
5645 	obj = gtk_cmctree_node_get_row_data( ctree, node );
5646 	if( obj == NULL ) return;
5647 
5648 	ds = addressbook_find_datasource( node );
5649 	if( ds == NULL ) return;
5650 	adbase = ( AddrBookBase * ) ds->rawDataSource;
5651 	cache = adbase->addressCache;
5652 	addressbook_exp_html( cache );
5653 }
5654 
5655 /**
5656  * Export LDIF file.
5657  */
addressbook_export_ldif_cb(GtkAction * action,gpointer data)5658 static void addressbook_export_ldif_cb( GtkAction *action, gpointer data ) {
5659 	GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
5660 	AddressObject *obj;
5661 	AddressDataSource *ds = NULL;
5662 	AddrBookBase *adbase;
5663 	AddressCache *cache;
5664 	GtkCMCTreeNode *node = NULL;
5665 
5666 	if( ! addrbook.treeSelected ) return;
5667 	node = addrbook.treeSelected;
5668 	if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
5669 	obj = gtk_cmctree_node_get_row_data( ctree, node );
5670 	if( obj == NULL ) return;
5671 
5672 	ds = addressbook_find_datasource( node );
5673 	if( ds == NULL ) return;
5674 	adbase = ( AddrBookBase * ) ds->rawDataSource;
5675 	cache = adbase->addressCache;
5676 	addressbook_exp_ldif( cache );
5677 }
5678 
addressbook_find_duplicates_cb(GtkAction * action,gpointer data)5679 static void addressbook_find_duplicates_cb(GtkAction *action, gpointer data)
5680 {
5681 	addrduplicates_find(GTK_WINDOW(addrbook.window));
5682 }
5683 
addressbook_edit_custom_attr_cb(GtkAction * action,gpointer data)5684 static void addressbook_edit_custom_attr_cb(GtkAction *action, gpointer data)
5685 {
5686 	addressbook_custom_attr_edit();
5687 }
5688 
addressbook_start_drag(GtkWidget * widget,gint button,GdkEvent * event,void * data)5689 static void addressbook_start_drag(GtkWidget *widget, gint button,
5690 				   GdkEvent *event,
5691 			           void *data)
5692 {
5693 	GdkDragContext *context;
5694 	if (addressbook_target_list == NULL)
5695 		addressbook_target_list = gtk_target_list_new(
5696 				addressbook_drag_types, 1);
5697 	context = gtk_drag_begin(widget, addressbook_target_list,
5698 				 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
5699 	gtk_drag_set_icon_default(context);
5700 }
5701 
addressbook_drag_data_get(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * selection_data,guint info,guint time,void * data)5702 static void addressbook_drag_data_get(GtkWidget        *widget,
5703 				     GdkDragContext   *drag_context,
5704 				     GtkSelectionData *selection_data,
5705 				     guint             info,
5706 				     guint             time,
5707 				     void	      *data)
5708 {
5709 	AddrItemObject *aio = NULL;
5710 	AddressObject *pobj = NULL;
5711 	AdapterDSource *ads = NULL;
5712 	AddressDataSource *ds = NULL;
5713 	GList *cur;
5714 
5715 	pobj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
5716 
5717 	if( pobj == NULL ) return;
5718 
5719 	if( pobj->type == ADDR_DATASOURCE ) {
5720 		ads = ADAPTER_DSOURCE(pobj);
5721 		ds = ads->dataSource;
5722 	} else if (pobj->type == ADDR_ITEM_GROUP) {
5723 
5724 		return;
5725 	}
5726 
5727 	else if( pobj->type != ADDR_INTERFACE ) {
5728 		ds = addressbook_find_datasource( addrbook.treeSelected );
5729 
5730 		if (!ds)
5731 			return;
5732 	}
5733 
5734 	for(cur = GTK_CMCLIST(addrbook.clist)->selection; cur; cur = cur->next) {
5735 		aio = (AddrItemObject *)gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.clist),
5736 			GTK_CMCTREE_NODE(cur->data));
5737 		while (aio && aio->type != ITEMTYPE_PERSON) {
5738 			aio = aio->parent;
5739 		}
5740        }
5741 
5742 	if (aio && aio->type == ITEMTYPE_PERSON) {
5743 		if( ds && ds->interface && ds->interface->readOnly)
5744 			gtk_selection_data_set(selection_data,
5745 				       gtk_selection_data_get_target(selection_data), 8,
5746 				       (const guchar *)"Dummy_addr_copy", 15);
5747 		else
5748 			gtk_selection_data_set(selection_data,
5749 				       gtk_selection_data_get_target(selection_data), 8,
5750 				       (const guchar *)"Dummy_addr_move", 15);
5751 	}
5752 }
5753 
addressbook_drag_motion_cb(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,void * data)5754 static gboolean addressbook_drag_motion_cb(GtkWidget      *widget,
5755 					  GdkDragContext *context,
5756 					  gint            x,
5757 					  gint            y,
5758 					  guint           time,
5759 					  void            *data)
5760 {
5761 	GtkAllocation allocation;
5762 	GtkRequisition requisition;
5763 	gint row, column;
5764 	GtkCMCTreeNode *node = NULL;
5765 	gboolean acceptable = FALSE;
5766 	gtk_widget_get_allocation(GTK_WIDGET(addrbook.ctree), &allocation);
5767 	gint height = allocation.height;
5768 	gtk_widget_get_requisition(GTK_WIDGET(addrbook.ctree), &requisition);
5769 	gint total_height = requisition.height;
5770 	GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
5771 				GTK_SCROLLED_WINDOW(addrbook.ctree_swin));
5772 	gfloat vpos = gtk_adjustment_get_value(pos);
5773 
5774 	if (gtk_cmclist_get_selection_info
5775 		(GTK_CMCLIST(widget), x - 24, y - 24, &row, &column)) {
5776 
5777 		if (y > height - 24 && height + vpos < total_height) {
5778 			gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
5779 			gtk_adjustment_changed(pos);
5780 		}
5781 		if (y < 24 && y > 0) {
5782 			gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
5783 			gtk_adjustment_changed(pos);
5784 		}
5785 		node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
5786 
5787 		if (node != NULL) {
5788 			AddressObject *obj = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node );
5789 			if( obj->type == ADDR_ITEM_FOLDER
5790 			|| obj->type == ADDR_ITEM_GROUP)
5791 				acceptable = TRUE;
5792 			else {
5793 				AdapterDSource *ads = NULL;
5794 				AddressDataSource *ds = NULL;
5795 				ads = ADAPTER_DSOURCE(obj);
5796 				if (ads == NULL ){ return FALSE;}
5797 				ds = ads->dataSource;
5798 				if (ds == NULL ) { return FALSE;}
5799 
5800 				acceptable = TRUE;
5801 			}
5802 		}
5803 	}
5804 
5805 	if (acceptable) {
5806 		g_signal_handlers_block_by_func
5807 			(G_OBJECT(widget),
5808 			 G_CALLBACK(addressbook_tree_selected), NULL);
5809 		gtk_sctree_select( GTK_SCTREE(widget), node);
5810 		g_signal_handlers_unblock_by_func
5811 			(G_OBJECT(widget),
5812 			 G_CALLBACK(addressbook_tree_selected), NULL);
5813 		gdk_drag_status(context,
5814 					(gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
5815 					GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
5816 	} else {
5817 		gdk_drag_status(context, 0, time);
5818 	}
5819 	return acceptable;
5820 }
5821 
addressbook_drag_leave_cb(GtkWidget * widget,GdkDragContext * context,guint time,void * data)5822 static void addressbook_drag_leave_cb(GtkWidget      *widget,
5823 				     GdkDragContext *context,
5824 				     guint           time,
5825 				     void           *data)
5826 {
5827 	if (addrbook.treeSelected) {
5828 		g_signal_handlers_block_by_func
5829 			(G_OBJECT(widget),
5830 			 G_CALLBACK(addressbook_tree_selected), NULL);
5831 		gtk_sctree_select( GTK_SCTREE(widget), addrbook.opened);
5832 		g_signal_handlers_unblock_by_func
5833 			(G_OBJECT(widget),
5834 			 G_CALLBACK(addressbook_tree_selected), NULL);
5835 	}
5836 
5837 }
5838 
addressbook_drag_received_cb(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint time,void * pdata)5839 static void addressbook_drag_received_cb(GtkWidget        *widget,
5840 					GdkDragContext   *drag_context,
5841 					gint              x,
5842 					gint              y,
5843 					GtkSelectionData *data,
5844 					guint             info,
5845 					guint             time,
5846 					void             *pdata)
5847 {
5848 	gint row, column;
5849 	GtkCMCTreeNode *node;
5850 	GtkCMCTreeNode *lastopened = addrbook.opened;
5851 
5852 	if (!strncmp(gtk_selection_data_get_data(data), "Dummy_addr", 10)) {
5853 		if (gtk_cmclist_get_selection_info
5854 			(GTK_CMCLIST(widget), x - 24, y - 24, &row, &column) == 0) {
5855 			return;
5856 		}
5857 
5858 		node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
5859 		if( !node || !gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree), node))
5860 			return;
5861 
5862 		gtk_cmclist_freeze(GTK_CMCLIST(addrbook.clist));
5863 		if (gdk_drag_context_get_selected_action(drag_context) == GDK_ACTION_COPY ||
5864 		    !strcmp(gtk_selection_data_get_data(data), "Dummy_addr_copy"))
5865 			addressbook_clip_copy_cb(NULL, NULL);
5866 		else
5867 			addressbook_clip_cut_cb(NULL, NULL);
5868 		gtk_sctree_select( GTK_SCTREE(addrbook.ctree), node);
5869 		addressbook_clip_paste_cb(NULL,NULL);
5870 		gtk_sctree_select( GTK_SCTREE(addrbook.ctree), lastopened);
5871 		gtk_cmclist_thaw(GTK_CMCLIST(addrbook.clist));
5872 		gtk_drag_finish(drag_context, TRUE, TRUE, time);
5873 	}
5874 }
5875 
5876 /*
5877 * End of Source.
5878 */
5879