2010-12-19 [colin] 3.7.8cvs11
[claws.git] / src / addrclip.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2002-2009 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         }
383         g_list_free( copyList );
384
385         /* Copy folders to new folder (recursive) */
386         node = folder->listFolder;
387         while( node ) {
388                 ItemFolder *item = node->data;
389                 node = g_list_next( node );
390                 addrclip_cache_copy_folder( cache, newFolder, item );
391         }
392
393         return newFolder;
394 }
395
396 static gboolean addrclip_is_subfolder_of(ItemFolder *is_parent, ItemFolder *is_child)
397 {
398         ItemFolder *folder;
399         AddrItemObject *obj;
400
401         cm_return_val_if_fail(is_parent != NULL, FALSE);
402         cm_return_val_if_fail(is_child != NULL, FALSE);
403
404         if (is_parent == is_child)
405                 return TRUE;
406
407         folder = is_child;
408         obj = folder->obj.parent;
409         while (obj) {
410                 if ((void*)obj == (void*)is_parent)
411                         return TRUE;
412                 obj = obj->parent;
413         }
414         return FALSE;
415 }
416
417 /*
418 * Paste item list into address book.
419 * Enter: cache     Target address cache.
420 *        folder    Target folder where data is pasted.
421 *        itemList  List of items to paste.
422 *        clipBoard Clipboard.
423 * Return: List of group or folder items added.
424 */
425 static GList *addrclip_cache_add_folder(
426         AddressCache *cache, ItemFolder *folder, GList *itemList,
427         AddressClipboard *clipBoard )
428 {
429         GList *folderGroup;
430         GList *node;
431         AddrSelectItem *item;
432         AddrItemObject *aio;
433         AddressCache *cacheFrom;
434         gboolean haveGroups;
435         GList *copyList;
436
437         folderGroup = NULL;
438         copyList = NULL;
439         haveGroups = FALSE;
440         node = itemList;
441         while( node ) {
442                 item = node->data;
443                 node = g_list_next( node );
444
445                 cacheFrom = addrindex_get_cache(
446                                 clipBoard->addressIndex, item->cacheID );
447                 if( cacheFrom == NULL ) continue;
448                 if( item->uid ) {
449                         aio = addrcache_get_object( cacheFrom, item->uid );
450                         if( aio ) {
451                                 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
452                                         ItemPerson *person;
453
454                                         person = ( ItemPerson * ) aio;
455                                         copyList = addrclip_cache_add_person(
456                                                 cache, folder, person, copyList );
457                                 }
458                                 /*
459                                 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) {
460                                 } 
461                                 */
462                                 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
463                                         haveGroups = TRUE;      /* Process later */
464                                 }
465                                 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
466                                         ItemFolder *itemFolder, *newFolder;
467
468                                         itemFolder = ( ItemFolder * ) aio;
469                                         if (!addrclip_is_subfolder_of(itemFolder, folder)) {
470                                                 newFolder = addrclip_cache_copy_folder(
471                                                                 cache, folder, itemFolder );
472                                                 folderGroup =
473                                                         g_list_append( folderGroup, newFolder );
474                                         } else {
475                                                 alertpanel_error(
476                                                         _("Cannot copy a folder to itself or to its sub-structure.") );
477                                         }
478                                 }
479                         }
480                 }
481                 else {
482                         if( item->objectType == ITEMTYPE_DATASOURCE ) {
483                                 /*
484                                 * Must be an address book - allow copy only if
485                                 * copying from a different cache.
486                                 */
487                                 if( cache != cacheFrom ) {
488                                         ItemFolder *itemFolder, *newFolder;
489
490                                         itemFolder = cacheFrom->rootFolder;
491                                         newFolder = addrclip_cache_copy_folder(
492                                                 cache, folder, itemFolder );
493                                         addritem_folder_set_name( newFolder,
494                                                 addrcache_get_name( cacheFrom ) );
495                                         folderGroup =
496                                                 g_list_append( folderGroup, newFolder );
497                                 } else {
498                                         alertpanel_error(
499                                                 _("Cannot copy an address book to itself.") );
500                                 }
501                         }
502                 }
503         }
504
505         /* Finally add any groups */
506         if( haveGroups ) {
507                 node = itemList;
508                 while( node ) {
509                         item = node->data;
510                         node = g_list_next( node );
511                         cacheFrom = addrindex_get_cache(
512                                         clipBoard->addressIndex, item->cacheID );
513                         if( cacheFrom == NULL ) continue;
514                         aio = addrcache_get_object( cacheFrom, item->uid );
515                         if( aio ) {
516                                 if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
517                                         ItemGroup *group, *newGroup;
518
519                                         group = ( ItemGroup * ) aio;
520                                         newGroup = addrclip_cache_add_group(
521                                                 cache, folder, group, copyList );
522                                         folderGroup =
523                                                 g_list_append( folderGroup, newGroup );
524                                 }
525                         }
526                 }
527         }
528
529         /* Free up stuff */
530         addrclip_free_copy_list( copyList );
531         g_list_free( copyList );
532         copyList = NULL;
533
534         return folderGroup;
535 }
536
537 /*
538 * Move items in list into new folder
539 * Enter: cache        Target address cache.
540 *        targetFolder Target folder where data is pasted.
541 *        itemList     List of items to paste.
542 *        clipBoard    Clipboard.
543 * Return: List of group or folder items added.
544 */
545 static GList *addrclip_cache_move_items(
546         AddressCache *cache, ItemFolder *targetFolder, GList *itemList,
547         AddressClipboard *clipBoard )
548 {
549         GList *folderGroup;
550         GList *node;
551         AddrSelectItem *item;
552         AddrItemObject *aio;
553         AddressCache *cacheFrom;
554
555         folderGroup = NULL;
556         node = itemList;
557         while( node ) {
558                 item = node->data;
559                 node = g_list_next( node );
560                 cacheFrom = addrindex_get_cache(
561                                 clipBoard->addressIndex, item->cacheID );
562                 if( cacheFrom == NULL ) continue;
563                 aio = addrcache_get_object( cacheFrom, item->uid );
564                 if( aio ) {
565                         if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
566                                 ItemPerson *person;
567
568                                 person = ( ItemPerson * ) aio;
569                                 addrcache_folder_move_person(
570                                         cache, person, targetFolder );
571                         }
572                         else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
573                                 ItemGroup *group;
574
575                                 group = ( ItemGroup * ) aio;
576                                 addrcache_folder_move_group(
577                                         cache, group, targetFolder );
578                                 folderGroup = g_list_append( folderGroup, group );
579                         }
580                         else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
581                                 ItemFolder *folder = ( ItemFolder * ) aio;
582
583                                 if (!addrclip_is_subfolder_of(folder, targetFolder)) {
584                                         addrcache_folder_move_folder(
585                                                 cache, folder, targetFolder );
586                                         folderGroup =
587                                                 g_list_append( folderGroup, folder );
588                                 } else {
589                                         alertpanel_error(
590                                                 _("Cannot move a folder to itself or to its sub-structure.") );
591                                 }
592                         }
593                 }
594         }
595         return folderGroup;
596 }
597
598 /*
599 * Get address cache of first item in list. This assumes that all items in
600 * the clipboard are located in the same cache.
601 * Enter: clipBoard Clipboard.
602 * Return: List of group or folder items added.
603 */
604 static AddressCache *addrclip_list_get_cache( AddressClipboard *clipBoard ) {
605         AddressCache *cache;
606         GList *itemList;
607         AddrSelectItem *item;
608
609         cache = NULL;
610         itemList = clipBoard->objectList;
611         if( itemList ) {
612                 item = itemList->data;
613                 cache = addrindex_get_cache(
614                                 clipBoard->addressIndex, item->cacheID );
615         }
616         return cache;
617 }
618
619 /*
620 * Paste (copy) clipboard into address book.
621 * Enter: clipBoard Clipboard.
622 *        book      Target address book.
623 *        folder    Target folder where data is pasted, or null for root folder.
624 * Return: List of group or folder items added.
625 */
626 GList *addrclip_paste_copy(
627         AddressClipboard *clipBoard, AddressBookFile *book,
628         ItemFolder *folder )
629 {
630         AddressCache *cache;
631         GList *itemList;
632         GList *folderGroup;
633
634         cm_return_val_if_fail( clipBoard != NULL, NULL );
635
636         cache = book->addressCache;
637         if( folder == NULL ) folder = cache->rootFolder;
638
639         folderGroup = NULL;
640         itemList = clipBoard->objectList;
641         folderGroup = addrclip_cache_add_folder(
642                         cache, folder, itemList, clipBoard );
643
644         return folderGroup;
645 }
646
647 /*
648 * Remove items that were cut from clipboard.
649 * Enter: clipBoard Clipboard.
650 */
651 void addrclip_delete_item( AddressClipboard *clipBoard ) {
652         AddrSelectItem *item;
653         AddrItemObject *aio;
654         AddressCache *cacheFrom;
655         GList *node;
656
657         /* If cutting within current cache, no deletion is necessary */
658         if( clipBoard->moveFlag ) return;
659
660         /* Remove groups */
661         node = clipBoard->objectList;
662         while( node ) {
663                 item = node->data;
664                 node = g_list_next( node );
665                 cacheFrom = addrindex_get_cache(
666                                 clipBoard->addressIndex, item->cacheID );
667                 if( cacheFrom == NULL ) continue;
668                 aio = addrcache_get_object( cacheFrom, item->uid );
669                 if( aio ) {
670                         if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
671                                 ItemGroup *group;
672
673                                 group = ( ItemGroup * ) aio;
674                                 group = addrcache_remove_group( cacheFrom, group );
675                                 if( group ) {
676                                         addritem_free_item_group( group );
677                                 }
678                         }
679                 }
680         }
681
682         /* Remove persons and folders */
683         node = clipBoard->objectList;
684         while( node ) {
685                 item = node->data;
686                 node = g_list_next( node );
687
688                 cacheFrom = addrindex_get_cache(
689                                 clipBoard->addressIndex, item->cacheID );
690                 if( cacheFrom == NULL ) continue;
691
692                 aio = addrcache_get_object( cacheFrom, item->uid );
693                 if( aio ) {
694                         if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
695                                 ItemPerson *person;
696
697                                 person = ( ItemPerson * ) aio;
698                                 person = addrcache_remove_person( cacheFrom, person );
699                                 if( person ) {
700                                         addritem_free_item_person( person );
701                                 }
702                         }
703                         else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
704                                 ItemFolder *itemFolder;
705
706                                 itemFolder = ( ItemFolder * ) aio;
707                                 itemFolder = addrcache_remove_folder_delete(
708                                                 cacheFrom, itemFolder );
709                                 addritem_free_item_folder( itemFolder );
710                         }
711                 }
712         }
713 }
714
715 /*
716 * Paste (move) clipboard into address book.
717 * Enter: clipBoard Clipboard.
718 *        book      Target address book.
719 *        folder    Target folder where data is pasted, or null for root folder.
720 * Return: List of group or folder items added.
721 */
722 GList *addrclip_paste_cut(
723         AddressClipboard *clipBoard, AddressBookFile *book,
724         ItemFolder *folder )
725 {
726         AddressCache *cache, *cacheFrom;
727         GList *itemList;
728         GList *folderGroup;
729
730         cm_return_val_if_fail( clipBoard != NULL, NULL );
731
732         cache = book->addressCache;
733         if( folder == NULL ) folder = cache->rootFolder;
734
735         folderGroup = NULL;
736         clipBoard->moveFlag = FALSE;
737         cacheFrom = addrclip_list_get_cache( clipBoard );
738         if( cacheFrom && cacheFrom == cache ) {
739                 /* Move items between folders in same book */
740                 itemList = clipBoard->objectList;
741                 folderGroup = addrclip_cache_move_items(
742                                 cache, folder, itemList, clipBoard );
743                 clipBoard->moveFlag = TRUE;
744         }
745         else {
746                 /* Move items across address books */
747                 itemList = clipBoard->objectList;
748                 folderGroup = addrclip_cache_add_folder(
749                                 cache, folder, itemList, clipBoard );
750         }
751
752         return folderGroup;
753 }
754 /*
755 * End of Source.
756 */
757