8a60266cb4ee409950b201f89bef1ff86ffb91c0
[claws.git] / src / addressbook.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <gtk/gtk.h>
30 #include <string.h>
31 #include <setjmp.h>
32 #include <sys/types.h>
33 #include <dirent.h>
34
35 #include "main.h"
36 #include "addressbook.h"
37 #include "manage_window.h"
38 #include "prefs_common.h"
39 #include "alertpanel.h"
40 #include "inputdialog.h"
41 #include "menu.h"
42 #include "stock_pixmap.h"
43 #include "xml.h"
44 #include "prefs_gtk.h"
45 #include "procmime.h"
46 #include "utils.h"
47 #include "gtkutils.h"
48 #include "codeconv.h"
49 #include "about.h"
50 #include "addr_compl.h"
51
52 #include "mgutils.h"
53 #include "addressitem.h"
54 #include "addritem.h"
55 #include "addrcache.h"
56 #include "addrbook.h"
57 #include "addrindex.h"
58 #include "addressadd.h"
59 #include "addrduplicates.h"
60 #include "addressbook_foldersel.h"
61 #include "vcard.h"
62 #include "editvcard.h"
63 #include "editgroup.h"
64 #include "editaddress.h"
65 #include "editbook.h"
66 #include "importldif.h"
67 #include "importmutt.h"
68 #include "importpine.h"
69 #include "manual.h"
70
71 #ifdef USE_JPILOT
72 #include "jpilot.h"
73 #include "editjpilot.h"
74 #endif
75
76 #ifdef USE_LDAP
77 #include <pthread.h>
78 #include "ldapserver.h"
79 #include "editldap.h"
80 #include "ldapupdate.h"
81
82 #define ADDRESSBOOK_LDAP_BUSYMSG "Busy"
83 #endif
84
85 #include "addrquery.h"
86 #include "addrselect.h"
87 #include "addrclip.h"
88 #include "addrgather.h"
89 #include "adbookbase.h"
90 #include "exphtmldlg.h"
91 #include "expldifdlg.h"
92 #include "browseldap.h"
93 #include "addrcustomattr.h"
94
95 typedef enum
96 {
97         COL_SOURCES     = 0,
98         N_INDEX_COLS    = 1
99 } AddressIndexColumns;
100
101 typedef enum
102 {
103         COL_NAME        = 0,
104         COL_ADDRESS     = 1,
105         COL_REMARKS     = 2,
106         N_LIST_COLS     = 3
107 } AddressListColumns;
108
109 typedef struct {
110         AddressBookFile *book;
111         ItemFolder      *folder;
112 } FolderInfo;
113
114 typedef struct {
115         gchar **folder_path;
116         gboolean matched;
117         gint index;
118         AddressDataSource *book;
119         ItemFolder *folder;
120 } FolderPathMatch;
121
122 static gchar *list_titles[] = { N_("Name"),
123                                 N_("Email Address"),
124                                 N_("Remarks") };
125
126 #define COL_NAME_WIDTH          164
127 #define COL_ADDRESS_WIDTH       156
128
129 #define COL_FOLDER_WIDTH        170
130 #define ADDRESSBOOK_WIDTH       640
131 #define ADDRESSBOOK_HEIGHT      360
132
133 #define ADDRESSBOOK_MSGBUF_SIZE 2048
134
135 static GdkPixmap *folderxpm;
136 static GdkBitmap *folderxpmmask;
137 static GdkPixmap *folderopenxpm;
138 static GdkBitmap *folderopenxpmmask;
139 static GdkPixmap *groupxpm;
140 static GdkBitmap *groupxpmmask;
141 static GdkPixmap *interfacexpm;
142 static GdkBitmap *interfacexpmmask;
143 static GdkPixmap *bookxpm;
144 static GdkBitmap *bookxpmmask;
145 static GdkPixmap *addressxpm;
146 static GdkBitmap *addressxpmmask;
147 static GdkPixmap *vcardxpm;
148 static GdkBitmap *vcardxpmmask;
149 static GdkPixmap *jpilotxpm;
150 static GdkBitmap *jpilotxpmmask;
151 static GdkPixmap *categoryxpm;
152 static GdkBitmap *categoryxpmmask;
153 static GdkPixmap *ldapxpm;
154 static GdkBitmap *ldapxpmmask;
155 static GdkPixmap *addrsearchxpm;
156 static GdkPixmap *addrsearchxpmmask;
157
158 /* Message buffer */
159 static gchar addressbook_msgbuf[ ADDRESSBOOK_MSGBUF_SIZE ];
160
161 /* Address list selection */
162 static AddrSelectList *_addressSelect_ = NULL;
163 static AddressClipboard *_clipBoard_ = NULL;
164
165 /* Address index file and interfaces */
166 static AddressIndex *_addressIndex_ = NULL;
167 static GList *_addressInterfaceList_ = NULL;
168 static GList *_addressIFaceSelection_ = NULL;
169 #define ADDRESSBOOK_IFACE_SELECTION "1/y,3/y,4/y,2/n"
170
171 static AddressBook_win addrbook;
172
173 static GHashTable *_addressBookTypeHash_ = NULL;
174 static GList *_addressBookTypeList_ = NULL;
175
176 static void addressbook_new_address_from_book_post_cb( ItemPerson *person );
177 static void addressbook_new_address_from_folder_post_cb( ItemPerson *person );
178 static void addressbook_edit_address_post_cb( ItemPerson *person );
179
180 static void addressbook_create                  (void);
181 static gint addressbook_close                   (void);
182 static void addressbook_button_set_sensitive    (void);
183
184 static gboolean address_index_has_focus = FALSE;
185 static gboolean address_list_has_focus = FALSE;
186
187 /* callback functions */
188 static void addressbook_del_clicked             (GtkButton      *button,
189                                                  gpointer        data);
190 static void addressbook_reg_clicked             (GtkButton      *button,
191                                                  gpointer        data);
192 static void addressbook_to_clicked              (GtkButton      *button,
193                                                  gpointer        data);
194 static void addressbook_lup_clicked             (GtkButton      *button,
195                                                  gpointer       data);
196 static void addressbook_close_clicked           (GtkButton      *button,
197                                                  gpointer       data);
198
199 static void addressbook_tree_selected           (GtkCMCTree     *ctree,
200                                                  GtkCMCTreeNode *node,
201                                                  gint            column,
202                                                  gpointer        data);
203 static void addressbook_select_row_tree         (GtkCMCTree     *ctree,
204                                                  GtkCMCTreeNode *node,
205                                                  gint            column,
206                                                  gpointer        data);
207 static void addressbook_list_row_selected       (GtkCMCTree     *clist,
208                                                  GtkCMCTreeNode *node,
209                                                  gint            column,
210                                                  gpointer        data);
211 static void addressbook_list_row_unselected     (GtkCMCTree     *clist,
212                                                  GtkCMCTreeNode *node,
213                                                  gint            column,
214                                                  gpointer        data);
215 static void addressbook_person_expand_node      (GtkCMCTree     *ctree,
216                                                  GList          *node,
217                                                  gpointer       *data );
218 static void addressbook_person_collapse_node    (GtkCMCTree     *ctree,
219                                                  GList          *node,
220                                                  gpointer       *data );
221
222 static gboolean addressbook_list_button_pressed (GtkWidget      *widget,
223                                                  GdkEventButton *event,
224                                                  gpointer        data);
225 static gboolean addressbook_list_button_released(GtkWidget      *widget,
226                                                  GdkEventButton *event,
227                                                  gpointer        data);
228 static gboolean addressbook_tree_button_pressed (GtkWidget      *ctree,
229                                                  GdkEventButton *event,
230                                                  gpointer        data);
231 static gboolean addressbook_tree_button_released(GtkWidget      *ctree,
232                                                  GdkEventButton *event,
233                                                  gpointer        data);
234
235 static void addressbook_new_folder_cb           (GtkAction      *action,
236                                                  gpointer        data);
237 static void addressbook_new_group_cb            (GtkAction      *action,
238                                                  gpointer        data);
239 static void addressbook_treenode_edit_cb        (GtkAction      *action,
240                                                  gpointer        data);
241 static void addressbook_treenode_delete_cb      (GtkAction      *action,
242                                                  gpointer        data);
243
244 static void addressbook_change_node_name        (GtkCMCTreeNode *node,
245                                                  const gchar    *name);
246
247 static void addressbook_new_address_cb          (GtkAction      *action,
248                                                  gpointer        data);
249 static void addressbook_edit_address_cb         (GtkAction      *action,
250                                                  gpointer        data);
251 static void addressbook_delete_address_cb       (GtkAction      *action,
252                                                  gpointer        data);
253
254 static void close_cb                            (GtkAction      *action,
255                                                  gpointer        data);
256 static void addressbook_file_save_cb            (GtkAction      *action,
257                                                  gpointer        data);
258
259 /* Data source edit stuff */
260 static void addressbook_new_book_cb             (GtkAction      *action,
261                                                  gpointer        data);
262 static void addressbook_new_vcard_cb            (GtkAction      *action,
263                                                  gpointer        data);
264
265 #ifdef USE_JPILOT
266 static void addressbook_new_jpilot_cb           (GtkAction      *action,
267                                                  gpointer        data);
268 #endif
269
270 #ifdef USE_LDAP
271 static void addressbook_new_ldap_cb             (GtkAction      *action,
272                                                  gpointer        data);
273 #endif
274
275 static void addressbook_set_clist               (AddressObject  *obj,
276                                                  gboolean        refresh);
277
278 static void addressbook_load_tree               (void);
279 void addressbook_read_file                      (void);
280
281 static GtkCMCTreeNode *addressbook_add_object   (GtkCMCTreeNode *node,
282                                                  AddressObject  *obj);
283 static void addressbook_treenode_remove_item    ( void );
284
285 static AddressDataSource *addressbook_find_datasource
286                                                 (GtkCMCTreeNode *node );
287
288 static AddressBookFile *addressbook_get_book_file(void);
289
290 static GtkCMCTreeNode *addressbook_node_add_folder
291                                                 (GtkCMCTreeNode *node,
292                                                 AddressDataSource *ds,
293                                                 ItemFolder      *itemFolder,
294                                                 AddressObjectType otype);
295 static GtkCMCTreeNode *addressbook_node_add_group (GtkCMCTreeNode       *node,
296                                                 AddressDataSource *ds,
297                                                 ItemGroup       *itemGroup);
298 static void addressbook_tree_remove_children    (GtkCMCTree     *ctree,
299                                                 GtkCMCTreeNode  *parent);
300 static void addressbook_move_nodes_up           (GtkCMCTree     *ctree,
301                                                 GtkCMCTreeNode  *node);
302 static GtkCMCTreeNode *addressbook_find_group_node (GtkCMCTreeNode      *parent,
303                                                    ItemGroup    *group);
304 static gboolean addressbook_entry_key_pressed   (GtkWidget      *widget,
305                                                  GdkEventKey    *event,
306                                                  gpointer        data);
307 static gint addressbook_treenode_compare_func   (GtkCMCList     *clist,
308                                                  gconstpointer   ptr1,
309                                                  gconstpointer   ptr2);
310 static void addressbook_folder_load_one_person  (GtkCMCTree *clist, 
311                                                  ItemPerson *person,  
312                                                  AddressTypeControlItem *atci, 
313                                                  AddressTypeControlItem *atciMail);
314 static void addressbook_folder_refresh_one_person(GtkCMCTree *clist, 
315                                                   ItemPerson *person);
316 static void addressbook_folder_remove_one_person(GtkCMCTree *clist, 
317                                                  ItemPerson *person);
318 static void addressbook_folder_remove_node      (GtkCMCTree *clist, 
319                                                  GtkCMCTreeNode *node);
320
321 static void addressbook_edit_address( gpointer data, guint action, GtkWidget *widget,
322                                                                           gboolean force_focus );
323
324 /* LUT's and IF stuff */
325 static void addressbook_free_treenode           ( gpointer data );
326 static AddressTypeControlItem *addrbookctl_lookup       (gint            ot);
327 static AddressTypeControlItem *addrbookctl_lookup_iface(AddressIfType    ifType);
328
329 static void addrbookctl_build_map                       (GtkWidget      *window);
330 static void addrbookctl_build_iflist                    (void);
331 static AdapterInterface *addrbookctl_find_interface     (AddressIfType   ifType);
332 static void addrbookctl_build_ifselect                  (void);
333
334 static void addrbookctl_free_interface          (AdapterInterface *adapter);
335 static void addrbookctl_free_datasource         (AdapterDSource   *adapter);
336 static void addrbookctl_free_folder             (AdapterFolder    *adapter);
337 static void addrbookctl_free_group              (AdapterGroup     *adapter);
338
339 static void addressbook_list_select_clear       ( void );
340 static void addressbook_list_select_add         ( AddrItemObject    *aio,
341                                                   AddressDataSource *ds );
342 static void addressbook_list_select_remove      ( AddrItemObject    *aio );
343
344 static void addressbook_import_ldif_cb          ( GtkAction *action, gpointer data );
345 static void addressbook_find_duplicates_cb      ( GtkAction *action, gpointer data );
346 static void addressbook_edit_custom_attr_cb     ( GtkAction *action, gpointer data );
347 static void addressbook_import_mutt_cb          ( GtkAction *action, gpointer data );
348 static void addressbook_import_pine_cb          ( GtkAction *action, gpointer data );
349 static void addressbook_export_html_cb          ( GtkAction *action, gpointer data );
350 static void addressbook_export_ldif_cb          ( GtkAction *action, gpointer data );
351 static void addressbook_select_all_cb           ( GtkAction *action, gpointer data );
352 static void addressbook_clip_cut_cb             ( GtkAction *action, gpointer data );
353 static void addressbook_clip_copy_cb            ( GtkAction *action, gpointer data );
354 static void addressbook_clip_paste_cb           ( GtkAction *action, gpointer data );
355 static void addressbook_treenode_cut_cb         ( GtkAction *action, gpointer data );
356 static void addressbook_treenode_copy_cb        ( GtkAction *action, gpointer data );
357 static void addressbook_treenode_paste_cb       ( GtkAction *action, gpointer data );
358
359 static void addressbook_mail_to_cb              ( GtkAction *action, gpointer data );
360
361 #ifdef USE_LDAP
362 static void addressbook_browse_entry_cb         ( GtkAction *action, gpointer data );
363 #endif
364 static void addressbook_edit_clicked(GtkButton *button, gpointer data);
365
366 static void addressbook_start_drag(GtkWidget *widget, gint button, 
367                                    GdkEvent *event,
368                                    void *data);
369 static void addressbook_drag_data_get(GtkWidget        *widget,
370                                      GdkDragContext   *drag_context,
371                                      GtkSelectionData *selection_data,
372                                      guint             info,
373                                      guint             time,
374                                      void             *data);
375 static gboolean addressbook_drag_motion_cb(GtkWidget      *widget,
376                                           GdkDragContext *context,
377                                           gint            x,
378                                           gint            y,
379                                           guint           time,
380                                           void           *data);
381 static void addressbook_drag_leave_cb(GtkWidget      *widget,
382                                      GdkDragContext *context,
383                                      guint           time,
384                                      void           *data);
385 static void addressbook_drag_received_cb(GtkWidget        *widget,
386                                         GdkDragContext   *drag_context,
387                                         gint              x,
388                                         gint              y,
389                                         GtkSelectionData *data,
390                                         guint             info,
391                                         guint             time,
392                                         void             *pdata);
393 static void addressbook_list_menu_setup( void );
394
395 static GtkTargetEntry addressbook_drag_types[] =
396 {
397         {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY}
398 };
399
400 static GtkTargetList *addressbook_target_list = NULL;
401
402 static void about_show_cb(GtkAction *action, gpointer data)
403 {
404         about_show();
405 }
406
407 static GtkActionEntry addressbook_entries[] =
408 {
409         {"Menu",                                NULL, "Menu" },
410 /* menus */
411         {"Book",                        NULL, N_("_Book") },
412         {"Address",                     NULL, N_("_Edit") },
413         {"Tools",                       NULL, N_("_Tools") },
414         {"Help",                        NULL, N_("_Help") },
415         
416 /* Book menu */
417         {"Book/NewBook",                NULL, N_("New _Book"), "<control>B", NULL, G_CALLBACK(addressbook_new_book_cb) },
418         {"Book/NewFolder",              NULL, N_("New _Folder"), "<control>R", NULL, G_CALLBACK(addressbook_new_folder_cb) },
419         {"Book/NewVCard",               NULL, N_("New _vCard"), "<control><shift>D", NULL, G_CALLBACK(addressbook_new_vcard_cb) },
420
421
422 #ifdef USE_JPILOT
423         {"Book/NewJPilot",              NULL, N_("New _JPilot"), "<control>J", NULL, G_CALLBACK(addressbook_new_jpilot_cb) },
424 #endif
425 #ifdef USE_LDAP
426         {"Book/NewLDAPServer",          NULL, N_("New LDAP _Server"), "<control><shift>S", NULL, G_CALLBACK(addressbook_new_ldap_cb) },
427 #endif
428         {"Book/---",                    NULL, "---", NULL, NULL, NULL },
429
430         {"Book/EditBook",               NULL, N_("_Edit book"), NULL, NULL, G_CALLBACK(addressbook_treenode_edit_cb) },
431         {"Book/DeleteBook",             NULL, N_("_Delete book"), NULL, NULL, G_CALLBACK(addressbook_treenode_delete_cb) },
432         /* {"Book/---",                 NULL, "---", NULL, NULL, NULL }, */
433         {"Book/Save",                   NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(addressbook_file_save_cb) },
434         {"Book/Close",                  NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(close_cb) },
435
436 /* Adress menu */
437         {"Address/SelectAll",           NULL, N_("_Select all"), "<control>A", NULL, G_CALLBACK(addressbook_select_all_cb) },
438         {"Address/---",                 NULL, "---", NULL, NULL, NULL },
439         {"Address/Cut",                 NULL, N_("C_ut"), "<control>X", NULL, G_CALLBACK(addressbook_clip_cut_cb) },
440         {"Address/Copy",                NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(addressbook_clip_copy_cb) },
441         {"Address/Paste",               NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(addressbook_clip_paste_cb) },
442         /* {"Address/---",                      NULL, "---", NULL, NULL, NULL }, */
443         {"Address/Edit",                NULL, N_("_Edit"), "<control>Return", NULL, G_CALLBACK(addressbook_edit_address_cb) },
444         {"Address/Delete",              NULL, N_("_Delete"), "<control>D", NULL, G_CALLBACK(addressbook_delete_address_cb) },
445         /* {"Address/---",                      NULL, "---", NULL, NULL, NULL }, */
446         {"Address/NewAddress",          NULL, N_("New _Address"), "<control>N", NULL, G_CALLBACK(addressbook_new_address_cb) },
447         {"Address/NewGroup",            NULL, N_("New _Group"), "<control>G", NULL, G_CALLBACK(addressbook_new_group_cb) },
448         /* {"Address/---",                      NULL, "---", NULL, NULL, NULL }, */
449         {"Address/Mailto",              NULL, N_("_Mail To"), "<control>M", NULL, G_CALLBACK(addressbook_mail_to_cb) },
450
451
452 /* Tools menu */
453         {"Tools/ImportLDIF",            NULL, N_("Import _LDIF file..."), NULL, NULL, G_CALLBACK(addressbook_import_ldif_cb) },
454         {"Tools/ImportMutt",            NULL, N_("Import M_utt file..."), NULL, NULL, G_CALLBACK(addressbook_import_mutt_cb) },
455         {"Tools/ImportPine",            NULL, N_("Import _Pine file..."), NULL, NULL, G_CALLBACK(addressbook_import_pine_cb) },
456         {"Tools/---",                   NULL, "---", NULL, NULL, NULL },
457         {"Tools/ExportHTML",            NULL, N_("Export _HTML..."), NULL, NULL, G_CALLBACK(addressbook_export_html_cb) },
458         {"Tools/ExportLDIF",            NULL, N_("Export LDI_F..."), NULL, NULL, G_CALLBACK(addressbook_export_ldif_cb) },
459         /* {"Tools/---",                        NULL, "---", NULL, NULL, NULL },*/
460         {"Tools/FindDuplicates",        NULL, N_("Find duplicates..."), NULL, NULL, G_CALLBACK(addressbook_find_duplicates_cb) },
461         {"Tools/EditAttrs",             NULL, N_("Edit custom attributes..."), NULL, NULL, G_CALLBACK(addressbook_edit_custom_attr_cb) },
462
463 /* Help menu */
464         {"Help/About",                  NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) }, 
465
466 };
467
468 static GtkActionEntry addressbook_tree_popup_entries[] =
469 {
470         {"ABTreePopup",                 NULL, "ABTreePopup" },
471         {"ABTreePopup/EditBook",        NULL, N_("_Edit"), NULL, NULL, G_CALLBACK(addressbook_treenode_edit_cb) },
472         {"ABTreePopup/DeleteBook",      NULL, N_("_Delete"), NULL, NULL, G_CALLBACK(addressbook_treenode_delete_cb) },
473         {"ABTreePopup/---",             NULL, "---", NULL, NULL, NULL },
474         {"ABTreePopup/NewBook",         NULL, N_("New _Book"), NULL, NULL, G_CALLBACK(addressbook_new_book_cb) },
475         {"ABTreePopup/NewFolder",       NULL, N_("New _Folder"), NULL, NULL, G_CALLBACK(addressbook_new_folder_cb) },
476         {"ABTreePopup/NewGroup",        NULL, N_("New _Group"), NULL, NULL, G_CALLBACK(addressbook_new_group_cb) },
477         /* {"ABTreePopup/---",          NULL, "---", NULL, NULL, NULL }, */
478         {"ABTreePopup/Cut",             NULL, N_("C_ut"), NULL, NULL, G_CALLBACK(addressbook_treenode_cut_cb) },
479         {"ABTreePopup/Copy",            NULL, N_("_Copy"), NULL, NULL, G_CALLBACK(addressbook_treenode_copy_cb) },
480         {"ABTreePopup/Paste",           NULL, N_("_Paste"), NULL, NULL, G_CALLBACK(addressbook_treenode_paste_cb) },
481 };
482
483 static GtkActionEntry addressbook_list_popup_entries[] =
484 {
485         {"ABListPopup",                 NULL, "ABListPopup" },
486         {"ABListPopup/SelectAll",       NULL, N_("_Select all"), NULL, NULL, G_CALLBACK(addressbook_select_all_cb) },
487         {"ABListPopup/---",             NULL, "---", NULL, NULL, NULL },
488         {"ABListPopup/Edit",            NULL, N_("_Edit"), NULL, NULL, G_CALLBACK(addressbook_edit_address_cb) },
489         {"ABListPopup/Delete",          NULL, N_("_Delete"), NULL, NULL, G_CALLBACK(addressbook_delete_address_cb) },
490         /* {"ABListPopup/---",          NULL, "---", NULL, NULL, NULL }, */
491         {"ABListPopup/NewAddress",      NULL, N_("New _Address"), NULL, NULL, G_CALLBACK(addressbook_new_address_cb) },
492         {"ABListPopup/NewGroup",        NULL, N_("New _Group"), NULL, NULL, G_CALLBACK(addressbook_new_group_cb) },
493         /* {"ABListPopup/---",          NULL, "---", NULL, NULL, NULL }, */
494         {"ABListPopup/Cut",             NULL, N_("C_ut"), NULL, NULL, G_CALLBACK(addressbook_clip_cut_cb) },
495         {"ABListPopup/Copy",            NULL, N_("_Copy"), NULL, NULL, G_CALLBACK(addressbook_clip_copy_cb) },
496         {"ABListPopup/Paste",           NULL, N_("_Paste"), NULL, NULL, G_CALLBACK(addressbook_clip_paste_cb) },
497         /* {"ABListPopup/---",          NULL, "---", NULL, NULL, NULL }, */
498         {"ABListPopup/Mailto",          NULL, N_("_Mail To"), NULL, NULL, G_CALLBACK(addressbook_mail_to_cb) },
499 #ifdef USE_LDAP
500         {"ABListPopup/BrowseEntry",     NULL, N_("_Browse Entry"), NULL, NULL, G_CALLBACK(addressbook_browse_entry_cb) },
501 #endif
502 };
503
504 /**
505  * Structure of error message table.
506  */
507 typedef struct _ErrMsgTableEntry ErrMsgTableEntry;
508 struct _ErrMsgTableEntry {
509         gint    code;
510         gchar   *description;
511 };
512
513 static gchar *_errMsgUnknown_ = N_( "Unknown" );
514
515 /**
516  * Lookup table of error messages for general errors. Note that a NULL
517  * description signifies the end of the table.
518  */
519 static ErrMsgTableEntry _lutErrorsGeneral_[] = {
520         { MGU_SUCCESS,          N_("Success") },
521         { MGU_BAD_ARGS,         N_("Bad arguments") },
522         { MGU_NO_FILE,          N_("File not specified") },
523         { MGU_OPEN_FILE,        N_("Error opening file") },
524         { MGU_ERROR_READ,       N_("Error reading file") },
525         { MGU_EOF,              N_("End of file encountered") },
526         { MGU_OO_MEMORY,        N_("Error allocating memory") },
527         { MGU_BAD_FORMAT,       N_("Bad file format") },
528         { MGU_ERROR_WRITE,      N_("Error writing to file") },
529         { MGU_OPEN_DIRECTORY,   N_("Error opening directory") },
530         { MGU_NO_PATH,          N_("No path specified") },
531         { 0,                    NULL }
532 };
533
534 #ifdef USE_LDAP
535 /**
536  * Lookup table of error messages for LDAP errors.
537  */
538 static ErrMsgTableEntry _lutErrorsLDAP_[] = {
539         { LDAPRC_SUCCESS,                       N_("Success") },
540         { LDAPRC_CONNECT,                       N_("Error connecting to LDAP server") },
541         { LDAPRC_INIT,                          N_("Error initializing LDAP") },
542         { LDAPRC_BIND,                          N_("Error binding to LDAP server") },
543         { LDAPRC_SEARCH,                        N_("Error searching LDAP database") },
544         { LDAPRC_TIMEOUT,                       N_("Timeout performing LDAP operation") },
545         { LDAPRC_CRITERIA,                      N_("Error in LDAP search criteria") },
546         { LDAPRC_NOENTRIES,                     N_("No LDAP entries found for search criteria") },
547         { LDAPRC_STOP_FLAG,                     N_("LDAP search terminated on request") },
548         { LDAPRC_TLS,                           N_("Error starting TLS connection") },
549         { LDAPRC_NODN,                          N_("Distinguished Name (dn) is missing") },
550         { LDAPRC_NAMING_VIOLATION,              N_("Missing required information") },
551         { LDAPRC_ALREADY_EXIST,                 N_("Another contact exists with that key") },
552         { LDAPRC_STRONG_AUTH,                   N_("Strong(er) authentication required") },
553         { 0,                                    NULL }
554 };
555 #endif
556
557 /**
558  * Lookup message for specified error code.
559  * \param lut  Lookup table.
560  * \param code Code to lookup.
561  * \return Description associated to code.
562  */
563 static gchar *addressbook_err2string( ErrMsgTableEntry lut[], gint code ) {
564         gchar *desc = NULL;
565         ErrMsgTableEntry entry;
566         gint i;
567
568         for( i = 0; ; i++ ) {
569                 entry = lut[ i ];
570                 if( entry.description == NULL ) break;
571                 if( entry.code == code ) {
572                         desc = entry.description;
573                         break;
574                 }
575         }
576         if( ! desc ) {
577                 desc = _errMsgUnknown_;
578         }
579         return desc;
580 }
581
582 static gboolean lastCanLookup = FALSE;
583
584 static void addressbook_show_buttons(gboolean add_and_delete, gboolean lookup, gboolean mail_ops)
585 {
586         if (add_and_delete) {
587                 gtk_widget_show(addrbook.edit_btn);
588                 gtk_widget_show(addrbook.del_btn);
589                 gtk_widget_show(addrbook.reg_btn);
590         } else {
591                 gtk_widget_hide(addrbook.edit_btn);
592                 gtk_widget_hide(addrbook.del_btn);
593                 gtk_widget_hide(addrbook.reg_btn);
594         }
595         
596         if (lookup) {
597                 gtk_widget_show(addrbook.lup_btn);
598                 gtk_widget_show(addrbook.entry);
599                 gtk_widget_show(addrbook.label);
600         } else {
601                 gtk_widget_hide(addrbook.lup_btn);
602                 gtk_widget_hide(addrbook.entry);
603                 gtk_widget_hide(addrbook.label);
604         }
605
606         lastCanLookup = lookup;
607
608         if (mail_ops) {
609                 gtk_widget_show(addrbook.to_btn);
610                 gtk_widget_show(addrbook.cc_btn);
611                 gtk_widget_show(addrbook.bcc_btn);
612         } else {
613                 gtk_widget_hide(addrbook.to_btn);
614                 gtk_widget_hide(addrbook.cc_btn);
615                 gtk_widget_hide(addrbook.bcc_btn);
616         }
617 }
618
619 void addressbook_open(Compose *target)
620 {
621         /* Initialize all static members */
622         if( _clipBoard_ == NULL ) {
623                 _clipBoard_ = addrclip_create();
624         }
625         if( _addressIndex_ != NULL ) {
626                 addrclip_set_index( _clipBoard_, _addressIndex_ );
627         }
628         if( _addressSelect_ == NULL ) {
629                 _addressSelect_ = addrselect_list_create();
630         }
631         if (!addrbook.window) {
632                 addressbook_read_file();
633                 addressbook_create();
634                 addressbook_load_tree();
635                 gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
636                                  GTK_CMCTREE_NODE(GTK_CMCLIST(addrbook.ctree)->row_list));
637         }
638         else {
639                 gtk_widget_hide(addrbook.window);
640         }
641
642         gtk_widget_show_all(addrbook.window);
643 #ifdef MAEMO
644                 maemo_window_full_screen_if_needed(GTK_WINDOW(addrbook.window));
645                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(addrbook.window));
646 #endif
647         if (!prefs_common.addressbook_use_editaddress_dialog)
648                 addressbook_edit_person_widgetset_hide();
649
650         address_completion_start(addrbook.window);
651
652         addressbook_show_buttons(target == NULL, lastCanLookup, target != NULL);
653         addressbook_set_target_compose(target);
654 }
655
656 /**
657  * Destroy addressbook.
658  */
659 void addressbook_destroy( void ) {
660         /* Free up address stuff */
661         if( _addressSelect_ != NULL ) {
662                 addrselect_list_free( _addressSelect_ );
663         }
664         if( _clipBoard_ != NULL ) {
665                 addrclip_free( _clipBoard_ );
666         }
667         if( _addressIndex_ != NULL ) {
668                 addrindex_free_index( _addressIndex_ );
669                 addrindex_teardown();
670         }
671         _addressSelect_ = NULL;
672         _clipBoard_ = NULL;
673         _addressIndex_ = NULL;
674 }
675
676 void addressbook_set_target_compose(Compose *target)
677 {
678         addrbook.target_compose = target;
679         addressbook_button_set_sensitive();
680 }
681
682 Compose *addressbook_get_target_compose(void)
683 {
684         return addrbook.target_compose;
685 }
686
687 /**
688  * Refresh addressbook and save to file(s).
689  */
690 void addressbook_refresh( void )
691 {
692         if (addrbook.window) {
693                 if (addrbook.treeSelected) {
694                         gtk_sctree_select( GTK_SCTREE(addrbook.ctree),
695                                          addrbook.treeSelected);
696                         addressbook_set_clist(
697                                 gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
698                                         addrbook.treeSelected),
699                                 TRUE);
700
701                 }
702         }
703         addressbook_export_to_file();
704 }
705
706 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
707 {
708         if (event && event->keyval == GDK_Escape)
709                 addressbook_close();
710         else if (event && event->keyval == GDK_Delete) {
711                 /* TODO: enable deletion when focus is in ctree (needs implementation in _del_clicked() */
712                 if ( /* address_index_has_focus || */ address_list_has_focus )
713                         addressbook_del_clicked(NULL, NULL);
714         }
715         return FALSE;
716 }
717
718 /*!
719  *\brief        Save Gtk object size to prefs dataset
720  */
721 static void addressbook_size_allocate_cb(GtkWidget *widget,
722                                          GtkAllocation *allocation)
723 {
724         g_return_if_fail(allocation != NULL);
725
726         prefs_common.addressbookwin_width = allocation->width;
727         prefs_common.addressbookwin_height = allocation->height;
728 }
729
730 static gint sort_column_number = 0;
731 static GtkSortType sort_column_type = GTK_SORT_ASCENDING;
732
733 static gint list_case_sort(
734         GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
735 {
736         GtkCMCListRow *row1 = (GtkCMCListRow *) ptr1;
737         GtkCMCListRow *row2 = (GtkCMCListRow *) ptr2;
738         gchar *name1 = NULL, *name2 = NULL;
739         AddrItemObject *aio1 = ((GtkCMCListRow *)ptr1)->data;
740         AddrItemObject *aio2 = ((GtkCMCListRow *)ptr2)->data;
741
742         if( aio1->type == aio2->type ) {
743                 if( row1 ) 
744                         name1 = GTK_CMCELL_TEXT (row1->cell[sort_column_number])->text;
745                 if( row2 ) 
746                         name2 = GTK_CMCELL_TEXT (row2->cell[sort_column_number])->text;
747                 if( ! name1 ) return ( name2 != NULL );
748                 if( ! name2 ) return -1;
749                 return g_utf8_collate( name1, name2 );
750         } else {
751                 /* Order groups before person */
752                 if( aio1->type == ITEMTYPE_GROUP ) {
753                         return (sort_column_type==GTK_SORT_ASCENDING) ? -1:+1;
754                 } else if( aio2->type == ITEMTYPE_GROUP ) {
755                         return (sort_column_type==GTK_SORT_ASCENDING) ? +1:-1;
756                 }
757                 return 0;
758         }
759 }
760
761 static void addressbook_sort_list(GtkCMCList *clist, const gint col,
762                 const GtkSortType sort_type)
763 {
764         gint pos;
765         GtkWidget *hbox, *label, *arrow;
766
767         sort_column_number = col;
768         sort_column_type = sort_type;
769         gtk_cmclist_set_compare_func(clist, list_case_sort);
770         gtk_cmclist_set_sort_type(clist, sort_type);
771         gtk_cmclist_set_sort_column(clist, col);        
772
773         gtk_cmclist_freeze(clist);
774         gtk_cmclist_sort(clist);
775         
776         for(pos = 0 ; pos < N_LIST_COLS ; pos++) {
777                 hbox = gtk_hbox_new(FALSE, 4);
778                 label = gtk_label_new(gettext(list_titles[pos]));
779                 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
780                 
781                 if(pos == col) {
782                         arrow = gtk_arrow_new(sort_type == GTK_SORT_ASCENDING ?
783                                 GTK_ARROW_DOWN : GTK_ARROW_UP, GTK_SHADOW_IN);
784                         gtk_box_pack_end(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
785                 }
786                 
787                 gtk_widget_show_all(hbox);
788                 gtk_cmclist_set_column_widget(clist, pos, hbox);
789         }
790         
791         gtk_cmclist_thaw(clist);        
792 }
793
794 static void addressbook_name_clicked(GtkWidget *button, GtkCMCList *clist)
795 {
796         static GtkSortType sort_type = GTK_SORT_ASCENDING;
797         
798         sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
799                         GTK_SORT_ASCENDING;
800         addressbook_sort_list(clist, COL_NAME, sort_type);
801 }
802
803 static void addressbook_address_clicked(GtkWidget *button, GtkCMCList *clist)
804 {
805         static GtkSortType sort_type = GTK_SORT_ASCENDING;
806
807         sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
808                         GTK_SORT_ASCENDING;
809         addressbook_sort_list(clist, COL_ADDRESS, sort_type);
810 }
811
812 static void addressbook_remarks_clicked(GtkWidget *button, GtkCMCList *clist)
813 {
814         static GtkSortType sort_type = GTK_SORT_ASCENDING;
815
816         sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
817                         GTK_SORT_ASCENDING;
818         addressbook_sort_list(clist, COL_REMARKS, sort_type);
819 }
820
821 static gboolean addressbook_address_index_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
822                                                                                          gpointer data)
823 {
824         address_index_has_focus = TRUE;
825         return FALSE;
826 }
827
828 static gboolean addressbook_address_index_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
829                                                                                          gpointer data)
830 {
831         address_index_has_focus = FALSE;
832         if (!prefs_common.addressbook_use_editaddress_dialog
833                         && !address_list_has_focus)
834                 addressbook_address_list_disable_some_actions();
835         return FALSE;
836 }
837
838 static gboolean addressbook_address_list_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
839                                                                                          gpointer data)
840 {
841         address_list_has_focus = TRUE;
842         return FALSE;
843 }
844
845 static gboolean addressbook_address_list_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
846                                                                                          gpointer data)
847 {
848         address_list_has_focus = FALSE;
849         if (!prefs_common.addressbook_use_editaddress_dialog
850                         && !address_index_has_focus)
851                 addressbook_address_list_disable_some_actions();
852         return FALSE;
853 }
854
855 /* save hpane and vpane's handle position when it moves */
856 static void addressbook_pane_save_position(void)
857 {
858         if (addrbook.hpaned)
859                 prefs_common.addressbook_hpaned_pos = 
860                         gtk_paned_get_position(GTK_PANED(addrbook.hpaned));
861         if (addrbook.vpaned)
862                 prefs_common.addressbook_vpaned_pos = 
863                         gtk_paned_get_position(GTK_PANED(addrbook.vpaned));
864 }
865
866 /*
867 * Create the address book widgets. The address book contains two CTree widgets: the
868 * address index tree on the left and the address list on the right.
869 *
870 * The address index tree displays a hierarchy of interfaces and groups. Each node in
871 * this tree is linked to an address Adapter. Adapters have been created for interfaces,
872 * data sources and folder objects.
873 *
874 * The address list displays group, person and email objects. These items are linked
875 * directly to ItemGroup, ItemPerson and ItemEMail objects inside the address book data
876 * sources.
877 *
878 * In the tradition of MVC architecture, the data stores have been separated from the
879 * GUI components. The addrindex.c file provides the interface to all data stores.
880 */
881 static void addressbook_create(void)
882 {
883         GtkWidget *window;
884         GtkWidget *vbox;
885         GtkWidget *menubar;
886         GtkWidget *vbox2;
887         GtkWidget *ctree_swin;
888         GtkWidget *ctree;
889         GtkWidget *editaddress_vbox;
890         GtkWidget *clist_vbox;
891         GtkWidget *clist_swin;
892         GtkWidget *clist;
893         GtkWidget *hpaned;
894         GtkWidget *vpaned;
895         GtkWidget *hbox;
896         GtkWidget *label;
897         GtkWidget *entry;
898         GtkWidget *statusbar;
899         GtkWidget *hbbox;
900         GtkWidget *hsbox;
901         GtkWidget *help_btn;
902         GtkWidget *del_btn;
903         GtkWidget *edit_btn;
904         GtkWidget *reg_btn;
905         GtkWidget *lup_btn;
906         GtkWidget *to_btn;
907         GtkWidget *cc_btn;
908         GtkWidget *bcc_btn;
909         GtkWidget *close_btn;
910         GtkWidget *tree_popup;
911         GtkWidget *list_popup;
912         GList *nodeIf;
913         GtkUIManager *ui_manager;
914         GtkActionGroup *action_group;
915         gchar *index_titles[N_INDEX_COLS];
916         gchar *text;
917         gint i;
918
919         static GdkGeometry geometry;
920
921         debug_print("Creating addressbook window...\n");
922
923         index_titles[COL_SOURCES] = _("Sources");
924
925         /* Address book window */
926         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "addressbook");
927         gtk_window_set_title(GTK_WINDOW(window), _("Address book"));
928         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
929         gtk_widget_realize(window);
930
931         g_signal_connect(G_OBJECT(window), "delete_event",
932                          G_CALLBACK(addressbook_close), NULL);
933         g_signal_connect(G_OBJECT(window), "size_allocate",
934                          G_CALLBACK(addressbook_size_allocate_cb), NULL);
935         g_signal_connect(G_OBJECT(window), "key_press_event",
936                          G_CALLBACK(key_pressed), NULL);
937         MANAGE_WINDOW_SIGNALS_CONNECT(window);
938
939         vbox = gtk_vbox_new(FALSE, 0);
940         gtk_container_add(GTK_CONTAINER(window), vbox);
941
942         /* Menu bar */
943         ui_manager = gtk_ui_manager_new();
944         action_group = cm_menu_create_action_group_full(ui_manager,"Menu", addressbook_entries,
945                         G_N_ELEMENTS(addressbook_entries), NULL);
946         gtk_action_group_add_actions(action_group, addressbook_tree_popup_entries,
947                         G_N_ELEMENTS(addressbook_tree_popup_entries), NULL);
948         gtk_action_group_add_actions(action_group, addressbook_list_popup_entries,
949                         G_N_ELEMENTS(addressbook_list_popup_entries), NULL);
950
951 #ifndef MAEMO
952         MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
953 #else
954         MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
955 #endif
956
957         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Book", "Book", GTK_UI_MANAGER_MENU)
958         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Address", "Address", GTK_UI_MANAGER_MENU)
959         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
960         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
961
962 /* Book menu */
963         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewBook", "Book/NewBook", GTK_UI_MANAGER_MENUITEM)
964         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewFolder", "Book/NewFolder", GTK_UI_MANAGER_MENUITEM)
965         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewVCard", "Book/NewVCard", GTK_UI_MANAGER_MENUITEM)
966 #ifdef USE_JPILOT
967         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewJPilot", "Book/NewJPilot", GTK_UI_MANAGER_MENUITEM)
968 #endif
969 #ifdef USE_LDAP
970         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "NewLDAPServer", "Book/NewLDAPServer", GTK_UI_MANAGER_MENUITEM)
971 #endif
972         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Separator1", "Book/---", GTK_UI_MANAGER_SEPARATOR)
973         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "EditBook", "Book/EditBook", GTK_UI_MANAGER_MENUITEM)
974         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "DeleteBook", "Book/DeleteBook", GTK_UI_MANAGER_MENUITEM)
975         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Separator2", "Book/---", GTK_UI_MANAGER_SEPARATOR)
976         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Save", "Book/Save", GTK_UI_MANAGER_MENUITEM)
977         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Book", "Close", "Book/Close", GTK_UI_MANAGER_MENUITEM)
978
979 /* Address menu */
980         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "SelectAll", "Address/SelectAll", GTK_UI_MANAGER_MENUITEM)
981         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Separator1", "Address/---", GTK_UI_MANAGER_SEPARATOR)
982         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Cut", "Address/Cut", GTK_UI_MANAGER_MENUITEM)
983         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Copy", "Address/Copy", GTK_UI_MANAGER_MENUITEM)
984         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Paste", "Address/Paste", GTK_UI_MANAGER_MENUITEM)
985         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Separator2", "Address/---", GTK_UI_MANAGER_SEPARATOR)
986         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Edit", "Address/Edit", GTK_UI_MANAGER_MENUITEM)
987         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Delete", "Address/Delete", GTK_UI_MANAGER_MENUITEM)
988         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Separator3", "Address/---", GTK_UI_MANAGER_SEPARATOR)
989         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "NewAddress", "Address/NewAddress", GTK_UI_MANAGER_MENUITEM)
990         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "NewGroup", "Address/NewGroup", GTK_UI_MANAGER_MENUITEM)
991         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Separator4", "Address/---", GTK_UI_MANAGER_SEPARATOR)
992         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Address", "Mailto", "Address/Mailto", GTK_UI_MANAGER_MENUITEM)
993
994 /* Tools menu */
995         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ImportLDIF", "Tools/ImportLDIF", GTK_UI_MANAGER_MENUITEM)
996         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ImportMutt", "Tools/ImportMutt", GTK_UI_MANAGER_MENUITEM)
997         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ImportPine", "Tools/ImportPine", GTK_UI_MANAGER_MENUITEM)
998         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "Separator1", "Tools/---", GTK_UI_MANAGER_SEPARATOR)
999         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ExportHTML", "Tools/ExportHTML", GTK_UI_MANAGER_MENUITEM)
1000         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "ExportLDIF", "Tools/ExportLDIF", GTK_UI_MANAGER_MENUITEM)
1001         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "Separator2", "Tools/---", GTK_UI_MANAGER_SEPARATOR)
1002         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "FindDuplicates", "Tools/FindDuplicates", GTK_UI_MANAGER_MENUITEM)
1003         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Tools", "EditAttrs", "Tools/EditAttrs", GTK_UI_MANAGER_MENUITEM)
1004
1005 /* Help menu */
1006         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
1007
1008         menubar = gtk_ui_manager_get_widget(ui_manager, "/Menu");
1009
1010 #ifndef MAEMO
1011         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
1012 #else
1013         hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
1014 #endif
1015
1016         vbox2 = gtk_vbox_new(FALSE, BORDER_WIDTH);
1017         gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
1018         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
1019
1020         ctree_swin = gtk_scrolled_window_new(NULL, NULL);
1021         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ctree_swin),
1022                                        GTK_POLICY_AUTOMATIC,
1023                                        GTK_POLICY_AUTOMATIC);
1024         gtk_widget_set_size_request(ctree_swin, COL_FOLDER_WIDTH + 20, -1);
1025
1026         /* Address index */
1027         ctree = gtk_sctree_new_with_titles(N_INDEX_COLS, 0, index_titles);
1028         GTK_WIDGET_UNSET_FLAGS(GTK_CMCLIST(ctree)->column[0].button,
1029                                GTK_CAN_FOCUS);
1030
1031         gtk_container_add(GTK_CONTAINER(ctree_swin), ctree);
1032         gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
1033         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), 0, COL_FOLDER_WIDTH);
1034         if (prefs_common.enable_dotted_lines) {
1035                 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_DOTTED);
1036                 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
1037                                      GTK_CMCTREE_EXPANDER_SQUARE);
1038         } else {
1039                 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
1040                 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
1041                                      GTK_CMCTREE_EXPANDER_TRIANGLE);
1042         }
1043         gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
1044         gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
1045         gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree),
1046                                    addressbook_treenode_compare_func);
1047
1048         g_signal_connect(G_OBJECT(ctree), "tree_select_row",
1049                          G_CALLBACK(addressbook_tree_selected), NULL);
1050         g_signal_connect(G_OBJECT(ctree), "button_press_event",
1051                          G_CALLBACK(addressbook_tree_button_pressed),
1052                          NULL);
1053         g_signal_connect(G_OBJECT(ctree), "button_release_event",
1054                          G_CALLBACK(addressbook_tree_button_released),
1055                          NULL);
1056         /* TEMPORARY */
1057         g_signal_connect(G_OBJECT(ctree), "select_row",
1058                          G_CALLBACK(addressbook_select_row_tree), NULL);
1059
1060         gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
1061                           addressbook_drag_types, 1,
1062                           GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
1063         g_signal_connect(G_OBJECT(ctree), "drag_motion",
1064                          G_CALLBACK(addressbook_drag_motion_cb),
1065                          ctree);
1066         g_signal_connect(G_OBJECT(ctree), "drag_leave",
1067                          G_CALLBACK(addressbook_drag_leave_cb),
1068                          ctree);
1069         g_signal_connect(G_OBJECT(ctree), "drag_data_received",
1070                          G_CALLBACK(addressbook_drag_received_cb),
1071                          ctree);
1072         g_signal_connect(G_OBJECT(ctree), "focus_in_event",
1073                 G_CALLBACK(addressbook_address_index_focus_evt_in), NULL);
1074         g_signal_connect(G_OBJECT(ctree), "focus_out_event",
1075                 G_CALLBACK(addressbook_address_index_focus_evt_out), NULL);
1076
1077         clist_vbox = gtk_vbox_new(FALSE, 4);
1078
1079         clist_swin = gtk_scrolled_window_new(NULL, NULL);
1080         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
1081                                        GTK_POLICY_AUTOMATIC,
1082                                        GTK_POLICY_AUTOMATIC);
1083         gtk_box_pack_start(GTK_BOX(clist_vbox), clist_swin, TRUE, TRUE, 0);
1084
1085         /* Address list */
1086         clist = gtk_sctree_new_with_titles(N_LIST_COLS, 0, list_titles);
1087         gtk_container_add(GTK_CONTAINER(clist_swin), clist);
1088         gtk_cmclist_set_selection_mode(GTK_CMCLIST(clist), GTK_SELECTION_EXTENDED);
1089         if (prefs_common.enable_dotted_lines) {
1090                 gtk_cmctree_set_line_style(GTK_CMCTREE(clist), GTK_CMCTREE_LINES_DOTTED);
1091                 gtk_cmctree_set_expander_style(GTK_CMCTREE(clist),
1092                                      GTK_CMCTREE_EXPANDER_SQUARE);
1093         } else {
1094                 gtk_cmctree_set_line_style(GTK_CMCTREE(clist), GTK_CMCTREE_LINES_NONE);
1095                 gtk_cmctree_set_expander_style(GTK_CMCTREE(clist),
1096                                      GTK_CMCTREE_EXPANDER_TRIANGLE);
1097         }
1098         gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
1099         gtk_cmctree_set_indent(GTK_CMCTREE(clist), CTREE_INDENT);
1100         gtk_cmclist_set_column_width(GTK_CMCLIST(clist), COL_NAME,
1101                                    COL_NAME_WIDTH);
1102         gtk_cmclist_set_column_width(GTK_CMCLIST(clist), COL_ADDRESS,
1103                                    COL_ADDRESS_WIDTH);
1104         gtk_widget_set_size_request(clist, -1, 80);
1105
1106         addressbook_sort_list(GTK_CMCLIST(clist), COL_NAME, GTK_SORT_ASCENDING);
1107         g_signal_connect(G_OBJECT(GTK_CMCLIST(clist)->column[COL_NAME].button),
1108                 "clicked", G_CALLBACK(addressbook_name_clicked), clist);
1109         g_signal_connect(G_OBJECT(GTK_CMCLIST(clist)->column[COL_ADDRESS].button),
1110                 "clicked", G_CALLBACK(addressbook_address_clicked), clist);
1111         g_signal_connect(G_OBJECT(GTK_CMCLIST(clist)->column[COL_REMARKS].button),
1112                 "clicked", G_CALLBACK(addressbook_remarks_clicked), clist);
1113         g_signal_connect(G_OBJECT(clist), "focus_in_event",
1114                 G_CALLBACK(addressbook_address_list_focus_evt_in), NULL);
1115         g_signal_connect(G_OBJECT(clist), "focus_out_event",
1116                 G_CALLBACK(addressbook_address_list_focus_evt_out), NULL);
1117
1118         for (i = 0; i < N_LIST_COLS; i++)
1119                 GTK_WIDGET_UNSET_FLAGS(GTK_CMCLIST(clist)->column[i].button,
1120                                        GTK_CAN_FOCUS);
1121
1122         g_signal_connect(G_OBJECT(clist), "tree_select_row",
1123                          G_CALLBACK(addressbook_list_row_selected), NULL);
1124         g_signal_connect(G_OBJECT(clist), "tree_unselect_row",
1125                          G_CALLBACK(addressbook_list_row_unselected), NULL);
1126         g_signal_connect(G_OBJECT(clist), "button_press_event",
1127                          G_CALLBACK(addressbook_list_button_pressed),
1128                          NULL);
1129         g_signal_connect(G_OBJECT(clist), "button_release_event",
1130                          G_CALLBACK(addressbook_list_button_released),
1131                          NULL);
1132         g_signal_connect(G_OBJECT(clist), "tree_expand",
1133                          G_CALLBACK(addressbook_person_expand_node), NULL );
1134         g_signal_connect(G_OBJECT(clist), "tree_collapse",
1135                          G_CALLBACK(addressbook_person_collapse_node), NULL );
1136         g_signal_connect(G_OBJECT(clist), "start_drag",
1137                          G_CALLBACK(addressbook_start_drag), NULL);
1138         g_signal_connect(G_OBJECT(clist), "drag_data_get",
1139                          G_CALLBACK(addressbook_drag_data_get), NULL);  
1140         hbox = gtk_hbox_new(FALSE, 4);
1141         gtk_box_pack_start(GTK_BOX(clist_vbox), hbox, FALSE, FALSE, 0);
1142
1143         label = gtk_label_new(_("Lookup name:"));
1144         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1145
1146         entry = gtk_entry_new();
1147         gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
1148
1149         address_completion_register_entry(GTK_ENTRY(entry), FALSE);
1150
1151         g_signal_connect(G_OBJECT(entry), "key_press_event",
1152                          G_CALLBACK(addressbook_entry_key_pressed),
1153                          NULL);
1154
1155         if (!prefs_common.addressbook_use_editaddress_dialog) {
1156                 editaddress_vbox = gtk_vbox_new(FALSE, 4);
1157                 vpaned = gtk_vpaned_new();
1158                 gtk_paned_pack1(GTK_PANED(vpaned), clist_vbox, FALSE, FALSE);
1159                 gtk_paned_pack2(GTK_PANED(vpaned), editaddress_vbox, TRUE, FALSE);
1160         } else {
1161                 vpaned = NULL;
1162                 editaddress_vbox = NULL;
1163         }
1164         hpaned = gtk_hpaned_new();
1165         gtk_box_pack_start(GTK_BOX(vbox2), hpaned, TRUE, TRUE, 0);
1166         gtk_paned_pack1(GTK_PANED(hpaned), ctree_swin, FALSE, FALSE);
1167         if (prefs_common.addressbook_use_editaddress_dialog)
1168                 gtk_paned_pack2(GTK_PANED(hpaned), clist_vbox, TRUE, FALSE);
1169         else
1170                 gtk_paned_pack2(GTK_PANED(hpaned), vpaned, TRUE, FALSE);
1171
1172         /* Status bar */
1173         hsbox = gtk_hbox_new(FALSE, 0);
1174         gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
1175         statusbar = gtk_statusbar_new();
1176         gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
1177
1178         /* Button panel */
1179         hbbox = gtk_hbutton_box_new();
1180         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
1181         gtk_box_set_spacing(GTK_BOX(hbbox), 2);
1182         gtk_container_set_border_width(GTK_CONTAINER(hbbox), 4);
1183         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
1184
1185         gtkut_stock_button_add_help(hbbox, &help_btn);
1186
1187         edit_btn = gtk_button_new_from_stock(GTK_STOCK_EDIT);
1188         GTK_WIDGET_SET_FLAGS(edit_btn, GTK_CAN_DEFAULT);
1189         gtk_box_pack_start(GTK_BOX(hbbox), edit_btn, TRUE, TRUE, 0);
1190         del_btn = gtk_button_new_from_stock(GTK_STOCK_DELETE);
1191         GTK_WIDGET_SET_FLAGS(del_btn, GTK_CAN_DEFAULT);
1192         gtk_box_pack_start(GTK_BOX(hbbox), del_btn, TRUE, TRUE, 0);
1193         reg_btn = gtk_button_new_from_stock(GTK_STOCK_NEW);
1194         GTK_WIDGET_SET_FLAGS(reg_btn, GTK_CAN_DEFAULT);
1195         gtk_box_pack_start(GTK_BOX(hbbox), reg_btn, TRUE, TRUE, 0);
1196
1197
1198         lup_btn = gtk_button_new_from_stock(GTK_STOCK_FIND);
1199         GTK_WIDGET_SET_FLAGS(lup_btn, GTK_CAN_DEFAULT);
1200         gtk_box_pack_start(GTK_BOX(hbox), lup_btn, TRUE, TRUE, 0);
1201
1202         g_signal_connect(G_OBJECT(help_btn), "clicked",
1203                          G_CALLBACK(manual_open_with_anchor_cb),
1204                          MANUAL_ANCHOR_ADDRBOOK);
1205
1206         g_signal_connect(G_OBJECT(edit_btn), "clicked",
1207                          G_CALLBACK(addressbook_edit_clicked), NULL);
1208         g_signal_connect(G_OBJECT(del_btn), "clicked",
1209                          G_CALLBACK(addressbook_del_clicked), NULL);
1210         g_signal_connect(G_OBJECT(reg_btn), "clicked",
1211                          G_CALLBACK(addressbook_reg_clicked), NULL);
1212         g_signal_connect(G_OBJECT(lup_btn), "clicked",
1213                          G_CALLBACK(addressbook_lup_clicked), NULL);
1214
1215         to_btn = gtk_button_new_with_label
1216                 (prefs_common_translated_header_name("To:"));
1217         GTK_WIDGET_SET_FLAGS(to_btn, GTK_CAN_DEFAULT);
1218         gtk_box_pack_start(GTK_BOX(hbbox), to_btn, TRUE, TRUE, 0);
1219         cc_btn = gtk_button_new_with_label
1220                 (prefs_common_translated_header_name("Cc:"));
1221         GTK_WIDGET_SET_FLAGS(cc_btn, GTK_CAN_DEFAULT);
1222         gtk_box_pack_start(GTK_BOX(hbbox), cc_btn, TRUE, TRUE, 0);
1223         bcc_btn = gtk_button_new_with_label
1224                 (prefs_common_translated_header_name("Bcc:"));
1225         GTK_WIDGET_SET_FLAGS(bcc_btn, GTK_CAN_DEFAULT);
1226         gtk_box_pack_start(GTK_BOX(hbbox), bcc_btn, TRUE, TRUE, 0);
1227
1228         close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
1229         GTK_WIDGET_SET_FLAGS(close_btn, GTK_CAN_DEFAULT);
1230         gtk_box_pack_start(GTK_BOX(hbbox), close_btn, TRUE, TRUE, 0);
1231
1232         g_signal_connect(G_OBJECT(to_btn), "clicked",
1233                          G_CALLBACK(addressbook_to_clicked),
1234                          GINT_TO_POINTER(COMPOSE_TO));
1235         g_signal_connect(G_OBJECT(cc_btn), "clicked",
1236                          G_CALLBACK(addressbook_to_clicked),
1237                          GINT_TO_POINTER(COMPOSE_CC));
1238         g_signal_connect(G_OBJECT(bcc_btn), "clicked",
1239                          G_CALLBACK(addressbook_to_clicked),
1240                          GINT_TO_POINTER(COMPOSE_BCC));
1241         g_signal_connect(G_OBJECT(close_btn), "clicked",
1242                          G_CALLBACK(addressbook_close_clicked), NULL);
1243
1244         /* Build icons for interface */
1245         stock_pixmap_gdk( window, STOCK_PIXMAP_INTERFACE,
1246                           &interfacexpm, &interfacexpmmask );
1247
1248         /* Build control tables */
1249         addrbookctl_build_map(window);
1250         addrbookctl_build_iflist();
1251         addrbookctl_build_ifselect();
1252
1253         addrbook.clist   = NULL;
1254
1255         /* Add each interface into the tree as a root level folder */
1256         nodeIf = _addressInterfaceList_;
1257         while( nodeIf ) {
1258                 AdapterInterface *adapter = nodeIf->data;
1259                 AddressInterface *iface = adapter->interface;
1260                 nodeIf = g_list_next(nodeIf);
1261
1262                 if(iface->useInterface) {
1263                         AddressTypeControlItem *atci = adapter->atci;
1264                         text = atci->displayName;
1265                         adapter->treeNode =
1266                                 gtk_sctree_insert_node( GTK_CMCTREE(ctree),
1267                                         NULL, NULL, &text, FOLDER_SPACING,
1268                                         interfacexpm, interfacexpmmask,
1269                                         interfacexpm, interfacexpmmask,
1270                                         FALSE, FALSE );
1271                         cm_menu_set_sensitive_full(ui_manager, atci->menuCommand, adapter->haveLibrary );
1272                         gtk_cmctree_node_set_row_data_full(
1273                                 GTK_CMCTREE(ctree), adapter->treeNode, adapter,
1274                                 addressbook_free_treenode );
1275                 }
1276         }
1277
1278         /* Popup menu */
1279
1280         MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popups", NULL, GTK_UI_MANAGER_MENUBAR);
1281         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups", "ABTreePopup", "ABTreePopup", GTK_UI_MANAGER_MENU)
1282         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "EditBook", "ABTreePopup/EditBook", GTK_UI_MANAGER_MENUITEM)
1283         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "DeleteBook", "ABTreePopup/DeleteBook", GTK_UI_MANAGER_MENUITEM)
1284         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Separator1", "ABTreePopup/---", GTK_UI_MANAGER_SEPARATOR)
1285         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "NewBook", "ABTreePopup/NewBook", GTK_UI_MANAGER_MENUITEM)
1286         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "NewFolder", "ABTreePopup/NewFolder", GTK_UI_MANAGER_MENUITEM)
1287         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "NewGroup", "ABTreePopup/NewGroup", GTK_UI_MANAGER_MENUITEM)
1288         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Separator2", "ABTreePopup/---", GTK_UI_MANAGER_SEPARATOR)
1289         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Cut", "ABTreePopup/Cut", GTK_UI_MANAGER_MENUITEM)
1290         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Copy", "ABTreePopup/Copy", GTK_UI_MANAGER_MENUITEM)
1291         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABTreePopup", "Paste", "ABTreePopup/Paste", GTK_UI_MANAGER_MENUITEM)
1292         
1293         tree_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1294                                 gtk_ui_manager_get_widget(ui_manager, "/Popups/ABTreePopup")));
1295
1296         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups", "ABListPopup", "ABListPopup", GTK_UI_MANAGER_MENU)
1297         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "SelectAll", "ABListPopup/SelectAll", GTK_UI_MANAGER_MENUITEM)
1298         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator1", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
1299         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Edit", "ABListPopup/Edit", GTK_UI_MANAGER_MENUITEM)
1300         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Delete", "ABListPopup/Delete", GTK_UI_MANAGER_MENUITEM)
1301         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator2", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
1302         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "NewAddress", "ABListPopup/NewAddress", GTK_UI_MANAGER_MENUITEM)
1303         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "NewGroup", "ABListPopup/NewGroup", GTK_UI_MANAGER_MENUITEM)
1304         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator3", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
1305         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Cut", "ABListPopup/Cut", GTK_UI_MANAGER_MENUITEM)
1306         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Copy", "ABListPopup/Copy", GTK_UI_MANAGER_MENUITEM)
1307         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Paste", "ABListPopup/Paste", GTK_UI_MANAGER_MENUITEM)
1308         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Separator4", "ABListPopup/---", GTK_UI_MANAGER_SEPARATOR)
1309         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "Mailto", "ABListPopup/Mailto", GTK_UI_MANAGER_MENUITEM)
1310 #ifdef USE_LDAP
1311         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popups/ABListPopup", "BrowseEntry", "ABListPopup/BrowseEntry", GTK_UI_MANAGER_MENUITEM)
1312 #endif
1313         list_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1314                                 gtk_ui_manager_get_widget(ui_manager, "/Popups/ABListPopup")));
1315
1316         addrbook.window  = window;
1317         addrbook.hpaned  = hpaned;
1318         addrbook.vpaned  = vpaned;
1319         addrbook.menubar = menubar;
1320         addrbook.ctree   = ctree;
1321         addrbook.ctree_swin
1322                          = ctree_swin;
1323         addrbook.editaddress_vbox = editaddress_vbox;
1324         addrbook.clist   = clist;
1325         addrbook.label   = label;
1326         addrbook.entry   = entry;
1327         addrbook.statusbar = statusbar;
1328         addrbook.status_cid = gtk_statusbar_get_context_id(
1329                         GTK_STATUSBAR(statusbar), "Addressbook Window" );
1330
1331         addrbook.help_btn = help_btn;
1332         addrbook.edit_btn = edit_btn;
1333         addrbook.del_btn = del_btn;
1334         addrbook.reg_btn = reg_btn;
1335         addrbook.lup_btn = lup_btn;
1336         addrbook.to_btn  = to_btn;
1337         addrbook.cc_btn  = cc_btn;
1338         addrbook.bcc_btn = bcc_btn;
1339
1340         addrbook.tree_popup   = tree_popup;
1341         addrbook.list_popup   = list_popup;
1342         addrbook.ui_manager   = ui_manager;
1343
1344         addrbook.listSelected = NULL;
1345
1346         if (!geometry.min_height) {
1347                 geometry.min_width = ADDRESSBOOK_WIDTH;
1348                 geometry.min_height = ADDRESSBOOK_HEIGHT;
1349         }
1350
1351         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
1352                                       GDK_HINT_MIN_SIZE);
1353         gtk_widget_set_size_request(window, prefs_common.addressbookwin_width,
1354                                     prefs_common.addressbookwin_height);
1355
1356         if (!prefs_common.addressbook_use_editaddress_dialog) {
1357                 if (prefs_common.addressbook_vpaned_pos > 0)
1358                         gtk_paned_set_position(GTK_PANED(vpaned), 
1359                                 prefs_common.addressbook_vpaned_pos);
1360         }       
1361         if (prefs_common.addressbook_hpaned_pos > 0)
1362                 gtk_paned_set_position(GTK_PANED(hpaned), 
1363                         prefs_common.addressbook_hpaned_pos);
1364
1365
1366         gtk_widget_show_all(window);
1367 }
1368
1369 /**
1370  * Close address book window and save to file(s).
1371  */
1372 static gint addressbook_close( void ) {
1373         address_completion_end(addrbook.window);
1374         if (!prefs_common.addressbook_use_editaddress_dialog)
1375                 addressbook_edit_person_invalidate(NULL, NULL, NULL);
1376         
1377         addressbook_pane_save_position();
1378         
1379         gtk_widget_hide(addrbook.window);
1380         addressbook_export_to_file();
1381         return TRUE;
1382 }
1383
1384 /**
1385  * Display message in status line.
1386  * \param msg Message to display.
1387  */
1388 static void addressbook_status_show( gchar *msg ) {
1389         if( addrbook.statusbar != NULL ) {
1390                 gtk_statusbar_pop(
1391                         GTK_STATUSBAR(addrbook.statusbar),
1392                         addrbook.status_cid );
1393                 if( msg ) {
1394                         gtk_statusbar_push(
1395                                 GTK_STATUSBAR(addrbook.statusbar),
1396                                 addrbook.status_cid, msg );
1397                 }
1398         }
1399 }
1400
1401 static void addressbook_ds_show_message( AddressDataSource *ds ) {
1402         gint retVal;
1403         gchar *name;
1404         gchar *desc;
1405         *addressbook_msgbuf = '\0';
1406         if( ds ) {
1407                 name = addrindex_ds_get_name( ds );
1408                 retVal = addrindex_ds_get_status_code( ds );
1409                 if( retVal == MGU_SUCCESS ) {
1410                         g_snprintf( addressbook_msgbuf,
1411                                     sizeof(addressbook_msgbuf), "%s", name );
1412                 }
1413                 else {
1414                         desc = addressbook_err2string( _lutErrorsGeneral_, retVal );
1415                         g_snprintf( addressbook_msgbuf, 
1416                             sizeof(addressbook_msgbuf), "%s: %s", name, desc );
1417                 }
1418         }
1419         addressbook_status_show( addressbook_msgbuf );
1420 }
1421
1422 static void addressbook_button_set_sensitive(void)
1423 {
1424         gboolean to_sens  = FALSE;
1425         gboolean cc_sens  = FALSE;
1426         gboolean bcc_sens = FALSE;
1427
1428         if (!addrbook.window) return;
1429
1430         if (addrbook.target_compose) {
1431                 to_sens = TRUE;
1432                 cc_sens = TRUE;
1433                 bcc_sens = TRUE;
1434         }
1435
1436         gtk_widget_set_sensitive(addrbook.to_btn, to_sens);
1437         gtk_widget_set_sensitive(addrbook.cc_btn, cc_sens);
1438         gtk_widget_set_sensitive(addrbook.bcc_btn, bcc_sens);
1439 }
1440
1441 static void addressbook_edit_clicked(GtkButton *button, gpointer data)
1442 {
1443         addressbook_edit_address_cb(NULL, NULL);
1444 }
1445
1446 static gboolean find_person(AddrSelectItem *item_a, ItemPerson *person)
1447 {
1448         return ((ItemPerson *)item_a->addressItem == person)?0:-1;
1449 }
1450
1451 /*
1452 * Delete one or more objects from address list.
1453 */
1454 static void addressbook_del_clicked(GtkButton *button, gpointer data)
1455 {
1456         GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
1457         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
1458         AddressObject *pobj;
1459         AdapterDSource *ads = NULL;
1460         GtkCMCTreeNode *nodeList;
1461         gboolean procFlag;
1462         AlertValue aval;
1463         AddressBookFile *abf = NULL;
1464         AddressDataSource *ds = NULL;
1465         AddressInterface *iface;
1466         AddrItemObject *aio;
1467         AddrSelectItem *item;
1468         GList *list, *node;
1469         gboolean refreshList = FALSE;
1470         
1471         pobj = gtk_cmctree_node_get_row_data(ctree, addrbook.opened );
1472         g_return_if_fail(pobj != NULL);
1473
1474         /* Test whether anything selected for deletion */
1475         nodeList = addrbook.listSelected;
1476
1477         aio = gtk_cmctree_node_get_row_data( clist, nodeList );
1478         if( aio == NULL) return;
1479         ds = addressbook_find_datasource( addrbook.treeSelected );
1480         if( ds == NULL ) return;
1481
1482         /* Test for read only */
1483         iface = ds->interface;
1484         if( iface->readOnly ) {
1485                 alertpanel( _("Delete address(es)"),
1486                         _("This address data is readonly and cannot be deleted."),
1487                         GTK_STOCK_CLOSE, NULL, NULL );
1488                 return;
1489         }
1490
1491         /* Test whether Ok to proceed */
1492         procFlag = FALSE;
1493         if( pobj->type == ADDR_DATASOURCE ) {
1494                 ads = ADAPTER_DSOURCE(pobj);
1495                 if( ads->subType == ADDR_BOOK ) procFlag = TRUE;
1496         }
1497         else if( pobj->type == ADDR_ITEM_FOLDER ) {
1498                 procFlag = TRUE;
1499         }
1500         else if( pobj->type == ADDR_ITEM_GROUP ) {
1501                 procFlag = TRUE;
1502         }
1503         if( ! procFlag ) return;
1504         abf = ds->rawDataSource;
1505         if( abf == NULL ) return;
1506
1507         gtk_cmclist_freeze(GTK_CMCLIST(addrbook.clist));
1508         g_signal_handlers_block_by_func
1509                 (G_OBJECT(addrbook.clist),
1510                  G_CALLBACK(addressbook_list_row_unselected), NULL);
1511
1512         /* Process deletions */
1513         if( pobj->type == ADDR_DATASOURCE || pobj->type == ADDR_ITEM_FOLDER ) {
1514                 GList *groups = NULL, *persons = NULL, *emails = NULL;
1515                 gboolean group_delete = TRUE;
1516                 /* Items inside folders */
1517                 list = addrselect_get_list( _addressSelect_ );
1518                 /* Confirm deletion */
1519                 node = list;
1520                 while( node ) {
1521                         item = node->data;
1522                         node = g_list_next( node );
1523                         aio = ( AddrItemObject * ) item->addressItem;
1524                         if( aio->type == ADDR_ITEM_PERSON || aio->type == ADDR_ITEM_EMAIL ) {
1525                                 group_delete = FALSE;
1526                                 break;
1527                         }
1528                 }
1529                 if (group_delete) {
1530                         aval = alertpanel( _("Delete group"),
1531                                         _("Really delete the group(s)?\n"
1532                                           "The addresses it contains will not be lost."),
1533                                         GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL );
1534                         if( aval != G_ALERTALTERNATE ) {
1535                                 goto thaw_ret;
1536                         }
1537                 } else {
1538                         aval = alertpanel( _("Delete address(es)"),
1539                                         _("Really delete the address(es)?"),
1540                                         GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL );
1541                         if( aval != G_ALERTALTERNATE ) {
1542                                 goto thaw_ret;
1543                         }
1544                 }
1545         
1546         /* first, set lists of groups and persons to remove */
1547                 node = list;
1548                 while( node ) {
1549                         item = node->data;
1550                         node = g_list_next( node );
1551                         aio = ( AddrItemObject * ) item->addressItem;
1552                         if (!aio)
1553                                 continue;
1554                         if( aio->type == ADDR_ITEM_GROUP ) {
1555                                 groups = g_list_prepend(groups, item);
1556                         }
1557                         else if( aio->type == ADDR_ITEM_PERSON ) {
1558                                 persons = g_list_prepend(persons, item);
1559                         }
1560                 }
1561         /* then set list of emails to remove *if* they're not children of
1562          * persons to remove */
1563                 node = list;
1564                 while( node ) {
1565                         item = node->data;
1566                         node = g_list_next( node );
1567                         aio = ( AddrItemObject * ) item->addressItem;
1568                         if (!aio)
1569                                 continue;
1570                         if( aio->type == ADDR_ITEM_EMAIL ) {
1571                                 ItemEMail *sitem = ( ItemEMail * ) aio;
1572                                 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
1573                                 if (!g_list_find_custom(persons, person, (GCompareFunc)(find_person))) {
1574                                         emails = g_list_prepend(emails, item);
1575                                 }
1576                                 /* else, the email will be removed via the parent person */
1577                         }
1578                 }
1579         /* then delete groups */
1580                 node = groups;
1581                 while( node ) {
1582                         item = node->data;
1583                         node = g_list_next( node );
1584                         aio = ( AddrItemObject * ) item->addressItem;
1585                         if (!aio)
1586                                 continue;
1587                         if( aio->type == ADDR_ITEM_GROUP ) {
1588                                 ItemGroup *item = ( ItemGroup * ) aio;
1589                                 GtkCMCTreeNode *nd = NULL;
1590                                 nd = addressbook_find_group_node( addrbook.opened, item );
1591                                 item = addrbook_remove_group( abf, item );
1592                                 if( item ) {
1593                                         addritem_free_item_group( item );
1594                                 }
1595                                 /* Remove group from parent node */
1596                                 gtk_cmctree_remove_node( ctree, nd );
1597                                 refreshList = TRUE;
1598                         }
1599                 }
1600         /* then delete persons */
1601                 node = persons;
1602                 while( node ) {
1603                         item = node->data;
1604                         node = g_list_next( node );
1605                         aio = ( AddrItemObject * ) item->addressItem;
1606                         if (!aio)
1607                                 continue;
1608                         if( aio->type == ADDR_ITEM_PERSON ) {
1609                                 ItemPerson *item = ( ItemPerson * ) aio;
1610                                 item->status = DELETE_ENTRY; 
1611                                 addressbook_folder_remove_one_person( clist, item );
1612                                 if (pobj->type == ADDR_ITEM_FOLDER)
1613                                         addritem_folder_remove_person(ADAPTER_FOLDER(pobj)->itemFolder, item);
1614                                 item = addrbook_remove_person( abf, item );
1615 #ifdef USE_LDAP
1616                                 if (ds && ds->type == ADDR_IF_LDAP) {
1617                                         LdapServer *server = ds->rawDataSource;
1618                                         ldapsvr_set_modified(server, TRUE);
1619                                         ldapsvr_update_book(server, item);
1620                                 }
1621 #endif
1622                                 if( item ) {
1623                                         gchar *filename = addritem_person_get_picture(item);
1624                                         if (filename && is_file_exist(filename))
1625                                                 claws_unlink(filename);
1626                                         g_free(filename);
1627                                         addritem_free_item_person( item );
1628                                 }
1629                         }
1630                 }
1631         /* then delete emails */
1632                 node = emails;
1633                 while( node ) {
1634                         item = node->data;
1635                         node = g_list_next( node );
1636                         aio = ( AddrItemObject * ) item->addressItem;
1637                         if (!aio)
1638                                 continue;
1639
1640                         if( aio->type == ADDR_ITEM_EMAIL ) {
1641                                 ItemEMail *sitem = ( ItemEMail * ) aio;
1642                                 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(sitem);
1643                                 sitem = addrbook_person_remove_email( abf, person, sitem );
1644                                 if( sitem ) {
1645                                         addrcache_remove_email(abf->addressCache, sitem);
1646                                         addritem_free_item_email( sitem );
1647                                 }
1648                                 addressbook_folder_refresh_one_person( clist, person );
1649                         }
1650                 }
1651                 g_list_free( groups );
1652                 g_list_free( persons );
1653                 g_list_free( emails );
1654                 g_list_free( list );
1655                 addressbook_list_select_clear();
1656                 if( refreshList ) {
1657                         gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened);
1658                         addressbook_set_clist(
1659                                 gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
1660                                         addrbook.opened),
1661                                 TRUE);
1662                 }
1663                 addrbook_set_dirty(abf, TRUE);
1664                 addressbook_export_to_file();
1665                 addressbook_list_menu_setup();
1666                 goto thaw_ret;
1667         }
1668         else if( pobj->type == ADDR_ITEM_GROUP ) {
1669                 /* Items inside groups */
1670                 list = addrselect_get_list( _addressSelect_ );
1671                 node = list;
1672                 while( node ) {
1673                         item = node->data;
1674                         node = g_list_next( node );
1675                         aio = ( AddrItemObject * ) item->addressItem;
1676                         if( aio->type == ADDR_ITEM_EMAIL ) {
1677                                 ItemEMail *item = ( ItemEMail * ) aio;
1678                                 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(item);
1679                                 item = addrbook_person_remove_email( abf, person, item );
1680                                 if( item ) {
1681                                         addritem_free_item_email( item );
1682                                 }
1683                         }
1684                 }
1685                 g_list_free( list );
1686                 addressbook_list_select_clear();
1687                 gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened);
1688                 addressbook_set_clist(
1689                         gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
1690                                 addrbook.opened),
1691                         TRUE);
1692                 
1693                 addrbook_set_dirty(abf, TRUE);
1694                 addressbook_export_to_file();
1695                 addressbook_list_menu_setup();
1696                 goto thaw_ret;
1697         }
1698
1699         gtk_cmctree_node_set_row_data( clist, nodeList, NULL );
1700         gtk_cmctree_remove_node( clist, nodeList );
1701 thaw_ret:
1702         gtk_cmclist_thaw(GTK_CMCLIST(addrbook.clist));
1703         g_signal_handlers_unblock_by_func
1704                 (G_OBJECT(addrbook.clist),
1705                  G_CALLBACK(addressbook_list_row_unselected), NULL);
1706 }
1707
1708 static void addressbook_reg_clicked(GtkButton *button, gpointer data)
1709 {
1710         addressbook_new_address_cb( NULL, NULL );
1711 }
1712
1713 static gchar *addressbook_format_address( AddrItemObject * aio ) {
1714         gchar *buf = NULL;
1715         gchar *name = NULL;
1716         gchar *address = NULL;
1717
1718         if( aio->type == ADDR_ITEM_EMAIL ) {
1719                 ItemPerson *person = NULL;
1720                 ItemEMail *email = ( ItemEMail * ) aio;
1721
1722                 person = ( ItemPerson * ) ADDRITEM_PARENT(email);
1723                 if( email->address ) {
1724                         if( ADDRITEM_NAME(email) ) {
1725                                 name = ADDRITEM_NAME(email);
1726                                 if( *name == '\0' ) {
1727                                         name = ADDRITEM_NAME(person);
1728                                 }
1729                         }
1730                         else if( ADDRITEM_NAME(person) ) {
1731                                 name = ADDRITEM_NAME(person);
1732                         }
1733                         else {
1734                                 buf = g_strdup( email->address );
1735                         }
1736                         address = email->address;
1737                 }
1738         }
1739         else if( aio->type == ADDR_ITEM_PERSON ) {
1740                 ItemPerson *person = ( ItemPerson * ) aio;
1741                 GList *node = person->listEMail;
1742
1743                 name = ADDRITEM_NAME(person);
1744                 if( node ) {
1745                         ItemEMail *email = ( ItemEMail * ) node->data;
1746                         address = email->address;
1747                 }
1748         }
1749         if( address ) {
1750                 if( name && name[0] != '\0' ) {
1751                         if( strchr_with_skip_quote( name, '"', ',' ) )
1752                                 buf = g_strdup_printf( "\"%s\" <%s>", name, address );
1753                         else
1754                                 buf = g_strdup_printf( "%s <%s>", name, address );
1755                 }
1756                 else {
1757                         buf = g_strdup( address );
1758                 }
1759         }
1760
1761         return buf;
1762 }
1763
1764 static void addressbook_to_clicked(GtkButton *button, gpointer data)
1765 {
1766         GList *list, *node;
1767         Compose *compose;
1768         AddrSelectItem *item;
1769         AddrItemObject *aio;
1770         gchar *addr;
1771
1772         compose = addrbook.target_compose;
1773         if( ! compose ) return;
1774
1775         /* Nothing selected, but maybe there is something in text entry */
1776         addr = (char *)gtk_entry_get_text( GTK_ENTRY( addrbook.entry) );
1777         if ( addr ) {
1778                 compose_entry_append(
1779                         compose, addr, (ComposeEntryType)data );
1780         }
1781
1782         /* Select from address list */
1783         list = addrselect_get_list( _addressSelect_ );
1784         node = list;
1785         if (node) {
1786                 while( node ) {
1787                         item = node->data;
1788                         node = g_list_next( node );
1789                         aio = item->addressItem;
1790                         if( aio->type == ADDR_ITEM_PERSON ||
1791                             aio->type == ADDR_ITEM_EMAIL ) {
1792                                 addr = addressbook_format_address( aio );
1793                                 compose_entry_append(
1794                                         compose, addr, (ComposeEntryType) data );
1795                                 g_free( addr );
1796                         }
1797                         else if( aio->type == ADDR_ITEM_GROUP ) {
1798                                 ItemGroup *group = ( ItemGroup * ) aio;
1799                                 GList *nodeMail = group->listEMail;
1800                                 while( nodeMail ) {
1801                                         ItemEMail *email = nodeMail->data;
1802
1803                                         addr = addressbook_format_address(
1804                                                         ( AddrItemObject * ) email );
1805                                         compose_entry_append(
1806                                                 compose, addr, (ComposeEntryType) data );
1807                                         g_free( addr );
1808                                         nodeMail = g_list_next( nodeMail );
1809                                 }
1810                         }
1811                 }
1812         } else {
1813                 AddressObject *obj = NULL;
1814
1815                 obj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
1816         
1817                 if( obj && obj->type == ADDR_ITEM_GROUP ) {
1818                         ItemGroup *itemGroup = ADAPTER_GROUP(obj)->itemGroup;
1819                         GList *nodeMail = itemGroup->listEMail;
1820                         while( nodeMail ) {
1821                                 ItemEMail *email = nodeMail->data;
1822
1823                                 addr = addressbook_format_address(
1824                                                 ( AddrItemObject * ) email );
1825                                 compose_entry_append(
1826                                         compose, addr, (ComposeEntryType) data );
1827                                 g_free( addr );
1828                                 nodeMail = g_list_next( nodeMail );
1829                         }
1830                 }
1831         }
1832         g_list_free( list );
1833 }
1834
1835 static void addressbook_menubar_set_sensitive( gboolean sensitive ) {
1836         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook",   sensitive );
1837         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook", sensitive );
1838         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/NewFolder",  sensitive );
1839
1840         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/SelectAll",    TRUE );
1841         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Cut",    sensitive );
1842         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Copy",   sensitive );
1843         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Paste",  sensitive );
1844
1845         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/NewAddress", sensitive );
1846         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/NewGroup",   sensitive );
1847         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Mailto",     sensitive );
1848         gtk_widget_set_sensitive( addrbook.edit_btn, sensitive );
1849         gtk_widget_set_sensitive( addrbook.del_btn, sensitive );
1850 }
1851
1852 static void addressbook_menuitem_set_sensitive( AddressObject *obj, GtkCMCTreeNode *node ) {
1853         gboolean canEdit = FALSE;
1854         gboolean canDelete = TRUE;
1855         gboolean canAdd = FALSE;
1856         gboolean canEditTr = TRUE;
1857         gboolean editAddress = FALSE;
1858         gboolean canExport = TRUE;
1859         AddressTypeControlItem *atci = NULL;
1860         AddressDataSource *ds = NULL;
1861         AddressInterface *iface = NULL;
1862
1863         if( obj == NULL ) return;
1864         if( obj->type == ADDR_INTERFACE ) {
1865                 AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
1866                 iface = adapter->interface;
1867                 if( iface ) {
1868                         if( iface->haveLibrary ) {
1869                                 /* Enable appropriate File / New command */
1870                                 atci = adapter->atci;
1871                                 cm_menu_set_sensitive_full(addrbook.ui_manager, atci->menuCommand, TRUE );
1872                         }
1873                 }
1874                 canEditTr = canExport = FALSE;
1875         }
1876         else if( obj->type == ADDR_DATASOURCE ) {
1877                 AdapterDSource *ads = ADAPTER_DSOURCE(obj);
1878                 ds = ads->dataSource;
1879                 iface = ds->interface;
1880                 if( ! iface->readOnly ) {
1881                         canAdd = canEdit = editAddress = canDelete = TRUE;
1882                 }
1883                 if( ! iface->haveLibrary ) {
1884                         canAdd = canEdit = editAddress = canExport = canDelete = FALSE;
1885                 }
1886         }
1887         else if( obj->type == ADDR_ITEM_FOLDER ) {
1888                 ds = addressbook_find_datasource( addrbook.treeSelected );
1889                 if( ds ) {
1890                         iface = ds->interface;
1891                         if( iface->readOnly ) {
1892                                 canEditTr = FALSE;
1893                                 canDelete = FALSE;
1894                         }
1895                         else {
1896                                 canAdd = editAddress = TRUE;
1897                         }
1898                 }
1899         }
1900         else if( obj->type == ADDR_ITEM_GROUP ) {
1901                 ds = addressbook_find_datasource( addrbook.treeSelected );
1902                 if( ds ) {
1903                         iface = ds->interface;
1904                         if( ! iface->readOnly ) {
1905                                 editAddress = TRUE;
1906                         }
1907                 }
1908         }
1909
1910         if( addrbook.listSelected == NULL )
1911                 canEdit = FALSE;
1912
1913         /* Enable add */
1914         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/NewAddress", editAddress );
1915         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/NewGroup",   canAdd );
1916         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/NewFolder",  canAdd );
1917         gtk_widget_set_sensitive( addrbook.reg_btn, editAddress );
1918
1919         /* Enable edit */
1920         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Edit",   canEdit );
1921         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Delete", canDelete );
1922         gtk_widget_set_sensitive( addrbook.edit_btn, canEdit );
1923         gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
1924
1925         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook",      canEditTr );
1926         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook",    canEditTr );
1927
1928         /* Export data */
1929         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Tools/ExportHTML", canExport );
1930         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Tools/ExportLDIF", canExport );
1931 }
1932
1933 /**
1934  * Address book tree callback function that responds to selection of tree
1935  * items.
1936  *
1937  * \param ctree  Tree widget.
1938  * \param node   Node that was selected.
1939  * \param column Column number where selected occurred.
1940  * \param data   Pointer to user data.
1941  */
1942 static void addressbook_tree_selected(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1943                                       gint column, gpointer data)
1944 {
1945         AddressObject *obj = NULL;
1946         AdapterDSource *ads = NULL;
1947         AddressDataSource *ds = NULL;
1948         ItemFolder *rootFolder = NULL;
1949         AddressObjectType aot;
1950
1951         addrbook.treeSelected = node;
1952         addrbook.listSelected = NULL;
1953         addressbook_status_show( "" );
1954         if( addrbook.entry != NULL ) gtk_entry_set_text(GTK_ENTRY(addrbook.entry), "");
1955
1956         if( node ) obj = gtk_cmctree_node_get_row_data( ctree, node );
1957         if( obj == NULL ) {
1958                 addressbook_set_clist(NULL, TRUE);
1959                 return;
1960         }
1961         addrbook.opened = node;
1962
1963         if( obj->type == ADDR_DATASOURCE ) {
1964                 /* Read from file */
1965                 static gboolean tVal = TRUE;
1966
1967                 ads = ADAPTER_DSOURCE(obj);
1968                 if( ads == NULL ) return;
1969                 ds = ads->dataSource;
1970                 if( ds == NULL ) return;                
1971
1972                 if( addrindex_ds_get_modify_flag( ds ) ) {
1973                         addrindex_ds_read_data( ds );
1974                 }
1975
1976                 if( ! addrindex_ds_get_read_flag( ds ) ) {
1977                         addrindex_ds_read_data( ds );
1978                 }
1979                 addressbook_ds_show_message( ds );
1980
1981                 if( ! addrindex_ds_get_access_flag( ds ) ) {
1982                         /* Remove existing folders and groups */
1983                         gtk_cmclist_freeze( GTK_CMCLIST(ctree) );
1984                         addressbook_tree_remove_children( ctree, node );
1985                         gtk_cmclist_thaw( GTK_CMCLIST(ctree) );
1986
1987                         /* Load folders into the tree */
1988                         rootFolder = addrindex_ds_get_root_folder( ds );
1989                         if( ds && ds->type == ADDR_IF_JPILOT ) {
1990                                 aot = ADDR_CATEGORY;
1991                         }
1992                         else if( ds && ds->type == ADDR_IF_LDAP ) {
1993                                 aot = ADDR_LDAP_QUERY;
1994                         }
1995                         else {
1996                                 aot = ADDR_ITEM_FOLDER;
1997                         }
1998                         addressbook_node_add_folder( node, ds, rootFolder, aot );
1999                         addrindex_ds_set_access_flag( ds, &tVal );
2000                         gtk_cmctree_expand( ctree, node );
2001                 }
2002         } else {
2003                 addressbook_set_clist(NULL, TRUE);
2004         }
2005
2006         /* Update address list */
2007         g_signal_handlers_block_by_func
2008                 (G_OBJECT(ctree),
2009                  G_CALLBACK(addressbook_tree_selected), NULL);
2010         addressbook_set_clist( obj, FALSE );
2011         g_signal_handlers_unblock_by_func
2012                 (G_OBJECT(ctree),
2013                  G_CALLBACK(addressbook_tree_selected), NULL);
2014         if (!prefs_common.addressbook_use_editaddress_dialog)
2015                 addressbook_edit_person_invalidate(NULL, NULL, NULL);
2016
2017         /* Setup main menu selections */
2018         addressbook_menubar_set_sensitive( FALSE );
2019         addressbook_menuitem_set_sensitive( obj, node );
2020         addressbook_list_select_clear();
2021         addressbook_list_menu_setup();
2022         return;
2023 }
2024
2025 /**
2026  * Setup address list popup menu items. Items are enabled or disabled as
2027  * required.
2028  */
2029 static void addressbook_list_menu_setup( void ) {
2030         GtkCMCTree *clist = NULL;
2031         AddressObject *pobj = NULL;
2032         AddressObject *obj = NULL;
2033         AdapterDSource *ads = NULL;
2034         AddressInterface *iface = NULL;
2035         AddressDataSource *ds = NULL;
2036         gboolean canEdit = FALSE;
2037         gboolean canDelete = FALSE;
2038         gboolean canCut = FALSE;
2039         gboolean canCopy = FALSE;
2040         gboolean canPaste = FALSE;
2041         gboolean canBrowse = FALSE;
2042
2043         pobj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
2044         if( pobj == NULL ) return;
2045
2046         clist = GTK_CMCTREE(addrbook.clist);
2047         obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
2048         if( obj == NULL ) canEdit = FALSE;
2049
2050         menu_set_insensitive_all( GTK_MENU_SHELL(addrbook.list_popup) );
2051         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/SelectAll", TRUE );
2052
2053         if( pobj->type == ADDR_DATASOURCE ) {
2054                 /* Parent object is a data source */
2055                 ads = ADAPTER_DSOURCE(pobj);
2056                 ds = ads->dataSource;
2057                 if (!ds)
2058                         return;
2059                 iface = ds->interface;
2060                 if (!iface)
2061                         return;
2062                 if( ! iface->readOnly ) {
2063                         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewAddress", TRUE );
2064                         if (iface->type != ADDR_IF_LDAP)
2065                                 cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewGroup", TRUE );
2066                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2067                         if( obj )
2068                                 canEdit = TRUE;
2069                         canDelete = canEdit;
2070                 }
2071         }
2072         else if( pobj->type != ADDR_INTERFACE ) {
2073                 /* Parent object is not an interface */
2074                 ds = addressbook_find_datasource( addrbook.treeSelected );
2075                 if (!ds)
2076                         return;
2077                 iface = ds->interface;
2078                 if (!iface)
2079                         return;
2080                 if( ! iface->readOnly ) {
2081                         /* Folder or group */
2082                         if( pobj->type == ADDR_ITEM_FOLDER || pobj->type == ADDR_ITEM_GROUP ) {
2083                                 cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewAddress", TRUE );
2084                                 gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2085                                 if( obj ) canEdit = TRUE;
2086                         }
2087                         /* Folder */
2088                         if( pobj->type == ADDR_ITEM_FOLDER ) {
2089                                 if (iface->type != ADDR_IF_LDAP)
2090                                         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewGroup", TRUE );
2091                                 if( obj ) canEdit = TRUE;
2092                         }
2093                         canDelete = canEdit;
2094                 }
2095                 if( iface->type == ADDR_IF_LDAP ) {
2096                         if( obj ) canBrowse = TRUE;
2097                         canEdit = TRUE;
2098                         canDelete = TRUE;
2099                 }
2100         }
2101
2102         if( iface ) {
2103                 /* Enable cut and paste */
2104                 if( ! addrclip_is_empty( _clipBoard_ ) )
2105                         canPaste = TRUE;
2106                 if( ! addrselect_test_empty( _addressSelect_ ) )
2107                         canCut = TRUE;
2108                 /* Enable copy if something is selected */
2109                 if( ! addrselect_test_empty( _addressSelect_ ) )
2110                         canCopy = TRUE;
2111         }
2112
2113         /* Disable edit or browse if more than one row selected */
2114         if( GTK_CMCLIST(clist)->selection && GTK_CMCLIST(clist)->selection->next ) {
2115                 canEdit = FALSE;
2116                 canBrowse = FALSE;
2117         }
2118
2119         /* Forbid write changes when read-only */
2120         if( iface && iface->readOnly ) {
2121                 canCut = FALSE;
2122                 canDelete = FALSE;
2123                 canPaste = FALSE;
2124         }
2125
2126         /* Now go finalize menu items */
2127         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Edit",   canEdit );
2128         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Delete", canDelete );
2129
2130         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Cut",           canCut );
2131         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Copy",          canCopy );
2132         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Paste",         canPaste );
2133
2134         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/Mailto",       canCopy );
2135
2136         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Cut",           canCut );
2137         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Copy",          canCopy );
2138         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Paste",         canPaste );
2139
2140         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Edit",    canEdit );
2141         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Delete",  canDelete );
2142         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Mailto", canCopy );
2143
2144         gtk_widget_set_sensitive( addrbook.edit_btn, canEdit );
2145         gtk_widget_set_sensitive( addrbook.del_btn, canDelete );
2146
2147 #ifdef USE_LDAP
2148         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/BrowseEntry",    canBrowse );
2149 #endif
2150 }
2151
2152 static void addressbook_select_row_tree (GtkCMCTree     *ctree,
2153                                          GtkCMCTreeNode *node,
2154                                          gint            column,
2155                                          gpointer        data)
2156 {
2157 }
2158
2159 /**
2160  * Add list of items into tree node below specified tree node.
2161  * \param treeNode  Tree node.
2162  * \param ds        Data source.
2163  * \param listItems List of items.
2164  */
2165 static void addressbook_treenode_add_list(
2166         GtkCMCTreeNode *treeNode, AddressDataSource *ds, GList *listItems )
2167 {
2168         GList *node;
2169
2170         node = listItems;
2171         while( node ) {
2172                 AddrItemObject *aio;
2173                 GtkCMCTreeNode *nn;
2174
2175                 aio = node->data;
2176                 if( ADDRESS_OBJECT_TYPE(aio) == ITEMTYPE_GROUP ) {
2177                         ItemGroup *group;
2178
2179                         group = ( ItemGroup * ) aio;
2180                         nn = addressbook_node_add_group( treeNode, ds, group );
2181                 }
2182                 else if( ADDRESS_OBJECT_TYPE(aio) == ITEMTYPE_FOLDER ) {
2183                         ItemFolder *folder;
2184
2185                         folder = ( ItemFolder * ) aio;
2186                         nn = addressbook_node_add_folder(
2187                                 treeNode, ds, folder, ADDR_ITEM_FOLDER );
2188                 }
2189                 node = g_list_next( node );
2190         }
2191 }
2192
2193 static void addressbook_select_all_cb( GtkAction *action, gpointer data ) {
2194         gtk_cmclist_select_all(GTK_CMCLIST(addrbook.clist));
2195 }
2196
2197 /**
2198  * Cut from address list widget.
2199  */
2200 static void addressbook_clip_cut_cb( GtkAction *action, gpointer data ) {
2201         _clipBoard_->cutFlag = TRUE;
2202         addrclip_clear( _clipBoard_ );
2203         addrclip_add( _clipBoard_, _addressSelect_ );
2204         /* addrclip_list_show( _clipBoard_, stdout ); */
2205 }
2206
2207 /**
2208  * Copy from address list widget.
2209  */
2210 static void addressbook_clip_copy_cb(GtkAction *action, gpointer data) {
2211         _clipBoard_->cutFlag = FALSE;
2212         addrclip_clear( _clipBoard_ );
2213         addrclip_add( _clipBoard_, _addressSelect_ );
2214         /* addrclip_list_show( _clipBoard_, stdout ); */
2215 }
2216
2217 /**
2218  * Paste clipboard into address list widget.
2219  */
2220 static void addressbook_clip_paste_cb( GtkAction *action, gpointer data ) {
2221         GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
2222         AddressObject *pobj = NULL;
2223         AddressDataSource *ds = NULL;
2224         AddressBookFile *abf = NULL;
2225         ItemFolder *folder = NULL;
2226         GList *folderGroup = NULL;
2227
2228         ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
2229         if( ds == NULL ) return;
2230         if( addrindex_ds_get_readonly( ds ) ) {
2231                 alertpanel_error( _("Cannot paste. Target address book is readonly.") );
2232                 return;
2233         }
2234
2235         pobj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
2236         if( pobj ) {
2237                 if( pobj->type == ADDR_ITEM_FOLDER ) {
2238                         folder = ADAPTER_FOLDER(pobj)->itemFolder;
2239                 }
2240                 else if( pobj->type == ADDR_ITEM_GROUP ) {
2241                         alertpanel_error( _("Cannot paste into an address group.") );
2242                         return;
2243                 }
2244         }
2245
2246         /* Get an address book */
2247         abf = addressbook_get_book_file();
2248         if( abf == NULL ) return;
2249
2250         if( _clipBoard_->cutFlag ) {
2251                 /* Paste/Cut */
2252                 folderGroup = addrclip_paste_cut( _clipBoard_, abf, folder );
2253
2254                 /* Remove all groups and folders in clipboard from tree node */
2255                 addressbook_treenode_remove_item();
2256
2257                 /* Remove all "cut" items */
2258                 addrclip_delete_item( _clipBoard_ );
2259
2260                 /* Clear clipboard - cut items??? */
2261                 addrclip_clear( _clipBoard_ );
2262         }
2263         else {
2264                 /* Paste/Copy */
2265                 folderGroup = addrclip_paste_copy( _clipBoard_, abf, folder );
2266         }
2267
2268         /* addrclip_list_show( _clipBoard_, stdout ); */
2269         if( folderGroup ) {
2270                 /* Update tree by inserting node for each folder or group */
2271                 addressbook_treenode_add_list(
2272                         addrbook.treeSelected, ds, folderGroup );
2273                 gtk_cmctree_expand( ctree, addrbook.treeSelected );
2274                 g_list_free( folderGroup );
2275                 folderGroup = NULL;
2276         }
2277
2278         /* Display items pasted */
2279         gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened );
2280         addressbook_set_clist(
2281                 gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
2282                         addrbook.opened),
2283                 TRUE);
2284         
2285
2286 }
2287
2288 /**
2289  * Add current treenode object to clipboard. Note that widget only allows
2290  * one entry from the tree list to be selected.
2291  */
2292 static void addressbook_treenode_to_clipboard( void ) {
2293         AddressObject *obj = NULL;
2294         AddressDataSource *ds = NULL;
2295         AddrSelectItem *item;
2296         GtkCMCTree *ctree = GTK_CMCTREE( addrbook.ctree );
2297         GtkCMCTreeNode *node;
2298
2299         node = addrbook.treeSelected;
2300         if( node == NULL ) return;
2301         obj = gtk_cmctree_node_get_row_data( ctree, node );
2302         if( obj == NULL ) return;
2303
2304         ds = addressbook_find_datasource( node );
2305         if( ds == NULL ) return;
2306
2307         item = NULL;
2308         if( obj->type == ADDR_ITEM_FOLDER ) {
2309                 AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2310                 ItemFolder *folder = adapter->itemFolder;
2311
2312                 item = addrselect_create_node( obj );
2313                 item->uid = g_strdup( ADDRITEM_ID(folder) );
2314         }
2315         else if( obj->type == ADDR_ITEM_GROUP ) {
2316                 AdapterGroup *adapter = ADAPTER_GROUP(obj);
2317                 ItemGroup *group = adapter->itemGroup;
2318
2319                 item = addrselect_create_node( obj );
2320                 item->uid = g_strdup( ADDRITEM_ID(group) );
2321         }
2322         else if( obj->type == ADDR_DATASOURCE ) {
2323                 /* Data source */
2324                 item = addrselect_create_node( obj );
2325                 item->uid = NULL;
2326         }
2327
2328         if( item ) {
2329                 /* Clear existing list and add item into list */
2330                 gchar *cacheID;
2331
2332                 addressbook_list_select_clear();
2333                 cacheID = addrindex_get_cache_id( _addressIndex_, ds );
2334                 addrselect_list_add( _addressSelect_, item, cacheID );
2335                 g_free( cacheID );
2336         }
2337 }
2338
2339 /**
2340  * Cut from tree widget.
2341  */
2342 static void addressbook_treenode_cut_cb( GtkAction *action, gpointer data ) {
2343         _clipBoard_->cutFlag = TRUE;
2344         addressbook_treenode_to_clipboard();
2345         addrclip_clear( _clipBoard_ );
2346         addrclip_add( _clipBoard_, _addressSelect_ );
2347         /* addrclip_list_show( _clipBoard_, stdout ); */
2348 }
2349
2350 /**
2351  * Copy from tree widget.
2352  */
2353 static void addressbook_treenode_copy_cb( GtkAction *action, gpointer data ) {
2354         _clipBoard_->cutFlag = FALSE;
2355         addressbook_treenode_to_clipboard();
2356         addrclip_clear( _clipBoard_ );
2357         addrclip_add( _clipBoard_, _addressSelect_ );
2358         /* addrclip_list_show( _clipBoard_, stdout ); */
2359 }
2360
2361 /**
2362  * Paste clipboard into address tree widget.
2363  */
2364 static void addressbook_treenode_paste_cb( GtkAction *action, gpointer data ) {
2365         addressbook_clip_paste_cb(NULL,NULL);
2366 }
2367
2368 /**
2369  * Clear selected entries in clipboard.
2370  */
2371 static void addressbook_list_select_clear( void ) {
2372         addrselect_list_clear( _addressSelect_ );
2373 }
2374
2375 /**
2376  * Add specified address item to selected address list.
2377  * \param aio Address item object.
2378  * \param ds  Datasource.
2379  */
2380 static void addressbook_list_select_add( AddrItemObject *aio, AddressDataSource *ds ) {
2381         gchar *cacheID;
2382
2383         if( ds == NULL ) return;
2384         cacheID = addrindex_get_cache_id( _addressIndex_, ds );
2385         addrselect_list_add_obj( _addressSelect_, aio, cacheID );
2386         g_free( cacheID );
2387 }
2388
2389 /**
2390  * Remove specified address item from selected address list.
2391  * \param aio Address item object.
2392  */
2393 static void addressbook_list_select_remove( AddrItemObject *aio ) {
2394         addrselect_list_remove( _addressSelect_, aio );
2395 }
2396
2397 /**
2398  * Invoke EMail compose window with addresses in selected address list.
2399  */
2400 static void addressbook_mail_to_cb( GtkAction *action, gpointer data ) {
2401         GList *listAddress;
2402
2403         if( ! addrselect_test_empty( _addressSelect_ ) ) {
2404                 listAddress = addrselect_build_list( _addressSelect_ );
2405                 compose_new_with_list( NULL, listAddress );
2406                 mgu_free_dlist( listAddress );
2407                 listAddress = NULL;
2408         }
2409 }
2410
2411 static void addressbook_list_row_selected( GtkCMCTree *clist,
2412                                            GtkCMCTreeNode *node,
2413                                            gint column,
2414                                            gpointer data )
2415 {
2416         GtkEntry *entry = GTK_ENTRY(addrbook.entry);
2417         AddrItemObject *aio = NULL;
2418         AddressObject *pobj = NULL;
2419         AdapterDSource *ads = NULL;
2420         AddressDataSource *ds = NULL;
2421
2422         gtk_entry_set_text( entry, "" );
2423         addrbook.listSelected = node;
2424
2425         pobj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected );
2426         if( pobj == NULL ) return;
2427
2428         if( pobj->type == ADDR_DATASOURCE ) {
2429                 ads = ADAPTER_DSOURCE(pobj);
2430                 ds = ads->dataSource;
2431         }
2432         else if( pobj->type != ADDR_INTERFACE ) {
2433                 ds = addressbook_find_datasource( addrbook.treeSelected );
2434         }
2435
2436         aio = gtk_cmctree_node_get_row_data( clist, node );
2437         if( aio ) {
2438                 /* g_print( "list select: %d : '%s'\n", aio->type, aio->name ); */
2439                 addressbook_list_select_add( aio, ds );
2440         }
2441
2442         addressbook_list_menu_setup();
2443
2444         if (!addrbook.target_compose && !prefs_common.addressbook_use_editaddress_dialog) {
2445                 AddressObject *obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
2446
2447                 if (obj && obj->type != ADDR_ITEM_GROUP)
2448                         addressbook_edit_address(NULL, 0, NULL, FALSE);
2449         }
2450 }
2451
2452 static void addressbook_list_row_unselected( GtkCMCTree *ctree,
2453                                              GtkCMCTreeNode *node,
2454                                              gint column,
2455                                              gpointer data )
2456 {
2457         AddrItemObject *aio;
2458
2459         aio = gtk_cmctree_node_get_row_data( ctree, node );
2460         if( aio != NULL ) {
2461                 /* g_print( "list unselect: %d : '%s'\n", aio->type, aio->name ); */
2462                 addressbook_list_select_remove( aio );
2463         }
2464
2465         if (!prefs_common.addressbook_use_editaddress_dialog)
2466                 addressbook_edit_person_invalidate(NULL, NULL, NULL);
2467 }
2468
2469 static gboolean addressbook_list_button_pressed(GtkWidget *widget,
2470                                                 GdkEventButton *event,
2471                                                 gpointer data)
2472 {
2473         if( ! event ) return FALSE;
2474
2475         addressbook_list_menu_setup();
2476
2477         if( event->button == 3 ) {
2478                 gtk_menu_popup( GTK_MENU(addrbook.list_popup), NULL, NULL, NULL, NULL,
2479                        event->button, event->time );
2480         } else if (event->button == 1) {
2481                 if (event->type == GDK_2BUTTON_PRESS) {
2482                         if (prefs_common.add_address_by_click &&
2483                             addrbook.target_compose)
2484                                 addressbook_to_clicked(NULL, GINT_TO_POINTER(COMPOSE_TO));
2485                         else
2486                                 if (prefs_common.addressbook_use_editaddress_dialog)
2487                                         addressbook_edit_address_cb(NULL, NULL);
2488                                 else {
2489                                         GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
2490                                         AddressObject *obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
2491                                         if( obj && obj->type == ADDR_ITEM_GROUP )
2492                                                 addressbook_edit_address_cb(NULL, NULL);
2493                                 }
2494                 }
2495         }
2496
2497         return FALSE;
2498 }
2499
2500 static gboolean addressbook_list_button_released(GtkWidget *widget,
2501                                                  GdkEventButton *event,
2502                                                  gpointer data)
2503 {
2504         return FALSE;
2505 }
2506
2507 static gboolean addressbook_tree_button_pressed(GtkWidget *ctree,
2508                                                 GdkEventButton *event,
2509                                                 gpointer data)
2510 {
2511         GtkCMCList *clist = GTK_CMCLIST(ctree);
2512         gint row, column;
2513         AddressObject *obj = NULL;
2514         AdapterDSource *ads = NULL;
2515         AddressInterface *iface = NULL;
2516         AddressDataSource *ds = NULL;
2517         gboolean canEdit = FALSE;
2518         gboolean canDelete = FALSE;
2519         gboolean canCut = FALSE;
2520         gboolean canCopy = FALSE;
2521         gboolean canPaste = FALSE;
2522         gboolean canTreeCut = FALSE;
2523         gboolean canTreeCopy = FALSE;
2524         gboolean canTreePaste = FALSE;
2525         gboolean canLookup = FALSE;
2526         GtkCMCTreeNode *node = NULL;
2527         
2528         if( ! event ) return FALSE;
2529 /*      if( ! event || event->type != GDK_BUTTON_PRESS) return FALSE;*/
2530
2531         if (event->button == 1) {
2532                 if (event->type == GDK_2BUTTON_PRESS) {
2533                         if( gtk_cmclist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
2534                                 gtkut_clist_set_focus_row(clist, row);
2535                                 obj = gtk_cmclist_get_row_data( clist, row );
2536                         }
2537                         if( obj == NULL )
2538                                 return FALSE;
2539
2540                         if (obj->type == ADDR_ITEM_GROUP) {
2541                                 /* edit group */
2542                                 addressbook_treenode_edit_cb(NULL, NULL);
2543                         } else {
2544                                 /* expand pr collapse */
2545                                 node = gtk_cmctree_node_nth(GTK_CMCTREE(ctree), row);
2546                                 gtk_cmctree_toggle_expansion(GTK_CMCTREE(ctree), node);
2547                         }
2548                         return FALSE;
2549                 }
2550         }
2551
2552         addressbook_menubar_set_sensitive( FALSE );
2553
2554         if( gtk_cmclist_get_selection_info( clist, event->x, event->y, &row, &column ) ) {
2555                 gtkut_clist_set_focus_row(clist, row);
2556                 obj = gtk_cmclist_get_row_data( clist, row );
2557         }
2558
2559         menu_set_insensitive_all(GTK_MENU_SHELL(addrbook.tree_popup));
2560
2561         if( obj == NULL )
2562                 return FALSE;
2563         node = gtk_cmctree_node_nth(GTK_CMCTREE(clist), row);
2564
2565         if( ! addrclip_is_empty( _clipBoard_ ) )
2566                 canTreePaste = TRUE;
2567
2568         if (obj->type == ADDR_INTERFACE) {
2569                 AdapterInterface *adapter = ADAPTER_INTERFACE(obj);
2570                 if( !adapter )
2571                         goto just_set_sens;
2572                 iface = adapter->interface;
2573                 if( !iface )
2574                         goto just_set_sens;
2575                 if( !iface->readOnly ) {
2576                         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewBook", TRUE );
2577                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2578                 }
2579                 if( iface->externalQuery )
2580                         canLookup = TRUE;
2581         }
2582         if (obj->type == ADDR_DATASOURCE) {
2583                 ads = ADAPTER_DSOURCE(obj);
2584                 ds = ads->dataSource;
2585                 if( !ds )
2586                         goto just_set_sens;
2587                 iface = ds->interface;
2588                 if( !iface )
2589                         goto just_set_sens;
2590                 if( !iface->readOnly ) {
2591                         canDelete = TRUE;
2592                         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewFolder", TRUE );
2593                         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewGroup", TRUE );
2594                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2595                 }
2596                 canEdit = TRUE;
2597                 canTreeCopy = TRUE;
2598                 if( iface->externalQuery )
2599                         canLookup = TRUE;
2600         }
2601         else if (obj->type == ADDR_ITEM_FOLDER) {
2602                 ds = addressbook_find_datasource( node );
2603                 if( !ds )
2604                         goto just_set_sens;
2605                 iface = ds->interface;
2606                 if( !iface )
2607                         goto just_set_sens;
2608                 if( !iface->readOnly ) {
2609                         canEdit = TRUE;
2610                         canDelete = TRUE;
2611                         canTreeCut = TRUE;
2612                         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewFolder", TRUE );
2613                         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/NewGroup", TRUE );
2614                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2615                 }
2616                 canTreeCopy = TRUE;
2617
2618                 if( iface->externalQuery ) {
2619                         /* Enable deletion of LDAP folder */
2620                         canLookup = TRUE;
2621                         canDelete = TRUE;
2622                 }
2623         }
2624         else if (obj->type == ADDR_ITEM_GROUP) {
2625                 ds = addressbook_find_datasource( node );
2626                 if( !ds )
2627                         goto just_set_sens;
2628                 iface = ds->interface;
2629                 if( !iface )
2630                         goto just_set_sens;
2631                 if( ! iface->readOnly ) {
2632                         canEdit = TRUE;
2633                         canDelete = TRUE;
2634                         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABListPopup/NewAddress", TRUE );
2635                         gtk_widget_set_sensitive( addrbook.reg_btn, TRUE );
2636                 }
2637         }
2638
2639         if( canEdit && !addrselect_test_empty( _addressSelect_ ) )
2640                 canCut = TRUE;
2641         if( ! addrselect_test_empty( _addressSelect_ ) )
2642                 canCopy = TRUE;
2643         if( ! addrclip_is_empty( _clipBoard_ ) )
2644                 canPaste = TRUE;
2645
2646         /* Forbid write changes when read-only */
2647         if( iface && iface->readOnly ) {
2648                 canTreeCut = FALSE;
2649                 canTreePaste = FALSE;
2650                 canCut = FALSE;
2651                 canDelete = FALSE;
2652                 canPaste = FALSE;
2653         }
2654
2655 just_set_sens:
2656         /* Enable edit */
2657         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/EditBook",   canEdit );
2658         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/DeleteBook", canDelete );
2659         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/Cut",    canTreeCut );
2660         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/Copy",   canTreeCopy );
2661         cm_menu_set_sensitive_full( addrbook.ui_manager, "Popups/ABTreePopup/Paste",  canTreePaste );
2662
2663         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/EditBook",          canEdit );
2664         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Book/DeleteBook",        canEdit );
2665         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Cut",           canCut );
2666         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Copy",          canCopy );
2667         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Paste",         canPaste );
2668
2669         addressbook_show_buttons(addrbook.target_compose == NULL, canLookup,
2670                         addrbook.target_compose != NULL);
2671
2672         if( event->button == 3 )
2673                 gtk_menu_popup(GTK_MENU(addrbook.tree_popup), NULL, NULL, NULL, NULL,
2674                                event->button, event->time);
2675
2676         return FALSE;
2677 }
2678
2679 static gboolean addressbook_tree_button_released(GtkWidget *ctree,
2680                                                  GdkEventButton *event,
2681                                                  gpointer data)
2682 {
2683         gtkut_ctree_set_focus_row(GTK_CMCTREE(addrbook.ctree), addrbook.opened);
2684         return FALSE;
2685 }
2686
2687 static void addressbook_new_folder_cb(GtkAction *action, gpointer data)
2688 {
2689         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2690         AddressObject *obj = NULL;
2691         AddressDataSource *ds = NULL;
2692         AddressBookFile *abf = NULL;
2693         ItemFolder *parentFolder = NULL;
2694         ItemFolder *folder = NULL;
2695
2696         if( ! addrbook.treeSelected ) return;
2697         obj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
2698         if( obj == NULL ) return;
2699         ds = addressbook_find_datasource( addrbook.treeSelected );
2700         if( ds == NULL ) return;
2701
2702         if( obj->type == ADDR_DATASOURCE ) {
2703                 if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
2704         }
2705         else if( obj->type == ADDR_ITEM_FOLDER ) {
2706                 parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
2707         }
2708         else {
2709                 return;
2710         }
2711
2712         abf = ds->rawDataSource;
2713         if( abf == NULL ) return;
2714         folder = addressbook_edit_folder( abf, parentFolder, NULL );
2715         if( folder ) {
2716                 GtkCMCTreeNode *nn;
2717                 nn = addressbook_node_add_folder(
2718                         addrbook.treeSelected, ds, folder, ADDR_ITEM_FOLDER );
2719                 gtk_cmctree_expand( ctree, addrbook.treeSelected );
2720                 if( addrbook.treeSelected == addrbook.opened )
2721                         addressbook_set_clist(obj, TRUE);
2722         }
2723 }
2724
2725 static void addressbook_new_group_cb(GtkAction *action, gpointer data)
2726 {
2727         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2728         AddressObject *obj = NULL;
2729         AddressDataSource *ds = NULL;
2730         AddressBookFile *abf = NULL;
2731         ItemFolder *parentFolder = NULL;
2732         ItemGroup *group = NULL;
2733
2734         if( ! addrbook.treeSelected ) return;
2735         obj = gtk_cmctree_node_get_row_data(ctree, addrbook.treeSelected);
2736         if( obj == NULL ) return;
2737         ds = addressbook_find_datasource( addrbook.treeSelected );
2738         if( ds == NULL ) return;
2739
2740         if( obj->type == ADDR_DATASOURCE ) {
2741                 if( ADAPTER_DSOURCE(obj)->subType != ADDR_BOOK ) return;
2742         }
2743         else if( obj->type == ADDR_ITEM_FOLDER ) {
2744                 parentFolder = ADAPTER_FOLDER(obj)->itemFolder;
2745         }
2746         else {
2747                 return;
2748         }
2749
2750         abf = ds->rawDataSource;
2751         if( abf == NULL ) return;
2752         group = addressbook_edit_group( abf, parentFolder, NULL );
2753         if( group ) {
2754                 GtkCMCTreeNode *nn;
2755                 nn = addressbook_node_add_group( addrbook.treeSelected, ds, group );
2756                 gtk_cmctree_expand( ctree, addrbook.treeSelected );
2757                 if( addrbook.treeSelected == addrbook.opened )
2758                         addressbook_set_clist(obj, TRUE);
2759         }
2760 }
2761
2762 static void addressbook_change_node_name(GtkCMCTreeNode *node, const gchar *name)
2763 {
2764         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2765         gchar *text[1];
2766         guint8 spacing;
2767         GdkPixmap *pix_cl, *pix_op;
2768         GdkBitmap *mask_cl, *mask_op;
2769         gboolean is_leaf, expanded;
2770
2771         gtk_cmctree_get_node_info(ctree, node, text, &spacing,
2772                                 &pix_cl, &mask_cl, &pix_op, &mask_op,
2773                                 &is_leaf, &expanded);
2774         gtk_sctree_set_node_info(ctree, node, name, spacing,
2775                                 pix_cl, mask_cl, pix_op, mask_op,
2776                                 is_leaf, expanded);
2777 }
2778
2779 /**
2780  * Edit data source.
2781  * \param obj  Address object to edit.
2782  * \param node Node in tree.
2783  * \return New name of data source.
2784  */
2785 static gchar *addressbook_edit_datasource( AddressObject *obj, GtkCMCTreeNode *node ) {
2786         gchar *newName = NULL;
2787         AddressDataSource *ds = NULL;
2788         AddressInterface *iface = NULL;
2789         AdapterDSource *ads = NULL;
2790
2791         ds = addressbook_find_datasource( node );
2792         if( ds == NULL ) return NULL;
2793         iface = ds->interface;
2794         if( ! iface->haveLibrary ) return NULL;
2795
2796         /* Read data from data source */
2797         if( addrindex_ds_get_modify_flag( ds ) ) {
2798                 addrindex_ds_read_data( ds );
2799         }
2800
2801         if( ! addrindex_ds_get_read_flag( ds ) ) {
2802                 addrindex_ds_read_data( ds );
2803         }
2804
2805         /* Handle edit */
2806         ads = ADAPTER_DSOURCE(obj);
2807         if( ads->subType == ADDR_BOOK ) {
2808                 if( addressbook_edit_book( _addressIndex_, ads ) == NULL ) return NULL;
2809         }
2810         else if( ads->subType == ADDR_VCARD ) {
2811                 if( addressbook_edit_vcard( _addressIndex_, ads ) == NULL ) return NULL;
2812         }
2813 #ifdef USE_JPILOT
2814         else if( ads->subType == ADDR_JPILOT ) {
2815                 if( addressbook_edit_jpilot( _addressIndex_, ads ) == NULL ) return NULL;
2816         }
2817 #endif
2818 #ifdef USE_LDAP
2819         else if( ads->subType == ADDR_LDAP ) {
2820                 if( addressbook_edit_ldap( _addressIndex_, ads ) == NULL ) return NULL;
2821         }
2822 #endif
2823         else {
2824                 return NULL;
2825         }
2826         newName = obj->name;
2827         return newName;
2828 }
2829
2830 /*
2831 * Edit an object that is in the address tree area.
2832 */
2833 static void addressbook_treenode_edit_cb(GtkAction *action, gpointer data)
2834 {
2835         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2836         AddressObject *obj;
2837         AddressDataSource *ds = NULL;
2838         AddressBookFile *abf = NULL;
2839         GtkCMCTreeNode *node = NULL, *parentNode = NULL;
2840         gchar *name = NULL;
2841
2842         if( ! addrbook.treeSelected ) return;
2843         node = addrbook.treeSelected;
2844         if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
2845         obj = gtk_cmctree_node_get_row_data( ctree, node );
2846         if( obj == NULL ) return;
2847         parentNode = GTK_CMCTREE_ROW(node)->parent;
2848
2849         ds = addressbook_find_datasource( node );
2850         if( ds == NULL ) return;
2851
2852         if( obj->type == ADDR_DATASOURCE ) {
2853                 name = addressbook_edit_datasource( obj, node );
2854                 if( name == NULL ) return;
2855         }
2856         else {
2857                 abf = ds->rawDataSource;
2858                 if( abf == NULL ) return;
2859                 if( obj->type == ADDR_ITEM_FOLDER ) {
2860                         AdapterFolder *adapter = ADAPTER_FOLDER(obj);
2861                         ItemFolder *item = adapter->itemFolder;
2862                         ItemFolder *parentFolder = NULL;
2863                         parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
2864                         if( addressbook_edit_folder( abf, parentFolder, item ) == NULL ) return;
2865                         name = ADDRITEM_NAME(item);
2866                 }
2867                 else if( obj->type == ADDR_ITEM_GROUP ) {
2868                         AdapterGroup *adapter = ADAPTER_GROUP(obj);
2869                         ItemGroup *item = adapter->itemGroup;
2870                         ItemFolder *parentFolder = NULL;
2871                         parentFolder = ( ItemFolder * ) ADDRITEM_PARENT(item);
2872                         if( addressbook_edit_group( abf, parentFolder, item ) == NULL ) return;
2873                         name = ADDRITEM_NAME(item);
2874                 }
2875         }
2876         if( name && parentNode ) {
2877                 /* Update node in tree view */
2878                 addressbook_change_node_name( node, name );
2879                 gtk_sctree_sort_node(ctree, parentNode);
2880                 gtk_cmctree_expand( ctree, node );
2881                 gtk_sctree_select( GTK_SCTREE( ctree), node );
2882         }
2883 }
2884
2885 typedef enum {
2886         ADDRTREE_DEL_NONE,
2887         ADDRTREE_DEL_DATA,
2888         ADDRTREE_DEL_FOLDER_ONLY,
2889         ADDRTREE_DEL_FOLDER_ADDR
2890 } TreeItemDelType ;
2891
2892 /**
2893  * Delete an item from the tree widget.
2894  * \param data   Data passed in.
2895  * \param action Action.
2896  * \param widget Widget issuing callback.
2897  */
2898 static void addressbook_treenode_delete_cb(GtkAction *action, gpointer data)
2899 {
2900         GtkCMCTree *ctree = GTK_CMCTREE(addrbook.ctree);
2901         GtkCMCTreeNode *node = NULL;
2902         AddressObject *obj;
2903         gchar *message;
2904         AlertValue aval;
2905         AddrBookBase *adbase;
2906         AddressCache *cache;
2907         AdapterDSource *ads = NULL;
2908         AddressInterface *iface = NULL;
2909         AddressDataSource *ds = NULL;
2910         gboolean remFlag = FALSE;
2911         TreeItemDelType delType;
2912
2913         if( ! addrbook.treeSelected ) return;
2914         node = addrbook.treeSelected;
2915         if( GTK_CMCTREE_ROW(node)->level == 1 ) return;
2916
2917         obj = gtk_cmctree_node_get_row_data( ctree, node );
2918         g_return_if_fail(obj != NULL);
2919
2920         if( obj->type == ADDR_DATASOURCE ) {
2921                 ads = ADAPTER_DSOURCE(obj);
2922                 if( ads == NULL ) return;
2923                 ds = ads->dataSource;
2924                 if( ds == NULL ) return;
2925         }
2926         else {
2927                 /* Must be folder or something else */
2928                 ds = addressbook_find_datasource( node );
2929                 if( ds == NULL ) return;
2930
2931                 /* Only allow deletion from non-readOnly */
2932                 iface = ds->interface;
2933                 if( iface->readOnly ) {
2934                         /* Allow deletion of query results */
2935                         if( ! iface->externalQuery ) return;
2936                 }
2937         }
2938
2939         /* Confirm deletion */
2940         delType = ADDRTREE_DEL_NONE;
2941         if( obj->type == ADDR_ITEM_FOLDER ) {
2942                 if( iface->externalQuery ) {
2943                         message = g_strdup_printf( _(
2944                                 "Do you want to delete the query " \
2945                                 "results and addresses in '%s' ?" ),
2946                                 obj->name );
2947                         aval = alertpanel( _("Delete"), message,
2948                                 GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL );
2949                         g_free(message);
2950                         if( aval == G_ALERTALTERNATE ) {
2951                                 delType = ADDRTREE_DEL_FOLDER_ADDR;
2952                         }
2953                 }
2954                 else {
2955                         message = g_strdup_printf
2956                                 ( _( "Do you want to delete '%s' ? "
2957                                      "If you delete the folder only, the addresses it contains will be moved into the parent folder." ),
2958                                  obj->name );
2959                         aval = alertpanel( _("Delete folder"), message,
2960                                 GTK_STOCK_CANCEL, _("+Delete _folder only"), _("Delete folder and _addresses"));
2961                         g_free(message);
2962                         if( aval == G_ALERTALTERNATE ) {
2963                                 delType = ADDRTREE_DEL_FOLDER_ONLY;
2964                         }
2965                         else if( aval == G_ALERTOTHER ) {
2966                                 delType = ADDRTREE_DEL_FOLDER_ADDR;
2967                         }
2968                 }
2969         }
2970         else if( obj->type == ADDR_ITEM_GROUP ) {
2971                 message = g_strdup_printf(_("Do you want to delete '%s'?\n"
2972                                             "The addresses it contains will not be lost."), obj->name);
2973                 aval = alertpanel(_("Delete"), message, GTK_STOCK_CANCEL, 
2974                                 "+" GTK_STOCK_DELETE, NULL);
2975                 g_free(message);
2976                 if( aval == G_ALERTALTERNATE ) delType = ADDRTREE_DEL_FOLDER_ONLY;
2977         } else {
2978                 message = g_strdup_printf(_("Do you want to delete '%s'?\n"
2979                                             "The addresses it contains will be lost."), obj->name);
2980                 aval = alertpanel(_("Delete"), message, GTK_STOCK_CANCEL, 
2981                                 "+" GTK_STOCK_DELETE, NULL);
2982                 g_free(message);
2983                 if( aval == G_ALERTALTERNATE ) delType = ADDRTREE_DEL_DATA;
2984         }
2985         if( delType == ADDRTREE_DEL_NONE ) return;
2986
2987         /* Proceed with deletion */
2988         if( obj->type == ADDR_DATASOURCE ) {
2989                 /* Remove node from tree */
2990                 gtk_cmctree_remove_node( ctree, node );
2991         
2992                 /* Remove data source. */
2993                 if( addrindex_index_remove_datasource( _addressIndex_, ds ) ) {
2994                         addrindex_free_datasource( ds );
2995                 }
2996                 return;
2997         }
2998
2999         /* Get reference to cache */
3000         adbase = ( AddrBookBase * ) ds->rawDataSource;
3001         if( adbase == NULL ) return;
3002         cache = adbase->addressCache;
3003
3004         /* Remove query results folder */
3005         if( iface->externalQuery ) {
3006                 AdapterFolder *adapter = ADAPTER_FOLDER(obj);
3007                 ItemFolder *folder = adapter->itemFolder;
3008
3009                 adapter->itemFolder = NULL;
3010                 /*
3011                 g_print( "remove folder for ::%s::\n", obj->name );
3012                 g_print( "      folder name ::%s::\n", ADDRITEM_NAME(folder) );
3013                 g_print( "-------------- remove results\n" );
3014                 */
3015                 addrindex_remove_results( ds, folder );
3016                 /* g_print( "-------------- remove node\n" ); */
3017                 gtk_cmctree_remove_node( ctree, node );
3018                 return;
3019         }
3020
3021         /* Code below is valid for regular address book deletion */
3022         if( obj->type == ADDR_ITEM_FOLDER ) {
3023                 AdapterFolder *adapter = ADAPTER_FOLDER(obj);
3024                 ItemFolder *item = adapter->itemFolder;
3025
3026                 if( delType == ADDRTREE_DEL_FOLDER_ONLY ) {
3027                         /* Remove folder only */
3028                         item = addrcache_remove_folder( cache, item );
3029                         if( item ) {
3030                                 addritem_free_item_folder( item );
3031                                 addressbook_move_nodes_up( ctree, node );
3032                                 remFlag = TRUE;
3033                         }
3034                 }
3035                 else if( delType == ADDRTREE_DEL_FOLDER_ADDR ) {
3036                         /* Remove folder and addresses */
3037                         item = addrcache_remove_folder_delete( cache, item );
3038                         if( item ) {
3039                                 addritem_free_item_folder( item );
3040                                 remFlag = TRUE;
3041                         }
3042                 }
3043         }
3044         else if( obj->type == ADDR_ITEM_GROUP ) {
3045                 AdapterGroup *adapter = ADAPTER_GROUP(obj);
3046                 ItemGroup *item = adapter->itemGroup;
3047
3048                 item = addrcache_remove_group( cache, item );
3049                 if( item ) {
3050                         addritem_free_item_group( item );
3051                         remFlag = TRUE;
3052                 }
3053         }
3054
3055         if( remFlag ) {
3056                 /* Remove node. */
3057                 gtk_cmctree_remove_node(ctree, node );
3058         }
3059 }
3060
3061 static void addressbook_new_address_from_book_post_cb( ItemPerson *person )
3062 {
3063         if( person && addrbook.treeSelected == addrbook.opened ) {
3064                 person->status = ADD_ENTRY;
3065                 gtk_cmclist_unselect_all( GTK_CMCLIST(addrbook.clist) );
3066                 addressbook_folder_refresh_one_person(
3067                         GTK_CMCTREE(addrbook.clist), person );
3068         }
3069         addressbook_address_list_set_focus();
3070 }
3071
3072 static void addressbook_new_address_from_folder_post_cb( ItemPerson *person )
3073 {
3074         if( person && addrbook.treeSelected == addrbook.opened) {
3075                 person->status = ADD_ENTRY;
3076                 gtk_sctree_select( GTK_SCTREE(addrbook.ctree), addrbook.opened );
3077                 addressbook_set_clist(
3078                         gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
3079                                 addrbook.opened),
3080                         TRUE);
3081         }
3082         addressbook_address_list_set_focus();
3083 }
3084
3085 /**
3086  * Label (a format string) that is used to name each folder.
3087  */
3088 static gchar *_queryFolderLabel_ = N_( "Search '%s'" );
3089
3090 /**
3091  * Search ctree widget callback function.
3092  * \param  pA Pointer to node.
3093  * \param  pB Pointer to data item being sought.
3094  * \return Zero (0) if folder found.
3095  */
3096 static int addressbook_treenode_find_folder_cb( gconstpointer pA, gconstpointer pB ) {
3097         AddressObject *aoA;
3098
3099         aoA = ( AddressObject * ) pA;
3100         if( aoA->type == ADDR_ITEM_FOLDER ) {
3101                 ItemFolder *folder, *fld;
3102
3103                 fld = ADAPTER_FOLDER(aoA)->itemFolder;
3104                 folder = ( ItemFolder * ) pB;
3105                 if( fld == folder ) return 0;   /* Found folder */
3106         }
3107         return 1;
3108 }
3109
3110 static ItemFolder * addressbook_setup_subf(
3111                 AddressDataSource *ds, gchar *title,
3112                 GtkCMCTreeNode *pNode )
3113 {
3114         AddrBookBase *adbase;
3115         AddressCache *cache;
3116         ItemFolder *folder;
3117         GtkCMCTree *ctree;
3118         GtkCMCTreeNode *nNode;
3119         gchar *name;
3120         AddressObjectType aoType = ADDR_NONE;
3121         GList *children;
3122         /* Setup a query */
3123         if( *title == '\0' || strlen( title ) < 1 ) return NULL;
3124
3125         if( ds && ds->type == ADDR_IF_LDAP ) {
3126 #if USE_LDAP
3127                 aoType = ADDR_LDAP_QUERY;
3128 #endif
3129         }
3130         else {
3131                 return NULL;
3132         }
3133
3134         ctree = GTK_CMCTREE(addrbook.ctree);
3135         /* Get reference to address cache */    
3136         adbase = ( AddrBookBase * ) ds->rawDataSource;
3137         cache = adbase->addressCache;
3138         
3139         if ((children = addrcache_get_list_folder(cache)) != NULL) {
3140                 GList *cur = children;
3141                 for (; cur; cur = cur->next) {
3142                         ItemFolder *child = (ItemFolder *) cur->data;
3143                         if (!strcmp2(ADDRITEM_NAME(child), title)) {
3144                                 nNode = gtk_cmctree_find_by_row_data_custom(
3145                                         ctree, NULL, child,
3146                                         addressbook_treenode_find_folder_cb );
3147                                 if( nNode ) {
3148                                         addrindex_remove_results( ds, child );
3149                                         while( child->listPerson ) {
3150                                                 ItemPerson *item = ( ItemPerson * ) child->listPerson->data;
3151                                                 item = addrcache_remove_person( cache, item );
3152                                                 if( item ) {
3153                                                         addritem_free_item_person( item );
3154                                                         item = NULL;
3155                                                 }
3156                                         }
3157                                         gtk_sctree_select( GTK_SCTREE(ctree), nNode );
3158                                         addrbook.treeSelected = nNode;
3159                                 }       
3160                                 return child;
3161                         }
3162                 }
3163         }
3164         
3165         /* Create a folder */
3166         folder = addrcache_add_new_folder( cache, NULL );
3167         name = g_strdup_printf( "%s", title );
3168         addritem_folder_set_name( folder, name );
3169         addritem_folder_set_remarks( folder, "" );
3170         g_free( name );
3171
3172         /* Now let's see the folder */
3173         nNode = addressbook_node_add_folder( pNode, ds, folder, aoType );
3174         gtk_cmctree_expand( ctree, pNode );
3175         if( nNode ) {
3176                 gtk_sctree_select( GTK_SCTREE(ctree), nNode );
3177                 addrbook.treeSelected = nNode;
3178                 return folder;
3179         }
3180         return NULL;
3181 }
3182
3183 static void addressbook_new_address_cb( GtkAction *action, gpointer data ) {
3184         AddressObject *pobj = NULL;
3185         AddressDataSource *ds = NULL;
3186         AddressBookFile *abf = NULL;
3187         debug_print("adding address\n");
3188         pobj = gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected);
3189         if( pobj == NULL ) {
3190                 debug_print("no row data\n");
3191                 return;
3192         }
3193         ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
3194         if( ds == NULL ) {
3195                 debug_print("no datasource\n");
3196                 return;
3197         }
3198
3199         abf = ds->rawDataSource;
3200         if( abf == NULL ) {
3201                 g_print("no addressbook file\n");
3202                 return;
3203         }
3204
3205         if( pobj->type == ADDR_DATASOURCE ) {
3206                 if (ADAPTER_DSOURCE(pobj)->subType == ADDR_BOOK ||
3207                     ADAPTER_DSOURCE(pobj)->subType == ADDR_LDAP) {
3208                         ItemPerson *person;
3209                         ItemFolder *folder = NULL;
3210 #ifdef USE_LDAP
3211                         if (abf && abf->type == ADDR_IF_LDAP) {
3212                                 GtkCMCTreeNode *parentNode;
3213                                 ds = addressbook_find_datasource( GTK_CMCTREE_NODE( addrbook.treeSelected ) );
3214                                 if( ds == NULL ) return;
3215
3216                                 /* We must have a datasource that is an external interface */
3217                                 if( ! ds->interface->haveLibrary ) return;
3218                                 if( ! ds->interface->externalQuery ) return;
3219
3220                                 if( pobj->type == ADDR_ITEM_FOLDER ) {
3221                                         parentNode = GTK_CMCTREE_ROW(GTK_CMCTREE_NODE( addrbook.treeSelected ) )->parent;
3222                                 }
3223                                 else {
3224                                         parentNode = GTK_CMCTREE_NODE( addrbook.treeSelected );
3225                                 }
3226                                 folder = addressbook_setup_subf( ds, _("New Contacts"), parentNode );
3227
3228                                 pobj = gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected);
3229                                 ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
3230                                 abf = ds->rawDataSource;
3231                         }
3232 #endif
3233                         person = addressbook_edit_person( abf, folder, NULL, FALSE,
3234                                                                   addrbook.editaddress_vbox,
3235                                                                   addressbook_new_address_from_book_post_cb,
3236                                                                   TRUE );
3237 #ifdef USE_LDAP
3238                         if (abf && abf->type == ADDR_IF_LDAP) {
3239                                 LdapServer *server = ds->rawDataSource;
3240                                 ldapsvr_set_modified(server, TRUE);
3241                                 ldapsvr_update_book(server, NULL);
3242                                 if (server->retVal != LDAPRC_SUCCESS) {
3243                                         alertpanel( _("Add address(es)"),
3244                                                 addressbook_err2string(_lutErrorsLDAP_, server->retVal),
3245                                                 GTK_STOCK_CLOSE, NULL, NULL );
3246                                         server->retVal = LDAPRC_SUCCESS;
3247                                         return;
3248                                 }
3249                         }
3250 #endif
3251                         if (prefs_common.addressbook_use_editaddress_dialog)
3252                                 addressbook_new_address_from_book_post_cb( person );
3253                 }
3254         }
3255         else if( pobj->type == ADDR_ITEM_FOLDER ) {
3256                 /* New address */
3257                 ItemFolder *folder = ADAPTER_FOLDER(pobj)->itemFolder;
3258                 ItemPerson *person;
3259 #ifdef USE_LDAP
3260                 if (abf && abf->type == ADDR_IF_LDAP) {
3261                         GtkCMCTreeNode *parentNode;
3262                         ds = addressbook_find_datasource( GTK_CMCTREE_NODE( addrbook.treeSelected ) );
3263                         if( ds == NULL ) return;
3264
3265                         /* We must have a datasource that is an external interface */
3266                         if( ! ds->interface->haveLibrary ) return;
3267                         if( ! ds->interface->externalQuery ) return;
3268
3269                         if( pobj->type == ADDR_ITEM_FOLDER ) {
3270                                 parentNode = GTK_CMCTREE_ROW(GTK_CMCTREE_NODE( addrbook.treeSelected ) )->parent;
3271                         }
3272                         else {
3273                                 parentNode = GTK_CMCTREE_NODE( addrbook.treeSelected );
3274                         }
3275                         folder = addressbook_setup_subf( ds, _("New Contacts"), parentNode );
3276                         if (!folder)
3277                                 return;
3278                         pobj = gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree), addrbook.treeSelected);
3279                         ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
3280                         abf = ds->rawDataSource;
3281                 }
3282 #endif
3283                 person = addressbook_edit_person( abf, folder, NULL, FALSE,
3284                                                           addrbook.editaddress_vbox,
3285                                                           addressbook_new_address_from_folder_post_cb,
3286                                                           TRUE );
3287 #ifdef USE_LDAP
3288                 if (abf && abf->type == ADDR_IF_LDAP) {
3289                         LdapServer *server = ds->rawDataSource;
3290                         ldapsvr_set_modified(server, TRUE);
3291                         ldapsvr_update_book(server, NULL);
3292                         if (server->retVal != LDAPRC_SUCCESS) {
3293                                 alertpanel( _("Add address(es)"),
3294                                                 addressbook_err2string(_lutErrorsLDAP_, server->retVal),
3295                                         GTK_STOCK_CLOSE, NULL, NULL );
3296                                 return;
3297                         }
3298                 }
3299 #endif
3300                 if (prefs_common.addressbook_use_editaddress_dialog)
3301                         addressbook_new_address_from_folder_post_cb( person );
3302         }
3303         else if( pobj->type == ADDR_ITEM_GROUP ) {
3304                 /* New address in group */
3305                 ItemGroup *group = ADAPTER_GROUP(pobj)->itemGroup;
3306                 if( addressbook_edit_group( abf, NULL, group ) == NULL ) return;
3307                 if (addrbook.treeSelected == addrbook.opened) {
3308                         /* Change node name in tree. */
3309                         addressbook_change_node_name( addrbook.treeSelected, ADDRITEM_NAME(group) );
3310                         gtk_sctree_select( GTK_SCTREE(addrbook.ctree), addrbook.opened );
3311                         addressbook_set_clist(
3312                                 gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
3313                                         addrbook.opened),
3314                                 TRUE);
3315                 }
3316         }
3317 }
3318
3319 /**
3320  * Search for specified child group node in address index tree.
3321  * \param parent Parent node.
3322  * \param group  Group to find.
3323  */
3324 static GtkCMCTreeNode *addressbook_find_group_node( GtkCMCTreeNode *parent, ItemGroup *group ) {
3325         GtkCMCTreeNode *node = NULL;
3326         GtkCMCTreeRow *currRow;
3327
3328         currRow = GTK_CMCTREE_ROW( parent );
3329         if( currRow ) {
3330                 node = currRow->children;
3331                 while( node ) {
3332                         AddressObject *obj;
3333
3334                         obj = gtk_cmctree_node_get_row_data( GTK_CMCTREE(addrbook.ctree), node );
3335                         if( obj->type == ADDR_ITEM_GROUP ) {
3336                                 ItemGroup *g = ADAPTER_GROUP(obj)->itemGroup;
3337                                 if( g == group ) return node;
3338                         }
3339                         currRow = GTK_CMCTREE_ROW(node);
3340                         node = currRow->sibling;
3341                 }
3342         }
3343         return NULL;
3344 }
3345
3346 static AddressBookFile *addressbook_get_book_file() {
3347         AddressBookFile *abf = NULL;
3348         AddressDataSource *ds = NULL;
3349
3350         ds = addressbook_find_datasource( addrbook.treeSelected );
3351         if( ds == NULL ) return NULL;
3352         if( ds->type == ADDR_IF_BOOK || ds->type == ADDR_IF_LDAP ) abf = ds->rawDataSource;
3353         return abf;
3354 }
3355
3356 static void addressbook_tree_remove_children( GtkCMCTree *ctree, GtkCMCTreeNode *parent ) {
3357         GtkCMCTreeNode *node;
3358         GtkCMCTreeRow *row;
3359
3360         /* Remove existing folders and groups */
3361         row = GTK_CMCTREE_ROW( parent );
3362         if( row ) {
3363                 while( (node = row->children) ) {
3364                         gtk_cmctree_remove_node( ctree, node );
3365                 }
3366         }
3367 }
3368
3369 static void addressbook_move_nodes_up( GtkCMCTree *ctree, GtkCMCTreeNode *node ) {
3370         GtkCMCTreeNode *parent, *child;
3371         GtkCMCTreeRow *currRow;
3372         currRow = GTK_CMCTREE_ROW( node );
3373         if( currRow ) {
3374                 parent = currRow->parent;
3375                 while( (child = currRow->children) ) {
3376                         gtk_cmctree_move( ctree, child, parent, node );
3377                 }
3378                 gtk_sctree_sort_node( ctree, parent );
3379         }
3380 }
3381
3382 static void addressbook_edit_address_post_cb( ItemPerson *person )
3383 {
3384         if( person ) {
3385 #ifdef USE_LDAP
3386                 AddressBookFile *abf = addressbook_get_book_file();
3387
3388                 if (abf && abf->type == ADDR_IF_LDAP) {
3389                         if (strcmp2(person->nickName, ADDRITEM_NAME(person)))
3390                                 addritem_person_set_nick_name( person, ADDRITEM_NAME(person));
3391                 }
3392 #endif
3393                 addressbook_folder_refresh_one_person( GTK_CMCTREE(addrbook.clist), person );
3394                 invalidate_address_completion();
3395         }
3396         addressbook_address_list_set_focus();
3397 }
3398
3399 void addressbook_address_list_set_focus( void )
3400 {
3401         if (!prefs_common.addressbook_use_editaddress_dialog) {
3402                 gtk_window_set_focus(GTK_WINDOW(addrbook.window), addrbook.clist);
3403                 addressbook_list_menu_setup();
3404         }
3405 }
3406
3407 void addressbook_address_list_disable_some_actions(void)
3408 {
3409         /* disable address copy/pasting when editing contact's detail (embedded form) */
3410         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Cut",   FALSE );
3411         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Copy",  FALSE );
3412         cm_menu_set_sensitive_full( addrbook.ui_manager, "Menu/Address/Paste", FALSE );
3413 }
3414
3415 static void addressbook_edit_address_cb( GtkAction *action, gpointer data ) {
3416         addressbook_edit_address(data, 0, NULL, TRUE);
3417 }
3418         
3419 static void addressbook_edit_address( gpointer data, guint action, GtkWidget *widget,
3420                                                                           gboolean force_focus ) {
3421         GtkCMCTree *clist = GTK_CMCTREE(addrbook.clist);
3422         GtkCMCTree *ctree;
3423         AddressObject *obj = NULL, *pobj = NULL;
3424         AddressDataSource *ds = NULL;
3425         GtkCMCTreeNode *node = NULL, *parentNode = NULL;
3426         gchar *name = NULL;
3427         AddressBookFile *abf = NULL;
3428
3429         if( addrbook.listSelected == NULL ) return;
3430         obj = gtk_cmctree_node_get_row_data( clist, addrbook.listSelected );
3431         g_return_if_fail(obj != NULL);
3432
3433         ctree = GTK_CMCTREE( addrbook.ctree );
3434         pobj = gtk_cmctree_node_get_row_data( ctree, addrbook.treeSelected );
3435         node = gtk_cmctree_find_by_row_data( ctree, addrbook.treeSelected, obj );
3436
3437         ds = addressbook_find_datasource( GTK_CMCTREE_NODE(addrbook.treeSelected) );
3438         if( ds == NULL ) return;
3439
3440         abf = addressbook_get_book_file();
3441         
3442         if( obj->type == ADDR_ITEM_EMAIL ) {
3443                 ItemEMail *email = ( ItemEMail * ) obj;
3444                 if( email == NULL ) return;
3445                 if( pobj && pobj->type == ADDR_ITEM_GROUP ) {
3446                         /* Edit parent group */
3447                         AdapterGroup *adapter = ADAPTER_GROUP(pobj);
3448                         ItemGroup *itemGrp = adapter->itemGroup;
3449                         if( abf == NULL ) return;
3450                         if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
3451                         name = ADDRITEM_NAME(itemGrp);
3452                         node = addrbook.treeSelected;
3453                         parentNode = GTK_CMCTREE_ROW(node)->parent;
3454                 }
3455                 else {
3456                         /* Edit person - email page */
3457                         ItemPerson *person;
3458                         person = ( ItemPerson * ) ADDRITEM_PARENT(email);
3459                         if  ( addressbook_edit_person( abf, NULL, person, TRUE, addrbook.editaddress_vbox,
3460                                                                                    addressbook_edit_address_post_cb,
3461                                                                                    (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
3462                                   != NULL ) { 
3463 #ifdef USE_LDAP
3464                                 if (abf && abf->type == ADDR_IF_LDAP) {
3465                                         ldapsvr_set_modified( (LdapServer *) abf, TRUE );
3466                                         person->status = UPDATE_ENTRY;
3467                                 }
3468 #endif
3469                                 if (prefs_common.addressbook_use_editaddress_dialog)
3470                                         addressbook_edit_address_post_cb( person );
3471                         }
3472                         return;
3473                 }
3474         }
3475         else if( obj->type == ADDR_ITEM_PERSON ) {
3476                 /* Edit person - basic page */
3477                 ItemPerson *person = ( ItemPerson * ) obj;
3478                 if( addressbook_edit_person( abf, NULL, person, FALSE, addrbook.editaddress_vbox,
3479                                                                           addressbook_edit_address_post_cb,
3480                                                                           (prefs_common.addressbook_use_editaddress_dialog||force_focus) )
3481                         != NULL ) {
3482 #ifdef USE_LDAP
3483                                 if (abf && abf->type == ADDR_IF_LDAP) {
3484                                         ldapsvr_set_modified( (LdapServer *) abf, TRUE );
3485                                         person->status = UPDATE_ENTRY;
3486                                 }
3487 #endif
3488                                 if (prefs_common.addressbook_use_editaddress_dialog)
3489                                         addressbook_edit_address_post_cb( person );
3490                 }
3491                 return;
3492         }
3493         else if( obj->type == ADDR_ITEM_GROUP ) {
3494                 ItemGroup *itemGrp = ( ItemGroup * ) obj;
3495                 if( addressbook_edit_group( abf, NULL, itemGrp ) == NULL ) return;
3496                 parentNode = addrbook.treeSelected;
3497                 node = addressbook_find_group_node( parentNode, itemGrp );
3498                 name = ADDRITEM_NAME(itemGrp);
3499                 invalidate_address_completion();
3500         }
3501         else {
3502                 return;
3503         }
3504
3505         /* Update tree node with node name */
3506         if( node == NULL ) return;
3507         addressbook_change_node_name( node, name );
3508         gtk_sctree_sort_node( ctree, parentNode );
3509         gtk_sctree_select( GTK_SCTREE(ctree), addrbook.opened ); 
3510         addressbook_set_clist(
3511                 gtk_cmctree_node_get_row_data(GTK_CMCTREE(addrbook.ctree),
3512                         addrbook.opened),
3513                 TRUE);
3514 }
3515
3516 static void addressbook_delete_address_cb(GtkAction *action, gpointer data)
3517 {
3518         addressbook_del_clicked(NULL, NULL);
3519 }
3520
3521 static void close_cb(GtkAction *action, gpointer data)
3522 {
3523         addressbook_close();
3524 }
3525
3526 static void addressbook_file_save_cb( GtkAction *action, gpointer data ) {
3527         addressbook_export_to_file();
3528 }
3529
3530 static void addressbook_person_expand_node( GtkCMCTree *ctree, GList *node, gpointer *data ) {
3531         if( node ) {
3532                 ItemPerson *person = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(node) );
3533                 if( person ) addritem_person_set_opened( person, TRUE );
3534         }
3535 }
3536
3537 static void addressbook_person_collapse_node( GtkCMCTree *ctree, GList *node, gpointer *data ) {
3538         if( node ) {
3539                 ItemPerson *person = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(node) );
3540                 if( person ) addritem_person_set_opened( person, FALSE );
3541         }
3542 }
3543
3544 static gchar *addressbook_format_item_clist( ItemPerson *person, ItemEMail *email ) {
3545         gchar *str = NULL;
3546         gchar *eMailAlias = ADDRITEM_NAME(email);
3547         if( eMailAlias && *eMailAlias != '\0' ) {
3548                 if( person ) {
3549                         str = g_strdup_printf( "%s - %s", ADDRITEM_NAME(person), eMailAlias );
3550                 }
3551                 else {
3552                         str = g_strdup( eMailAlias );
3553                 }
3554         }
3555         return str;
3556 }
3557
3558 static void addressbook_load_group( GtkCMCTree *clist, ItemGroup *itemGroup ) {
3559         GList *items = itemGroup->listEMail;
3560         AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3561         for( ; items != NULL; items = g_list_next( items ) ) {
3562                 GtkCMCTreeNode *nodeEMail = NULL;
3563                 gchar *text[N_LIST_COLS];
3564                 ItemEMail *email = items->data;
3565                 ItemPerson *person;
3566                 gchar *str = NULL;
3567
3568                 if( ! email ) continue;
3569
3570                 person = ( ItemPerson * ) ADDRITEM_PARENT(email);
3571                 str = addressbook_format_item_clist( person, email );
3572                 if( str ) {
3573                         text[COL_NAME] = addressbook_set_col_name_guard(str);
3574                 }
3575                 else {
3576                         text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3577                 }
3578                 text[COL_ADDRESS] = email->address;
3579                 text[COL_REMARKS] = email->remarks;
3580                 nodeEMail = gtk_sctree_insert_node(
3581                                 clist, NULL, NULL,
3582                                 text, FOLDER_SPACING,
3583                                 atci->iconXpm, atci->maskXpm,
3584                                 atci->iconXpmOpen, atci->maskXpmOpen,
3585                                 FALSE, FALSE );
3586                 gtk_cmctree_node_set_row_data( clist, nodeEMail, email );
3587                 g_free( str );
3588                 str = NULL;
3589         }
3590 }
3591
3592 gchar *addressbook_set_col_name_guard(gchar *value)
3593 {
3594         gchar *ret = "<not set>";
3595         gchar *tmp = g_strdup(value);
3596         g_strstrip(tmp);
3597         if (tmp !=NULL && *tmp != '\0')
3598                 ret = value;
3599         g_free(tmp);
3600         return ret;
3601 }
3602
3603 static void addressbook_folder_load_one_person(
3604                 GtkCMCTree *clist, ItemPerson *person,
3605                 AddressTypeControlItem *atci,
3606                 AddressTypeControlItem *atciMail )
3607 {
3608         GtkCMCTreeNode *nodePerson = NULL;
3609         GtkCMCTreeNode *nodeEMail = NULL;
3610         gchar *text[N_LIST_COLS];
3611         gboolean flgFirst = TRUE, haveAddr = FALSE;
3612         GList *node;
3613 #ifdef USE_LDAP
3614         AddressBookFile *abf = addressbook_get_book_file();
3615 #endif
3616
3617         if( person == NULL ) return;
3618
3619         text[COL_NAME] = "";
3620         node = person->listEMail;
3621         while( node ) {
3622                 ItemEMail *email = node->data;
3623                 gchar *eMailAddr = NULL;
3624                 node = g_list_next( node );
3625
3626                 text[COL_ADDRESS] = email->address;
3627                 text[COL_REMARKS] = email->remarks;
3628                 eMailAddr = ADDRITEM_NAME(email);
3629                 if( eMailAddr && *eMailAddr == '\0' ) eMailAddr = NULL;
3630                 if( flgFirst ) {
3631                         /* First email belongs with person */
3632                         gchar *str = addressbook_format_item_clist( person, email );
3633                         if( str ) {
3634                                 text[COL_NAME] = addressbook_set_col_name_guard(str);
3635                         }
3636 #ifdef USE_LDAP
3637                         else if( abf && abf->type == ADDR_IF_LDAP && 
3638                                  person && person->nickName ) {
3639                                 if (person->nickName) {
3640                                         if (strcmp(person->nickName, "") != 0) {
3641                                                 text[COL_NAME] = addressbook_set_col_name_guard(person->nickName);
3642                                         }
3643                                         else {
3644                                                 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3645                                         }
3646                                 }
3647                         }
3648 #endif
3649                         else {
3650                                 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3651                         }
3652                         nodePerson = gtk_sctree_insert_node(
3653                                         clist, NULL, NULL,
3654                                         text, FOLDER_SPACING,
3655                                         atci->iconXpm, atci->maskXpm,
3656                                         atci->iconXpmOpen, atci->maskXpmOpen,
3657                                         FALSE, person->isOpened );
3658                         g_free( str );
3659                         str = NULL;
3660                         gtk_cmctree_node_set_row_data(clist, nodePerson, person );
3661                 }
3662                 else {
3663                         /* Subsequent email is a child node of person */
3664                         text[COL_NAME] = ADDRITEM_NAME(email);
3665                         nodeEMail = gtk_sctree_insert_node(
3666                                         clist, nodePerson, NULL,
3667                                         text, FOLDER_SPACING,
3668                                         atciMail->iconXpm, atciMail->maskXpm,
3669                                         atciMail->iconXpmOpen, atciMail->maskXpmOpen,
3670                                         FALSE, TRUE );
3671                         gtk_cmctree_node_set_row_data(clist, nodeEMail, email );
3672                 }
3673                 flgFirst = FALSE;
3674                 haveAddr = TRUE;
3675         }
3676         if( ! haveAddr ) {
3677                 /* Have name without EMail */
3678                 text[COL_NAME] = addressbook_set_col_name_guard(ADDRITEM_NAME(person));
3679                 text[COL_ADDRESS] = "";
3680                 text[COL_REMARKS] = "";
3681                 nodePerson = gtk_sctree_insert_node(
3682                                 clist, NULL, NULL,
3683                                 text, FOLDER_SPACING,
3684                                 atci->iconXpm, atci->maskXpm,
3685                                 atci->iconXpmOpen, atci->maskXpmOpen,
3686                                 FALSE, person->isOpened );
3687                 gtk_cmctree_node_set_row_data(clist, nodePerson, person );
3688         }
3689         return;
3690 }
3691
3692 static void addressbook_folder_load_person( GtkCMCTree *clist, ItemFolder *itemFolder ) {
3693         GList *items;
3694         AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
3695         AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3696
3697         if( atci == NULL ) return;
3698         if( atciMail == NULL ) return;
3699
3700         /* Load email addresses */
3701         items = addritem_folder_get_person_list( itemFolder );
3702         for( ; items != NULL; items = g_list_next( items ) ) {
3703                 addressbook_folder_load_one_person( clist, items->data, atci, atciMail );
3704         }
3705         /* Free up the list */
3706         mgu_clear_list( items );
3707         g_list_free( items );
3708 }
3709
3710 static void addressbook_folder_remove_node( GtkCMCTree *clist, GtkCMCTreeNode *node ) { 
3711         addrbook.listSelected = NULL;
3712         gtk_cmctree_remove_node( clist, node );
3713         addressbook_menubar_set_sensitive( FALSE );
3714         addressbook_menuitem_set_sensitive(
3715                 gtk_cmctree_node_get_row_data(
3716                         GTK_CMCTREE(clist), addrbook.treeSelected ),
3717                 addrbook.treeSelected );
3718 }
3719
3720 static void addressbook_folder_refresh_one_person( GtkCMCTree *clist, ItemPerson *person ) {
3721         AddressTypeControlItem *atci = addrbookctl_lookup( ADDR_ITEM_PERSON );
3722         AddressTypeControlItem *atciMail = addrbookctl_lookup( ADDR_ITEM_EMAIL );
3723         GtkCMCTreeNode *node;
3724         if( atci == NULL ) return;
3725         if( atciMail == NULL ) return;
3726         if( person == NULL ) return;
3727         /* unload the person */
3728         
3729         node = gtk_cmctree_find_by_row_data( clist, NULL, person );
3730         if( node )
3731                 addressbook_folder_remove_node( clist, node );