2 * Claws Mail -- 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.
52 #include <glib/gi18n.h>
54 #include "addrcache.h"
56 #include "addrselect.h"
57 #include "addrindex.h"
59 #include "alertpanel.h"
61 #include "file-utils.h"
66 AddressClipboard
*addrclip_create( void ) {
67 AddressClipboard
*clipBoard
;
69 clipBoard
= g_new0( AddressClipboard
, 1 );
70 clipBoard
->cutFlag
= FALSE
;
71 clipBoard
->objectList
= NULL
;
78 void addrclip_clear( AddressClipboard
*clipBoard
) {
82 cm_return_if_fail( clipBoard
!= NULL
);
83 node
= clipBoard
->objectList
;
86 addrselect_item_free( item
);
88 node
= g_list_next( node
);
90 g_list_free( clipBoard
->objectList
);
91 clipBoard
->objectList
= NULL
;
95 * Free up a clipboard.
97 void addrclip_free( AddressClipboard
*clipBoard
) {
98 cm_return_if_fail( clipBoard
!= NULL
);
100 addrclip_clear( clipBoard
);
101 clipBoard
->cutFlag
= FALSE
;
106 * Setup reference to address index.
108 void addrclip_set_index(
109 AddressClipboard
*clipBoard
, AddressIndex
*addrIndex
)
111 cm_return_if_fail( clipBoard
!= NULL
);
112 cm_return_if_fail( addrIndex
!= NULL
);
113 clipBoard
->addressIndex
= addrIndex
;
117 * Test whether clipboard is empty.
118 * Enter: clipBoard Clipboard.
119 * Return: TRUE if clipboard is empty.
121 gboolean
addrclip_is_empty( AddressClipboard
*clipBoard
) {
122 gboolean retVal
= TRUE
;
125 if( clipBoard
->objectList
) retVal
= FALSE
;
131 * Add a list of address selection objects to clipbard.
132 * Enter: clipBoard Clipboard.
133 * addrList List of address selection objects.
135 void addrclip_add( AddressClipboard
*clipBoard
, AddrSelectList
*asl
) {
138 cm_return_if_fail( clipBoard
!= NULL
);
139 cm_return_if_fail( asl
!= NULL
);
140 node
= asl
->listSelect
;
142 AddrSelectItem
*item
, *itemCopy
;
145 itemCopy
= addrselect_item_copy( item
);
146 clipBoard
->objectList
=
147 g_list_append( clipBoard
->objectList
, itemCopy
);
148 node
= g_list_next( node
);
153 * Show clipboard contents.
154 * Enter: clipBoard Clipboard.
155 * stream Output stream.
157 void addrclip_list_show( AddressClipboard
*clipBoard
, FILE *stream
) {
162 cm_return_if_fail( clipBoard
!= NULL
);
163 node
= clipBoard
->objectList
;
164 while( node
!= NULL
) {
165 AddrSelectItem
*item
;
168 addrselect_item_print( item
, stream
);
170 cache
= addrindex_get_cache( clipBoard
->addressIndex
, item
->cacheID
);
171 aio
= addrcache_get_object( cache
, item
->uid
);
173 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
174 addritem_print_item_person( ( ItemPerson
* ) aio
, stream
);
176 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_EMAIL
) {
177 addritem_print_item_email( ( ItemEMail
* ) aio
, stream
);
179 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
180 addritem_print_item_group( ( ItemGroup
* ) aio
, stream
);
182 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
183 addritem_print_item_folder( ( ItemFolder
* ) aio
, stream
);
186 node
= g_list_next( node
);
190 /* Pasted address pointers */
191 typedef struct _AddrClip_EMail_ AddrClip_EMail
;
192 struct _AddrClip_EMail_
{
198 * Free up specified list of addresses.
200 static void addrclip_free_copy_list( GList
*copyList
) {
205 AddrClip_EMail
*em
= node
->data
;
210 node
= g_list_next( node
);
215 * Paste person into cache.
216 * Enter: cache Address cache to paste into.
217 * folder Folder to store
218 * person Person to paste.
219 * copyLIst List of email addresses pasted.
220 * Return: Update list of email addresses pasted.
222 static GList
*addrclip_cache_add_person(
223 AddressCache
*cache
, ItemFolder
*folder
, ItemPerson
*person
,
226 ItemPerson
*newPerson
;
229 UserAttribute
*attrib
;
230 UserAttribute
*newAttrib
;
235 newPerson
= addritem_copy_item_person( person
);
236 addrcache_id_person( cache
, newPerson
);
237 addrcache_folder_add_person( cache
, folder
, newPerson
);
239 /* Copy email addresses */
240 node
= person
->listEMail
;
243 newEMail
= addritem_copy_item_email( email
);
244 addrcache_id_email( cache
, newEMail
);
245 addrcache_person_add_email( cache
, newPerson
, newEMail
);
246 node
= g_list_next( node
);
248 /* Take a copy of the original */
249 em
= g_new0( AddrClip_EMail
, 1 );
250 em
->original
= email
;
252 copyList
= g_list_append( copyList
, em
);
255 /* Copy user attributes */
256 node
= person
->listAttrib
;
259 newAttrib
= addritem_copy_attribute( attrib
);
260 addrcache_id_attribute( cache
, newAttrib
);
261 addritem_person_add_attribute( newPerson
, newAttrib
);
262 node
= g_list_next( node
);
265 /* Set picture name and create picture file (from copy) if missing */
266 addritem_person_set_picture(newPerson
, ADDRITEM_ID(newPerson
));
267 if( strcmp(ADDRITEM_ID(newPerson
), ADDRITEM_ID(person
)) ) {
269 gchar
*newPictureFile
;
271 pictureFile
= g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S
, ADDRBOOK_DIR
, G_DIR_SEPARATOR_S
,
272 person
->picture
, ".png", NULL
);
273 newPictureFile
= g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S
, ADDRBOOK_DIR
, G_DIR_SEPARATOR_S
,
274 newPerson
->picture
, ".png", NULL
);
275 if (file_exist(pictureFile
, FALSE
) && !file_exist(newPictureFile
, FALSE
)) {
276 debug_print("copying contact picture file: %s -> %s\n", person
->picture
, newPerson
->picture
);
277 copy_file(pictureFile
, newPictureFile
, FALSE
);
279 g_free( pictureFile
);
280 g_free( newPictureFile
);
287 * Search for new email record in copied email list.
288 * Enter: copyList List of copied email address mappings.
289 * emailOrig Original email item.
290 * Return: New email item corresponding to original item if pasted. Or NULL if
293 static ItemEMail
*addrclip_find_copied_email(
294 GList
*copyList
, ItemEMail
*emailOrig
)
296 ItemEMail
*emailCopy
;
304 if( em
->original
== emailOrig
) {
305 emailCopy
= em
->copy
;
308 node
= g_list_next( node
);
314 * Paste group into cache.
315 * Enter: cache Address cache to paste into.
316 * folder Folder to store
317 * group Group to paste.
318 * copyList List of email addresses pasted.
319 * Return: Group added.
321 static ItemGroup
*addrclip_cache_add_group(
322 AddressCache
*cache
, ItemFolder
*folder
, ItemGroup
*group
,
326 ItemEMail
*emailOrig
, *emailCopy
;
330 newGroup
= addritem_copy_item_group( group
);
331 addrcache_id_group( cache
, newGroup
);
332 addrcache_folder_add_group( cache
, folder
, newGroup
);
334 /* Add references of copied addresses to group */
335 node
= group
->listEMail
;
337 emailOrig
= ( ItemEMail
* ) node
->data
;
338 emailCopy
= addrclip_find_copied_email( copyList
, emailOrig
);
340 addrcache_group_add_email( cache
, newGroup
, emailCopy
);
342 node
= g_list_next( node
);
348 * Copy specified folder into cache. Note this functions uses pointers to
349 * folders to copy from. There should not be any deleted items referenced
350 * by these pointers!!!
351 * Enter: cache Address cache to copy into.
352 * targetFolder Target folder.
353 * folder Folder to copy.
354 * Return: Folder added.
356 static ItemFolder
*addrclip_cache_copy_folder(
357 AddressCache
*cache
, ItemFolder
*targetFolder
, ItemFolder
*folder
)
359 ItemFolder
*newFolder
;
365 newFolder
= addritem_copy_item_folder( folder
);
366 addrcache_id_folder( cache
, newFolder
);
367 addrcache_folder_add_folder( cache
, targetFolder
, newFolder
);
369 /* Copy people to new folder */
371 node
= folder
->listPerson
;
373 ItemPerson
*item
= node
->data
;
374 node
= g_list_next( node
);
375 copyList
= addrclip_cache_add_person(
376 cache
, newFolder
, item
, copyList
);
379 /* Copy groups to new folder */
380 node
= folder
->listGroup
;
382 ItemGroup
*item
= node
->data
;
383 node
= g_list_next( node
);
384 newGroup
= addrclip_cache_add_group(
385 cache
, newFolder
, item
, copyList
);
386 if (newGroup
== NULL
) {
387 g_message("error allocating memory for new group\n");
390 g_list_free( copyList
);
392 /* Copy folders to new folder (recursive) */
393 node
= folder
->listFolder
;
395 ItemFolder
*item
= node
->data
;
396 node
= g_list_next( node
);
397 addrclip_cache_copy_folder( cache
, newFolder
, item
);
403 static gboolean
addrclip_is_subfolder_of(ItemFolder
*is_parent
, ItemFolder
*is_child
)
408 cm_return_val_if_fail(is_parent
!= NULL
, FALSE
);
409 cm_return_val_if_fail(is_child
!= NULL
, FALSE
);
411 if (is_parent
== is_child
)
415 obj
= folder
->obj
.parent
;
417 if ((void*)obj
== (void*)is_parent
)
425 * Paste item list into address book.
426 * Enter: cache Target address cache.
427 * folder Target folder where data is pasted.
428 * itemList List of items to paste.
429 * clipBoard Clipboard.
430 * Return: List of group or folder items added.
432 static GList
*addrclip_cache_add_folder(
433 AddressCache
*cache
, ItemFolder
*folder
, GList
*itemList
,
434 AddressClipboard
*clipBoard
)
438 AddrSelectItem
*item
;
440 AddressCache
*cacheFrom
;
450 node
= g_list_next( node
);
452 cacheFrom
= addrindex_get_cache(
453 clipBoard
->addressIndex
, item
->cacheID
);
454 if( cacheFrom
== NULL
) continue;
456 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
458 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
461 person
= ( ItemPerson
* ) aio
;
462 copyList
= addrclip_cache_add_person(
463 cache
, folder
, person
, copyList
);
466 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) {
469 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
470 haveGroups
= TRUE
; /* Process later */
472 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
473 ItemFolder
*itemFolder
, *newFolder
;
475 itemFolder
= ( ItemFolder
* ) aio
;
476 if (!addrclip_is_subfolder_of(itemFolder
, folder
)) {
477 newFolder
= addrclip_cache_copy_folder(
478 cache
, folder
, itemFolder
);
480 g_list_append( folderGroup
, newFolder
);
483 _("Cannot copy a folder to itself or to its sub-structure.") );
489 if( item
->objectType
== ITEMTYPE_DATASOURCE
) {
491 * Must be an address book - allow copy only if
492 * copying from a different cache.
494 if( cache
!= cacheFrom
) {
495 ItemFolder
*itemFolder
, *newFolder
;
497 itemFolder
= cacheFrom
->rootFolder
;
498 newFolder
= addrclip_cache_copy_folder(
499 cache
, folder
, itemFolder
);
500 addritem_folder_set_name( newFolder
,
501 addrcache_get_name( cacheFrom
) );
503 g_list_append( folderGroup
, newFolder
);
506 _("Cannot copy an address book to itself.") );
512 /* Finally add any groups */
517 node
= g_list_next( node
);
518 cacheFrom
= addrindex_get_cache(
519 clipBoard
->addressIndex
, item
->cacheID
);
520 if( cacheFrom
== NULL
) continue;
521 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
523 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
524 ItemGroup
*group
, *newGroup
;
526 group
= ( ItemGroup
* ) aio
;
527 newGroup
= addrclip_cache_add_group(
528 cache
, folder
, group
, copyList
);
530 g_list_append( folderGroup
, newGroup
);
537 addrclip_free_copy_list( copyList
);
538 g_list_free( copyList
);
545 * Move items in list into new folder
546 * Enter: cache Target address cache.
547 * targetFolder Target folder where data is pasted.
548 * itemList List of items to paste.
549 * clipBoard Clipboard.
550 * Return: List of group or folder items added.
552 static GList
*addrclip_cache_move_items(
553 AddressCache
*cache
, ItemFolder
*targetFolder
, GList
*itemList
,
554 AddressClipboard
*clipBoard
)
558 AddrSelectItem
*item
;
560 AddressCache
*cacheFrom
;
566 node
= g_list_next( node
);
567 cacheFrom
= addrindex_get_cache(
568 clipBoard
->addressIndex
, item
->cacheID
);
569 if( cacheFrom
== NULL
) continue;
570 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
572 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
575 person
= ( ItemPerson
* ) aio
;
576 addrcache_folder_move_person(
577 cache
, person
, targetFolder
);
579 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
582 group
= ( ItemGroup
* ) aio
;
583 addrcache_folder_move_group(
584 cache
, group
, targetFolder
);
585 folderGroup
= g_list_append( folderGroup
, group
);
587 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
588 ItemFolder
*folder
= ( ItemFolder
* ) aio
;
590 if (!addrclip_is_subfolder_of(folder
, targetFolder
)) {
591 addrcache_folder_move_folder(
592 cache
, folder
, targetFolder
);
594 g_list_append( folderGroup
, folder
);
597 _("Cannot move a folder to itself or to its sub-structure.") );
606 * Get address cache of first item in list. This assumes that all items in
607 * the clipboard are located in the same cache.
608 * Enter: clipBoard Clipboard.
609 * Return: List of group or folder items added.
611 static AddressCache
*addrclip_list_get_cache( AddressClipboard
*clipBoard
) {
614 AddrSelectItem
*item
;
617 itemList
= clipBoard
->objectList
;
619 item
= itemList
->data
;
620 cache
= addrindex_get_cache(
621 clipBoard
->addressIndex
, item
->cacheID
);
627 * Paste (copy) clipboard into address book.
628 * Enter: clipBoard Clipboard.
629 * book Target address book.
630 * folder Target folder where data is pasted, or null for root folder.
631 * Return: List of group or folder items added.
633 GList
*addrclip_paste_copy(
634 AddressClipboard
*clipBoard
, AddressBookFile
*book
,
641 cm_return_val_if_fail( clipBoard
!= NULL
, NULL
);
643 cache
= book
->addressCache
;
644 if( folder
== NULL
) folder
= cache
->rootFolder
;
647 itemList
= clipBoard
->objectList
;
648 folderGroup
= addrclip_cache_add_folder(
649 cache
, folder
, itemList
, clipBoard
);
655 * Remove items that were cut from clipboard.
656 * Enter: clipBoard Clipboard.
658 void addrclip_delete_item( AddressClipboard
*clipBoard
) {
659 AddrSelectItem
*item
;
661 AddressCache
*cacheFrom
;
664 /* If cutting within current cache, no deletion is necessary */
665 if( clipBoard
->moveFlag
) return;
668 node
= clipBoard
->objectList
;
671 node
= g_list_next( node
);
672 cacheFrom
= addrindex_get_cache(
673 clipBoard
->addressIndex
, item
->cacheID
);
674 if( cacheFrom
== NULL
) continue;
675 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
677 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
680 group
= ( ItemGroup
* ) aio
;
681 group
= addrcache_remove_group( cacheFrom
, group
);
683 addritem_free_item_group( group
);
689 /* Remove persons and folders */
690 node
= clipBoard
->objectList
;
693 node
= g_list_next( node
);
695 cacheFrom
= addrindex_get_cache(
696 clipBoard
->addressIndex
, item
->cacheID
);
697 if( cacheFrom
== NULL
) continue;
699 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
701 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
704 person
= ( ItemPerson
* ) aio
;
705 person
= addrcache_remove_person( cacheFrom
, person
);
707 addritem_free_item_person( person
);
710 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
711 ItemFolder
*itemFolder
;
713 itemFolder
= ( ItemFolder
* ) aio
;
714 itemFolder
= addrcache_remove_folder_delete(
715 cacheFrom
, itemFolder
);
716 addritem_free_item_folder( itemFolder
);
723 * Paste (move) clipboard into address book.
724 * Enter: clipBoard Clipboard.
725 * book Target address book.
726 * folder Target folder where data is pasted, or null for root folder.
727 * Return: List of group or folder items added.
729 GList
*addrclip_paste_cut(
730 AddressClipboard
*clipBoard
, AddressBookFile
*book
,
733 AddressCache
*cache
, *cacheFrom
;
737 cm_return_val_if_fail( clipBoard
!= NULL
, NULL
);
739 cache
= book
->addressCache
;
740 if( folder
== NULL
) folder
= cache
->rootFolder
;
743 clipBoard
->moveFlag
= FALSE
;
744 cacheFrom
= addrclip_list_get_cache( clipBoard
);
745 if( cacheFrom
&& cacheFrom
== cache
) {
746 /* Move items between folders in same book */
747 itemList
= clipBoard
->objectList
;
748 folderGroup
= addrclip_cache_move_items(
749 cache
, folder
, itemList
, clipBoard
);
750 clipBoard
->moveFlag
= TRUE
;
753 /* Move items across address books */
754 itemList
= clipBoard
->objectList
;
755 folderGroup
= addrclip_cache_add_folder(
756 cache
, folder
, itemList
, clipBoard
);