2007-08-27 [colin] 2.10.0cvs179
[claws.git] / src / addrclip.c
blobb7ca5756160c28237d7e0f635e8c827c22e43711
1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2002-2007 Match Grun and the Claws Mail team
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.
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.
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/>.
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:
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.
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.
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.
48 #include <stdio.h>
49 #include <glib.h>
51 #include "addrcache.h"
52 #include "addrbook.h"
53 #include "addrselect.h"
54 #include "addrindex.h"
55 #include "addrclip.h"
58 * Create a clipboard.
60 AddressClipboard *addrclip_create( void ) {
61 AddressClipboard *clipBoard;
63 clipBoard = g_new0( AddressClipboard, 1 );
64 clipBoard->cutFlag = FALSE;
65 clipBoard->objectList = NULL;
66 return clipBoard;
70 * Clear clipboard.
72 void addrclip_clear( AddressClipboard *clipBoard ) {
73 GList *node;
74 AddrSelectItem *item;
76 g_return_if_fail( clipBoard != NULL );
77 node = clipBoard->objectList;
78 while( node ) {
79 item = node->data;
80 addrselect_item_free( item );
81 node->data = NULL;
82 node = g_list_next( node );
84 g_list_free( clipBoard->objectList );
85 clipBoard->objectList = NULL;
89 * Free up a clipboard.
91 void addrclip_free( AddressClipboard *clipBoard ) {
92 g_return_if_fail( clipBoard != NULL );
94 addrclip_clear( clipBoard );
95 clipBoard->cutFlag = FALSE;
99 * Setup reference to address index.
101 void addrclip_set_index(
102 AddressClipboard *clipBoard, AddressIndex *addrIndex )
104 g_return_if_fail( clipBoard != NULL );
105 g_return_if_fail( addrIndex != NULL );
106 clipBoard->addressIndex = addrIndex;
110 * Test whether clipboard is empty.
111 * Enter: clipBoard Clipboard.
112 * Return: TRUE if clipboard is empty.
114 gboolean addrclip_is_empty( AddressClipboard *clipBoard ) {
115 gboolean retVal = TRUE;
117 if( clipBoard ) {
118 if( clipBoard->objectList ) retVal = FALSE;
120 return retVal;
124 * Add a list of address selection objects to clipbard.
125 * Enter: clipBoard Clipboard.
126 * addrList List of address selection objects.
128 void addrclip_add( AddressClipboard *clipBoard, AddrSelectList *asl ) {
129 GList *node;
131 g_return_if_fail( clipBoard != NULL );
132 g_return_if_fail( asl != NULL );
133 node = asl->listSelect;
134 while( node ) {
135 AddrSelectItem *item, *itemCopy;
137 item = node->data;
138 itemCopy = addrselect_item_copy( item );
139 clipBoard->objectList =
140 g_list_append( clipBoard->objectList, itemCopy );
141 node = g_list_next( node );
146 * Show clipboard contents.
147 * Enter: clipBoard Clipboard.
148 * stream Output stream.
150 void addrclip_list_show( AddressClipboard *clipBoard, FILE *stream ) {
151 GList *node;
152 AddrItemObject *aio;
153 AddressCache *cache;
155 g_return_if_fail( clipBoard != NULL );
156 node = clipBoard->objectList;
157 while( node != NULL ) {
158 AddrSelectItem *item;
160 item = node->data;
161 addrselect_item_print( item, stream );
163 cache = addrindex_get_cache( clipBoard->addressIndex, item->cacheID );
164 aio = addrcache_get_object( cache, item->uid );
165 if( aio ) {
166 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
167 addritem_print_item_person( ( ItemPerson * ) aio, stream );
169 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) {
170 addritem_print_item_email( ( ItemEMail * ) aio, stream );
172 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
173 addritem_print_item_group( ( ItemGroup * ) aio, stream );
175 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
176 addritem_print_item_folder( ( ItemFolder * ) aio, stream );
179 node = g_list_next( node );
183 /* Pasted address pointers */
184 typedef struct _AddrClip_EMail_ AddrClip_EMail;
185 struct _AddrClip_EMail_ {
186 ItemEMail *original;
187 ItemEMail *copy;
191 * Free up specified list of addresses.
193 static void addrclip_free_copy_list( GList *copyList ) {
194 GList *node;
196 node = copyList;
197 while( node ) {
198 AddrClip_EMail *em = node->data;
199 em->original = NULL;
200 em->copy = NULL;
201 g_free( em );
202 em = NULL;
203 node = g_list_next( node );
208 * Paste person into cache.
209 * Enter: cache Address cache to paste into.
210 * folder Folder to store
211 * person Person to paste.
212 * copyLIst List of email addresses pasted.
213 * Return: Update list of email addresses pasted.
215 static GList *addrclip_cache_add_person(
216 AddressCache *cache, ItemFolder *folder, ItemPerson *person,
217 GList *copyList )
219 ItemPerson *newPerson;
220 ItemEMail *email;
221 ItemEMail *newEMail;
222 UserAttribute *attrib;
223 UserAttribute *newAttrib;
224 GList *node;
225 AddrClip_EMail *em;
227 /* Copy person */
228 newPerson = addritem_copy_item_person( person );
229 addrcache_id_person( cache, newPerson );
230 addrcache_folder_add_person( cache, folder, newPerson );
232 /* Copy email addresses */
233 node = person->listEMail;
234 while( node ) {
235 email = node->data;
236 newEMail = addritem_copy_item_email( email );
237 addrcache_id_email( cache, newEMail );
238 addrcache_person_add_email( cache, newPerson, newEMail );
239 node = g_list_next( node );
241 /* Take a copy of the original */
242 em = g_new0( AddrClip_EMail, 1 );
243 em->original = email;
244 em->copy = newEMail;
245 copyList = g_list_append( copyList, em );
248 /* Copy user attributes */
249 node = person->listAttrib;
250 while( node ) {
251 attrib = node->data;
252 newAttrib = addritem_copy_attribute( attrib );
253 addrcache_id_attribute( cache, newAttrib );
254 addritem_person_add_attribute( newPerson, newAttrib );
255 node = g_list_next( node );
258 return copyList;
262 * Search for new email record in copied email list.
263 * Enter: copyList List of copied email address mappings.
264 * emailOrig Original email item.
265 * Return: New email item corresponding to original item if pasted. Or NULL if
266 * not found.
268 static ItemEMail *addrclip_find_copied_email(
269 GList *copyList, ItemEMail *emailOrig )
271 ItemEMail *emailCopy;
272 GList *node;
273 AddrClip_EMail *em;
275 emailCopy = NULL;
276 node = copyList;
277 while( node ) {
278 em = node->data;
279 if( em->original == emailOrig ) {
280 emailCopy = em->copy;
281 break;
283 node = g_list_next( node );
285 return emailCopy;
289 * Paste group into cache.
290 * Enter: cache Address cache to paste into.
291 * folder Folder to store
292 * group Group to paste.
293 * copyList List of email addresses pasted.
294 * Return: Group added.
296 static ItemGroup *addrclip_cache_add_group(
297 AddressCache *cache, ItemFolder *folder, ItemGroup *group,
298 GList *copyList )
300 ItemGroup *newGroup;
301 ItemEMail *emailOrig, *emailCopy;
302 GList *node;
304 /* Copy group */
305 newGroup = addritem_copy_item_group( group );
306 addrcache_id_group( cache, newGroup );
307 addrcache_folder_add_group( cache, folder, newGroup );
309 /* Add references of copied addresses to group */
310 node = group->listEMail;
311 while( node ) {
312 emailOrig = ( ItemEMail * ) node->data;
313 emailCopy = addrclip_find_copied_email( copyList, emailOrig );
314 if( emailCopy ) {
315 addrcache_group_add_email( cache, newGroup, emailCopy );
317 node = g_list_next( node );
319 return newGroup;
323 * Copy specified folder into cache. Note this functions uses pointers to
324 * folders to copy from. There should not be any deleted items referenced
325 * by these pointers!!!
326 * Enter: cache Address cache to copy into.
327 * targetFolder Target folder.
328 * folder Folder to copy.
329 * Return: Folder added.
331 static ItemFolder *addrclip_cache_copy_folder(
332 AddressCache *cache, ItemFolder *targetFolder, ItemFolder *folder )
334 ItemFolder *newFolder;
335 ItemGroup *newGroup;
336 GList *node;
337 GList *copyList;
339 /* Copy folder */
340 newFolder = addritem_copy_item_folder( folder );
341 addrcache_id_folder( cache, newFolder );
342 addrcache_folder_add_folder( cache, targetFolder, newFolder );
344 /* Copy people to new folder */
345 copyList = NULL;
346 node = folder->listPerson;
347 while( node ) {
348 ItemPerson *item = node->data;
349 node = g_list_next( node );
350 copyList = addrclip_cache_add_person(
351 cache, newFolder, item, copyList );
354 /* Copy groups to new folder */
355 node = folder->listGroup;
356 while( node ) {
357 ItemGroup *item = node->data;
358 node = g_list_next( node );
359 newGroup = addrclip_cache_add_group(
360 cache, newFolder, item, copyList );
362 g_list_free( copyList );
364 /* Copy folders to new folder (recursive) */
365 node = folder->listFolder;
366 while( node ) {
367 ItemFolder *item = node->data;
368 node = g_list_next( node );
369 addrclip_cache_copy_folder( cache, newFolder, item );
372 return newFolder;
376 * Paste item list into address book.
377 * Enter: cache Target address cache.
378 * folder Target folder where data is pasted.
379 * itemList List of items to paste.
380 * clipBoard Clipboard.
381 * Return: List of group or folder items added.
383 static GList *addrclip_cache_add_folder(
384 AddressCache *cache, ItemFolder *folder, GList *itemList,
385 AddressClipboard *clipBoard )
387 GList *folderGroup;
388 GList *node;
389 AddrSelectItem *item;
390 AddrItemObject *aio;
391 AddressCache *cacheFrom;
392 gboolean haveGroups;
393 GList *copyList;
395 folderGroup = NULL;
396 copyList = NULL;
397 haveGroups = FALSE;
398 node = itemList;
399 while( node ) {
400 item = node->data;
401 node = g_list_next( node );
403 cacheFrom = addrindex_get_cache(
404 clipBoard->addressIndex, item->cacheID );
405 if( cacheFrom == NULL ) continue;
406 if( item->uid ) {
407 aio = addrcache_get_object( cacheFrom, item->uid );
408 if( aio ) {
409 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
410 ItemPerson *person;
412 person = ( ItemPerson * ) aio;
413 copyList = addrclip_cache_add_person(
414 cache, folder, person, copyList );
417 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) {
420 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
421 haveGroups = TRUE; /* Process later */
423 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
424 ItemFolder *itemFolder, *newFolder;
426 itemFolder = ( ItemFolder * ) aio;
427 newFolder = addrclip_cache_copy_folder(
428 cache, folder, itemFolder );
429 folderGroup =
430 g_list_append( folderGroup, newFolder );
434 else {
435 if( item->objectType == ITEMTYPE_DATASOURCE ) {
437 * Must be an address book - allow copy only if
438 * copying from a different cache.
440 if( cache != cacheFrom ) {
441 ItemFolder *itemFolder, *newFolder;
443 itemFolder = cacheFrom->rootFolder;
444 newFolder = addrclip_cache_copy_folder(
445 cache, folder, itemFolder );
446 addritem_folder_set_name( newFolder,
447 addrcache_get_name( cacheFrom ) );
448 folderGroup =
449 g_list_append( folderGroup, newFolder );
455 /* Finally add any groups */
456 if( haveGroups ) {
457 node = itemList;
458 while( node ) {
459 item = node->data;
460 node = g_list_next( node );
461 cacheFrom = addrindex_get_cache(
462 clipBoard->addressIndex, item->cacheID );
463 if( cacheFrom == NULL ) continue;
464 aio = addrcache_get_object( cacheFrom, item->uid );
465 if( aio ) {
466 if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
467 ItemGroup *group, *newGroup;
469 group = ( ItemGroup * ) aio;
470 newGroup = addrclip_cache_add_group(
471 cache, folder, group, copyList );
472 folderGroup =
473 g_list_append( folderGroup, newGroup );
479 /* Free up stuff */
480 addrclip_free_copy_list( copyList );
481 g_list_free( copyList );
482 copyList = NULL;
484 return folderGroup;
488 * Move items in list into new folder
489 * Enter: cache Target address cache.
490 * targetFolder Target folder where data is pasted.
491 * itemList List of items to paste.
492 * clipBoard Clipboard.
493 * Return: List of group or folder items added.
495 static GList *addrclip_cache_move_items(
496 AddressCache *cache, ItemFolder *targetFolder, GList *itemList,
497 AddressClipboard *clipBoard )
499 GList *folderGroup;
500 GList *node;
501 AddrSelectItem *item;
502 AddrItemObject *aio;
503 AddressCache *cacheFrom;
505 folderGroup = NULL;
506 node = itemList;
507 while( node ) {
508 item = node->data;
509 node = g_list_next( node );
510 cacheFrom = addrindex_get_cache(
511 clipBoard->addressIndex, item->cacheID );
512 if( cacheFrom == NULL ) continue;
513 aio = addrcache_get_object( cacheFrom, item->uid );
514 if( aio ) {
515 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
516 ItemPerson *person;
518 person = ( ItemPerson * ) aio;
519 addrcache_folder_move_person(
520 cache, person, targetFolder );
522 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
523 ItemGroup *group;
525 group = ( ItemGroup * ) aio;
526 addrcache_folder_move_group(
527 cache, group, targetFolder );
528 folderGroup = g_list_append( folderGroup, group );
530 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
531 ItemFolder *folder;
533 folder = ( ItemFolder * ) aio;
534 addrcache_folder_move_folder(
535 cache, folder, targetFolder );
536 folderGroup =
537 g_list_append( folderGroup, folder );
541 return folderGroup;
545 * Get address cache of first item in list. This assumes that all items in
546 * the clipboard are located in the same cache.
547 * Enter: clipBoard Clipboard.
548 * Return: List of group or folder items added.
550 static AddressCache *addrclip_list_get_cache( AddressClipboard *clipBoard ) {
551 AddressCache *cache;
552 GList *itemList;
553 AddrSelectItem *item;
555 cache = NULL;
556 itemList = clipBoard->objectList;
557 if( itemList ) {
558 item = itemList->data;
559 cache = addrindex_get_cache(
560 clipBoard->addressIndex, item->cacheID );
562 return cache;
566 * Paste (copy) clipboard into address book.
567 * Enter: clipBoard Clipboard.
568 * book Target address book.
569 * folder Target folder where data is pasted, or null for root folder.
570 * Return: List of group or folder items added.
572 GList *addrclip_paste_copy(
573 AddressClipboard *clipBoard, AddressBookFile *book,
574 ItemFolder *folder )
576 AddressCache *cache;
577 GList *itemList;
578 GList *folderGroup;
580 g_return_val_if_fail( clipBoard != NULL, NULL );
582 cache = book->addressCache;
583 if( folder == NULL ) folder = cache->rootFolder;
585 folderGroup = NULL;
586 itemList = clipBoard->objectList;
587 folderGroup = addrclip_cache_add_folder(
588 cache, folder, itemList, clipBoard );
590 return folderGroup;
594 * Remove items that were cut from clipboard.
595 * Enter: clipBoard Clipboard.
597 void addrclip_delete_item( AddressClipboard *clipBoard ) {
598 AddrSelectItem *item;
599 AddrItemObject *aio;
600 AddressCache *cacheFrom;
601 GList *node;
603 /* If cutting within current cache, no deletion is necessary */
604 if( clipBoard->moveFlag ) return;
606 /* Remove groups */
607 node = clipBoard->objectList;
608 while( node ) {
609 item = node->data;
610 node = g_list_next( node );
611 cacheFrom = addrindex_get_cache(
612 clipBoard->addressIndex, item->cacheID );
613 if( cacheFrom == NULL ) continue;
614 aio = addrcache_get_object( cacheFrom, item->uid );
615 if( aio ) {
616 if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
617 ItemGroup *group;
619 group = ( ItemGroup * ) aio;
620 group = addrcache_remove_group( cacheFrom, group );
621 if( group ) {
622 addritem_free_item_group( group );
628 /* Remove persons and folders */
629 node = clipBoard->objectList;
630 while( node ) {
631 item = node->data;
632 node = g_list_next( node );
634 cacheFrom = addrindex_get_cache(
635 clipBoard->addressIndex, item->cacheID );
636 if( cacheFrom == NULL ) continue;
638 aio = addrcache_get_object( cacheFrom, item->uid );
639 if( aio ) {
640 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
641 ItemPerson *person;
643 person = ( ItemPerson * ) aio;
644 person = addrcache_remove_person( cacheFrom, person );
645 if( person ) {
646 addritem_free_item_person( person );
649 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
650 ItemFolder *itemFolder;
652 itemFolder = ( ItemFolder * ) aio;
653 itemFolder = addrcache_remove_folder_delete(
654 cacheFrom, itemFolder );
655 addritem_free_item_folder( itemFolder );
662 * Paste (move) clipboard into address book.
663 * Enter: clipBoard Clipboard.
664 * book Target address book.
665 * folder Target folder where data is pasted, or null for root folder.
666 * Return: List of group or folder items added.
668 GList *addrclip_paste_cut(
669 AddressClipboard *clipBoard, AddressBookFile *book,
670 ItemFolder *folder )
672 AddressCache *cache, *cacheFrom;
673 GList *itemList;
674 GList *folderGroup;
676 g_return_val_if_fail( clipBoard != NULL, NULL );
678 cache = book->addressCache;
679 if( folder == NULL ) folder = cache->rootFolder;
681 folderGroup = NULL;
682 clipBoard->moveFlag = FALSE;
683 cacheFrom = addrclip_list_get_cache( clipBoard );
684 if( cacheFrom && cacheFrom == cache ) {
685 /* Move items between folders in same book */
686 itemList = clipBoard->objectList;
687 folderGroup = addrclip_cache_move_items(
688 cache, folder, itemList, clipBoard );
689 clipBoard->moveFlag = TRUE;
691 else {
692 /* Move items across address books */
693 itemList = clipBoard->objectList;
694 folderGroup = addrclip_cache_add_folder(
695 cache, folder, itemList, clipBoard );
698 return folderGroup;
702 * ============================================================================
703 * Paste address only.
704 * ============================================================================
708 * Copy email addresses from specified list.
709 * Enter: cache Address cache to paste into.
710 * target Person to receive email addresses.
711 * listEMail List of email addresses.
712 * Return: Number of addresses added.
714 static gint addrclip_person_add_email(
715 AddressCache *cache, ItemPerson *target, GList *listEMail )
717 gint cnt;
718 GList *node;
720 /* Copy email addresses */
721 cnt = 0;
722 node = listEMail;
723 while( node ) {
724 ItemEMail *email, *newEMail;
726 email = node->data;
727 newEMail = addritem_copy_item_email( email );
728 addrcache_id_email( cache, newEMail );
729 addrcache_person_add_email( cache, target, newEMail );
730 node = g_list_next( node );
731 cnt++;
733 return cnt;
737 * Paste (copy) E-Mail addresses from clipboard into specified person.
738 * Enter: aio Address item to copy from.
739 * cache Target address cache.
740 * person Target person where data is pasted.
741 * Return: Number of EMail records added.
743 static gint addrclip_copy_email_to_person(
744 AddrItemObject *aio, AddressCache *cache, ItemPerson *person )
746 gint cnt;
747 GList *listEMail;
749 cnt = 0;
751 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
752 ItemPerson *fromPerson;
754 fromPerson = ( ItemPerson * ) aio;
755 listEMail = fromPerson->listEMail;
756 cnt += addrclip_person_add_email(
757 cache, person, listEMail );
759 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) {
760 ItemEMail *email, *newEMail;
762 email = ( ItemEMail * ) aio;
763 newEMail = addritem_copy_item_email( email );
764 addrcache_id_email( cache, newEMail );
765 addrcache_person_add_email( cache, person, newEMail );
766 cnt++;
768 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
769 ItemGroup *group;
771 group = ( ItemGroup * ) aio;
772 listEMail = group->listEMail;
773 cnt += addrclip_person_add_email(
774 cache, person, listEMail );
776 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
777 ItemFolder *folder;
778 AddrItemObject *item;
779 GList *node;
781 folder = ( ItemFolder * ) aio;
782 node = folder->listPerson;
783 while( node ) {
784 item = node->data;
785 node = g_list_next( node );
786 cnt += addrclip_copy_email_to_person( item, cache, person );
789 node = folder->listGroup;
790 while( node ) {
791 item = node->data;
792 node = g_list_next( node );
793 cnt += addrclip_copy_email_to_person( item, cache, person );
796 node = folder->listFolder;
797 while( node ) {
798 item = node->data;
799 node = g_list_next( node );
800 cnt += addrclip_copy_email_to_person( item, cache, person );
803 return cnt;
807 * End of Source.