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