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.
51 #include "addrcache.h"
53 #include "addrselect.h"
54 #include "addrindex.h"
60 AddressClipboard
*addrclip_create( void ) {
61 AddressClipboard
*clipBoard
;
63 clipBoard
= g_new0( AddressClipboard
, 1 );
64 clipBoard
->cutFlag
= FALSE
;
65 clipBoard
->objectList
= NULL
;
72 void addrclip_clear( AddressClipboard
*clipBoard
) {
76 g_return_if_fail( clipBoard
!= NULL
);
77 node
= clipBoard
->objectList
;
80 addrselect_item_free( item
);
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
;
118 if( clipBoard
->objectList
) retVal
= FALSE
;
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
) {
131 g_return_if_fail( clipBoard
!= NULL
);
132 g_return_if_fail( asl
!= NULL
);
133 node
= asl
->listSelect
;
135 AddrSelectItem
*item
, *itemCopy
;
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
) {
155 g_return_if_fail( clipBoard
!= NULL
);
156 node
= clipBoard
->objectList
;
157 while( node
!= NULL
) {
158 AddrSelectItem
*item
;
161 addrselect_item_print( item
, stream
);
163 cache
= addrindex_get_cache( clipBoard
->addressIndex
, item
->cacheID
);
164 aio
= addrcache_get_object( cache
, item
->uid
);
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_
{
191 * Free up specified list of addresses.
193 static void addrclip_free_copy_list( GList
*copyList
) {
198 AddrClip_EMail
*em
= node
->data
;
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
,
219 ItemPerson
*newPerson
;
222 UserAttribute
*attrib
;
223 UserAttribute
*newAttrib
;
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
;
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
;
245 copyList
= g_list_append( copyList
, em
);
248 /* Copy user attributes */
249 node
= person
->listAttrib
;
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
);
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
268 static ItemEMail
*addrclip_find_copied_email(
269 GList
*copyList
, ItemEMail
*emailOrig
)
271 ItemEMail
*emailCopy
;
279 if( em
->original
== emailOrig
) {
280 emailCopy
= em
->copy
;
283 node
= g_list_next( node
);
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
,
301 ItemEMail
*emailOrig
, *emailCopy
;
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
;
312 emailOrig
= ( ItemEMail
* ) node
->data
;
313 emailCopy
= addrclip_find_copied_email( copyList
, emailOrig
);
315 addrcache_group_add_email( cache
, newGroup
, emailCopy
);
317 node
= g_list_next( node
);
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
;
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 */
346 node
= folder
->listPerson
;
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
;
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
;
367 ItemFolder
*item
= node
->data
;
368 node
= g_list_next( node
);
369 addrclip_cache_copy_folder( cache
, newFolder
, item
);
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
)
389 AddrSelectItem
*item
;
391 AddressCache
*cacheFrom
;
401 node
= g_list_next( node
);
403 cacheFrom
= addrindex_get_cache(
404 clipBoard
->addressIndex
, item
->cacheID
);
405 if( cacheFrom
== NULL
) continue;
407 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
409 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_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
);
430 g_list_append( folderGroup
, newFolder
);
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
) );
449 g_list_append( folderGroup
, newFolder
);
455 /* Finally add any groups */
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
);
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
);
473 g_list_append( folderGroup
, newGroup
);
480 addrclip_free_copy_list( copyList
);
481 g_list_free( copyList
);
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
)
501 AddrSelectItem
*item
;
503 AddressCache
*cacheFrom
;
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
);
515 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
518 person
= ( ItemPerson
* ) aio
;
519 addrcache_folder_move_person(
520 cache
, person
, targetFolder
);
522 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_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
) {
533 folder
= ( ItemFolder
* ) aio
;
534 addrcache_folder_move_folder(
535 cache
, folder
, targetFolder
);
537 g_list_append( folderGroup
, folder
);
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
) {
553 AddrSelectItem
*item
;
556 itemList
= clipBoard
->objectList
;
558 item
= itemList
->data
;
559 cache
= addrindex_get_cache(
560 clipBoard
->addressIndex
, item
->cacheID
);
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
,
580 g_return_val_if_fail( clipBoard
!= NULL
, NULL
);
582 cache
= book
->addressCache
;
583 if( folder
== NULL
) folder
= cache
->rootFolder
;
586 itemList
= clipBoard
->objectList
;
587 folderGroup
= addrclip_cache_add_folder(
588 cache
, folder
, itemList
, clipBoard
);
594 * Remove items that were cut from clipboard.
595 * Enter: clipBoard Clipboard.
597 void addrclip_delete_item( AddressClipboard
*clipBoard
) {
598 AddrSelectItem
*item
;
600 AddressCache
*cacheFrom
;
603 /* If cutting within current cache, no deletion is necessary */
604 if( clipBoard
->moveFlag
) return;
607 node
= clipBoard
->objectList
;
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
);
616 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
619 group
= ( ItemGroup
* ) aio
;
620 group
= addrcache_remove_group( cacheFrom
, group
);
622 addritem_free_item_group( group
);
628 /* Remove persons and folders */
629 node
= clipBoard
->objectList
;
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
);
640 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
643 person
= ( ItemPerson
* ) aio
;
644 person
= addrcache_remove_person( cacheFrom
, 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
,
672 AddressCache
*cache
, *cacheFrom
;
676 g_return_val_if_fail( clipBoard
!= NULL
, NULL
);
678 cache
= book
->addressCache
;
679 if( folder
== NULL
) folder
= cache
->rootFolder
;
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
;
692 /* Move items across address books */
693 itemList
= clipBoard
->objectList
;
694 folderGroup
= addrclip_cache_add_folder(
695 cache
, folder
, itemList
, clipBoard
);
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
)
720 /* Copy email addresses */
724 ItemEMail
*email
, *newEMail
;
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
);
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
)
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
);
768 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_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
) {
778 AddrItemObject
*item
;
781 folder
= ( ItemFolder
* ) aio
;
782 node
= folder
->listPerson
;
785 node
= g_list_next( node
);
786 cnt
+= addrclip_copy_email_to_person( item
, cache
, person
);
789 node
= folder
->listGroup
;
792 node
= g_list_next( node
);
793 cnt
+= addrclip_copy_email_to_person( item
, cache
, person
);
796 node
= folder
->listFolder
;
799 node
= g_list_next( node
);
800 cnt
+= addrclip_copy_email_to_person( item
, cache
, person
);