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