Improve some sieve-related translations
[claws.git] / src / addrclip.c
blobc81c047954141aa0537abfdfeb79d36a58e3de25
1 /*
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.
48 #include "config.h"
50 #include <stdio.h>
51 #include <glib.h>
52 #include <glib/gi18n.h>
54 #include "addrcache.h"
55 #include "addrbook.h"
56 #include "addrselect.h"
57 #include "addrindex.h"
58 #include "addrclip.h"
59 #include "alertpanel.h"
60 #include "defs.h"
61 #include "file-utils.h"
64 * Create a clipboard.
66 AddressClipboard *addrclip_create( void ) {
67 AddressClipboard *clipBoard;
69 clipBoard = g_new0( AddressClipboard, 1 );
70 clipBoard->cutFlag = FALSE;
71 clipBoard->objectList = NULL;
72 return clipBoard;
76 * Clear clipboard.
78 void addrclip_clear( AddressClipboard *clipBoard ) {
79 GList *node;
80 AddrSelectItem *item;
82 cm_return_if_fail( clipBoard != NULL );
83 node = clipBoard->objectList;
84 while( node ) {
85 item = node->data;
86 addrselect_item_free( item );
87 node->data = NULL;
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;
102 g_free(clipBoard);
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;
124 if( clipBoard ) {
125 if( clipBoard->objectList ) retVal = FALSE;
127 return retVal;
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 ) {
136 GList *node;
138 cm_return_if_fail( clipBoard != NULL );
139 cm_return_if_fail( asl != NULL );
140 node = asl->listSelect;
141 while( node ) {
142 AddrSelectItem *item, *itemCopy;
144 item = node->data;
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 ) {
158 GList *node;
159 AddrItemObject *aio;
160 AddressCache *cache;
162 cm_return_if_fail( clipBoard != NULL );
163 node = clipBoard->objectList;
164 while( node != NULL ) {
165 AddrSelectItem *item;
167 item = node->data;
168 addrselect_item_print( item, stream );
170 cache = addrindex_get_cache( clipBoard->addressIndex, item->cacheID );
171 aio = addrcache_get_object( cache, item->uid );
172 if( aio ) {
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_ {
193 ItemEMail *original;
194 ItemEMail *copy;
198 * Free up specified list of addresses.
200 static void addrclip_free_copy_list( GList *copyList ) {
201 GList *node;
203 node = copyList;
204 while( node ) {
205 AddrClip_EMail *em = node->data;
206 em->original = NULL;
207 em->copy = NULL;
208 g_free( em );
209 em = NULL;
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,
224 GList *copyList )
226 ItemPerson *newPerson;
227 ItemEMail *email;
228 ItemEMail *newEMail;
229 UserAttribute *attrib;
230 UserAttribute *newAttrib;
231 GList *node;
232 AddrClip_EMail *em;
234 /* Copy person */
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;
241 while( node ) {
242 email = node->data;
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;
251 em->copy = newEMail;
252 copyList = g_list_append( copyList, em );
255 /* Copy user attributes */
256 node = person->listAttrib;
257 while( node ) {
258 attrib = node->data;
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)) ) {
268 gchar *pictureFile;
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 );
283 return copyList;
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
291 * not found.
293 static ItemEMail *addrclip_find_copied_email(
294 GList *copyList, ItemEMail *emailOrig )
296 ItemEMail *emailCopy;
297 GList *node;
298 AddrClip_EMail *em;
300 emailCopy = NULL;
301 node = copyList;
302 while( node ) {
303 em = node->data;
304 if( em->original == emailOrig ) {
305 emailCopy = em->copy;
306 break;
308 node = g_list_next( node );
310 return emailCopy;
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,
323 GList *copyList )
325 ItemGroup *newGroup;
326 ItemEMail *emailOrig, *emailCopy;
327 GList *node;
329 /* Copy group */
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;
336 while( node ) {
337 emailOrig = ( ItemEMail * ) node->data;
338 emailCopy = addrclip_find_copied_email( copyList, emailOrig );
339 if( emailCopy ) {
340 addrcache_group_add_email( cache, newGroup, emailCopy );
342 node = g_list_next( node );
344 return newGroup;
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;
360 ItemGroup *newGroup;
361 GList *node;
362 GList *copyList;
364 /* Copy folder */
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 */
370 copyList = NULL;
371 node = folder->listPerson;
372 while( node ) {
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;
381 while( node ) {
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;
394 while( node ) {
395 ItemFolder *item = node->data;
396 node = g_list_next( node );
397 addrclip_cache_copy_folder( cache, newFolder, item );
400 return newFolder;
403 static gboolean addrclip_is_subfolder_of(ItemFolder *is_parent, ItemFolder *is_child)
405 ItemFolder *folder;
406 AddrItemObject *obj;
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)
412 return TRUE;
414 folder = is_child;
415 obj = folder->obj.parent;
416 while (obj) {
417 if ((void*)obj == (void*)is_parent)
418 return TRUE;
419 obj = obj->parent;
421 return FALSE;
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 )
436 GList *folderGroup;
437 GList *node;
438 AddrSelectItem *item;
439 AddrItemObject *aio;
440 AddressCache *cacheFrom;
441 gboolean haveGroups;
442 GList *copyList;
444 folderGroup = NULL;
445 copyList = NULL;
446 haveGroups = FALSE;
447 node = itemList;
448 while( node ) {
449 item = node->data;
450 node = g_list_next( node );
452 cacheFrom = addrindex_get_cache(
453 clipBoard->addressIndex, item->cacheID );
454 if( cacheFrom == NULL ) continue;
455 if( item->uid ) {
456 aio = addrcache_get_object( cacheFrom, item->uid );
457 if( aio ) {
458 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
459 ItemPerson *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 );
479 folderGroup =
480 g_list_append( folderGroup, newFolder );
481 } else {
482 alertpanel_error(
483 _("Cannot copy a folder to itself or to its sub-structure.") );
488 else {
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 ) );
502 folderGroup =
503 g_list_append( folderGroup, newFolder );
504 } else {
505 alertpanel_error(
506 _("Cannot copy an address book to itself.") );
512 /* Finally add any groups */
513 if( haveGroups ) {
514 node = itemList;
515 while( node ) {
516 item = node->data;
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 );
522 if( aio ) {
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 );
529 folderGroup =
530 g_list_append( folderGroup, newGroup );
536 /* Free up stuff */
537 addrclip_free_copy_list( copyList );
538 g_list_free( copyList );
539 copyList = NULL;
541 return folderGroup;
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 )
556 GList *folderGroup;
557 GList *node;
558 AddrSelectItem *item;
559 AddrItemObject *aio;
560 AddressCache *cacheFrom;
562 folderGroup = NULL;
563 node = itemList;
564 while( node ) {
565 item = node->data;
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 );
571 if( aio ) {
572 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
573 ItemPerson *person;
575 person = ( ItemPerson * ) aio;
576 addrcache_folder_move_person(
577 cache, person, targetFolder );
579 else if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
580 ItemGroup *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 );
593 folderGroup =
594 g_list_append( folderGroup, folder );
595 } else {
596 alertpanel_error(
597 _("Cannot move a folder to itself or to its sub-structure.") );
602 return folderGroup;
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 ) {
612 AddressCache *cache;
613 GList *itemList;
614 AddrSelectItem *item;
616 cache = NULL;
617 itemList = clipBoard->objectList;
618 if( itemList ) {
619 item = itemList->data;
620 cache = addrindex_get_cache(
621 clipBoard->addressIndex, item->cacheID );
623 return cache;
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,
635 ItemFolder *folder )
637 AddressCache *cache;
638 GList *itemList;
639 GList *folderGroup;
641 cm_return_val_if_fail( clipBoard != NULL, NULL );
643 cache = book->addressCache;
644 if( folder == NULL ) folder = cache->rootFolder;
646 folderGroup = NULL;
647 itemList = clipBoard->objectList;
648 folderGroup = addrclip_cache_add_folder(
649 cache, folder, itemList, clipBoard );
651 return folderGroup;
655 * Remove items that were cut from clipboard.
656 * Enter: clipBoard Clipboard.
658 void addrclip_delete_item( AddressClipboard *clipBoard ) {
659 AddrSelectItem *item;
660 AddrItemObject *aio;
661 AddressCache *cacheFrom;
662 GList *node;
664 /* If cutting within current cache, no deletion is necessary */
665 if( clipBoard->moveFlag ) return;
667 /* Remove groups */
668 node = clipBoard->objectList;
669 while( node ) {
670 item = node->data;
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 );
676 if( aio ) {
677 if( ADDRITEM_TYPE(aio) == ITEMTYPE_GROUP ) {
678 ItemGroup *group;
680 group = ( ItemGroup * ) aio;
681 group = addrcache_remove_group( cacheFrom, group );
682 if( group ) {
683 addritem_free_item_group( group );
689 /* Remove persons and folders */
690 node = clipBoard->objectList;
691 while( node ) {
692 item = node->data;
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 );
700 if( aio ) {
701 if( ADDRITEM_TYPE(aio) == ITEMTYPE_PERSON ) {
702 ItemPerson *person;
704 person = ( ItemPerson * ) aio;
705 person = addrcache_remove_person( cacheFrom, person );
706 if( 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,
731 ItemFolder *folder )
733 AddressCache *cache, *cacheFrom;
734 GList *itemList;
735 GList *folderGroup;
737 cm_return_val_if_fail( clipBoard != NULL, NULL );
739 cache = book->addressCache;
740 if( folder == NULL ) folder = cache->rootFolder;
742 folderGroup = NULL;
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;
752 else {
753 /* Move items across address books */
754 itemList = clipBoard->objectList;
755 folderGroup = addrclip_cache_add_folder(
756 cache, folder, itemList, clipBoard );
759 return folderGroup;
762 * End of Source.