2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2002-2012 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.
50 #include <glib/gi18n.h>
52 #include "addrcache.h"
54 #include "addrselect.h"
55 #include "addrindex.h"
57 #include "alertpanel.h"
59 #include "file-utils.h"
64 AddressClipboard
*addrclip_create( void ) {
65 AddressClipboard
*clipBoard
;
67 clipBoard
= g_new0( AddressClipboard
, 1 );
68 clipBoard
->cutFlag
= FALSE
;
69 clipBoard
->objectList
= NULL
;
76 void addrclip_clear( AddressClipboard
*clipBoard
) {
80 cm_return_if_fail( clipBoard
!= NULL
);
81 node
= clipBoard
->objectList
;
84 addrselect_item_free( item
);
86 node
= g_list_next( node
);
88 g_list_free( clipBoard
->objectList
);
89 clipBoard
->objectList
= NULL
;
93 * Free up a clipboard.
95 void addrclip_free( AddressClipboard
*clipBoard
) {
96 cm_return_if_fail( clipBoard
!= NULL
);
98 addrclip_clear( clipBoard
);
99 clipBoard
->cutFlag
= FALSE
;
104 * Setup reference to address index.
106 void addrclip_set_index(
107 AddressClipboard
*clipBoard
, AddressIndex
*addrIndex
)
109 cm_return_if_fail( clipBoard
!= NULL
);
110 cm_return_if_fail( addrIndex
!= NULL
);
111 clipBoard
->addressIndex
= addrIndex
;
115 * Test whether clipboard is empty.
116 * Enter: clipBoard Clipboard.
117 * Return: TRUE if clipboard is empty.
119 gboolean
addrclip_is_empty( AddressClipboard
*clipBoard
) {
120 gboolean retVal
= TRUE
;
123 if( clipBoard
->objectList
) retVal
= FALSE
;
129 * Add a list of address selection objects to clipbard.
130 * Enter: clipBoard Clipboard.
131 * addrList List of address selection objects.
133 void addrclip_add( AddressClipboard
*clipBoard
, AddrSelectList
*asl
) {
136 cm_return_if_fail( clipBoard
!= NULL
);
137 cm_return_if_fail( asl
!= NULL
);
138 node
= asl
->listSelect
;
140 AddrSelectItem
*item
, *itemCopy
;
143 itemCopy
= addrselect_item_copy( item
);
144 clipBoard
->objectList
=
145 g_list_append( clipBoard
->objectList
, itemCopy
);
146 node
= g_list_next( node
);
151 * Show clipboard contents.
152 * Enter: clipBoard Clipboard.
153 * stream Output stream.
155 void addrclip_list_show( AddressClipboard
*clipBoard
, FILE *stream
) {
160 cm_return_if_fail( clipBoard
!= NULL
);
161 node
= clipBoard
->objectList
;
162 while( node
!= NULL
) {
163 AddrSelectItem
*item
;
166 addrselect_item_print( item
, stream
);
168 cache
= addrindex_get_cache( clipBoard
->addressIndex
, item
->cacheID
);
169 aio
= addrcache_get_object( cache
, item
->uid
);
171 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
172 addritem_print_item_person( ( ItemPerson
* ) aio
, stream
);
174 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_EMAIL
) {
175 addritem_print_item_email( ( ItemEMail
* ) aio
, stream
);
177 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
178 addritem_print_item_group( ( ItemGroup
* ) aio
, stream
);
180 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
181 addritem_print_item_folder( ( ItemFolder
* ) aio
, stream
);
184 node
= g_list_next( node
);
188 /* Pasted address pointers */
189 typedef struct _AddrClip_EMail_ AddrClip_EMail
;
190 struct _AddrClip_EMail_
{
196 * Free up specified list of addresses.
198 static void addrclip_free_copy_list( GList
*copyList
) {
203 AddrClip_EMail
*em
= node
->data
;
208 node
= g_list_next( node
);
213 * Paste person into cache.
214 * Enter: cache Address cache to paste into.
215 * folder Folder to store
216 * person Person to paste.
217 * copyLIst List of email addresses pasted.
218 * Return: Update list of email addresses pasted.
220 static GList
*addrclip_cache_add_person(
221 AddressCache
*cache
, ItemFolder
*folder
, ItemPerson
*person
,
224 ItemPerson
*newPerson
;
227 UserAttribute
*attrib
;
228 UserAttribute
*newAttrib
;
233 newPerson
= addritem_copy_item_person( person
);
234 addrcache_id_person( cache
, newPerson
);
235 addrcache_folder_add_person( cache
, folder
, newPerson
);
237 /* Copy email addresses */
238 node
= person
->listEMail
;
241 newEMail
= addritem_copy_item_email( email
);
242 addrcache_id_email( cache
, newEMail
);
243 addrcache_person_add_email( cache
, newPerson
, newEMail
);
244 node
= g_list_next( node
);
246 /* Take a copy of the original */
247 em
= g_new0( AddrClip_EMail
, 1 );
248 em
->original
= email
;
250 copyList
= g_list_append( copyList
, em
);
253 /* Copy user attributes */
254 node
= person
->listAttrib
;
257 newAttrib
= addritem_copy_attribute( attrib
);
258 addrcache_id_attribute( cache
, newAttrib
);
259 addritem_person_add_attribute( newPerson
, newAttrib
);
260 node
= g_list_next( node
);
263 /* Set picture name and create picture file (from copy) if missing */
264 addritem_person_set_picture(newPerson
, ADDRITEM_ID(newPerson
));
265 if( strcmp(ADDRITEM_ID(newPerson
), ADDRITEM_ID(person
)) ) {
267 gchar
*newPictureFile
;
269 pictureFile
= g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S
, ADDRBOOK_DIR
, G_DIR_SEPARATOR_S
,
270 person
->picture
, ".png", NULL
);
271 newPictureFile
= g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S
, ADDRBOOK_DIR
, G_DIR_SEPARATOR_S
,
272 newPerson
->picture
, ".png", NULL
);
273 if (file_exist(pictureFile
, FALSE
) && !file_exist(newPictureFile
, FALSE
)) {
274 debug_print("copying contact picture file: %s -> %s\n", person
->picture
, newPerson
->picture
);
275 copy_file(pictureFile
, newPictureFile
, FALSE
);
277 g_free( pictureFile
);
278 g_free( newPictureFile
);
285 * Search for new email record in copied email list.
286 * Enter: copyList List of copied email address mappings.
287 * emailOrig Original email item.
288 * Return: New email item corresponding to original item if pasted. Or NULL if
291 static ItemEMail
*addrclip_find_copied_email(
292 GList
*copyList
, ItemEMail
*emailOrig
)
294 ItemEMail
*emailCopy
;
302 if( em
->original
== emailOrig
) {
303 emailCopy
= em
->copy
;
306 node
= g_list_next( node
);
312 * Paste group into cache.
313 * Enter: cache Address cache to paste into.
314 * folder Folder to store
315 * group Group to paste.
316 * copyList List of email addresses pasted.
317 * Return: Group added.
319 static ItemGroup
*addrclip_cache_add_group(
320 AddressCache
*cache
, ItemFolder
*folder
, ItemGroup
*group
,
324 ItemEMail
*emailOrig
, *emailCopy
;
328 newGroup
= addritem_copy_item_group( group
);
329 addrcache_id_group( cache
, newGroup
);
330 addrcache_folder_add_group( cache
, folder
, newGroup
);
332 /* Add references of copied addresses to group */
333 node
= group
->listEMail
;
335 emailOrig
= ( ItemEMail
* ) node
->data
;
336 emailCopy
= addrclip_find_copied_email( copyList
, emailOrig
);
338 addrcache_group_add_email( cache
, newGroup
, emailCopy
);
340 node
= g_list_next( node
);
346 * Copy specified folder into cache. Note this functions uses pointers to
347 * folders to copy from. There should not be any deleted items referenced
348 * by these pointers!!!
349 * Enter: cache Address cache to copy into.
350 * targetFolder Target folder.
351 * folder Folder to copy.
352 * Return: Folder added.
354 static ItemFolder
*addrclip_cache_copy_folder(
355 AddressCache
*cache
, ItemFolder
*targetFolder
, ItemFolder
*folder
)
357 ItemFolder
*newFolder
;
363 newFolder
= addritem_copy_item_folder( folder
);
364 addrcache_id_folder( cache
, newFolder
);
365 addrcache_folder_add_folder( cache
, targetFolder
, newFolder
);
367 /* Copy people to new folder */
369 node
= folder
->listPerson
;
371 ItemPerson
*item
= node
->data
;
372 node
= g_list_next( node
);
373 copyList
= addrclip_cache_add_person(
374 cache
, newFolder
, item
, copyList
);
377 /* Copy groups to new folder */
378 node
= folder
->listGroup
;
380 ItemGroup
*item
= node
->data
;
381 node
= g_list_next( node
);
382 newGroup
= addrclip_cache_add_group(
383 cache
, newFolder
, item
, copyList
);
384 if (newGroup
== NULL
) {
385 g_message("error allocating memory for new group\n");
388 g_list_free( copyList
);
390 /* Copy folders to new folder (recursive) */
391 node
= folder
->listFolder
;
393 ItemFolder
*item
= node
->data
;
394 node
= g_list_next( node
);
395 addrclip_cache_copy_folder( cache
, newFolder
, item
);
401 static gboolean
addrclip_is_subfolder_of(ItemFolder
*is_parent
, ItemFolder
*is_child
)
406 cm_return_val_if_fail(is_parent
!= NULL
, FALSE
);
407 cm_return_val_if_fail(is_child
!= NULL
, FALSE
);
409 if (is_parent
== is_child
)
413 obj
= folder
->obj
.parent
;
415 if ((void*)obj
== (void*)is_parent
)
423 * Paste item list into address book.
424 * Enter: cache Target address cache.
425 * folder Target folder where data is pasted.
426 * itemList List of items to paste.
427 * clipBoard Clipboard.
428 * Return: List of group or folder items added.
430 static GList
*addrclip_cache_add_folder(
431 AddressCache
*cache
, ItemFolder
*folder
, GList
*itemList
,
432 AddressClipboard
*clipBoard
)
436 AddrSelectItem
*item
;
438 AddressCache
*cacheFrom
;
448 node
= g_list_next( node
);
450 cacheFrom
= addrindex_get_cache(
451 clipBoard
->addressIndex
, item
->cacheID
);
452 if( cacheFrom
== NULL
) continue;
454 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
456 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
459 person
= ( ItemPerson
* ) aio
;
460 copyList
= addrclip_cache_add_person(
461 cache
, folder
, person
, copyList
);
464 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) {
467 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
468 haveGroups
= TRUE
; /* Process later */
470 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
471 ItemFolder
*itemFolder
, *newFolder
;
473 itemFolder
= ( ItemFolder
* ) aio
;
474 if (!addrclip_is_subfolder_of(itemFolder
, folder
)) {
475 newFolder
= addrclip_cache_copy_folder(
476 cache
, folder
, itemFolder
);
478 g_list_append( folderGroup
, newFolder
);
481 _("Cannot copy a folder to itself or to its sub-structure.") );
487 if( item
->objectType
== ITEMTYPE_DATASOURCE
) {
489 * Must be an address book - allow copy only if
490 * copying from a different cache.
492 if( cache
!= cacheFrom
) {
493 ItemFolder
*itemFolder
, *newFolder
;
495 itemFolder
= cacheFrom
->rootFolder
;
496 newFolder
= addrclip_cache_copy_folder(
497 cache
, folder
, itemFolder
);
498 addritem_folder_set_name( newFolder
,
499 addrcache_get_name( cacheFrom
) );
501 g_list_append( folderGroup
, newFolder
);
504 _("Cannot copy an address book to itself.") );
510 /* Finally add any groups */
515 node
= g_list_next( node
);
516 cacheFrom
= addrindex_get_cache(
517 clipBoard
->addressIndex
, item
->cacheID
);
518 if( cacheFrom
== NULL
) continue;
519 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
521 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
522 ItemGroup
*group
, *newGroup
;
524 group
= ( ItemGroup
* ) aio
;
525 newGroup
= addrclip_cache_add_group(
526 cache
, folder
, group
, copyList
);
528 g_list_append( folderGroup
, newGroup
);
535 addrclip_free_copy_list( copyList
);
536 g_list_free( copyList
);
543 * Move items in list into new folder
544 * Enter: cache Target address cache.
545 * targetFolder Target folder where data is pasted.
546 * itemList List of items to paste.
547 * clipBoard Clipboard.
548 * Return: List of group or folder items added.
550 static GList
*addrclip_cache_move_items(
551 AddressCache
*cache
, ItemFolder
*targetFolder
, GList
*itemList
,
552 AddressClipboard
*clipBoard
)
556 AddrSelectItem
*item
;
558 AddressCache
*cacheFrom
;
564 node
= g_list_next( node
);
565 cacheFrom
= addrindex_get_cache(
566 clipBoard
->addressIndex
, item
->cacheID
);
567 if( cacheFrom
== NULL
) continue;
568 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
570 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
573 person
= ( ItemPerson
* ) aio
;
574 addrcache_folder_move_person(
575 cache
, person
, targetFolder
);
577 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
580 group
= ( ItemGroup
* ) aio
;
581 addrcache_folder_move_group(
582 cache
, group
, targetFolder
);
583 folderGroup
= g_list_append( folderGroup
, group
);
585 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
586 ItemFolder
*folder
= ( ItemFolder
* ) aio
;
588 if (!addrclip_is_subfolder_of(folder
, targetFolder
)) {
589 addrcache_folder_move_folder(
590 cache
, folder
, targetFolder
);
592 g_list_append( folderGroup
, folder
);
595 _("Cannot move a folder to itself or to its sub-structure.") );
604 * Get address cache of first item in list. This assumes that all items in
605 * the clipboard are located in the same cache.
606 * Enter: clipBoard Clipboard.
607 * Return: List of group or folder items added.
609 static AddressCache
*addrclip_list_get_cache( AddressClipboard
*clipBoard
) {
612 AddrSelectItem
*item
;
615 itemList
= clipBoard
->objectList
;
617 item
= itemList
->data
;
618 cache
= addrindex_get_cache(
619 clipBoard
->addressIndex
, item
->cacheID
);
625 * Paste (copy) clipboard into address book.
626 * Enter: clipBoard Clipboard.
627 * book Target address book.
628 * folder Target folder where data is pasted, or null for root folder.
629 * Return: List of group or folder items added.
631 GList
*addrclip_paste_copy(
632 AddressClipboard
*clipBoard
, AddressBookFile
*book
,
639 cm_return_val_if_fail( clipBoard
!= NULL
, NULL
);
641 cache
= book
->addressCache
;
642 if( folder
== NULL
) folder
= cache
->rootFolder
;
645 itemList
= clipBoard
->objectList
;
646 folderGroup
= addrclip_cache_add_folder(
647 cache
, folder
, itemList
, clipBoard
);
653 * Remove items that were cut from clipboard.
654 * Enter: clipBoard Clipboard.
656 void addrclip_delete_item( AddressClipboard
*clipBoard
) {
657 AddrSelectItem
*item
;
659 AddressCache
*cacheFrom
;
662 /* If cutting within current cache, no deletion is necessary */
663 if( clipBoard
->moveFlag
) return;
666 node
= clipBoard
->objectList
;
669 node
= g_list_next( node
);
670 cacheFrom
= addrindex_get_cache(
671 clipBoard
->addressIndex
, item
->cacheID
);
672 if( cacheFrom
== NULL
) continue;
673 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
675 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
678 group
= ( ItemGroup
* ) aio
;
679 group
= addrcache_remove_group( cacheFrom
, group
);
681 addritem_free_item_group( group
);
687 /* Remove persons and folders */
688 node
= clipBoard
->objectList
;
691 node
= g_list_next( node
);
693 cacheFrom
= addrindex_get_cache(
694 clipBoard
->addressIndex
, item
->cacheID
);
695 if( cacheFrom
== NULL
) continue;
697 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
699 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
702 person
= ( ItemPerson
* ) aio
;
703 person
= addrcache_remove_person( cacheFrom
, person
);
705 addritem_free_item_person( person
);
708 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
709 ItemFolder
*itemFolder
;
711 itemFolder
= ( ItemFolder
* ) aio
;
712 itemFolder
= addrcache_remove_folder_delete(
713 cacheFrom
, itemFolder
);
714 addritem_free_item_folder( itemFolder
);
721 * Paste (move) clipboard into address book.
722 * Enter: clipBoard Clipboard.
723 * book Target address book.
724 * folder Target folder where data is pasted, or null for root folder.
725 * Return: List of group or folder items added.
727 GList
*addrclip_paste_cut(
728 AddressClipboard
*clipBoard
, AddressBookFile
*book
,
731 AddressCache
*cache
, *cacheFrom
;
735 cm_return_val_if_fail( clipBoard
!= NULL
, NULL
);
737 cache
= book
->addressCache
;
738 if( folder
== NULL
) folder
= cache
->rootFolder
;
741 clipBoard
->moveFlag
= FALSE
;
742 cacheFrom
= addrclip_list_get_cache( clipBoard
);
743 if( cacheFrom
&& cacheFrom
== cache
) {
744 /* Move items between folders in same book */
745 itemList
= clipBoard
->objectList
;
746 folderGroup
= addrclip_cache_move_items(
747 cache
, folder
, itemList
, clipBoard
);
748 clipBoard
->moveFlag
= TRUE
;
751 /* Move items across address books */
752 itemList
= clipBoard
->objectList
;
753 folderGroup
= addrclip_cache_add_folder(
754 cache
, folder
, itemList
, clipBoard
);