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