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