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"
63 AddressClipboard *addrclip_create( void ) {
64 AddressClipboard *clipBoard;
66 clipBoard = g_new0( AddressClipboard, 1 );
67 clipBoard->cutFlag = FALSE;
68 clipBoard->objectList = NULL;
75 void addrclip_clear( AddressClipboard *clipBoard ) {
79 cm_return_if_fail( clipBoard != NULL );
80 node = clipBoard->objectList;
83 addrselect_item_free( item );
85 node = g_list_next( node );
87 g_list_free( clipBoard->objectList );
88 clipBoard->objectList = NULL;
92 * Free up a clipboard.
94 void addrclip_free( AddressClipboard *clipBoard ) {
95 cm_return_if_fail( clipBoard != NULL );
97 addrclip_clear( clipBoard );
98 clipBoard->cutFlag = FALSE;
103 * Setup reference to address index.
105 void addrclip_set_index(
106 AddressClipboard *clipBoard, AddressIndex *addrIndex )
108 cm_return_if_fail( clipBoard != NULL );
109 cm_return_if_fail( addrIndex != NULL );
110 clipBoard->addressIndex = addrIndex;
114 * Test whether clipboard is empty.
115 * Enter: clipBoard Clipboard.
116 * Return: TRUE if clipboard is empty.
118 gboolean addrclip_is_empty( AddressClipboard *clipBoard ) {
119 gboolean retVal = TRUE;
122 if( clipBoard->objectList ) retVal = FALSE;
128 * Add a list of address selection objects to clipbard.
129 * Enter: clipBoard Clipboard.
130 * addrList List of address selection objects.
132 void addrclip_add( AddressClipboard *clipBoard, AddrSelectList *asl ) {
135 cm_return_if_fail( clipBoard != NULL );
136 cm_return_if_fail( asl != NULL );
137 node = asl->listSelect;
139 AddrSelectItem *item, *itemCopy;
142 itemCopy = addrselect_item_copy( item );
143 clipBoard->objectList =
144 g_list_append( clipBoard->objectList, itemCopy );
145 node = g_list_next( node );
150 * Show clipboard contents.
151 * Enter: clipBoard Clipboard.
152 * stream Output stream.
154 void addrclip_list_show( AddressClipboard *clipBoard, FILE *stream ) {
159 cm_return_if_fail( clipBoard != NULL );
160 node = clipBoard->objectList;
161 while( node != NULL ) {
162 AddrSelectItem *item;
165 addrselect_item_print( item, stream );
167 cache = addrindex_get_cache( clipBoard->addressIndex, item->cacheID );
168 aio = addrcache_get_object( cache, item->uid );
170 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
171 addritem_print_item_person( ( ItemPerson * ) aio, stream );
173 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) {
174 addritem_print_item_email( ( ItemEMail * ) aio, stream );
176 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
177 addritem_print_item_group( ( ItemGroup * ) aio, stream );
179 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
180 addritem_print_item_folder( ( ItemFolder * ) aio, stream );
183 node = g_list_next( node );
187 /* Pasted address pointers */
188 typedef struct _AddrClip_EMail_ AddrClip_EMail;
189 struct _AddrClip_EMail_ {
195 * Free up specified list of addresses.
197 static void addrclip_free_copy_list( GList *copyList ) {
202 AddrClip_EMail *em = node->data;
207 node = g_list_next( node );
212 * Paste person into cache.
213 * Enter: cache Address cache to paste into.
214 * folder Folder to store
215 * person Person to paste.
216 * copyLIst List of email addresses pasted.
217 * Return: Update list of email addresses pasted.
219 static GList *addrclip_cache_add_person(
220 AddressCache *cache, ItemFolder *folder, ItemPerson *person,
223 ItemPerson *newPerson;
226 UserAttribute *attrib;
227 UserAttribute *newAttrib;
232 newPerson = addritem_copy_item_person( person );
233 addrcache_id_person( cache, newPerson );
234 addrcache_folder_add_person( cache, folder, newPerson );
236 /* Copy email addresses */
237 node = person->listEMail;
240 newEMail = addritem_copy_item_email( email );
241 addrcache_id_email( cache, newEMail );
242 addrcache_person_add_email( cache, newPerson, newEMail );
243 node = g_list_next( node );
245 /* Take a copy of the original */
246 em = g_new0( AddrClip_EMail, 1 );
247 em->original = email;
249 copyList = g_list_append( copyList, em );
252 /* Copy user attributes */
253 node = person->listAttrib;
256 newAttrib = addritem_copy_attribute( attrib );
257 addrcache_id_attribute( cache, newAttrib );
258 addritem_person_add_attribute( newPerson, newAttrib );
259 node = g_list_next( node );
262 /* Set picture name and create picture file (from copy) if missing */
263 addritem_person_set_picture(newPerson, ADDRITEM_ID(newPerson));
264 if( strcmp(ADDRITEM_ID(newPerson), ADDRITEM_ID(person)) ) {
266 gchar *newPictureFile;
268 pictureFile = g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S, ADDRBOOK_DIR, G_DIR_SEPARATOR_S,
269 person->picture, ".png", NULL );
270 newPictureFile = g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S, ADDRBOOK_DIR, G_DIR_SEPARATOR_S,
271 newPerson->picture, ".png", NULL );
272 if (file_exist(pictureFile, FALSE) && !file_exist(newPictureFile, FALSE)) {
273 debug_print("copying contact picture file: %s -> %s\n", person->picture, newPerson->picture);
274 copy_file(pictureFile, newPictureFile, FALSE);
276 g_free( pictureFile );
277 g_free( newPictureFile );
284 * Search for new email record in copied email list.
285 * Enter: copyList List of copied email address mappings.
286 * emailOrig Original email item.
287 * Return: New email item corresponding to original item if pasted. Or NULL if
290 static ItemEMail *addrclip_find_copied_email(
291 GList *copyList, ItemEMail *emailOrig )
293 ItemEMail *emailCopy;
301 if( em->original == emailOrig ) {
302 emailCopy = em->copy;
305 node = g_list_next( node );
311 * Paste group into cache.
312 * Enter: cache Address cache to paste into.
313 * folder Folder to store
314 * group Group to paste.
315 * copyList List of email addresses pasted.
316 * Return: Group added.
318 static ItemGroup *addrclip_cache_add_group(
319 AddressCache *cache, ItemFolder *folder, ItemGroup *group,
323 ItemEMail *emailOrig, *emailCopy;
327 newGroup = addritem_copy_item_group( group );
328 addrcache_id_group( cache, newGroup );
329 addrcache_folder_add_group( cache, folder, newGroup );
331 /* Add references of copied addresses to group */
332 node = group->listEMail;
334 emailOrig = ( ItemEMail * ) node->data;
335 emailCopy = addrclip_find_copied_email( copyList, emailOrig );
337 addrcache_group_add_email( cache, newGroup, emailCopy );
339 node = g_list_next( node );
345 * Copy specified folder into cache. Note this functions uses pointers to
346 * folders to copy from. There should not be any deleted items referenced
347 * by these pointers!!!
348 * Enter: cache Address cache to copy into.
349 * targetFolder Target folder.
350 * folder Folder to copy.
351 * Return: Folder added.
353 static ItemFolder *addrclip_cache_copy_folder(
354 AddressCache *cache, ItemFolder *targetFolder, ItemFolder *folder )
356 ItemFolder *newFolder;
362 newFolder = addritem_copy_item_folder( folder );
363 addrcache_id_folder( cache, newFolder );
364 addrcache_folder_add_folder( cache, targetFolder, newFolder );
366 /* Copy people to new folder */
368 node = folder->listPerson;
370 ItemPerson *item = node->data;
371 node = g_list_next( node );
372 copyList = addrclip_cache_add_person(
373 cache, newFolder, item, copyList );
376 /* Copy groups to new folder */
377 node = folder->listGroup;
379 ItemGroup *item = node->data;
380 node = g_list_next( node );
381 newGroup = addrclip_cache_add_group(
382 cache, newFolder, item, copyList );
383 if (newGroup == NULL) {
384 g_message("error allocating memory for new group\n");
387 g_list_free( copyList );
389 /* Copy folders to new folder (recursive) */
390 node = folder->listFolder;
392 ItemFolder *item = node->data;
393 node = g_list_next( node );
394 addrclip_cache_copy_folder( cache, newFolder, item );
400 static gboolean addrclip_is_subfolder_of(ItemFolder *is_parent, ItemFolder *is_child)
405 cm_return_val_if_fail(is_parent != NULL, FALSE);
406 cm_return_val_if_fail(is_child != NULL, FALSE);
408 if (is_parent == is_child)
412 obj = folder->obj.parent;
414 if ((void*)obj == (void*)is_parent)
422 * Paste item list into address book.
423 * Enter: cache Target address cache.
424 * folder Target folder where data is pasted.
425 * itemList List of items to paste.
426 * clipBoard Clipboard.
427 * Return: List of group or folder items added.
429 static GList *addrclip_cache_add_folder(
430 AddressCache *cache, ItemFolder *folder, GList *itemList,
431 AddressClipboard *clipBoard )
435 AddrSelectItem *item;
437 AddressCache *cacheFrom;
447 node = g_list_next( node );
449 cacheFrom = addrindex_get_cache(
450 clipBoard->addressIndex, item->cacheID );
451 if( cacheFrom == NULL ) continue;
453 aio = addrcache_get_object( cacheFrom, item->uid );
455 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
458 person = ( ItemPerson * ) aio;
459 copyList = addrclip_cache_add_person(
460 cache, folder, person, copyList );
463 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_EMAIL ) {
466 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
467 haveGroups = TRUE; /* Process later */
469 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
470 ItemFolder *itemFolder, *newFolder;
472 itemFolder = ( ItemFolder * ) aio;
473 if (!addrclip_is_subfolder_of(itemFolder, folder)) {
474 newFolder = addrclip_cache_copy_folder(
475 cache, folder, itemFolder );
477 g_list_append( folderGroup, newFolder );
480 _("Cannot copy a folder to itself or to its sub-structure.") );
486 if( item->objectType == ITEMTYPE_DATASOURCE ) {
488 * Must be an address book - allow copy only if
489 * copying from a different cache.
491 if( cache != cacheFrom ) {
492 ItemFolder *itemFolder, *newFolder;
494 itemFolder = cacheFrom->rootFolder;
495 newFolder = addrclip_cache_copy_folder(
496 cache, folder, itemFolder );
497 addritem_folder_set_name( newFolder,
498 addrcache_get_name( cacheFrom ) );
500 g_list_append( folderGroup, newFolder );
503 _("Cannot copy an address book to itself.") );
509 /* Finally add any groups */
514 node = g_list_next( node );
515 cacheFrom = addrindex_get_cache(
516 clipBoard->addressIndex, item->cacheID );
517 if( cacheFrom == NULL ) continue;
518 aio = addrcache_get_object( cacheFrom, item->uid );
520 if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
521 ItemGroup *group, *newGroup;
523 group = ( ItemGroup * ) aio;
524 newGroup = addrclip_cache_add_group(
525 cache, folder, group, copyList );
527 g_list_append( folderGroup, newGroup );
534 addrclip_free_copy_list( copyList );
535 g_list_free( copyList );
542 * Move items in list into new folder
543 * Enter: cache Target address cache.
544 * targetFolder Target folder where data is pasted.
545 * itemList List of items to paste.
546 * clipBoard Clipboard.
547 * Return: List of group or folder items added.
549 static GList *addrclip_cache_move_items(
550 AddressCache *cache, ItemFolder *targetFolder, GList *itemList,
551 AddressClipboard *clipBoard )
555 AddrSelectItem *item;
557 AddressCache *cacheFrom;
563 node = g_list_next( node );
564 cacheFrom = addrindex_get_cache(
565 clipBoard->addressIndex, item->cacheID );
566 if( cacheFrom == NULL ) continue;
567 aio = addrcache_get_object( cacheFrom, item->uid );
569 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
572 person = ( ItemPerson * ) aio;
573 addrcache_folder_move_person(
574 cache, person, targetFolder );
576 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
579 group = ( ItemGroup * ) aio;
580 addrcache_folder_move_group(
581 cache, group, targetFolder );
582 folderGroup = g_list_append( folderGroup, group );
584 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
585 ItemFolder *folder = ( ItemFolder * ) aio;
587 if (!addrclip_is_subfolder_of(folder, targetFolder)) {
588 addrcache_folder_move_folder(
589 cache, folder, targetFolder );
591 g_list_append( folderGroup, folder );
594 _("Cannot move a folder to itself or to its sub-structure.") );
603 * Get address cache of first item in list. This assumes that all items in
604 * the clipboard are located in the same cache.
605 * Enter: clipBoard Clipboard.
606 * Return: List of group or folder items added.
608 static AddressCache *addrclip_list_get_cache( AddressClipboard *clipBoard ) {
611 AddrSelectItem *item;
614 itemList = clipBoard->objectList;
616 item = itemList->data;
617 cache = addrindex_get_cache(
618 clipBoard->addressIndex, item->cacheID );
624 * Paste (copy) clipboard into address book.
625 * Enter: clipBoard Clipboard.
626 * book Target address book.
627 * folder Target folder where data is pasted, or null for root folder.
628 * Return: List of group or folder items added.
630 GList *addrclip_paste_copy(
631 AddressClipboard *clipBoard, AddressBookFile *book,
638 cm_return_val_if_fail( clipBoard != NULL, NULL );
640 cache = book->addressCache;
641 if( folder == NULL ) folder = cache->rootFolder;
644 itemList = clipBoard->objectList;
645 folderGroup = addrclip_cache_add_folder(
646 cache, folder, itemList, clipBoard );
652 * Remove items that were cut from clipboard.
653 * Enter: clipBoard Clipboard.
655 void addrclip_delete_item( AddressClipboard *clipBoard ) {
656 AddrSelectItem *item;
658 AddressCache *cacheFrom;
661 /* If cutting within current cache, no deletion is necessary */
662 if( clipBoard->moveFlag ) return;
665 node = clipBoard->objectList;
668 node = g_list_next( node );
669 cacheFrom = addrindex_get_cache(
670 clipBoard->addressIndex, item->cacheID );
671 if( cacheFrom == NULL ) continue;
672 aio = addrcache_get_object( cacheFrom, item->uid );
674 if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
677 group = ( ItemGroup * ) aio;
678 group = addrcache_remove_group( cacheFrom, group );
680 addritem_free_item_group( group );
686 /* Remove persons and folders */
687 node = clipBoard->objectList;
690 node = g_list_next( node );
692 cacheFrom = addrindex_get_cache(
693 clipBoard->addressIndex, item->cacheID );
694 if( cacheFrom == NULL ) continue;
696 aio = addrcache_get_object( cacheFrom, item->uid );
698 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
701 person = ( ItemPerson * ) aio;
702 person = addrcache_remove_person( cacheFrom, person );
704 addritem_free_item_person( person );
707 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_FOLDER ) {
708 ItemFolder *itemFolder;
710 itemFolder = ( ItemFolder * ) aio;
711 itemFolder = addrcache_remove_folder_delete(
712 cacheFrom, itemFolder );
713 addritem_free_item_folder( itemFolder );
720 * Paste (move) clipboard into address book.
721 * Enter: clipBoard Clipboard.
722 * book Target address book.
723 * folder Target folder where data is pasted, or null for root folder.
724 * Return: List of group or folder items added.
726 GList *addrclip_paste_cut(
727 AddressClipboard *clipBoard, AddressBookFile *book,
730 AddressCache *cache, *cacheFrom;
734 cm_return_val_if_fail( clipBoard != NULL, NULL );
736 cache = book->addressCache;
737 if( folder == NULL ) folder = cache->rootFolder;
740 clipBoard->moveFlag = FALSE;
741 cacheFrom = addrclip_list_get_cache( clipBoard );
742 if( cacheFrom && cacheFrom == cache ) {
743 /* Move items between folders in same book */
744 itemList = clipBoard->objectList;
745 folderGroup = addrclip_cache_move_items(
746 cache, folder, itemList, clipBoard );
747 clipBoard->moveFlag = TRUE;
750 /* Move items across address books */
751 itemList = clipBoard->objectList;
752 folderGroup = addrclip_cache_add_folder(
753 cache, folder, itemList, clipBoard );