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