2008-08-04 [colin] 3.5.0cvs48
[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_("_Message") },
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_MENU)
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                 if (strcmp2(person->nickName, ADDRITEM_NAME(person)))
3387                         addritem_person_set_nick_name( person, ADDRITEM_NAME(person));
3388 #endif
3389                 addressbook_folder_refresh_one_person( GTK_CMCTREE(addrbook.clist), person );
3390                 invalidate_address_completion();
3391         }
3392         addressbook_address_list_set_focus();
3393 }
3394
3395 void addressbook_address_list_set_focus( void )
3396 {
3397         if (!prefs_common.addressbook_use_editaddress_dialog) {
3398                 gtk_window_set_focus(GTK_WINDOW(addrbook.window), addrbook.clist);
3399                 addressbook_list_menu_setup();
3400         }
3401 }
3402
3403 void addressbook_address_list_disable_some_actions(void)
3404 {
3405         /* disable address copy/pasting when editing contact's detail (embedded form) */
3406         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Cut",   FALSE );
3407         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Copy",  FALSE );
3408         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Paste", FALSE );
3409 }
3410
3411 static void addressbook_edit_address_cb( GtkAction *action, gpointer data ) {
3412         addressbook_edit_address(data, 0, NULL, TRUE);
3413 }
3414         
3415 static void addressbook_edit_address( gpointer data, guint action, GtkWidget *widget,
3416                                                                           gboolean force_focus ) {
3417         GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
3418         GtkCMCTree *ctree;
3419         AddressObject *obj = NULL, *pobj = NULL;
3420         AddressDataSource *ds = NULL;
3421         GtkCMCTreeNode *node = NULL, *parentNode = NULL;
3422         gchar *name = NULL;
3423         AddressBookFile *abf = NULL;
3424
3425         if( addrbook.listSelected == NULL ) return;
3426         obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
3427         g_return_if_fail(obj != NULL);
3428
3429         ctree = GTK_CMCTREE( addrbook.ctree );
3430         pobj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
3431         node = gtk_cmctree_find_by_row_data( ctree, addrbook.treeSelected, obj );
3432
3433         ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
3434         if( ds == NULL ) return;
3435
3436         abf = addressbook_get_book_file();
3437         
3438         if( obj->type == ADDR_ITEM_EMAIL ) {
3439                 ItemEMail *email = ( ItemEMail * ) obj;
3440                 if( email == NULL ) return;
3441                 if( pobj && pobj->type == ADDR_ITEM_GROUP ) {
3442                         /* Edit parent group */
3443                         AdapterGroup *adapter = ADAPTER_GROUP(pobj);
3444                         ItemGroup *itemGrp = adapter->itemGroup;
3445                         if( abf == NULL ) return;
3446                         if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
3447                         name = ADDRITEM_NAME(itemGrp);
3448                         node = addrbook.treeSelected;
3449                         parentNode = GTK_CMCTREE_ROW(node)->parent;
3450                 }
3451                 else {
3452                         /* Edit person - email page */
3453                         ItemPerson *person;
3454                         person = ( ItemPerson * ) ADDRITEM_PARENT(email);
3455                         if  ( addressbook_edit_person( abf, NULL, person, TRUE, addrbook.editaddress_vbox,
3456                                                                                    addressbook_edit_address_post_cb,
3457                                                                                    (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
3458                                   != NULL ) { 
3459 #ifdef USE_LDAP
3460                                 if (abf && abf->type == ADDR_IF_LDAP) {
3461                                         ldapsvr_set_modified( (LdapServer *) abf, TRUE );
3462                                         person->status = UPDATE_ENTRY;
3463                                 }
3464 #endif
3465                                 if (prefs_common.addressbook_use_editaddress_dialog)
3466                                         addressbook_edit_address_post_cb( person );
3467                         }
3468                         return;
3469                 }
3470         }
3471         else if( obj->type == ADDR_ITEM_PERSON ) {
3472                 /* Edit person - basic page */
3473                 ItemPerson *person = ( ItemPerson * ) obj;
3474                 if( addressbook_edit_person( abf, NULL, person, FALSE, addrbook.editaddress_vbox,
3475                                                                           addressbook_edit_address_post_cb,
3476                                                                           (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
3477                         != NULL ) {
3478 #ifdef USE_LDAP
3479                                 if (abf && abf->type == ADDR_IF_LDAP) {
3480                                         ldapsvr_set_modified( (LdapServer *) abf, TRUE );
3481                                         person->status = UPDATE_ENTRY;
3482                                 }
3483 #endif
3484                                 if (prefs_common.addressbook_use_editaddress_dialog)
3485                                         addressbook_edit_address_post_cb( person );
3486                 }
3487                 return;
3488         }
3489         else if( obj->type == ADDR_ITEM_GROUP ) {
3490                 ItemGroup *itemGrp = ( ItemGroup * ) obj;
3491                 if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
3492                 parentNode = addrbook.treeSelected;
3493                 node = addressbook_find_group_node( parentNode, itemGrp );
3494                 name = ADDRITEM_NAME(itemGrp);
3495                 invalidate_address_completion();
3496         }
3497         else {
3498                 return;
3499         }
3500
3501         /* Update tree node with node name */
3502         if( node == NULL ) return;
3503         addressbook_change_node_name( node, name );
3504         gtk_sctree_sort_node( ctree, parentNode );
3505         gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened ); 
3506         addressbook_set_clist(
3507                 gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
3508                         addrbook.opened),
3509                 TRUE);
3510 }
3511
3512 static void addressbook_delete_address_cb(GtkAction *action, gpointer data)
3513 {
3514         addressbook_del_clicked(NULL, NULL);
3515 }
3516
3517 static void close_cb(GtkAction *action, gpointer data)
3518 {
3519         addressbook_close();
3520 }
3521
3522 static void addressbook_file_save_cb( GtkAction *action, gpointer data ) {
3523         addressbook_export_to_file();
3524 }
3525
3526 static void addressbook_person_expand_node( GtkCMCTree *ctree, GList *node, gpointer *data ) {
3527         if( node ) {
3528                 ItemPerson *person = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(node) );
3529                 if( person ) addritem_person_set_opened( person, TRUE );
3530         }
3531 }
3532
3533 static void addressbook_person_collapse_node( GtkCMCTree *ctree, GList *node, gpointer *data ) {
3534         if( node ) {
3535                 ItemPerson *person = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(node) );
3536                 if( person ) addritem_person_set_opened( person, FALSE );
3537         }
3538 }
3539
3540 static gchar *addressbook_format_item_clist( ItemPerson *person, ItemEMail *email ) {
3541         gchar *str = NULL;
3542         gchar *eMailAlias = ADDRITEM_NAME(email);
3543         if( eMailAlias && *eMailAlias != '\0' ) {
3544                 if( person ) {
3545                         str = g_strdup_printf( "%s - %s", ADDRITEM_NAME(person), eMailAlias );
3546                 }
3547                 else {
3548                         str = g_strdup( eMailAlias );
3549                 }
3550         }
3551         return str;
3552 }
3553
3554 static void addressbook_load_group( GtkCMCTree *clist, ItemGroup *itemGroup ) {
3555         GList *items = itemGroup->listEMail;
3556         AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3557         for( ; items != NULL; items = g_list_next( items ) ) {
3558                 GtkCMCTreeNode *nodeEMail = NULL;
3559                 gchar *text[N_LIST_COLS];
3560                 ItemEMail *email = items->data;
3561                 ItemPerson *person;
3562                 gchar *str = NULL;
3563
3564                 if( ! email ) continue;
3565
3566                 person = ( ItemPerson * ) ADDRITEM_PARENT(email);
3567                 str = addressbook_format_item_clist( person, email );
3568                 if( str ) {
3569                         text[COL_NAME] = addressbook_set_col_name_guard(str);
3570                 }
3571                 else {
3572                         text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3573                 }
3574                 text[COL_ADDRESS] = email->address;
3575                 text[COL_REMARKS] = email->remarks;
3576                 nodeEMail = gtk_sctree_insert_node(
3577                                 clist, NULL, NULL,
3578                                 text, FOLDER_SPACING,
3579                                 atci->iconXpm, atci->maskXpm,
3580                                 atci->iconXpmOpen, atci->maskXpmOpen,
3581                                 FALSE, FALSE );
3582                 gtk_cmctree_node_set_row_data( clist, nodeEMail, email );
3583                 g_free( str );
3584                 str = NULL;
3585         }
3586 }
3587
3588 gchar *addressbook_set_col_name_guard(gchar *value)
3589 {
3590         gchar *ret = "<not set>";
3591         gchar *tmp = g_strdup(value);
3592         g_strstrip(tmp);
3593         if (tmp !=NULL && *tmp != '\0')
3594                 ret = value;
3595         g_free(tmp);
3596         return ret;
3597 }
3598
3599 static void addressbook_folder_load_one_person(
3600                 GtkCMCTree *clist, ItemPerson *person,
3601                 AddressTypeControlItem *atci,
3602                 AddressTypeControlItem *atciMail )
3603 {
3604         GtkCMCTreeNode *nodePerson = NULL;
3605         GtkCMCTreeNode *nodeEMail = NULL;
3606         gchar *text[N_LIST_COLS];
3607         gboolean flgFirst = TRUE, haveAddr = FALSE;
3608         GList *node;
3609 #ifdef USE_LDAP
3610         AddressBookFile *abf = addressbook_get_book_file();
3611 #endif
3612
3613         if( person == NULL ) return;
3614
3615         text[COL_NAME] = "";
3616         node = person->listEMail;
3617         while( node ) {
3618                 ItemEMail *email = node->data;
3619                 gchar *eMailAddr = NULL;
3620                 node = g_list_next( node );
3621
3622                 text[COL_ADDRESS] = email->address;
3623                 text[COL_REMARKS] = email->remarks;
3624                 eMailAddr = ADDRITEM_NAME(email);
3625                 if( eMailAddr && *eMailAddr == '\0' ) eMailAddr = NULL;
3626                 if( flgFirst ) {
3627                         /* First email belongs with person */
3628                         gchar *str = addressbook_format_item_clist( person, email );
3629                         if( str ) {
3630                                 text[COL_NAME] = addressbook_set_col_name_guard(str);
3631                         }
3632 #ifdef USE_LDAP
3633                         else if( abf && abf->type == ADDR_IF_LDAP && 
3634                                  person && person->nickName ) {
3635                                 if (person->nickName) {
3636                                         if (strcmp(person->nickName, "") != 0) {
3637                                                 text[COL_NAME] = addressbook_set_col_name_guard(person->nickName);
3638                                         }
3639                                         else {
3640                                                 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3641                                         }
3642                                 }
3643                         }
3644 #endif
3645                         else {
3646                                 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3647                         }
3648                         nodePerson = gtk_sctree_insert_node(
3649                                         clist, NULL, NULL,
3650                                         text, FOLDER_SPACING,
3651                                         atci->iconXpm, atci->maskXpm,
3652                                         atci->iconXpmOpen, atci->maskXpmOpen,
3653                                         FALSE, person->isOpened );
3654                         g_free( str );
3655                         str = NULL;
3656                         gtk_cmctree_node_set_row_data(clist, nodePerson, person );
3657                 }
3658                 else {
3659                         /* Subsequent email is a child node of person */
3660                         text[COL_NAME] = ADDRITEM_NAME(email);
3661                         nodeEMail = gtk_sctree_insert_node(
3662                                         clist, nodePerson, NULL,
3663                                         text, FOLDER_SPACING,
3664                                         atciMail->iconXpm, atciMail->maskXpm,
3665                                         atciMail->iconXpmOpen, atciMail->maskXpmOpen,
3666                                         FALSE, TRUE );
3667                         gtk_cmctree_node_set_row_data(clist, nodeEMail, email );
3668                 }
3669                 flgFirst = FALSE;
3670                 haveAddr = TRUE;
3671         }
3672         if( ! haveAddr ) {
3673                 /* Have name without EMail */
3674                 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3675                 text[COL_ADDRESS] = "";
3676                 text[COL_REMARKS] = "";
3677                 nodePerson = gtk_sctree_insert_node(
3678                                 clist, NULL, NULL,
3679                                 text, FOLDER_SPACING,
3680                                 atci->iconXpm, atci->maskXpm,
3681                                 atci->iconXpmOpen, atci->maskXpmOpen,
3682                                 FALSE, person->isOpened );
3683                 gtk_cmctree_node_set_row_data(clist, nodePerson, person );
3684         }
3685         return;
3686 }
3687
3688 static void addressbook_folder_load_person( GtkCMCTree *clist, ItemFolder *itemFolder ) {
3689         GList *items;
3690         AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
3691         AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3692
3693         if( atci == NULL ) return;
3694         if( atciMail == NULL ) return;
3695
3696         /* Load email addresses */
3697         items = addritem_folder_get_person_list( itemFolder );
3698         for( ; items != NULL; items = g_list_next( items ) ) {
3699                 addressbook_folder_load_one_person( clist, items->data, atci, atciMail );
3700         }
3701         /* Free up the list */
3702         mgu_clear_list( items );
3703         g_list_free( items );
3704 }
3705
3706 static void addressbook_folder_remove_node( GtkCMCTree *clist, GtkCMCTreeNode *node ) { 
3707         addrbook.listSelected = NULL;
3708         gtk_cmctree_remove_node( clist, node );
3709         addressbook_menubar_set_sensitive( FALSE );
3710         addressbook_menuitem_set_sensitive(
3711                 gtk_cmctree_node_get_row_data(
3712                         GTK_CMCTREE(clist), addrbook.treeSelected ),
3713                 addrbook.treeSelected );
3714 }
3715
3716 static void addressbook_folder_refresh_one_person( GtkCMCTree *clist, ItemPerson *person ) {
3717         AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
3718         AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3719         GtkCMCTreeNode *node;
3720         if( atci == NULL ) return;
3721         if( atciMail == NULL ) return;
3722         if( person == NULL ) return;
3723         /* unload the person */
3724         
3725         node = gtk_cmctree_find_by_row_data( clist, NULL, person );
3726         if( node )
3727                 addressbook_folder_remove_node( clist, node );
3728         addressbook_folder_load_one_person( clist, person, atci, atciMail );
3729         gtk_sctree_sort_node( clist, NULL );
3730         node = gtk_cmctree_find_by_row_data( clist, NULL, person );
3731         if( node ) {
3732                 gtk_sctree_select( GTK_SCTREE(clist), node );
3733                 if (!gtk_cmctree_node_is_visible( clist, node ) ) 
3734                         gtk_cmctree_node_moveto( clist, node, 0, 0, 0 );
3735         }
3736 }
3737
3738 static void addressbook_folder_remove_one_person( GtkCMCTree *clist, ItemPerson *person ) {
3739         GtkCMCTreeNode *node;
3740         gint row;
3741         
3742         if( person == NULL ) return;
3743         node = gtk_cmctree_find_by_row_data( clist, NULL, person );
3744         row  = gtk_cmclist_find_row_from_data( GTK_CMCLIST(clist), person );
3745         if( node ) {
3746                 addressbook_folder_remove_node( clist, node );
3747         }
3748 }
3749
3750 static void addressbook_folder_load_group( GtkCMCTree *clist, ItemFolder *itemFolder ) {
3751         GList *items;
3752         AddressTypeControlItem *atci =  addrbookctl_lookup( ADDR_ITEM_GROUP );
3753
3754         /* Load any groups */
3755         if( ! atci ) return;
3756         items = addritem_folder_get_group_list( itemFolder );
3757         for( ; items != NULL; items = g_list_next( items ) ) {
3758                 GtkCMCTreeNode *nodeGroup = NULL;
3759                 gchar *text[N_LIST_COLS];
3760                 ItemGroup *group = items->data;
3761                 if( group == NULL ) continue;
3762                 text[COL_NAME] = ADDRITEM_NAME(group);
3763                 text[COL_ADDRESS] = "";
3764                 text[COL_REMARKS] = "";
3765                 nodeGroup = gtk_sctree_insert_node(clist, NULL, NULL,
3766                                       text, FOLDER_SPACING,
3767                                       atci->iconXpm, atci->maskXpm,
3768                                       atci->iconXpmOpen, atci->maskXpmOpen,
3769                                       FALSE, FALSE);
3770                 gtk_cmctree_node_set_row_data(clist, nodeGroup, group );
3771                 gtk_sctree_sort_node(clist, NULL);
3772         }
3773         /* Free up the list */
3774         mgu_clear_list( items );
3775         g_list_free( items );
3776 }
3777
3778 /**
3779  * Search ctree widget callback function.
3780  * \param  pA Pointer to node.
3781  * \param  pB Pointer to data item being sought.
3782  * \return Zero (0) if group found.
3783  */
3784 static int addressbook_treenode_find_group_cb( gconstpointer pA, gconstpointer pB ) {
3785         AddressObject *aoA;
3786
3787         aoA = ( AddressObject * ) pA;
3788         if( aoA->type == ADDR_ITEM_GROUP ) {
3789                 ItemGroup *group, *grp;
3790
3791                 grp = ADAPTER_GROUP(aoA)->itemGroup;
3792                 group = ( ItemGroup * ) pB;
3793                 if( grp == group ) return 0;    /* Found group */
3794         }
3795         return 1;
3796 }
3797
3798 /*
3799 * Remove folder and group nodes from tree widget for items contained ("cut")
3800 * in clipboard.
3801 */
3802 static void addressbook_treenode_remove_item( void ) {
3803         GList *node;
3804         AddrSelectItem *cutItem;
3805         AddressCache *cache;
3806         AddrItemObject *aio;
3807         GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
3808         GtkCMCTreeNode *tn;
3809
3810         node = _clipBoard_->objectList;
3811         while( node ) {
3812                 cutItem = node->data;
3813                 node = g_list_next( node );
3814                 cache = addrindex_get_cache(
3815                         _clipBoard_->addressIndex, cutItem->cacheID );
3816                 if( cache == NULL ) continue;
3817                 aio = addrcache_get_object( cache, cutItem->uid );
3818                 if( aio ) {
3819                         tn = NULL;
3820                         if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
3821                                 ItemFolder *folder;
3822
3823                                 folder = ( ItemFolder * ) aio;
3824                                 tn = gtk_cmctree_find_by_row_data_custom(
3825                                         ctree, NULL, folder,
3826                                         addressbook_treenode_find_folder_cb );
3827                         }
3828                         else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
3829                                 ItemGroup *group;
3830
3831                                 group = ( ItemGroup * ) aio;
3832                                 tn = gtk_cmctree_find_by_row_data_custom(
3833                                         ctree, NULL, group,
3834                                         addressbook_treenode_find_group_cb );
3835                         }
3836
3837                         if( tn ) {
3838                                 /* Free up adapter and remove node. */
3839                                 gtk_cmctree_remove_node( ctree, tn );
3840                         }
3841                 }
3842         }
3843 }
3844
3845 /**
3846  * Find parent datasource for specified tree node.
3847  * \param  node Node to test.
3848  * \return Data source, or NULL if not found.
3849  */
3850 static AddressDataSource *addressbook_find_datasource( GtkCMCTreeNode *node ) {
3851         AddressDataSource *ds = NULL;
3852         AddressObject *ao;
3853
3854         g_return_val_if_fail(addrbook.ctree != NULL, NULL);
3855
3856         while( node ) {
3857                 if( GTK_CMCTREE_ROW(node)->level < 2 ) return NULL;
3858                 ao = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), node );
3859                 if( ao ) {
3860                         /* g_print( "ao->type = %d\n", ao->type ); */
3861                         if( ao->type == ADDR_DATASOURCE ) {
3862                                 AdapterDSource *ads = ADAPTER_DSOURCE(ao);
3863                                 /* g_print( "found it\n" ); */
3864                                 ds = ads->dataSource;
3865                                 break;
3866                         }
3867                 }
3868                 node = GTK_CMCTREE_ROW(node)->parent;
3869         }
3870         return ds;
3871 }
3872
3873 /**
3874  * Load address list widget with children of specified object.
3875  * \param obj Parent object to be loaded.
3876  */
3877 static void addressbook_set_clist( AddressObject *obj, gboolean refresh ) {
3878         GtkCMCTree *ctreelist = GTK_CMCTREE(addrbook.clist);
3879         GtkCMCList *clist = GTK_CMCLIST(addrbook.clist);
3880         AddressDataSource *ds = NULL;
3881         AdapterDSource *ads = NULL;
3882         static AddressObject *last_obj = NULL;
3883
3884         if (addrbook.clist == NULL) {
3885                 return;
3886         }
3887         if (obj == last_obj && !refresh)
3888                 return;
3889
3890         last_obj = obj;
3891         if( obj == NULL ) {
3892                 gtk_cmclist_clear(clist);
3893                 return;
3894         }
3895
3896         if( obj->type == ADDR_INTERFACE ) {
3897                 /* g_print( "set_clist: loading datasource...\n" ); */
3898                 /* addressbook_node_load_datasource( GTK_CMCTREE(clist), obj ); */
3899                 return;
3900         }
3901
3902         gtk_cmclist_freeze(clist);
3903         gtk_cmclist_clear(clist);
3904
3905         if( obj->type == ADDR_DATASOURCE ) {
3906                 ads = ADAPTER_DSOURCE(obj);
3907                 ds = ADAPTER_DSOURCE(obj)->dataSource;
3908                 if( ds ) {
3909                         /* Load root folder */
3910                         ItemFolder *rootFolder = NULL;
3911                         rootFolder = addrindex_ds_get_root_folder( ds );
3912                         addressbook_folder_load_person(
3913                                 ctreelist, addrindex_ds_get_root_folder( ds ) );
3914                         addressbook_folder_load_group(
3915                                 ctreelist, addrindex_ds_get_root_folder( ds ) );
3916                 }
3917         }
3918         else {
3919                 if( obj->type == ADDR_ITEM_GROUP ) {
3920                         /* Load groups */
3921                         ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup;
3922                         addressbook_load_group( ctreelist, itemGroup );
3923                 }
3924                 else if( obj->type == ADDR_ITEM_FOLDER ) {
3925                         /* Load folders */
3926                         ItemFolder *itemFolder = ADAPTER_FOLDER(obj)->itemFolder;
3927                         addressbook_folder_load_person( ctreelist, itemFolder );
3928                         addressbook_folder_load_group( ctreelist, itemFolder );
3929                 }
3930         }
3931         gtk_sctree_sort_recursive(GTK_CMCTREE(clist), NULL);
3932         clist->focus_row = -1;
3933         gtk_cmclist_thaw(clist);
3934 }
3935
3936 /**
3937  * Call back function to free adaptor. Call back is setup by function
3938  * gtk_cmctree_node_set_row_data_full() when node is populated. This function is
3939  * called when the address book tree widget node is removed by calling
3940  * function gtk_cmctree_remove_node().
3941  * 
3942  * \param data Tree node's row data.
3943  */
3944 static void addressbook_free_treenode( gpointer data ) {
3945         AddressObject *ao;
3946
3947         ao = ( AddressObject * ) data;
3948         if( ao == NULL ) return;
3949         if( ao->type == ADDR_INTERFACE ) {
3950                 AdapterInterface *ai = ADAPTER_INTERFACE(ao);
3951                 addrbookctl_free_interface( ai );
3952         }
3953         else if( ao->type == ADDR_DATASOURCE ) {
3954                 AdapterDSource *ads = ADAPTER_DSOURCE(ao);
3955                 addrbookctl_free_datasource( ads );
3956         }
3957         else if( ao->type == ADDR_ITEM_FOLDER ) {
3958                 AdapterFolder *af = ADAPTER_FOLDER(ao);
3959                 addrbookctl_free_folder( af );
3960         }
3961         else if( ao->type == ADDR_ITEM_GROUP ) {
3962                 AdapterGroup *ag = ADAPTER_GROUP(ao);
3963                 addrbookctl_free_group( ag );
3964         }
3965 }
3966
3967 /*
3968 * Create new adaptor for specified data source.
3969 */
3970 AdapterDSource *addressbook_create_ds_adapter( AddressDataSource *ds,
3971                                 AddressObjectType otype, gchar *name )
3972 {
3973         AdapterDSource *adapter = g_new0( AdapterDSource, 1 );
3974         ADDRESS_OBJECT(adapter)->type = ADDR_DATASOURCE;
3975         ADDRESS_OBJECT_NAME(adapter) = g_strdup( name );
3976         adapter->dataSource = ds;
3977         adapter->subType = otype;
3978         return adapter;
3979 }
3980
3981 void addressbook_ads_set_name( AdapterDSource *adapter, gchar *value ) {
3982         ADDRESS_OBJECT_NAME(adapter) =
3983                 mgu_replace_string( ADDRESS_OBJECT_NAME(adapter), value );
3984 }
3985
3986 /*
3987  * Load tree from address index with the initial data.
3988  */
3989 static void addressbook_load_tree( void ) {
3990         GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
3991         GList *nodeIf, *nodeDS;
3992         AdapterInterface *adapter;
3993         AddressInterface *iface;
3994         AddressTypeControlItem *atci;
3995         AddressDataSource *ds;
3996         AdapterDSource *ads;
3997         GtkCMCTreeNode *node, *newNode;
3998         gchar *name;
3999
4000         nodeIf = _addressInterfaceList_;
4001         while( nodeIf ) {
4002                 adapter = nodeIf->data;
4003                 node = adapter->treeNode;
4004                 iface = adapter->interface;
4005                 atci = adapter->atci;
4006                 if( iface ) {
4007                         if( iface->useInterface ) {
4008                                 /* Load data sources below interface node */
4009                                 nodeDS = iface->listSource;
4010                                 while( nodeDS ) {
4011                                         ds = nodeDS->data;
4012                                         newNode = NULL;
4013                                         name = addrindex_ds_get_name( ds );
4014                                         ads = addressbook_create_ds_adapter(
4015                                                         ds, atci->objectType, name );
4016                                         newNode = addressbook_add_object(
4017                                                         node, ADDRESS_OBJECT(ads) );
4018                                         nodeDS = g_list_next( nodeDS );
4019                                 }
4020                                 gtk_cmctree_expand( ctree, node );
4021                         }
4022                 }
4023                 nodeIf = g_list_next( nodeIf );
4024         }
4025 }
4026
4027 /*
4028  * Convert the old address book to new format.
4029  */
4030 static gboolean addressbook_convert( AddressIndex *addrIndex ) {
4031         gboolean retVal = FALSE;
4032         gboolean errFlag = TRUE;
4033         gchar *msg = NULL;
4034
4035         /* Read old address book, performing conversion */
4036         debug_print( "Reading and converting old address book...\n" );
4037         addrindex_set_file_name( addrIndex, ADDRESSBOOK_OLD_FILE );
4038         addrindex_read_data( addrIndex );
4039         if( addrIndex->retVal == MGU_NO_FILE ) {
4040                 /* We do not have a file - new user */
4041                 debug_print( "New user... create new books...\n" );
4042                 addrindex_create_new_books( addrIndex );
4043                 if( addrIndex->retVal == MGU_SUCCESS ) {
4044                         /* Save index file */
4045                         addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4046                         addrindex_save_data( addrIndex );
4047                         if( addrIndex->retVal == MGU_SUCCESS ) {
4048                                 retVal = TRUE;
4049                                 errFlag = FALSE;
4050                         }
4051                         else {
4052                                 msg = _( "New user, could not save index file." );
4053                         }
4054                 }
4055                 else {
4056                         msg = _( "New user, could not save address book files." );
4057                 }
4058         }
4059         else {
4060                 /* We have an old file */
4061                 if( addrIndex->wasConverted ) {
4062                         /* Converted successfully - save address index */
4063                         addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4064                         addrindex_save_data( addrIndex );
4065                         if( addrIndex->retVal == MGU_SUCCESS ) {
4066                                 msg = _( "Old address book converted successfully." );
4067                                 retVal = TRUE;
4068                                 errFlag = FALSE;
4069                         }
4070                         else {
4071                                 msg = _("Old address book converted,\n"
4072                                         "could not save new address index file." );
4073                         }
4074                 }
4075                 else {
4076                         /* File conversion failed - just create new books */
4077                         debug_print( "File conversion failed... just create new books...\n" );
4078                         addrindex_create_new_books( addrIndex );
4079                         if( addrIndex->retVal == MGU_SUCCESS ) {
4080                                 /* Save index */
4081                                 addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4082                                 addrindex_save_data( addrIndex );
4083                                 if( addrIndex->retVal == MGU_SUCCESS ) {
4084                                         msg = _("Could not convert address book,\n"
4085                                                 "but created empty new address book files." );
4086                                         retVal = TRUE;
4087                                         errFlag = FALSE;
4088                                 }
4089                                 else {
4090                                         msg = _("Could not convert address book,\n"
4091                                                 "could not save new address index file." );
4092                                 }
4093                         }
4094                         else {
4095                                 msg = _("Could not convert address book\n"
4096                                         "and could not create new address book files." );
4097                         }
4098                 }
4099         }
4100         if( errFlag ) {
4101                 debug_print( "Error\n%s\n", msg );
4102                 alertpanel_full(_("Addressbook conversion error"), msg,
4103                                 GTK_STOCK_CLOSE, NULL, NULL, FALSE,
4104                                 NULL, ALERT_ERROR, G_ALERTDEFAULT);
4105         }
4106         else if( msg ) {
4107                 debug_print( "Warning\n%s\n", msg );
4108                 alertpanel_full(_("Addressbook conversion error"), msg,
4109                                 GTK_STOCK_CLOSE, NULL, NULL, FALSE,
4110                                 NULL, ALERT_WARNING, G_ALERTDEFAULT);
4111         }
4112
4113         return retVal;
4114 }
4115
4116 static gboolean migrate_addrbook(const gchar *origdir, const gchar *destdir)
4117 {
4118         DIR *dp;
4119         struct dirent *d;
4120         gboolean failed = FALSE;
4121
4122         if( ( dp = opendir( origdir ) ) == NULL ) {
4123                 return FALSE;
4124         }
4125         
4126         while( ( d = readdir( dp ) ) != NULL ) {
4127                 if (strncmp(d->d_name, "addrbook-", strlen("addrbook-")))
4128                         continue;
4129                 else {
4130                         gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S, 
4131                                         d->d_name, NULL);
4132                         gchar *dest_file = g_strconcat(destdir, G_DIR_SEPARATOR_S, 
4133                                         d->d_name, NULL);
4134                         if (copy_file(orig_file, dest_file, FALSE) < 0) {
4135                                 failed = TRUE;
4136                         }
4137                         g_free(orig_file);
4138                         g_free(dest_file);
4139                         if (failed) {
4140                                 break;
4141                         }
4142                 }
4143         }
4144
4145         closedir( dp );
4146         if (!failed) {
4147                 /* all copies succeeded, we can remove source files */
4148                 if( ( dp = opendir( origdir ) ) == NULL ) {
4149                         return FALSE;
4150                 }
4151                 while( ( d = readdir( dp ) ) != NULL ) {
4152                         if (strncmp(d->d_name, "addrbook-", strlen("addrbook-")))
4153                                 continue;
4154                         else {
4155                                 gchar *orig_file = g_strconcat(origdir, G_DIR_SEPARATOR_S, 
4156                                                 d->d_name, NULL);
4157                                 claws_unlink(orig_file);
4158                                 g_free(orig_file);
4159                         }
4160                 }
4161                 closedir( dp );
4162         }
4163         
4164         return !failed;
4165 }
4166
4167 void addressbook_read_file( void ) {
4168         AddressIndex *addrIndex = NULL;
4169         gchar *indexdir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRBOOK_DIR, NULL);
4170         
4171         debug_print( "Reading address index...\n" );
4172         if( _addressIndex_ ) {
4173                 debug_print( "address book already read!!!\n" );
4174                 return;
4175         }
4176
4177         addrIndex = addrindex_create_index();
4178         addrindex_initialize();
4179
4180         /* Use new address book index. */
4181         
4182         if ( !is_dir_exist(indexdir) ) {
4183                 if ( make_dir(indexdir) < 0 ) {
4184                         addrindex_set_file_path( addrIndex, get_rc_dir() );
4185                         g_warning( "couldn't create dir %s\n", indexdir);
4186                 } else {
4187                         if (!migrate_addrbook(get_rc_dir(), indexdir)) {
4188                                 remove_dir_recursive(indexdir);
4189                                 addrindex_set_file_path( addrIndex, get_rc_dir() );
4190                                 g_error("couldn't migrate dir %s", indexdir);
4191                         } else {
4192                                 addrindex_set_file_path( addrIndex, indexdir);
4193                         }
4194                 }
4195         } else {
4196                 addrindex_set_file_path( addrIndex, indexdir);
4197         }
4198         g_free(indexdir);
4199         addrindex_set_file_name( addrIndex, ADDRESSBOOK_INDEX_FILE );
4200         addrindex_read_data( addrIndex );
4201         if( addrIndex->retVal == MGU_NO_FILE ) {
4202                 /* Conversion required */
4203                 debug_print( "Converting...\n" );
4204                 if( addressbook_convert( addrIndex ) ) {
4205                         _addressIndex_ = addrIndex;
4206                 }
4207         }
4208         else if( addrIndex->retVal == MGU_SUCCESS ) {
4209                 _addressIndex_ = addrIndex;
4210         }
4211         else {
4212                 /* Error reading address book */
4213                 debug_print( "Could not read address index.\n" );
4214                 addrindex_print_index( addrIndex, stdout );
4215                 alertpanel_full(_("Addressbook Error"),
4216                                 _("Could not read address index"),
4217                                 GTK_STOCK_CLOSE, NULL, NULL, FALSE,
4218                                 NULL, ALERT_ERROR, G_ALERTDEFAULT);
4219         }
4220         debug_print( "done.\n" );
4221 }
4222
4223 /*
4224 * Add object into the address index tree widget.
4225 * Enter: node   Parent node.
4226 *        obj    Object to add.
4227 * Return: Node that was added, or NULL if object not added.
4228 */
4229 static GtkCMCTreeNode *addressbook_add_object(GtkCMCTreeNode *node,
4230                                             AddressObject *obj)
4231 {
4232         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
4233         GtkCMCTreeNode *added;
4234         AddressObject *pobj;
4235         AddressObjectType otype;
4236         AddressTypeControlItem *atci = NULL;
4237
4238         g_return_val_if_fail(node != NULL, NULL);
4239         g_return_val_if_fail(obj  != NULL, NULL);
4240
4241         pobj = gtk_cmctree_node_get_row_data(ctree, node);
4242         g_return_val_if_fail(pobj != NULL, NULL);
4243
4244         /* Determine object type to be displayed */
4245         if( obj->type == ADDR_DATASOURCE ) {
4246                 otype = ADAPTER_DSOURCE(obj)->subType;
4247         }
4248         else {
4249                 otype = obj->type;
4250         }
4251
4252         /* Handle any special conditions. */
4253         added = node;
4254         atci = addrbookctl_lookup( otype );
4255         if( atci ) {
4256                 if( atci->showInTree ) {
4257                         /* Add object to tree */
4258                         gchar **name;
4259                         name = &obj->name;
4260                         added = gtk_sctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
4261                                 atci->iconXpm, atci->maskXpm, atci->iconXpmOpen, atci->maskXpmOpen,
4262                                 atci->treeLeaf, atci->treeExpand );
4263                         gtk_cmctree_node_set_row_data_full( ctree, added, obj,
4264                                 addressbook_free_treenode );
4265                 }
4266         }
4267
4268         gtk_sctree_sort_node(ctree, node);
4269
4270         return added;
4271 }
4272
4273 /**
4274  * Add group into the address index tree.
4275  * \param  node      Parent node.
4276  * \param  ds        Data source.
4277  * \param  itemGroup Group to add.
4278  * \return Inserted node.
4279  */
4280 static GtkCMCTreeNode *addressbook_node_add_group(
4281                 GtkCMCTreeNode *node, AddressDataSource *ds,
4282                 ItemGroup *itemGroup )
4283 {
4284         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
4285         GtkCMCTreeNode *newNode;
4286         AdapterGroup *adapter;
4287         AddressTypeControlItem *atci = NULL;
4288         gchar **name;
4289
4290         if( ds == NULL ) return NULL;
4291         if( node == NULL || itemGroup == NULL ) return NULL;
4292
4293         name = &itemGroup->obj.name;
4294
4295         atci = addrbookctl_lookup( ADDR_ITEM_GROUP );
4296
4297         adapter = g_new0( AdapterGroup, 1 );
4298         ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_GROUP;
4299         ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemGroup) );
4300         adapter->itemGroup = itemGroup;
4301
4302         newNode = gtk_sctree_insert_node( ctree, node, NULL, name, FOLDER_SPACING,
4303                         atci->iconXpm, atci->maskXpm, atci->iconXpm, atci->maskXpm,
4304                         atci->treeLeaf, atci->treeExpand );
4305         gtk_cmctree_node_set_row_data_full( ctree, newNode, adapter,
4306                 addressbook_free_treenode );
4307         gtk_sctree_sort_node( ctree, node );
4308         return newNode;
4309 }
4310
4311 /**
4312  * Add folder into the address index tree. Only visible folders are loaded into
4313  * the address index tree. Note that the root folder is not inserted into the
4314  * tree.
4315  *
4316  * \param  node       Parent node.
4317  * \param  ds         Data source.
4318  * \param  itemFolder Folder to add.
4319  * \param  otype      Object type to display.
4320  * \return Inserted node for the folder.
4321 */
4322 static GtkCMCTreeNode *addressbook_node_add_folder(
4323                 GtkCMCTreeNode *node, AddressDataSource *ds,
4324                 ItemFolder *itemFolder, AddressObjectType otype )
4325 {
4326         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
4327         GtkCMCTreeNode *newNode = NULL;
4328         AdapterFolder *adapter;
4329         AddressTypeControlItem *atci = NULL;
4330         GList *listItems = NULL;
4331         gchar *name;
4332         ItemFolder *rootFolder;
4333
4334         /* Only visible folders */
4335         if( itemFolder->isHidden ) return NULL;
4336
4337         if( ds == NULL ) return NULL;
4338         if( node == NULL || itemFolder == NULL ) return NULL;
4339
4340         /* Determine object type */
4341         atci = addrbookctl_lookup( otype );
4342         if( atci == NULL ) return NULL;
4343
4344         rootFolder = addrindex_ds_get_root_folder( ds );
4345         if( itemFolder == rootFolder ) {
4346                 newNode = node;
4347         }
4348         else {
4349                 adapter = g_new0( AdapterFolder, 1 );
4350                 ADDRESS_OBJECT_TYPE(adapter) = ADDR_ITEM_FOLDER;
4351                 ADDRESS_OBJECT_NAME(adapter) = g_strdup( ADDRITEM_NAME(itemFolder) );
4352                 adapter->itemFolder = itemFolder;
4353
4354                 name = ADDRITEM_NAME(itemFolder);
4355                 newNode = gtk_sctree_insert_node( ctree, node, NULL, &name, FOLDER_SPACING,
4356                                 atci->iconXpm, atci->maskXpm, atci->iconXpm, atci->maskXpm,
4357                                 atci->treeLeaf, atci->treeExpand );
4358                 if( newNode ) {
4359                         gtk_cmctree_node_set_row_data_full( ctree, newNode, adapter,
4360                                 addressbook_free_treenode );
4361                 }
4362         }
4363
4364         listItems = itemFolder->listFolder;
4365         while( listItems ) {
4366                 ItemFolder *item = listItems->data;
4367                 addressbook_node_add_folder( newNode, ds, item, otype );
4368                 listItems = g_list_next( listItems );
4369         }
4370         listItems = itemFolder->listGroup;
4371         while( listItems ) {
4372                 ItemGroup *item = listItems->data;
4373                 addressbook_node_add_group( newNode, ds, item );
4374                 listItems = g_list_next( listItems );
4375         }
4376         gtk_sctree_sort_node( ctree, node );
4377         return newNode;
4378 }
4379
4380 void addressbook_export_to_file( void ) {
4381         if( _addressIndex_ ) {
4382                 /* Save all new address book data */
4383                 debug_print( "Saving address books...\n" );
4384                 addrindex_save_all_books( _addressIndex_ );
4385
4386                 debug_print( "Exporting addressbook to file...\n" );
4387                 addrindex_save_data( _addressIndex_ );
4388                 if( _addressIndex_->retVal != MGU_SUCCESS ) {
4389                         addrindex_print_index( _addressIndex_, stdout );
4390                 }
4391
4392                 /* Notify address completion of new data */
4393                 invalidate_address_completion();
4394         }
4395 }
4396
4397 static gboolean addressbook_entry_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
4398 {
4399         if (event && (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter))
4400                 addressbook_lup_clicked(NULL, NULL);
4401         return FALSE;
4402 }
4403
4404 /*
4405 * Comparison using cell contents (text in first column). Used for sort
4406 * address index widget.
4407 */
4408 static gint addressbook_treenode_compare_func(
4409         GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
4410 {
4411         GtkCMCell *cell1 = ((GtkCMCListRow *)ptr1)->cell;
4412         GtkCMCell *cell2 = ((GtkCMCListRow *)ptr2)->cell;
4413         gchar *name1 = NULL, *name2 = NULL;
4414         if( cell1 ) name1 = cell1->u.text;
4415         if( cell2 ) name2 = cell2->u.text;
4416         if( ! name1 ) return ( name2 != NULL );
4417         if( ! name2 ) return -1;
4418         return g_utf8_collate( name1, name2 );
4419 }
4420
4421 static void addressbook_new_book_cb( GtkAction *action, gpointer data ) {
4422         AdapterDSource *ads;
4423         AdapterInterface *adapter;
4424         GtkCMCTreeNode *newNode;
4425
4426         adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
4427         if( adapter == NULL ) return;
4428         ads = addressbook_edit_book( _addressIndex_, NULL );
4429         if( ads ) {
4430                 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4431                 if( newNode ) {
4432                         gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4433                         addrbook.treeSelected = newNode;
4434                 }
4435         }
4436 }
4437
4438 static void addressbook_new_vcard_cb( GtkAction *action, gpointer data ) {
4439         AdapterDSource *ads;
4440         AdapterInterface *adapter;
4441         GtkCMCTreeNode *newNode;
4442
4443         adapter = addrbookctl_find_interface( ADDR_IF_VCARD );
4444         if( adapter == NULL ) return;
4445         ads = addressbook_edit_vcard( _addressIndex_, NULL );
4446         if( ads ) {
4447                 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4448                 if( newNode ) {
4449                         gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4450                         addrbook.treeSelected = newNode;
4451                 }
4452         }
4453 }
4454
4455 #ifdef USE_JPILOT
4456 static void addressbook_new_jpilot_cb( GtkAction *action, gpointer data ) {
4457         AdapterDSource *ads;
4458         AdapterInterface *adapter;
4459         AddressInterface *iface;
4460         GtkCMCTreeNode *newNode;
4461
4462         adapter = addrbookctl_find_interface( ADDR_IF_JPILOT );
4463         if( adapter == NULL ) return;
4464         iface = adapter->interface;
4465         if( ! iface->haveLibrary ) return;
4466         ads = addressbook_edit_jpilot( _addressIndex_, NULL );
4467         if( ads ) {
4468                 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4469                 if( newNode ) {
4470                         gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4471                         addrbook.treeSelected = newNode;
4472                 }
4473         }
4474 }
4475 #endif
4476
4477 #ifdef USE_LDAP
4478 static void addressbook_new_ldap_cb( GtkAction *action, gpointer data ) {
4479         AdapterDSource *ads;
4480         AdapterInterface *adapter;
4481         AddressInterface *iface;
4482         GtkCMCTreeNode *newNode;
4483
4484         adapter = addrbookctl_find_interface( ADDR_IF_LDAP );
4485         if( adapter == NULL ) return;
4486         iface = adapter->interface;
4487         if( ! iface->haveLibrary ) return;
4488         ads = addressbook_edit_ldap( _addressIndex_, NULL );
4489         if( ads ) {
4490                 newNode = addressbook_add_object( adapter->treeNode, ADDRESS_OBJECT(ads) );
4491                 if( newNode ) {
4492                         gtk_sctree_select( GTK_SCTREE(addrbook.ctree), newNode );
4493                         addrbook.treeSelected = newNode;
4494                 }
4495         }
4496 }
4497 #endif
4498
4499 /**
4500  * Display address search status message.
4501  * \param queryType Query type.
4502  * \param status    Status/Error code.
4503  */
4504 static void addressbook_search_message( gint queryType, gint sts ) {
4505         gchar *desc = NULL;
4506         *addressbook_msgbuf = '\0';
4507
4508         if( sts != MGU_SUCCESS ) {
4509                 if( queryType == ADDRQUERY_LDAP ) {
4510 #ifdef USE_LDAP                 
4511                         desc = addressbook_err2string( _lutErrorsLDAP_, sts );
4512 #endif
4513                 }
4514         }
4515         if( desc ) {
4516                 g_snprintf( addressbook_msgbuf,
4517                         sizeof(addressbook_msgbuf), "%s", desc );
4518                 addressbook_status_show( addressbook_msgbuf );
4519         }
4520         else {
4521                 addressbook_status_show( "" );
4522         }
4523 }
4524
4525 /**
4526  * Refresh addressbook by forcing refresh of current selected object in
4527  * tree.
4528  */
4529 static void addressbook_refresh_current( void ) {
4530         AddressObject *obj;
4531         GtkCMCTree *ctree;
4532
4533         ctree = GTK_CMCTREE(addrbook.ctree);
4534         obj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
4535         if( obj == NULL ) return;
4536         addressbook_set_clist( obj, TRUE );
4537 }
4538
4539 /**
4540  * Message that is displayed whilst a query is executing in a background
4541  * thread.
4542  */
4543 static gchar *_tempMessage_ = N_( "Busy searching..." );
4544
4545 /**
4546  * Address search idle function. This function is called during UI idle time
4547  * while a search is in progress.
4548  *
4549  * \param data Idler data.
4550  */
4551 static void addressbook_search_idle( gpointer data ) {
4552         /*
4553         gint queryID;
4554
4555         queryID = GPOINTER_TO_INT( data );
4556         g_print( "addressbook_ldap_idle... queryID=%d\n", queryID );
4557         */
4558 }
4559
4560 /**
4561  * Search completion callback function. This removes the query from the idle
4562  * list.
4563  *
4564  * \param sender  Sender of query.
4565  * \param queryID Query ID of search request.
4566  * \param status  Search status.
4567  * \param data    Query data.
4568  */
4569 static void addressbook_search_callback_end(
4570                 gpointer sender, gint queryID, gint status, gpointer data )
4571 {
4572         gpointer ptrQID;
4573         QueryRequest *req;
4574         AddrQueryObject *aqo;
4575
4576         /* Remove idler function */
4577         ptrQID = GINT_TO_POINTER( queryID );
4578         if( ptrQID ) {
4579                 g_idle_remove_by_data( ptrQID );
4580         }
4581
4582         /* Refresh addressbook contents */
4583         addressbook_refresh_current();
4584         req = qrymgr_find_request( queryID );
4585         if( req != NULL ) {
4586                 aqo = ( AddrQueryObject * ) req->queryList->data;
4587                 addressbook_search_message( aqo->queryType, status );
4588         }
4589
4590         /* Stop the search */
4591         addrindex_stop_search( queryID );
4592 }
4593
4594 /**
4595  * Perform search.
4596  *
4597  * \param ds         Data source to search.
4598  * \param searchTerm String to lookup.
4599  * \param pNode      Parent data source node.
4600  */
4601 static void addressbook_perform_search(
4602                 AddressDataSource *ds, gchar *searchTerm,
4603                 GtkCMCTreeNode *pNode )
4604 {
4605         AddrBookBase *adbase;
4606         AddressCache *cache;
4607         ItemFolder *folder;
4608         gchar *name;
4609         gint queryID;
4610         guint idleID;
4611 #ifdef USE_LDAP
4612         AddressObjectType aoType = ADDR_NONE;
4613 #endif
4614
4615         /* Setup a query */
4616         if( *searchTerm == '\0' || strlen( searchTerm ) < 1 ) return;
4617
4618         if( ds && ds->type == ADDR_IF_LDAP ) {
4619 #if USE_LDAP
4620                 aoType = ADDR_LDAP_QUERY;
4621 #endif
4622         }
4623         else {
4624                 return;
4625         }
4626         /* Get reference to address cache */    
4627         adbase = ( AddrBookBase * ) ds->rawDataSource;
4628         cache = adbase->addressCache;
4629
4630         /* Create a folder for the search results */
4631         name = g_strdup_printf( _queryFolderLabel_, searchTerm );
4632         folder = addressbook_setup_subf(ds, name, pNode);
4633         g_free( name );
4634
4635         /* Setup the search */
4636         queryID = addrindex_setup_explicit_search(
4637                 ds, searchTerm, folder, addressbook_search_callback_end, NULL );
4638         if( queryID == 0 ) return;
4639
4640         /* Set up idler function */
4641         idleID = g_idle_add(
4642                         ( GtkFunction ) addressbook_search_idle,
4643                         GINT_TO_POINTER( queryID ) );
4644
4645         /* Start search, sit back and wait for something to happen */
4646         addrindex_start_search( queryID );
4647
4648         addressbook_status_show( _tempMessage_ );
4649 }
4650
4651 /**
4652  * Lookup button handler. Address search is only performed against
4653  * address interfaces for external queries.
4654  *
4655  * \param button Lookup button widget.
4656  * \param data   Data object.
4657  */
4658 static void addressbook_lup_clicked( GtkButton *button, gpointer data ) {
4659         GtkCMCTree *ctree;
4660         AddressObject *obj;
4661         AddressDataSource *ds;
4662         AddressInterface *iface;
4663         gchar *searchTerm;
4664         GtkCMCTreeNode *node, *parentNode;
4665
4666         node = addrbook.treeSelected;
4667         if( ! node ) return;
4668         if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
4669
4670         ctree = GTK_CMCTREE(addrbook.ctree);
4671         obj = gtk_cmctree_node_get_row_data( ctree, node );
4672         if( obj == NULL ) return;
4673
4674         ds = addressbook_find_datasource( node );
4675         if( ds == NULL ) return;
4676
4677         /* We must have a datasource that is an external interface */
4678         iface = ds->interface;
4679         if( ! iface->haveLibrary ) return;
4680         if( ! iface->externalQuery ) return;
4681
4682         searchTerm =
4683                 gtk_editable_get_chars( GTK_EDITABLE(addrbook.entry), 0, -1 );
4684         g_strchomp( searchTerm );
4685
4686         if( obj->type == ADDR_ITEM_FOLDER ) {
4687                 parentNode = GTK_CMCTREE_ROW(node)->parent;
4688         }
4689         else {
4690                 parentNode = node;
4691         }
4692         addressbook_perform_search( ds, searchTerm, parentNode );
4693         
4694         gtk_widget_grab_focus( addrbook.entry );
4695
4696         g_free( searchTerm );
4697 }
4698
4699 static void addressbook_close_clicked( GtkButton *button, gpointer data ) {
4700         addressbook_close();
4701 }
4702
4703 #ifdef USE_LDAP
4704 /**
4705  * Browse address entry for highlighted entry.
4706  */
4707 static void addressbook_browse_entry_cb( GtkAction *action, gpointer data)
4708 {
4709         GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
4710         AddressObject *obj;
4711         AddressDataSource *ds;
4712         AddressInterface *iface;
4713         ItemPerson *person;
4714         ItemEMail *email;
4715
4716         if(addrbook.listSelected == NULL)
4717                 return;
4718
4719         obj = gtk_cmctree_node_get_row_data(clist, addrbook.listSelected);
4720         if (obj == NULL)
4721                 return;
4722
4723         ds = addressbook_find_datasource(GTK_CMCTREE_NODE(addrbook.treeSelected));
4724         if(ds == NULL)
4725                 return;
4726
4727         iface = ds->interface;
4728         if(! iface->haveLibrary )
4729                 return;
4730
4731         person = NULL;
4732         if (obj->type == ADDR_ITEM_EMAIL) {
4733                 email = ( ItemEMail * ) obj;
4734                 if (email == NULL)
4735                         return;
4736                 
4737                 person = (ItemPerson *) ADDRITEM_PARENT(email);
4738         }
4739         else if (obj->type == ADDR_ITEM_PERSON) {
4740                 person = (ItemPerson *) obj;
4741         }
4742         else {
4743                 /* None of these */
4744                 return;
4745         }
4746
4747         if( iface && iface->type == ADDR_IF_LDAP ) {
4748                 browseldap_entry(ds, person->externalID);
4749         }
4750 }
4751 #endif
4752
4753 /* **********************************************************************
4754 * Build lookup tables.
4755 * ***********************************************************************
4756 */
4757
4758 /*
4759  * Remap object types.
4760  * Enter:  abType AddressObjectType (used in tree node).
4761  * Return: ItemObjectType (used in address cache data).
4762  */
4763 ItemObjectType addressbook_type2item( AddressObjectType abType ) {
4764         ItemObjectType ioType;
4765
4766         switch( abType ) {
4767                 case ADDR_ITEM_PERSON: ioType = ITEMTYPE_PERSON;     break;
4768                 case ADDR_ITEM_EMAIL:  ioType = ITEMTYPE_EMAIL;      break;
4769                 case ADDR_ITEM_FOLDER: ioType = ITEMTYPE_FOLDER;     break;
4770                 case ADDR_ITEM_GROUP:  ioType = ITEMTYPE_GROUP;      break;
4771                 case ADDR_DATASOURCE:  ioType = ITEMTYPE_DATASOURCE; break;
4772                 default:               ioType = ITEMTYPE_NONE;       break;
4773         }
4774         return ioType;
4775 }
4776
4777 /*
4778 * Build table that controls the rendering of object types.
4779 */
4780 static void addrbookctl_build_map( GtkWidget *window ) {
4781         AddressTypeControlItem *atci;
4782
4783         /* Build icons */
4784         stock_pixmap_gdk(window, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
4785         stock_pixmap_gdk(window, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
4786         stock_pixmap_gdk(window, STOCK_PIXMAP_GROUP, &groupxpm, &groupxpmmask);
4787         stock_pixmap_gdk(window, STOCK_PIXMAP_VCARD, &vcardxpm, &vcardxpmmask);
4788         stock_pixmap_gdk(window, STOCK_PIXMAP_BOOK, &bookxpm, &bookxpmmask);
4789         stock_pixmap_gdk(window, STOCK_PIXMAP_ADDRESS, &addressxpm, &addressxpmmask);
4790         stock_pixmap_gdk(window, STOCK_PIXMAP_JPILOT, &jpilotxpm, &jpilotxpmmask);
4791         stock_pixmap_gdk(window, STOCK_PIXMAP_CATEGORY, &categoryxpm, &categoryxpmmask);
4792         stock_pixmap_gdk(window, STOCK_PIXMAP_LDAP, &ldapxpm, &ldapxpmmask);
4793         stock_pixmap_gdk(window, STOCK_PIXMAP_ADDRESS_SEARCH, &addrsearchxpm, &addrsearchxpmmask);
4794
4795         _addressBookTypeHash_ = g_hash_table_new( g_int_hash, g_int_equal );
4796         _addressBookTypeList_ = NULL;
4797
4798         /* Interface */
4799         atci = g_new0( AddressTypeControlItem, 1 );
4800         atci->objectType = ADDR_INTERFACE;
4801         atci->interfaceType = ADDR_IF_NONE;
4802         atci->showInTree = TRUE;
4803         atci->treeExpand = TRUE;
4804         atci->treeLeaf = FALSE;
4805         atci->displayName = _( "Interface" );
4806         atci->iconXpm = folderxpm;
4807         atci->maskXpm = folderxpmmask;
4808         atci->iconXpmOpen = folderopenxpm;
4809         atci->maskXpmOpen = folderopenxpmmask;
4810         atci->menuCommand = NULL;
4811         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4812         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4813
4814         /* Address book */
4815         atci = g_new0( AddressTypeControlItem, 1 );
4816         atci->objectType = ADDR_BOOK;
4817         atci->interfaceType = ADDR_IF_BOOK;
4818         atci->showInTree = TRUE;
4819         atci->treeExpand = TRUE;
4820         atci->treeLeaf = FALSE;
4821         atci->displayName = _( "Address Book" );
4822         atci->iconXpm = bookxpm;
4823         atci->maskXpm = bookxpmmask;
4824         atci->iconXpmOpen = bookxpm;
4825         atci->maskXpmOpen = bookxpmmask;
4826         atci->menuCommand = "Menu/Book/NewBook";
4827         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4828         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4829
4830         /* Item person */
4831         atci = g_new0( AddressTypeControlItem, 1 );
4832         atci->objectType = ADDR_ITEM_PERSON;
4833         atci->interfaceType = ADDR_IF_NONE;
4834         atci->showInTree = FALSE;
4835         atci->treeExpand = FALSE;
4836         atci->treeLeaf = FALSE;
4837         atci->displayName = _( "Person" );
4838         atci->iconXpm = NULL;
4839         atci->maskXpm = NULL;
4840         atci->iconXpmOpen = NULL;
4841         atci->maskXpmOpen = NULL;
4842         atci->menuCommand = NULL;
4843         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4844         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4845
4846         /* Item email */
4847         atci = g_new0( AddressTypeControlItem, 1 );
4848         atci->objectType = ADDR_ITEM_EMAIL;
4849         atci->interfaceType = ADDR_IF_NONE;
4850         atci->showInTree = FALSE;
4851         atci->treeExpand = FALSE;
4852         atci->treeLeaf = TRUE;
4853         atci->displayName = _( "Email Address" );
4854         atci->iconXpm = addressxpm;
4855         atci->maskXpm = addressxpmmask;
4856         atci->iconXpmOpen = addressxpm;
4857         atci->maskXpmOpen = addressxpmmask;
4858         atci->menuCommand = NULL;
4859         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4860         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4861
4862         /* Item group */
4863         atci = g_new0( AddressTypeControlItem, 1 );
4864         atci->objectType = ADDR_ITEM_GROUP;
4865         atci->interfaceType = ADDR_IF_BOOK;
4866         atci->showInTree = TRUE;
4867         atci->treeExpand = FALSE;
4868         atci->treeLeaf = FALSE;
4869         atci->displayName = _( "Group" );
4870         atci->iconXpm = groupxpm;
4871         atci->maskXpm = groupxpmmask;
4872         atci->iconXpmOpen = groupxpm;
4873         atci->maskXpmOpen = groupxpmmask;
4874         atci->menuCommand = NULL;
4875         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4876         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4877
4878         /* Item folder */
4879         atci = g_new0( AddressTypeControlItem, 1 );
4880         atci->objectType = ADDR_ITEM_FOLDER;
4881         atci->interfaceType = ADDR_IF_BOOK;
4882         atci->showInTree = TRUE;
4883         atci->treeExpand = FALSE;
4884         atci->treeLeaf = FALSE;
4885         atci->displayName = _( "Folder" );
4886         atci->iconXpm = folderxpm;
4887         atci->maskXpm = folderxpmmask;
4888         atci->iconXpmOpen = folderopenxpm;
4889         atci->maskXpmOpen = folderopenxpmmask;
4890         atci->menuCommand = NULL;
4891         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4892         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4893
4894         /* vCard */
4895         atci = g_new0( AddressTypeControlItem, 1 );
4896         atci->objectType = ADDR_VCARD;
4897         atci->interfaceType = ADDR_IF_VCARD;
4898         atci->showInTree = TRUE;
4899         atci->treeExpand = TRUE;
4900         atci->treeLeaf = TRUE;
4901         atci->displayName = _( "vCard" );
4902         atci->iconXpm = vcardxpm;
4903         atci->maskXpm = vcardxpmmask;
4904         atci->iconXpmOpen = vcardxpm;
4905         atci->maskXpmOpen = vcardxpmmask;
4906         atci->menuCommand = "Menu/Book/NewVCard";
4907         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4908         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4909
4910         /* J-Pilot */
4911         atci = g_new0( AddressTypeControlItem, 1 );
4912         atci->objectType = ADDR_JPILOT;
4913         atci->interfaceType = ADDR_IF_JPILOT;
4914         atci->showInTree = TRUE;
4915         atci->treeExpand = TRUE;
4916         atci->treeLeaf = FALSE;
4917         atci->displayName = _( "JPilot" );
4918         atci->iconXpm = jpilotxpm;
4919         atci->maskXpm = jpilotxpmmask;
4920         atci->iconXpmOpen = jpilotxpm;
4921         atci->maskXpmOpen = jpilotxpmmask;
4922         atci->menuCommand = "Menu/Book/NewJPilot";
4923         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4924         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4925
4926         /* Category */
4927         atci = g_new0( AddressTypeControlItem, 1 );
4928         atci->objectType = ADDR_CATEGORY;
4929         atci->interfaceType = ADDR_IF_JPILOT;
4930         atci->showInTree = TRUE;
4931         atci->treeExpand = TRUE;
4932         atci->treeLeaf = TRUE;
4933         atci->displayName = _( "JPilot" );
4934         atci->iconXpm = categoryxpm;
4935         atci->maskXpm = categoryxpmmask;
4936         atci->iconXpmOpen = categoryxpm;
4937         atci->maskXpmOpen = categoryxpmmask;
4938         atci->menuCommand = NULL;
4939         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4940         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4941
4942         /* LDAP Server */
4943         atci = g_new0( AddressTypeControlItem, 1 );
4944         atci->objectType = ADDR_LDAP;
4945         atci->interfaceType = ADDR_IF_LDAP;
4946         atci->showInTree = TRUE;
4947         atci->treeExpand = TRUE;
4948         atci->treeLeaf = FALSE;
4949         atci->displayName = _( "LDAP servers" );
4950         atci->iconXpm = ldapxpm;
4951         atci->maskXpm = ldapxpmmask;
4952         atci->iconXpmOpen = ldapxpm;
4953         atci->maskXpmOpen = ldapxpmmask;
4954         atci->menuCommand = "Menu/Book/NewLDAPServer";
4955         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4956         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4957
4958         /* LDAP Query  */
4959         atci = g_new0( AddressTypeControlItem, 1 );
4960         atci->objectType = ADDR_LDAP_QUERY;
4961         atci->interfaceType = ADDR_IF_LDAP;
4962         atci->showInTree = TRUE;
4963         atci->treeExpand = FALSE;
4964         atci->treeLeaf = TRUE;
4965         atci->displayName = _( "LDAP Query" );
4966         atci->iconXpm = addrsearchxpm;
4967         atci->maskXpm = addrsearchxpmmask;
4968         atci->iconXpmOpen = addrsearchxpm;
4969         atci->maskXpmOpen = addrsearchxpmmask;
4970         atci->menuCommand = NULL;
4971         g_hash_table_insert( _addressBookTypeHash_, &atci->objectType, atci );
4972         _addressBookTypeList_ = g_list_append( _addressBookTypeList_, atci );
4973
4974 }
4975
4976 /*
4977 * Search for specified object type.
4978 */
4979 static AddressTypeControlItem *addrbookctl_lookup( gint ot ) {
4980         gint objType = ot;
4981         return ( AddressTypeControlItem * ) g_hash_table_lookup( _addressBookTypeHash_, &objType );
4982 }
4983
4984 /*
4985 * Search for specified interface type.
4986 */
4987 static AddressTypeControlItem *addrbookctl_lookup_iface( AddressIfType ifType ) {
4988         GList *node = _addressBookTypeList_;
4989         while( node ) {
4990                 AddressTypeControlItem *atci = node->data;
4991                 if( atci->interfaceType == ifType ) return atci;
4992                 node = g_list_next( node );
4993         }
4994         return NULL;
4995 }
4996
4997 static void addrbookctl_free_address( AddressObject *obj ) {
4998         g_free( obj->name );
4999         obj->type = ADDR_NONE;
5000         obj->name = NULL;
5001 }
5002
5003 static void addrbookctl_free_interface( AdapterInterface *adapter ) {
5004         addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
5005         adapter->interface = NULL;
5006         adapter->interfaceType = ADDR_IF_NONE;
5007         adapter->atci = NULL;
5008         adapter->enabled = FALSE;
5009         adapter->haveLibrary = FALSE;
5010         adapter->treeNode = NULL;
5011         g_free( adapter );
5012 }
5013
5014 static void addrbookctl_free_datasource( AdapterDSource *adapter ) {
5015         addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
5016         adapter->dataSource = NULL;
5017         adapter->subType = ADDR_NONE;
5018         g_free( adapter );
5019 }
5020
5021 static void addrbookctl_free_folder( AdapterFolder *adapter ) {
5022         addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
5023         adapter->itemFolder = NULL;
5024         g_free( adapter );
5025 }
5026
5027 static void addrbookctl_free_group( AdapterGroup *adapter ) {
5028         addrbookctl_free_address( ADDRESS_OBJECT(adapter) );
5029         adapter->itemGroup = NULL;
5030         g_free( adapter );
5031 }
5032
5033 /**
5034  * Build GUI interface list.
5035  */
5036 static void addrbookctl_build_iflist( void ) {
5037         AddressTypeControlItem *atci;
5038         AdapterInterface *adapter;
5039         GList *list = NULL;
5040
5041         if( _addressIndex_ == NULL ) {
5042                 _addressIndex_ = addrindex_create_index();
5043                 if( _clipBoard_ == NULL ) {
5044                         _clipBoard_ = addrclip_create();
5045                 }
5046                 addrclip_set_index( _clipBoard_, _addressIndex_ );
5047         }
5048         _addressInterfaceList_ = NULL;
5049         list = addrindex_get_interface_list( _addressIndex_ );
5050         while( list ) {
5051                 AddressInterface *interface = list->data;
5052                 atci = addrbookctl_lookup_iface( interface->type );
5053                 if( atci ) {
5054                         adapter = g_new0( AdapterInterface, 1 );
5055                         adapter->interfaceType = interface->type;
5056                         adapter->atci = atci;
5057                         adapter->interface = interface;
5058                         adapter->treeNode = NULL;
5059                         adapter->enabled = TRUE;
5060                         adapter->haveLibrary = interface->haveLibrary;
5061                         ADDRESS_OBJECT(adapter)->type = ADDR_INTERFACE;
5062                         ADDRESS_OBJECT_NAME(adapter) = g_strdup( atci->displayName );
5063                         _addressInterfaceList_ =
5064                                 g_list_append( _addressInterfaceList_, adapter );
5065                 }
5066                 list = g_list_next( list );
5067         }
5068 }
5069
5070 /**
5071  * Find GUI interface type specified interface type.
5072  * \param  ifType Interface type.
5073  * \return Interface item, or NULL if not found.
5074  */
5075 static AdapterInterface *addrbookctl_find_interface( AddressIfType ifType ) {
5076         GList *node = _addressInterfaceList_;
5077         while( node ) {
5078                 AdapterInterface *adapter = node->data;
5079                 if( adapter->interfaceType == ifType ) return adapter;
5080                 node = g_list_next( node );
5081         }
5082         return NULL;
5083 }
5084
5085 /**
5086  * Build interface list selection.
5087  */
5088 static void addrbookctl_build_ifselect( void ) {
5089         GList *newList = NULL;
5090         gchar *selectStr;
5091         gchar **splitStr;
5092         gint ifType;
5093         gint i;
5094         gchar *endptr = NULL;
5095         gboolean enabled;
5096         AdapterInterface *adapter;
5097
5098         selectStr = g_strdup( ADDRESSBOOK_IFACE_SELECTION );
5099
5100         /* Parse string */
5101         splitStr = g_strsplit( selectStr, ",", -1 );
5102         for( i = 0; i < ADDRESSBOOK_MAX_IFACE; i++ ) {
5103                 if( splitStr[i] ) {
5104                         /* g_print( "%d : %s\n", i, splitStr[i] ); */
5105                         ifType = strtol( splitStr[i], &endptr, 10 );
5106                         enabled = TRUE;
5107                         if( *endptr ) {
5108                                 if( strcmp( endptr, "/n" ) == 0 ) {
5109                                         enabled = FALSE;
5110                                 }
5111                         }
5112                         /* g_print( "\t%d : %s\n", ifType, enabled ? "yes" : "no" ); */
5113                         adapter = addrbookctl_find_interface( ifType );
5114                         if( adapter ) {
5115                                 newList = g_list_append( newList, adapter );
5116                         }
5117                 }
5118                 else {
5119                         break;
5120                 }
5121         }
5122         /* g_print( "i=%d\n", i ); */
5123         g_strfreev( splitStr );
5124         g_free( selectStr );
5125
5126         /* Replace existing list */
5127         mgu_clear_list( _addressIFaceSelection_ );
5128         g_list_free( _addressIFaceSelection_ );
5129         _addressIFaceSelection_ = newList;
5130         newList = NULL;
5131 }
5132
5133 /* ***********************************************************************
5134  * Add sender to address book.
5135  * ***********************************************************************
5136  */
5137
5138 /*
5139  * This function is used by the Add sender to address book function.
5140  */
5141 gboolean addressbook_add_contact(
5142                 const gchar *name, const gchar *address, const gchar *remarks,
5143                 GdkPixbuf *picture )
5144 {
5145         debug_print( "addressbook_add_contact: name/address: %s - %s\n", name, address );
5146         if( addressadd_selection( _addressIndex_, name, address, remarks, picture ) ) {
5147                 debug_print( "addressbook_add_contact - added\n" );
5148                 addressbook_refresh();
5149         }
5150         return TRUE;
5151 }
5152
5153 /* ***********************************************************************
5154  * Book/folder selection.
5155  * ***********************************************************************
5156  */
5157
5158 /*
5159  * This function is used by the matcher dialog to select a book/folder.
5160  */
5161 gchar *addressbook_folder_selection( const gchar *folderpath)
5162 {
5163         AddressBookFile *book = NULL;
5164         ItemFolder *folder = NULL;
5165         gchar *path = NULL;
5166
5167         g_return_val_if_fail( folderpath != NULL, NULL);
5168
5169         if ( addressbook_foldersel_selection( _addressIndex_, &book, &folder, folderpath )
5170                 && book != NULL ) {
5171                 if ( folder != NULL) {
5172                         gchar *tmp = NULL;
5173                         gchar *oldtmp = NULL;
5174                         AddrItemObject *obj = NULL;
5175
5176                         /* walk thru folder->parent to build the full folder path */
5177                         /* TODO: wwp: optimize this */
5178                         obj = &folder->obj;
5179                         tmp = g_strdup(obj->uid);
5180                         while ( obj->parent ) {
5181                                 obj = obj->parent;
5182                                 if ( obj->name != NULL ) {
5183                                         oldtmp = g_strdup(tmp);
5184                                         g_free(tmp);
5185                                         tmp = g_strdup_printf("%s/%s", obj->uid, oldtmp);
5186                                         g_free(oldtmp);
5187                                 }
5188                         }
5189                         path = g_strdup_printf("%s/%s", book->fileName, tmp);
5190                         g_free(tmp);
5191                 } else {
5192                         path = g_strdup_printf("%s", book->fileName);
5193                 }
5194                 debug_print( "addressbook_foldersel: %s\n", path?path:"(null)");
5195                 return path;
5196         }
5197         return NULL;
5198 }
5199
5200 /* ***********************************************************************
5201  * Book/folder checking.
5202  * ***********************************************************************
5203  */
5204
5205 static FolderInfo *addressbook_peek_subfolder_exists_create_folderinfo( AddressBookFile *abf, ItemFolder *folder )
5206 {
5207         FolderInfo *fi = g_new0( FolderInfo, 1 );
5208         fi->book   = abf;
5209         fi->folder = folder;
5210         return fi;
5211 }
5212
5213 static void addressbook_peek_subfolder_exists_load_folder( ItemFolder *parentFolder,
5214                                         FolderInfo *fiParent, FolderPathMatch *match )
5215 {
5216         GList *list;
5217         ItemFolder *folder;
5218         gchar *fName;
5219         FolderInfo *fi;
5220         FolderPathMatch *nextmatch = NULL;
5221
5222         if (!parentFolder)
5223                 return;
5224
5225         list = parentFolder->listFolder;
5226         while ( list ) {
5227                 folder = list->data;
5228                 fName = g_strdup( ADDRITEM_NAME(folder) );
5229
5230                 /* match folder name, match pointer will be set to NULL if next recursive call
5231                    doesn't need to match subfolder name */
5232                 if ( match != NULL &&
5233                          match->matched == FALSE ) {
5234                         if ( strcmp(match->folder_path[match->index], folder->obj.uid) == 0 ) {
5235                                 /* folder name matches, prepare next subfolder match */
5236                                 debug_print("matched folder name '%s'\n", fName);
5237                                 match->index++;
5238                                 if ( match->folder_path[match->index] == NULL ) {
5239                                         /* we've matched all elements */
5240                                         match->matched = TRUE;
5241                                         match->folder = folder;
5242                                         debug_print("book/folder path matched!\n");
5243                                 } else {
5244                                         /* keep on matching */
5245                                         nextmatch = match;
5246                                 }
5247                         }
5248                 }
5249
5250                 g_free( fName );
5251
5252                 fi = addressbook_peek_subfolder_exists_create_folderinfo( fiParent->book, folder );
5253                 addressbook_peek_subfolder_exists_load_folder( folder, fi, nextmatch );
5254                 g_free(fi);
5255                 list = g_list_next( list );
5256         }
5257 }
5258
5259 /*
5260  * This function is used by to check if a matcher book/folder path corresponds to an
5261    existing addressbook book/folder ("" or "Any" are considered as valid, NULL invalid).
5262    Caution: returned book and folder pointers can be NULL even when returning TRUE:
5263    if book AND folder are NULL this means that folderpath was empty or Any.
5264    If folderpath is a simple book name (without folder), book will not be NULL and folder
5265    will be NULL. It's not expected to return book as NULL and folder as non NULL.
5266  */
5267
5268 gboolean addressbook_peek_folder_exists( gchar *folderpath,
5269                                                                                  AddressDataSource **book,
5270                                                                                  ItemFolder **folder )
5271 {
5272         AddressDataSource *ds;
5273         GList *list, *nodeDS;
5274         ItemFolder *rootFolder;
5275         AddressBookFile *abf;
5276         FolderInfo *fi;
5277         FolderPathMatch folder_path_match = { NULL, FALSE, 0, NULL, NULL };
5278
5279         if ( book )
5280                 *book = NULL;
5281         if ( folder )
5282                 *folder = NULL;
5283
5284         if ( folderpath == NULL )
5285                 return FALSE;
5286
5287         if ( strcasecmp(folderpath, "Any") == 0 || *folderpath == '\0' )
5288                 return TRUE;
5289
5290         /* split the folder path we've received, we'll try to match this path, subpath by
5291            subpath against the book/folder structure in order */
5292         folder_path_match.folder_path = g_strsplit( folderpath, "/", 256 );
5293         if (!folder_path_match.folder_path)
5294                 return FALSE;
5295
5296         list = addrindex_get_interface_list( _addressIndex_ );
5297         while ( list && !folder_path_match.matched ) {
5298                 AddressInterface *interface = list->data;
5299                 if ( interface && interface->type == ADDR_IF_BOOK ) {
5300                         nodeDS = interface->listSource;
5301                         while ( nodeDS && !folder_path_match.matched ) {
5302                                 ds = nodeDS->data;
5303
5304                                 /* Read address book */
5305                                 if( ! addrindex_ds_get_read_flag( ds ) ) {
5306                                         addrindex_ds_read_data( ds );
5307                                 }
5308
5309                                 /* Add node for address book */
5310                                 abf = ds->rawDataSource;
5311
5312                                 /* match book name */
5313                                 if ( abf && abf->fileName &&
5314                                     strcmp(folder_path_match.folder_path[0], abf->fileName) == 0 ) {
5315
5316                                         debug_print("matched book name '%s'\n", abf->fileName);
5317                                         folder_path_match.book = ds;
5318
5319                                         if ( folder_path_match.folder_path[1] == NULL ) {
5320                                                 /* no folder part to match */
5321
5322                                                 folder_path_match.matched = TRUE;
5323                                                 folder_path_match.folder = NULL;
5324                                                 debug_print("book path matched!\n");
5325
5326                                         } else {
5327                                                 /* match folder part */
5328
5329                                                 fi = addressbook_peek_subfolder_exists_create_folderinfo( abf, NULL );
5330                                                 rootFolder = addrindex_ds_get_root_folder( ds );
5331
5332                                                 /* prepare for recursive call */
5333                                                 folder_path_match.index = 1;
5334                                                 /* this call will set folder_path_match.matched and folder_path_match.folder */
5335                                                 addressbook_peek_subfolder_exists_load_folder( rootFolder, fi, &folder_path_match );
5336                                                 g_free(fi);
5337                                         }
5338                                 }
5339
5340                                 nodeDS = g_list_next( nodeDS );
5341                         }
5342                 }
5343                 list = g_list_next( list );
5344         }
5345
5346         g_strfreev( folder_path_match.folder_path );
5347
5348         if ( book )
5349                 *book = folder_path_match.book;
5350         if ( folder )
5351                 *folder = folder_path_match.folder;
5352         return folder_path_match.matched;
5353 }
5354
5355
5356 /* **********************************************************************
5357  * Address Import.
5358  * ***********************************************************************
5359  */
5360
5361 /**
5362  * Import LDIF file.
5363  */
5364 static void addressbook_import_ldif_cb( GtkAction *action, gpointer data ) {
5365         AddressDataSource *ds = NULL;
5366         AdapterDSource *ads = NULL;
5367         AddressBookFile *abf = NULL;
5368         AdapterInterface *adapter;
5369         GtkCMCTreeNode *newNode;
5370
5371         adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5372         if( adapter ) {
5373                 if( adapter->treeNode ) {
5374                         abf = addressbook_imp_ldif( _addressIndex_ );
5375                         if( abf ) {
5376                                 ds = addrindex_index_add_datasource(
5377                                         _addressIndex_, ADDR_IF_BOOK, abf );
5378                                 ads = addressbook_create_ds_adapter(
5379                                         ds, ADDR_BOOK, NULL );
5380                                 addressbook_ads_set_name(
5381                                         ads, addrbook_get_name( abf ) );
5382                                 newNode = addressbook_add_object(
5383                                         adapter->treeNode,
5384                                         ADDRESS_OBJECT(ads) );
5385                                 if( newNode ) {
5386                                         gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
5387                                                 newNode );
5388                                         addrbook.treeSelected = newNode;
5389                                 }
5390
5391                                 /* Notify address completion */
5392                                 invalidate_address_completion();
5393                         }
5394                 }
5395         }
5396 }
5397
5398 /**
5399  * Import MUTT file.
5400  */
5401 static void addressbook_import_mutt_cb( GtkAction *action, gpointer data ) {
5402         AddressDataSource *ds = NULL;
5403         AdapterDSource *ads = NULL;
5404         AddressBookFile *abf = NULL;
5405         AdapterInterface *adapter;
5406         GtkCMCTreeNode *newNode;
5407
5408         adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5409         if( adapter ) {
5410                 if( adapter->treeNode ) {
5411                         abf = addressbook_imp_mutt( _addressIndex_ );
5412                         if( abf ) {
5413                                 ds = addrindex_index_add_datasource(
5414                                         _addressIndex_, ADDR_IF_BOOK, abf );
5415                                 ads = addressbook_create_ds_adapter(
5416                                         ds, ADDR_BOOK, NULL );
5417                                 addressbook_ads_set_name(
5418                                         ads, addrbook_get_name( abf ) );
5419                                 newNode = addressbook_add_object(
5420                                         adapter->treeNode,
5421                                         ADDRESS_OBJECT(ads) );
5422                                 if( newNode ) {
5423                                         gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
5424                                                 newNode );
5425                                         addrbook.treeSelected = newNode;
5426                                 }
5427
5428                                 /* Notify address completion */
5429                                 invalidate_address_completion();
5430                         }
5431                 }
5432         }
5433 }
5434
5435 /**
5436  * Import Pine file.
5437  */
5438 static void addressbook_import_pine_cb( GtkAction *action, gpointer data ) {
5439         AddressDataSource *ds = NULL;
5440         AdapterDSource *ads = NULL;
5441         AddressBookFile *abf = NULL;
5442         AdapterInterface *adapter;
5443         GtkCMCTreeNode *newNode;
5444
5445         adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5446         if( adapter ) {
5447                 if( adapter->treeNode ) {
5448                         abf = addressbook_imp_pine( _addressIndex_ );
5449                         if( abf ) {
5450                                 ds = addrindex_index_add_datasource(
5451                                         _addressIndex_, ADDR_IF_BOOK, abf );
5452                                 ads = addressbook_create_ds_adapter(
5453                                         ds, ADDR_BOOK, NULL );
5454                                 addressbook_ads_set_name(
5455                                         ads, addrbook_get_name( abf ) );
5456                                 newNode = addressbook_add_object(
5457                                         adapter->treeNode,
5458                                         ADDRESS_OBJECT(ads) );
5459                                 if( newNode ) {
5460                                         gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
5461                                                 newNode );
5462                                         addrbook.treeSelected = newNode;
5463                                 }
5464
5465                                 /* Notify address completion */
5466                                 invalidate_address_completion();
5467                         }
5468                 }
5469         }
5470 }
5471
5472 /**
5473  * Harvest addresses.
5474  * \param folderItem Folder to import.
5475  * \param sourceInd  Source indicator: FALSE - Folder, TRUE - Messages.
5476  * \param msgList    List of message numbers, or NULL to process folder.
5477  */
5478 void addressbook_harvest(
5479         FolderItem *folderItem, gboolean sourceInd, GList *msgList )
5480 {
5481         AddressDataSource *ds = NULL;
5482         AdapterDSource *ads = NULL;
5483         AddressBookFile *abf = NULL;
5484         AdapterInterface *adapter;
5485         GtkCMCTreeNode *newNode;
5486
5487         abf = addrgather_dlg_execute(
5488                 folderItem, _addressIndex_, sourceInd, msgList );
5489         if( abf ) {
5490                 ds = addrindex_index_add_datasource(
5491                         _addressIndex_, ADDR_IF_BOOK, abf );
5492
5493                 adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
5494                 if( adapter ) {
5495                         if( adapter->treeNode ) {
5496                                 ads = addressbook_create_ds_adapter(
5497                                         ds, ADDR_BOOK, addrbook_get_name( abf ) );
5498                                 newNode = addressbook_add_object(
5499                                                 adapter->treeNode,
5500                                                 ADDRESS_OBJECT(ads) );
5501                         }
5502                 }
5503
5504                 /* Notify address completion */
5505                 invalidate_address_completion();
5506         }
5507 }
5508
5509 /**
5510  * Export HTML file.
5511  */
5512 static void addressbook_export_html_cb( GtkAction *action, gpointer data ) {
5513         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
5514         AddressObject *obj;
5515         AddressDataSource *ds = NULL;
5516         AddrBookBase *adbase;
5517         AddressCache *cache;
5518         GtkCMCTreeNode *node = NULL;
5519
5520         if( ! addrbook.treeSelected ) return;
5521         node = addrbook.treeSelected;
5522         if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
5523         obj = gtk_cmctree_node_get_row_data( ctree, node );
5524         if( obj == NULL ) return;
5525
5526         ds = addressbook_find_datasource( node );
5527         if( ds == NULL ) return;
5528         adbase = ( AddrBookBase * ) ds->rawDataSource;
5529         cache = adbase->addressCache;
5530         addressbook_exp_html( cache );
5531 }
5532
5533 /**
5534  * Export LDIF file.
5535  */
5536 static void addressbook_export_ldif_cb( GtkAction *action, gpointer data ) {
5537         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
5538         AddressObject *obj;
5539         AddressDataSource *ds = NULL;
5540         AddrBookBase *adbase;
5541         AddressCache *cache;
5542         GtkCMCTreeNode *node = NULL;
5543
5544         if( ! addrbook.treeSelected ) return;
5545         node = addrbook.treeSelected;
5546         if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
5547         obj = gtk_cmctree_node_get_row_data( ctree, node );
5548         if( obj == NULL ) return;
5549
5550         ds = addressbook_find_datasource( node );
5551         if( ds == NULL ) return;
5552         adbase = ( AddrBookBase * ) ds->rawDataSource;
5553         cache = adbase->addressCache;
5554         addressbook_exp_ldif( cache );
5555 }
5556
5557 static void addressbook_find_duplicates_cb(GtkAction *action, gpointer data)
5558 {
5559         addrduplicates_find(GTK_WINDOW(addrbook.window));       
5560 }
5561
5562 static void addressbook_edit_custom_attr_cb(GtkAction *action, gpointer data)
5563 {
5564         addressbook_custom_attr_edit();
5565 }
5566                 
5567 static void addressbook_start_drag(GtkWidget *widget, gint button, 
5568                                    GdkEvent *event,
5569                                    void *data)
5570 {
5571         GdkDragContext *context;
5572         if (addressbook_target_list == NULL)
5573                 addressbook_target_list = gtk_target_list_new(
5574                                 addressbook_drag_types, 1);
5575         context = gtk_drag_begin(widget, addressbook_target_list,
5576                                  GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
5577         gtk_drag_set_icon_default(context);
5578 }
5579
5580 static void addressbook_drag_data_get(GtkWidget        *widget,
5581                                      GdkDragContext   *drag_context,
5582                                      GtkSelectionData *selection_data,
5583                                      guint             info,
5584                                      guint             time,
5585                                      void             *data)
5586 {
5587         AddrItemObject *aio = NULL;
5588         AddressObject *pobj = NULL;
5589         AdapterDSource *ads = NULL;
5590         AddressDataSource *ds = NULL;
5591         GList *cur;
5592
5593         pobj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
5594
5595         if( pobj == NULL ) return;
5596
5597         if( pobj->type == ADDR_DATASOURCE ) {
5598                 ads = ADAPTER_DSOURCE(pobj);
5599                 ds = ads->dataSource;
5600         } else if (pobj->type == ADDR_ITEM_GROUP) {
5601
5602                 return;
5603         }
5604         
5605         else if( pobj->type != ADDR_INTERFACE ) {
5606                 ds = addressbook_find_datasource( addrbook.treeSelected );
5607
5608                 if (!ds)
5609                         return;
5610         }
5611         
5612         for(cur = GTK_CMCLIST(addrbook.clist)->selection; cur; cur = cur->next) {
5613                 aio = (AddrItemObject *)gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.clist),
5614                         GTK_CMCTREE_NODE(cur->data));
5615                 while (aio && aio->type != ADDR_ITEM_PERSON) {
5616                         aio = aio->parent;
5617                 }
5618        }
5619
5620         if (aio && aio->type == ADDR_ITEM_PERSON) {
5621                 if( ds && ds->interface && ds->interface->readOnly)
5622                         gtk_selection_data_set(selection_data,
5623                                        selection_data->target, 8,
5624                                        (const guchar *)"Dummy_addr_copy", 15);
5625                 else
5626                         gtk_selection_data_set(selection_data,
5627                                        selection_data->target, 8,
5628                                        (const guchar *)"Dummy_addr_move", 15);
5629         } 
5630 }
5631
5632 static gboolean addressbook_drag_motion_cb(GtkWidget      *widget,
5633                                           GdkDragContext *context,
5634                                           gint            x,
5635                                           gint            y,
5636                                           guint           time,
5637                                           void            *data)
5638 {
5639         gint row, column;
5640         GtkCMCTreeNode *node = NULL;
5641         gboolean acceptable = FALSE;
5642         gint height = addrbook.ctree->allocation.height;
5643         gint total_height = addrbook.ctree->requisition.height;
5644         GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
5645                                 GTK_SCROLLED_WINDOW(addrbook.ctree_swin));
5646         gfloat vpos = pos->value;
5647         
5648         if (gtk_cmclist_get_selection_info
5649                 (GTK_CMCLIST(widget), x - 24, y - 24, &row, &column)) {
5650
5651                 if (y > height - 24 && height + vpos < total_height) {
5652                         gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
5653                         gtk_adjustment_changed(pos);
5654                 }
5655                 if (y < 24 && y > 0) {
5656                         gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
5657                         gtk_adjustment_changed(pos);
5658                 }
5659                 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
5660
5661                 if (node != NULL) {
5662                         AddressObject *obj = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node );
5663                         if( obj->type == ADDR_ITEM_FOLDER 
5664                         || obj->type == ADDR_ITEM_GROUP)
5665                                 acceptable = TRUE;
5666                         else {
5667                                 AdapterDSource *ads = NULL;
5668                                 AddressDataSource *ds = NULL;
5669                                 ads = ADAPTER_DSOURCE(obj);
5670                                 if (ads == NULL ){ return FALSE;}
5671                                 ds = ads->dataSource;
5672                                 if (ds == NULL ) { return FALSE;}
5673
5674                                 acceptable = TRUE;
5675                         }
5676                 }
5677         }
5678
5679         if (acceptable) {
5680                 g_signal_handlers_block_by_func
5681                         (G_OBJECT(widget),
5682                          G_CALLBACK(addressbook_tree_selected), NULL);
5683                 gtk_sctree_select( GTK_SCTREE(widget), node);
5684                 g_signal_handlers_unblock_by_func
5685                         (G_OBJECT(widget),
5686                          G_CALLBACK(addressbook_tree_selected), NULL);
5687                 gdk_drag_status(context, 
5688                                         (context->actions == GDK_ACTION_COPY ?
5689                                         GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
5690         } else {
5691                 gdk_drag_status(context, 0, time);
5692         }
5693         return acceptable;
5694 }
5695
5696 static void addressbook_drag_leave_cb(GtkWidget      *widget,
5697                                      GdkDragContext *context,
5698                                      guint           time,
5699                                      void           *data)
5700 {
5701         if (addrbook.treeSelected) {
5702                 g_signal_handlers_block_by_func
5703                         (G_OBJECT(widget),
5704                          G_CALLBACK(addressbook_tree_selected), NULL);
5705                 gtk_sctree_select( GTK_SCTREE(widget), addrbook.opened);
5706                 g_signal_handlers_unblock_by_func
5707                         (G_OBJECT(widget),
5708                          G_CALLBACK(addressbook_tree_selected), NULL);
5709         }
5710         
5711 }
5712
5713 static void addressbook_drag_received_cb(GtkWidget        *widget,
5714                                         GdkDragContext   *drag_context,
5715                                         gint              x,
5716                                         gint              y,
5717                                         GtkSelectionData *data,
5718                                         guint             info,
5719                                         guint             time,
5720                                         void             *pdata)
5721 {
5722         gint row, column;
5723         GtkCMCTreeNode *node;
5724         GtkCMCTreeNode *lastopened = addrbook.opened;
5725
5726         if (!strncmp(data->data, "Dummy_addr", 10)) {
5727                 if (gtk_cmclist_get_selection_info
5728                         (GTK_CMCLIST(widget), x - 24, y - 24, &row, &column) == 0) {
5729                         return;
5730                 }
5731                 
5732                 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
5733                 if( !node || !gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree), node)) 
5734                         return;
5735                 
5736                 gtk_cmclist_freeze(GTK_CMCLIST(addrbook.clist));
5737                 if (drag_context->action == GDK_ACTION_COPY || 
5738                     !strcmp(data->data, "Dummy_addr_copy"))
5739                         addressbook_clip_copy_cb(NULL, NULL);
5740                 else
5741                         addressbook_clip_cut_cb(NULL, NULL);
5742                 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), node);
5743                 addressbook_clip_paste_cb(NULL,NULL);
5744                 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), lastopened);
5745                 gtk_cmclist_thaw(GTK_CMCLIST(addrbook.clist));
5746                 gtk_drag_finish(drag_context, TRUE, TRUE, time);
5747         }
5748 }
5749
5750 /*
5751 * End of Source.
5752 */