talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

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 }