2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2002-2022 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/>.
20 * Contains address clipboard objects and related functions. The address
21 * clipboard is implemented as a linked list of AddrSelectItem objects.
22 * The address clipboard offers two groups of functions:
24 * a) Cut, copy and paste of address item objects (ItemFolder, ItemGroup,
25 * ItemPerson) into a folder. With this method, we can paste ItemPerson
26 * objects but not unattached ItemEMail objects into a folder. ItemEMail
27 * objects are owned by an ItemPerson object. Any ItemEMail objects that
28 * appear in the clipboard are ignored. If an ItemPerson object is found,
29 * the ItemPerson *and* ItemEMail objects that it owns are pasted.
31 * b) Copy and paste of ItemEMail address objects only into (below)
32 * ItemPerson objects. All ItemEMail objects which are owned by
33 * ItemPerson and referenced by ItemGroup objects are pasted. Any
34 * ItemFolder objects in the clipboard, and any objects owned by
35 * ItemFolder objects are ignored.
37 * Objects are inserted to the clipboard by copying (cloning)
38 * AddrSelectItem objects from the address books selection list to the
39 * clipboard's internal selection list. The clipboard makes use of the
40 * object id's and address cache id's to access objects contained in
41 * the address cache. If the referenced object is not found, it is
42 * ignored. This eliminates the need to delete pointers in multiple
43 * linked lists when an address object is deleted.
51 #include <glib/gi18n.h>
53 #include "addrcache.h"
55 #include "addrselect.h"
56 #include "addrindex.h"
58 #include "alertpanel.h"
60 #include "file-utils.h"
65 AddressClipboard
*addrclip_create( void ) {
66 AddressClipboard
*clipBoard
;
68 clipBoard
= g_new0( AddressClipboard
, 1 );
69 clipBoard
->cutFlag
= FALSE
;
70 clipBoard
->objectList
= NULL
;
77 void addrclip_clear( AddressClipboard
*clipBoard
) {
81 cm_return_if_fail( clipBoard
!= NULL
);
82 node
= clipBoard
->objectList
;
85 addrselect_item_free( item
);
87 node
= g_list_next( node
);
89 g_list_free( clipBoard
->objectList
);
90 clipBoard
->objectList
= NULL
;
94 * Free up a clipboard.
96 void addrclip_free( AddressClipboard
*clipBoard
) {
97 cm_return_if_fail( clipBoard
!= NULL
);
99 addrclip_clear( clipBoard
);
100 clipBoard
->cutFlag
= FALSE
;
105 * Setup reference to address index.
107 void addrclip_set_index(
108 AddressClipboard
*clipBoard
, AddressIndex
*addrIndex
)
110 cm_return_if_fail( clipBoard
!= NULL
);
111 cm_return_if_fail( addrIndex
!= NULL
);
112 clipBoard
->addressIndex
= addrIndex
;
116 * Test whether clipboard is empty.
117 * Enter: clipBoard Clipboard.
118 * Return: TRUE if clipboard is empty.
120 gboolean
addrclip_is_empty( AddressClipboard
*clipBoard
) {
121 gboolean retVal
= TRUE
;
124 if( clipBoard
->objectList
) retVal
= FALSE
;
130 * Add a list of address selection objects to clipbard.
131 * Enter: clipBoard Clipboard.
132 * addrList List of address selection objects.
134 void addrclip_add( AddressClipboard
*clipBoard
, AddrSelectList
*asl
) {
137 cm_return_if_fail( clipBoard
!= NULL
);
138 cm_return_if_fail( asl
!= NULL
);
139 node
= asl
->listSelect
;
141 AddrSelectItem
*item
, *itemCopy
;
144 itemCopy
= addrselect_item_copy( item
);
145 clipBoard
->objectList
=
146 g_list_append( clipBoard
->objectList
, itemCopy
);
147 node
= g_list_next( node
);
151 #ifdef DEBUG_ADDRBOOK
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
);
191 /* Pasted address pointers */
192 typedef struct _AddrClip_EMail_ AddrClip_EMail
;
193 struct _AddrClip_EMail_
{
199 * Free up specified list of addresses.
201 static void addrclip_free_copy_list( GList
*copyList
) {
206 AddrClip_EMail
*em
= node
->data
;
211 node
= g_list_next( node
);
216 * Paste person into cache.
217 * Enter: cache Address cache to paste into.
218 * folder Folder to store
219 * person Person to paste.
220 * copyLIst List of email addresses pasted.
221 * Return: Update list of email addresses pasted.
223 static GList
*addrclip_cache_add_person(
224 AddressCache
*cache
, ItemFolder
*folder
, ItemPerson
*person
,
227 ItemPerson
*newPerson
;
230 UserAttribute
*attrib
;
231 UserAttribute
*newAttrib
;
236 newPerson
= addritem_copy_item_person( person
);
237 addrcache_id_person( cache
, newPerson
);
238 addrcache_folder_add_person( cache
, folder
, newPerson
);
240 /* Copy email addresses */
241 node
= person
->listEMail
;
244 newEMail
= addritem_copy_item_email( email
);
245 addrcache_id_email( cache
, newEMail
);
246 addrcache_person_add_email( cache
, newPerson
, newEMail
);
247 node
= g_list_next( node
);
249 /* Take a copy of the original */
250 em
= g_new0( AddrClip_EMail
, 1 );
251 em
->original
= email
;
253 copyList
= g_list_append( copyList
, em
);
256 /* Copy user attributes */
257 node
= person
->listAttrib
;
260 newAttrib
= addritem_copy_attribute( attrib
);
261 addrcache_id_attribute( cache
, newAttrib
);
262 addritem_person_add_attribute( newPerson
, newAttrib
);
263 node
= g_list_next( node
);
266 /* Set picture name and create picture file (from copy) if missing */
267 addritem_person_set_picture(newPerson
, ADDRITEM_ID(newPerson
));
268 if( strcmp(ADDRITEM_ID(newPerson
), ADDRITEM_ID(person
)) ) {
270 gchar
*newPictureFile
;
272 pictureFile
= g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S
, ADDRBOOK_DIR
, G_DIR_SEPARATOR_S
,
273 person
->picture
, ".png", NULL
);
274 newPictureFile
= g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S
, ADDRBOOK_DIR
, G_DIR_SEPARATOR_S
,
275 newPerson
->picture
, ".png", NULL
);
276 if (file_exist(pictureFile
, FALSE
) && !file_exist(newPictureFile
, FALSE
)) {
277 debug_print("copying contact picture file: %s -> %s\n", person
->picture
, newPerson
->picture
);
278 copy_file(pictureFile
, newPictureFile
, FALSE
);
280 g_free( pictureFile
);
281 g_free( newPictureFile
);
288 * Search for new email record in copied email list.
289 * Enter: copyList List of copied email address mappings.
290 * emailOrig Original email item.
291 * Return: New email item corresponding to original item if pasted. Or NULL if
294 static ItemEMail
*addrclip_find_copied_email(
295 GList
*copyList
, ItemEMail
*emailOrig
)
297 ItemEMail
*emailCopy
;
305 if( em
->original
== emailOrig
) {
306 emailCopy
= em
->copy
;
309 node
= g_list_next( node
);
315 * Paste group into cache.
316 * Enter: cache Address cache to paste into.
317 * folder Folder to store
318 * group Group to paste.
319 * copyList List of email addresses pasted.
320 * Return: Group added.
322 static ItemGroup
*addrclip_cache_add_group(
323 AddressCache
*cache
, ItemFolder
*folder
, ItemGroup
*group
,
327 ItemEMail
*emailOrig
, *emailCopy
;
331 newGroup
= addritem_copy_item_group( group
);
332 addrcache_id_group( cache
, newGroup
);
333 addrcache_folder_add_group( cache
, folder
, newGroup
);
335 /* Add references of copied addresses to group */
336 node
= group
->listEMail
;
338 emailOrig
= ( ItemEMail
* ) node
->data
;
339 emailCopy
= addrclip_find_copied_email( copyList
, emailOrig
);
341 addrcache_group_add_email( cache
, newGroup
, emailCopy
);
343 node
= g_list_next( node
);
349 * Copy specified folder into cache. Note this functions uses pointers to
350 * folders to copy from. There should not be any deleted items referenced
351 * by these pointers!!!
352 * Enter: cache Address cache to copy into.
353 * targetFolder Target folder.
354 * folder Folder to copy.
355 * Return: Folder added.
357 static ItemFolder
*addrclip_cache_copy_folder(
358 AddressCache
*cache
, ItemFolder
*targetFolder
, ItemFolder
*folder
)
360 ItemFolder
*newFolder
;
366 newFolder
= addritem_copy_item_folder( folder
);
367 addrcache_id_folder( cache
, newFolder
);
368 addrcache_folder_add_folder( cache
, targetFolder
, newFolder
);
370 /* Copy people to new folder */
372 node
= folder
->listPerson
;
374 ItemPerson
*item
= node
->data
;
375 node
= g_list_next( node
);
376 copyList
= addrclip_cache_add_person(
377 cache
, newFolder
, item
, copyList
);
380 /* Copy groups to new folder */
381 node
= folder
->listGroup
;
383 ItemGroup
*item
= node
->data
;
384 node
= g_list_next( node
);
385 newGroup
= addrclip_cache_add_group(
386 cache
, newFolder
, item
, copyList
);
387 if (newGroup
== NULL
) {
388 g_message("error allocating memory for new group\n");
391 g_list_free( copyList
);
393 /* Copy folders to new folder (recursive) */
394 node
= folder
->listFolder
;
396 ItemFolder
*item
= node
->data
;
397 node
= g_list_next( node
);
398 addrclip_cache_copy_folder( cache
, newFolder
, item
);
404 static gboolean
addrclip_is_subfolder_of(ItemFolder
*is_parent
, ItemFolder
*is_child
)
409 cm_return_val_if_fail(is_parent
!= NULL
, FALSE
);
410 cm_return_val_if_fail(is_child
!= NULL
, FALSE
);
412 if (is_parent
== is_child
)
416 obj
= folder
->obj
.parent
;
418 if ((void*)obj
== (void*)is_parent
)
426 * Paste item list into address book.
427 * Enter: cache Target address cache.
428 * folder Target folder where data is pasted.
429 * itemList List of items to paste.
430 * clipBoard Clipboard.
431 * Return: List of group or folder items added.
433 static GList
*addrclip_cache_add_folder(
434 AddressCache
*cache
, ItemFolder
*folder
, GList
*itemList
,
435 AddressClipboard
*clipBoard
)
439 AddrSelectItem
*item
;
441 AddressCache
*cacheFrom
;
451 node
= g_list_next( node
);
453 cacheFrom
= addrindex_get_cache(
454 clipBoard
->addressIndex
, item
->cacheID
);
455 if( cacheFrom
== NULL
) continue;
457 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
459 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
462 person
= ( ItemPerson
* ) aio
;
463 copyList
= addrclip_cache_add_person(
464 cache
, folder
, person
, copyList
);
467 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) {
470 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
471 haveGroups
= TRUE
; /* Process later */
473 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
474 ItemFolder
*itemFolder
, *newFolder
;
476 itemFolder
= ( ItemFolder
* ) aio
;
477 if (!addrclip_is_subfolder_of(itemFolder
, folder
)) {
478 newFolder
= addrclip_cache_copy_folder(
479 cache
, folder
, itemFolder
);
481 g_list_append( folderGroup
, newFolder
);
484 _("Cannot copy a folder to itself or to its sub-structure.") );
490 if( item
->objectType
== ITEMTYPE_DATASOURCE
) {
492 * Must be an address book - allow copy only if
493 * copying from a different cache.
495 if( cache
!= cacheFrom
) {
496 ItemFolder
*itemFolder
, *newFolder
;
498 itemFolder
= cacheFrom
->rootFolder
;
499 newFolder
= addrclip_cache_copy_folder(
500 cache
, folder
, itemFolder
);
501 addritem_folder_set_name( newFolder
,
502 addrcache_get_name( cacheFrom
) );
504 g_list_append( folderGroup
, newFolder
);
507 _("Cannot copy an address book to itself.") );
513 /* Finally add any groups */
518 node
= g_list_next( node
);
519 cacheFrom
= addrindex_get_cache(
520 clipBoard
->addressIndex
, item
->cacheID
);
521 if( cacheFrom
== NULL
) continue;
522 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
524 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
525 ItemGroup
*group
, *newGroup
;
527 group
= ( ItemGroup
* ) aio
;
528 newGroup
= addrclip_cache_add_group(
529 cache
, folder
, group
, copyList
);
531 g_list_append( folderGroup
, newGroup
);
538 addrclip_free_copy_list( copyList
);
539 g_list_free( copyList
);
546 * Move items in list into new folder
547 * Enter: cache Target address cache.
548 * targetFolder Target folder where data is pasted.
549 * itemList List of items to paste.
550 * clipBoard Clipboard.
551 * Return: List of group or folder items added.
553 static GList
*addrclip_cache_move_items(
554 AddressCache
*cache
, ItemFolder
*targetFolder
, GList
*itemList
,
555 AddressClipboard
*clipBoard
)
559 AddrSelectItem
*item
;
561 AddressCache
*cacheFrom
;
567 node
= g_list_next( node
);
568 cacheFrom
= addrindex_get_cache(
569 clipBoard
->addressIndex
, item
->cacheID
);
570 if( cacheFrom
== NULL
) continue;
571 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
573 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
576 person
= ( ItemPerson
* ) aio
;
577 addrcache_folder_move_person(
578 cache
, person
, targetFolder
);
580 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
583 group
= ( ItemGroup
* ) aio
;
584 addrcache_folder_move_group(
585 cache
, group
, targetFolder
);
586 folderGroup
= g_list_append( folderGroup
, group
);
588 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
589 ItemFolder
*folder
= ( ItemFolder
* ) aio
;
591 if (!addrclip_is_subfolder_of(folder
, targetFolder
)) {
592 addrcache_folder_move_folder(
593 cache
, folder
, targetFolder
);
595 g_list_append( folderGroup
, folder
);
598 _("Cannot move a folder to itself or to its sub-structure.") );
607 * Get address cache of first item in list. This assumes that all items in
608 * the clipboard are located in the same cache.
609 * Enter: clipBoard Clipboard.
610 * Return: List of group or folder items added.
612 static AddressCache
*addrclip_list_get_cache( AddressClipboard
*clipBoard
) {
615 AddrSelectItem
*item
;
618 itemList
= clipBoard
->objectList
;
620 item
= itemList
->data
;
621 cache
= addrindex_get_cache(
622 clipBoard
->addressIndex
, item
->cacheID
);
628 * Paste (copy) clipboard into address book.
629 * Enter: clipBoard Clipboard.
630 * book Target address book.
631 * folder Target folder where data is pasted, or null for root folder.
632 * Return: List of group or folder items added.
634 GList
*addrclip_paste_copy(
635 AddressClipboard
*clipBoard
, AddressBookFile
*book
,
642 cm_return_val_if_fail( clipBoard
!= NULL
, NULL
);
644 cache
= book
->addressCache
;
645 if( folder
== NULL
) folder
= cache
->rootFolder
;
648 itemList
= clipBoard
->objectList
;
649 folderGroup
= addrclip_cache_add_folder(
650 cache
, folder
, itemList
, clipBoard
);
656 * Remove items that were cut from clipboard.
657 * Enter: clipBoard Clipboard.
659 void addrclip_delete_item( AddressClipboard
*clipBoard
) {
660 AddrSelectItem
*item
;
662 AddressCache
*cacheFrom
;
665 /* If cutting within current cache, no deletion is necessary */
666 if( clipBoard
->moveFlag
) return;
669 node
= clipBoard
->objectList
;
672 node
= g_list_next( node
);
673 cacheFrom
= addrindex_get_cache(
674 clipBoard
->addressIndex
, item
->cacheID
);
675 if( cacheFrom
== NULL
) continue;
676 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
678 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_GROUP
) {
681 group
= ( ItemGroup
* ) aio
;
682 group
= addrcache_remove_group( cacheFrom
, group
);
684 addritem_free_item_group( group
);
690 /* Remove persons and folders */
691 node
= clipBoard
->objectList
;
694 node
= g_list_next( node
);
696 cacheFrom
= addrindex_get_cache(
697 clipBoard
->addressIndex
, item
->cacheID
);
698 if( cacheFrom
== NULL
) continue;
700 aio
= addrcache_get_object( cacheFrom
, item
->uid
);
702 if( ADDRITEM_TYPE(aio
) == ITEMTYPE_PERSON
) {
705 person
= ( ItemPerson
* ) aio
;
706 person
= addrcache_remove_person( cacheFrom
, person
);
708 addritem_free_item_person( person
);
711 else if( ADDRITEM_TYPE(aio
) == ITEMTYPE_FOLDER
) {
712 ItemFolder
*itemFolder
;
714 itemFolder
= ( ItemFolder
* ) aio
;
715 itemFolder
= addrcache_remove_folder_delete(
716 cacheFrom
, itemFolder
);
717 addritem_free_item_folder( itemFolder
);
724 * Paste (move) clipboard into address book.
725 * Enter: clipBoard Clipboard.
726 * book Target address book.
727 * folder Target folder where data is pasted, or null for root folder.
728 * Return: List of group or folder items added.
730 GList
*addrclip_paste_cut(
731 AddressClipboard
*clipBoard
, AddressBookFile
*book
,
734 AddressCache
*cache
, *cacheFrom
;
738 cm_return_val_if_fail( clipBoard
!= NULL
, NULL
);
740 cache
= book
->addressCache
;
741 if( folder
== NULL
) folder
= cache
->rootFolder
;
744 clipBoard
->moveFlag
= FALSE
;
745 cacheFrom
= addrclip_list_get_cache( clipBoard
);
746 if( cacheFrom
&& cacheFrom
== cache
) {
747 /* Move items between folders in same book */
748 itemList
= clipBoard
->objectList
;
749 folderGroup
= addrclip_cache_move_items(
750 cache
, folder
, itemList
, clipBoard
);
751 clipBoard
->moveFlag
= TRUE
;
754 /* Move items across address books */
755 itemList
= clipBoard
->objectList
;
756 folderGroup
= addrclip_cache_add_folder(
757 cache
, folder
, itemList
, clipBoard
);