addrclip.c (20481B)
1 /* 2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client 3 * Copyright (C) 2002-2022 Match Grun and the Claws Mail team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 /* 20 * Contains address clipboard objects and related functions. The address 21 * clipboard is implemented as a linked list of AddrSelectItem objects. 22 * The address clipboard offers two groups of functions: 23 * 24 * a) Cut, copy and paste of address item objects (ItemFolder, ItemGroup, 25 * ItemPerson) into a folder. With this method, we can paste ItemPerson 26 * objects but not unattached ItemEMail objects into a folder. ItemEMail 27 * objects are owned by an ItemPerson object. Any ItemEMail objects that 28 * appear in the clipboard are ignored. If an ItemPerson object is found, 29 * the ItemPerson *and* ItemEMail objects that it owns are pasted. 30 * 31 * b) Copy and paste of ItemEMail address objects only into (below) 32 * ItemPerson objects. All ItemEMail objects which are owned by 33 * ItemPerson and referenced by ItemGroup objects are pasted. Any 34 * ItemFolder objects in the clipboard, and any objects owned by 35 * ItemFolder objects are ignored. 36 * 37 * Objects are inserted to the clipboard by copying (cloning) 38 * AddrSelectItem objects from the address books selection list to the 39 * clipboard's internal selection list. The clipboard makes use of the 40 * object id's and address cache id's to access objects contained in 41 * the address cache. If the referenced object is not found, it is 42 * ignored. This eliminates the need to delete pointers in multiple 43 * linked lists when an address object is deleted. 44 * 45 */ 46 47 #include <stdio.h> 48 #include <glib.h> 49 #include <glib/gi18n.h> 50 51 #include "addrcache.h" 52 #include "addrbook.h" 53 #include "addrselect.h" 54 #include "addrindex.h" 55 #include "addrclip.h" 56 #include "alertpanel.h" 57 #include "defs.h" 58 #include "file-utils.h" 59 60 /* 61 * Create a clipboard. 62 */ 63 AddressClipboard *addrclip_create( void ) { 64 AddressClipboard *clipBoard; 65 66 clipBoard = g_new0( AddressClipboard, 1 ); 67 clipBoard->cutFlag = FALSE; 68 clipBoard->objectList = NULL; 69 return clipBoard; 70 } 71 72 /* 73 * Clear clipboard. 74 */ 75 void addrclip_clear( AddressClipboard *clipBoard ) { 76 GList *node; 77 AddrSelectItem *item; 78 79 cm_return_if_fail( clipBoard != NULL ); 80 node = clipBoard->objectList; 81 while( node ) { 82 item = node->data; 83 addrselect_item_free( item ); 84 node->data = NULL; 85 node = g_list_next( node ); 86 } 87 g_list_free( clipBoard->objectList ); 88 clipBoard->objectList = NULL; 89 } 90 91 /* 92 * Free up a clipboard. 93 */ 94 void addrclip_free( AddressClipboard *clipBoard ) { 95 cm_return_if_fail( clipBoard != NULL ); 96 97 addrclip_clear( clipBoard ); 98 clipBoard->cutFlag = FALSE; 99 g_free(clipBoard); 100 } 101 102 /* 103 * Setup reference to address index. 104 */ 105 void addrclip_set_index( 106 AddressClipboard *clipBoard, AddressIndex *addrIndex ) 107 { 108 cm_return_if_fail( clipBoard != NULL ); 109 cm_return_if_fail( addrIndex != NULL ); 110 clipBoard->addressIndex = addrIndex; 111 } 112 113 /* 114 * Test whether clipboard is empty. 115 * Enter: clipBoard Clipboard. 116 * Return: TRUE if clipboard is empty. 117 */ 118 gboolean addrclip_is_empty( AddressClipboard *clipBoard ) { 119 gboolean retVal = TRUE; 120 121 if( clipBoard ) { 122 if( clipBoard->objectList ) retVal = FALSE; 123 } 124 return retVal; 125 } 126 127 /* 128 * Add a list of address selection objects to clipbard. 129 * Enter: clipBoard Clipboard. 130 * addrList List of address selection objects. 131 */ 132 void addrclip_add( AddressClipboard *clipBoard, AddrSelectList *asl ) { 133 GList *node; 134 135 cm_return_if_fail( clipBoard != NULL ); 136 cm_return_if_fail( asl != NULL ); 137 node = asl->listSelect; 138 while( node ) { 139 AddrSelectItem *item, *itemCopy; 140 141 item = node->data; 142 itemCopy = addrselect_item_copy( item ); 143 clipBoard->objectList = 144 g_list_append( clipBoard->objectList, itemCopy ); 145 node = g_list_next( node ); 146 } 147 } 148 149 #ifdef DEBUG_ADDRBOOK 150 /* 151 * Show clipboard contents. 152 * Enter: clipBoard Clipboard. 153 * stream Output stream. 154 */ 155 void addrclip_list_show( AddressClipboard *clipBoard, FILE *stream ) { 156 GList *node; 157 AddrItemObject *aio; 158 AddressCache *cache; 159 160 cm_return_if_fail( clipBoard != NULL ); 161 node = clipBoard->objectList; 162 while( node != NULL ) { 163 AddrSelectItem *item; 164 165 item = node->data; 166 addrselect_item_print( item, stream ); 167 168 cache = addrindex_get_cache( clipBoard->addressIndex, item->cacheID ); 169 aio = addrcache_get_object( cache, item->uid ); 170 if( aio ) { 171 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) { 172 addritem_print_item_person( ( ItemPerson * ) aio, stream ); 173 } 174 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) { 175 addritem_print_item_email( ( ItemEMail * ) aio, stream ); 176 } 177 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) { 178 addritem_print_item_group( ( ItemGroup * ) aio, stream ); 179 } 180 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) { 181 addritem_print_item_folder( ( ItemFolder * ) aio, stream ); 182 } 183 } 184 node = g_list_next( node ); 185 } 186 } 187 #endif 188 189 /* Pasted address pointers */ 190 typedef struct _AddrClip_EMail_ AddrClip_EMail; 191 struct _AddrClip_EMail_ { 192 ItemEMail *original; 193 ItemEMail *copy; 194 }; 195 196 /* 197 * Free up specified list of addresses. 198 */ 199 static void addrclip_free_copy_list( GList *copyList ) { 200 GList *node; 201 202 node = copyList; 203 while( node ) { 204 AddrClip_EMail *em = node->data; 205 em->original = NULL; 206 em->copy = NULL; 207 g_free( em ); 208 em = NULL; 209 node = g_list_next( node ); 210 } 211 } 212 213 /* 214 * Paste person into cache. 215 * Enter: cache Address cache to paste into. 216 * folder Folder to store 217 * person Person to paste. 218 * copyLIst List of email addresses pasted. 219 * Return: Update list of email addresses pasted. 220 */ 221 static GList *addrclip_cache_add_person( 222 AddressCache *cache, ItemFolder *folder, ItemPerson *person, 223 GList *copyList ) 224 { 225 ItemPerson *newPerson; 226 ItemEMail *email; 227 ItemEMail *newEMail; 228 UserAttribute *attrib; 229 UserAttribute *newAttrib; 230 GList *node; 231 AddrClip_EMail *em; 232 233 /* Copy person */ 234 newPerson = addritem_copy_item_person( person ); 235 addrcache_id_person( cache, newPerson ); 236 addrcache_folder_add_person( cache, folder, newPerson ); 237 238 /* Copy email addresses */ 239 node = person->listEMail; 240 while( node ) { 241 email = node->data; 242 newEMail = addritem_copy_item_email( email ); 243 addrcache_id_email( cache, newEMail ); 244 addrcache_person_add_email( cache, newPerson, newEMail ); 245 node = g_list_next( node ); 246 247 /* Take a copy of the original */ 248 em = g_new0( AddrClip_EMail, 1 ); 249 em->original = email; 250 em->copy = newEMail; 251 copyList = g_list_append( copyList, em ); 252 } 253 254 /* Copy user attributes */ 255 node = person->listAttrib; 256 while( node ) { 257 attrib = node->data; 258 newAttrib = addritem_copy_attribute( attrib ); 259 addrcache_id_attribute( cache, newAttrib ); 260 addritem_person_add_attribute( newPerson, newAttrib ); 261 node = g_list_next( node ); 262 } 263 264 /* Set picture name and create picture file (from copy) if missing */ 265 addritem_person_set_picture(newPerson, ADDRITEM_ID(newPerson)); 266 if( strcmp(ADDRITEM_ID(newPerson), ADDRITEM_ID(person)) ) { 267 gchar *pictureFile; 268 gchar *newPictureFile; 269 270 pictureFile = g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S, ADDRBOOK_DIR, G_DIR_SEPARATOR_S, 271 person->picture, ".png", NULL ); 272 newPictureFile = g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S, ADDRBOOK_DIR, G_DIR_SEPARATOR_S, 273 newPerson->picture, ".png", NULL ); 274 if (file_exist(pictureFile, FALSE) && !file_exist(newPictureFile, FALSE)) { 275 debug_print("copying contact picture file: %s -> %s\n", person->picture, newPerson->picture); 276 copy_file(pictureFile, newPictureFile, FALSE); 277 } 278 g_free( pictureFile ); 279 g_free( newPictureFile ); 280 } 281 282 return copyList; 283 } 284 285 /* 286 * Search for new email record in copied email list. 287 * Enter: copyList List of copied email address mappings. 288 * emailOrig Original email item. 289 * Return: New email item corresponding to original item if pasted. Or NULL if 290 * not found. 291 */ 292 static ItemEMail *addrclip_find_copied_email( 293 GList *copyList, ItemEMail *emailOrig ) 294 { 295 ItemEMail *emailCopy; 296 GList *node; 297 AddrClip_EMail *em; 298 299 emailCopy = NULL; 300 node = copyList; 301 while( node ) { 302 em = node->data; 303 if( em->original == emailOrig ) { 304 emailCopy = em->copy; 305 break; 306 } 307 node = g_list_next( node ); 308 } 309 return emailCopy; 310 } 311 312 /* 313 * Paste group into cache. 314 * Enter: cache Address cache to paste into. 315 * folder Folder to store 316 * group Group to paste. 317 * copyList List of email addresses pasted. 318 * Return: Group added. 319 */ 320 static ItemGroup *addrclip_cache_add_group( 321 AddressCache *cache, ItemFolder *folder, ItemGroup *group, 322 GList *copyList ) 323 { 324 ItemGroup *newGroup; 325 ItemEMail *emailOrig, *emailCopy; 326 GList *node; 327 328 /* Copy group */ 329 newGroup = addritem_copy_item_group( group ); 330 addrcache_id_group( cache, newGroup ); 331 addrcache_folder_add_group( cache, folder, newGroup ); 332 333 /* Add references of copied addresses to group */ 334 node = group->listEMail; 335 while( node ) { 336 emailOrig = ( ItemEMail * ) node->data; 337 emailCopy = addrclip_find_copied_email( copyList, emailOrig ); 338 if( emailCopy ) { 339 addrcache_group_add_email( cache, newGroup, emailCopy ); 340 } 341 node = g_list_next( node ); 342 } 343 return newGroup; 344 } 345 346 /* 347 * Copy specified folder into cache. Note this functions uses pointers to 348 * folders to copy from. There should not be any deleted items referenced 349 * by these pointers!!! 350 * Enter: cache Address cache to copy into. 351 * targetFolder Target folder. 352 * folder Folder to copy. 353 * Return: Folder added. 354 */ 355 static ItemFolder *addrclip_cache_copy_folder( 356 AddressCache *cache, ItemFolder *targetFolder, ItemFolder *folder ) 357 { 358 ItemFolder *newFolder; 359 ItemGroup *newGroup; 360 GList *node; 361 GList *copyList; 362 363 /* Copy folder */ 364 newFolder = addritem_copy_item_folder( folder ); 365 addrcache_id_folder( cache, newFolder ); 366 addrcache_folder_add_folder( cache, targetFolder, newFolder ); 367 368 /* Copy people to new folder */ 369 copyList = NULL; 370 node = folder->listPerson; 371 while( node ) { 372 ItemPerson *item = node->data; 373 node = g_list_next( node ); 374 copyList = addrclip_cache_add_person( 375 cache, newFolder, item, copyList ); 376 } 377 378 /* Copy groups to new folder */ 379 node = folder->listGroup; 380 while( node ) { 381 ItemGroup *item = node->data; 382 node = g_list_next( node ); 383 newGroup = addrclip_cache_add_group( 384 cache, newFolder, item, copyList ); 385 if (newGroup == NULL) { 386 g_message("error allocating memory for new group\n"); 387 } 388 } 389 g_list_free( copyList ); 390 391 /* Copy folders to new folder (recursive) */ 392 node = folder->listFolder; 393 while( node ) { 394 ItemFolder *item = node->data; 395 node = g_list_next( node ); 396 addrclip_cache_copy_folder( cache, newFolder, item ); 397 } 398 399 return newFolder; 400 } 401 402 static gboolean addrclip_is_subfolder_of(ItemFolder *is_parent, ItemFolder *is_child) 403 { 404 ItemFolder *folder; 405 AddrItemObject *obj; 406 407 cm_return_val_if_fail(is_parent != NULL, FALSE); 408 cm_return_val_if_fail(is_child != NULL, FALSE); 409 410 if (is_parent == is_child) 411 return TRUE; 412 413 folder = is_child; 414 obj = folder->obj.parent; 415 while (obj) { 416 if ((void*)obj == (void*)is_parent) 417 return TRUE; 418 obj = obj->parent; 419 } 420 return FALSE; 421 } 422 423 /* 424 * Paste item list into address book. 425 * Enter: cache Target address cache. 426 * folder Target folder where data is pasted. 427 * itemList List of items to paste. 428 * clipBoard Clipboard. 429 * Return: List of group or folder items added. 430 */ 431 static GList *addrclip_cache_add_folder( 432 AddressCache *cache, ItemFolder *folder, GList *itemList, 433 AddressClipboard *clipBoard ) 434 { 435 GList *folderGroup; 436 GList *node; 437 AddrSelectItem *item; 438 AddrItemObject *aio; 439 AddressCache *cacheFrom; 440 gboolean haveGroups; 441 GList *copyList; 442 443 folderGroup = NULL; 444 copyList = NULL; 445 haveGroups = FALSE; 446 node = itemList; 447 while( node ) { 448 item = node->data; 449 node = g_list_next( node ); 450 451 cacheFrom = addrindex_get_cache( 452 clipBoard->addressIndex, item->cacheID ); 453 if( cacheFrom == NULL ) continue; 454 if( item->uid ) { 455 aio = addrcache_get_object( cacheFrom, item->uid ); 456 if( aio ) { 457 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) { 458 ItemPerson *person; 459 460 person = ( ItemPerson * ) aio; 461 copyList = addrclip_cache_add_person( 462 cache, folder, person, copyList ); 463 } 464 /* 465 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) { 466 } 467 */ 468 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) { 469 haveGroups = TRUE; /* Process later */ 470 } 471 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) { 472 ItemFolder *itemFolder, *newFolder; 473 474 itemFolder = ( ItemFolder * ) aio; 475 if (!addrclip_is_subfolder_of(itemFolder, folder)) { 476 newFolder = addrclip_cache_copy_folder( 477 cache, folder, itemFolder ); 478 folderGroup = 479 g_list_append( folderGroup, newFolder ); 480 } else { 481 alertpanel_error( 482 _("Cannot copy a folder to itself or to its sub-structure.") ); 483 } 484 } 485 } 486 } 487 else { 488 if( item->objectType == ITEMTYPE_DATASOURCE ) { 489 /* 490 * Must be an address book - allow copy only if 491 * copying from a different cache. 492 */ 493 if( cache != cacheFrom ) { 494 ItemFolder *itemFolder, *newFolder; 495 496 itemFolder = cacheFrom->rootFolder; 497 newFolder = addrclip_cache_copy_folder( 498 cache, folder, itemFolder ); 499 addritem_folder_set_name( newFolder, 500 addrcache_get_name( cacheFrom ) ); 501 folderGroup = 502 g_list_append( folderGroup, newFolder ); 503 } else { 504 alertpanel_error( 505 _("Cannot copy an address book to itself.") ); 506 } 507 } 508 } 509 } 510 511 /* Finally add any groups */ 512 if( haveGroups ) { 513 node = itemList; 514 while( node ) { 515 item = node->data; 516 node = g_list_next( node ); 517 cacheFrom = addrindex_get_cache( 518 clipBoard->addressIndex, item->cacheID ); 519 if( cacheFrom == NULL ) continue; 520 aio = addrcache_get_object( cacheFrom, item->uid ); 521 if( aio ) { 522 if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) { 523 ItemGroup *group, *newGroup; 524 525 group = ( ItemGroup * ) aio; 526 newGroup = addrclip_cache_add_group( 527 cache, folder, group, copyList ); 528 folderGroup = 529 g_list_append( folderGroup, newGroup ); 530 } 531 } 532 } 533 } 534 535 /* Free up stuff */ 536 addrclip_free_copy_list( copyList ); 537 g_list_free( copyList ); 538 copyList = NULL; 539 540 return folderGroup; 541 } 542 543 /* 544 * Move items in list into new folder 545 * Enter: cache Target address cache. 546 * targetFolder Target folder where data is pasted. 547 * itemList List of items to paste. 548 * clipBoard Clipboard. 549 * Return: List of group or folder items added. 550 */ 551 static GList *addrclip_cache_move_items( 552 AddressCache *cache, ItemFolder *targetFolder, GList *itemList, 553 AddressClipboard *clipBoard ) 554 { 555 GList *folderGroup; 556 GList *node; 557 AddrSelectItem *item; 558 AddrItemObject *aio; 559 AddressCache *cacheFrom; 560 561 folderGroup = NULL; 562 node = itemList; 563 while( node ) { 564 item = node->data; 565 node = g_list_next( node ); 566 cacheFrom = addrindex_get_cache( 567 clipBoard->addressIndex, item->cacheID ); 568 if( cacheFrom == NULL ) continue; 569 aio = addrcache_get_object( cacheFrom, item->uid ); 570 if( aio ) { 571 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) { 572 ItemPerson *person; 573 574 person = ( ItemPerson * ) aio; 575 addrcache_folder_move_person( 576 cache, person, targetFolder ); 577 } 578 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) { 579 ItemGroup *group; 580 581 group = ( ItemGroup * ) aio; 582 addrcache_folder_move_group( 583 cache, group, targetFolder ); 584 folderGroup = g_list_append( folderGroup, group ); 585 } 586 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) { 587 ItemFolder *folder = ( ItemFolder * ) aio; 588 589 if (!addrclip_is_subfolder_of(folder, targetFolder)) { 590 addrcache_folder_move_folder( 591 cache, folder, targetFolder ); 592 folderGroup = 593 g_list_append( folderGroup, folder ); 594 } else { 595 alertpanel_error( 596 _("Cannot move a folder to itself or to its sub-structure.") ); 597 } 598 } 599 } 600 } 601 return folderGroup; 602 } 603 604 /* 605 * Get address cache of first item in list. This assumes that all items in 606 * the clipboard are located in the same cache. 607 * Enter: clipBoard Clipboard. 608 * Return: List of group or folder items added. 609 */ 610 static AddressCache *addrclip_list_get_cache( AddressClipboard *clipBoard ) { 611 AddressCache *cache; 612 GList *itemList; 613 AddrSelectItem *item; 614 615 cache = NULL; 616 itemList = clipBoard->objectList; 617 if( itemList ) { 618 item = itemList->data; 619 cache = addrindex_get_cache( 620 clipBoard->addressIndex, item->cacheID ); 621 } 622 return cache; 623 } 624 625 /* 626 * Paste (copy) clipboard into address book. 627 * Enter: clipBoard Clipboard. 628 * book Target address book. 629 * folder Target folder where data is pasted, or null for root folder. 630 * Return: List of group or folder items added. 631 */ 632 GList *addrclip_paste_copy( 633 AddressClipboard *clipBoard, AddressBookFile *book, 634 ItemFolder *folder ) 635 { 636 AddressCache *cache; 637 GList *itemList; 638 GList *folderGroup; 639 640 cm_return_val_if_fail( clipBoard != NULL, NULL ); 641 642 cache = book->addressCache; 643 if( folder == NULL ) folder = cache->rootFolder; 644 645 folderGroup = NULL; 646 itemList = clipBoard->objectList; 647 folderGroup = addrclip_cache_add_folder( 648 cache, folder, itemList, clipBoard ); 649 650 return folderGroup; 651 } 652 653 /* 654 * Remove items that were cut from clipboard. 655 * Enter: clipBoard Clipboard. 656 */ 657 void addrclip_delete_item( AddressClipboard *clipBoard ) { 658 AddrSelectItem *item; 659 AddrItemObject *aio; 660 AddressCache *cacheFrom; 661 GList *node; 662 663 /* If cutting within current cache, no deletion is necessary */ 664 if( clipBoard->moveFlag ) return; 665 666 /* Remove groups */ 667 node = clipBoard->objectList; 668 while( node ) { 669 item = node->data; 670 node = g_list_next( node ); 671 cacheFrom = addrindex_get_cache( 672 clipBoard->addressIndex, item->cacheID ); 673 if( cacheFrom == NULL ) continue; 674 aio = addrcache_get_object( cacheFrom, item->uid ); 675 if( aio ) { 676 if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) { 677 ItemGroup *group; 678 679 group = ( ItemGroup * ) aio; 680 group = addrcache_remove_group( cacheFrom, group ); 681 if( group ) { 682 addritem_free_item_group( group ); 683 } 684 } 685 } 686 } 687 688 /* Remove persons and folders */ 689 node = clipBoard->objectList; 690 while( node ) { 691 item = node->data; 692 node = g_list_next( node ); 693 694 cacheFrom = addrindex_get_cache( 695 clipBoard->addressIndex, item->cacheID ); 696 if( cacheFrom == NULL ) continue; 697 698 aio = addrcache_get_object( cacheFrom, item->uid ); 699 if( aio ) { 700 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) { 701 ItemPerson *person; 702 703 person = ( ItemPerson * ) aio; 704 person = addrcache_remove_person( cacheFrom, person ); 705 if( person ) { 706 addritem_free_item_person( person ); 707 } 708 } 709 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) { 710 ItemFolder *itemFolder; 711 712 itemFolder = ( ItemFolder * ) aio; 713 itemFolder = addrcache_remove_folder_delete( 714 cacheFrom, itemFolder ); 715 addritem_free_item_folder( itemFolder ); 716 } 717 } 718 } 719 } 720 721 /* 722 * Paste (move) clipboard into address book. 723 * Enter: clipBoard Clipboard. 724 * book Target address book. 725 * folder Target folder where data is pasted, or null for root folder. 726 * Return: List of group or folder items added. 727 */ 728 GList *addrclip_paste_cut( 729 AddressClipboard *clipBoard, AddressBookFile *book, 730 ItemFolder *folder ) 731 { 732 AddressCache *cache, *cacheFrom; 733 GList *itemList; 734 GList *folderGroup; 735 736 cm_return_val_if_fail( clipBoard != NULL, NULL ); 737 738 cache = book->addressCache; 739 if( folder == NULL ) folder = cache->rootFolder; 740 741 folderGroup = NULL; 742 clipBoard->moveFlag = FALSE; 743 cacheFrom = addrclip_list_get_cache( clipBoard ); 744 if( cacheFrom && cacheFrom == cache ) { 745 /* Move items between folders in same book */ 746 itemList = clipBoard->objectList; 747 folderGroup = addrclip_cache_move_items( 748 cache, folder, itemList, clipBoard ); 749 clipBoard->moveFlag = TRUE; 750 } 751 else { 752 /* Move items across address books */ 753 itemList = clipBoard->objectList; 754 folderGroup = addrclip_cache_add_folder( 755 cache, folder, itemList, clipBoard ); 756 } 757 758 return folderGroup; 759 }