2 * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "evolution-config.h"
20 #include <glib/gi18n-lib.h>
23 #include <camel/camel.h>
24 #include <libedataserver/libedataserver.h>
26 #include "shell/e-shell-view.h"
28 #include "e-templates-store.h"
30 struct _ETemplatesStorePrivate
{
31 GWeakRef
*account_store_weakref
; /* EMailAccountStore * */
33 gulong service_enabled_handler_id
;
34 gulong service_disabled_handler_id
;
35 gulong service_removed_handler_id
;
36 gulong source_changed_handler_id
;
39 GCancellable
*cancellable
;
40 GSList
*stores
; /* TmplStoreData *, sorted by account_store options; those with set templates dir */
41 guint menu_refresh_idle_id
;
44 G_DEFINE_TYPE (ETemplatesStore
, e_templates_store
, G_TYPE_OBJECT
);
56 static guint signals
[LAST_SIGNAL
];
59 templates_store_lock (ETemplatesStore
*templates_store
)
61 g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store
));
63 g_mutex_lock (&templates_store
->priv
->busy_lock
);
67 templates_store_unlock (ETemplatesStore
*templates_store
)
69 g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store
));
71 g_mutex_unlock (&templates_store
->priv
->busy_lock
);
75 templates_store_emit_changed (ETemplatesStore
*templates_store
)
77 g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store
));
79 g_signal_emit (templates_store
, signals
[CHANGED
], 0, NULL
);
83 tmpl_folder_data_folder_changed_cb (CamelFolder
*folder
,
84 CamelFolderChangeInfo
*change_info
,
88 tmpl_store_data_folder_created_cb (CamelStore
*store
,
89 CamelFolderInfo
*folder_info
,
92 tmpl_store_data_folder_deleted_cb (CamelStore
*store
,
93 CamelFolderInfo
*folder_info
,
96 tmpl_store_data_folder_renamed_cb (CamelStore
*store
,
97 const gchar
*old_name
,
98 CamelFolderInfo
*folder_info
,
102 tmpl_store_data_notify_display_name_cb (CamelService
*service
,
106 typedef struct _TmplMessageData
{
107 const gchar
*subject
; /* Allocated by camel-pstring */
108 const gchar
*uid
; /* Allocated by camel-pstring */
112 tmpl_sanitized_subject (const gchar
*subject
)
114 if (!subject
|| !*subject
)
115 subject
= _("No Title");
120 static TmplMessageData
*
121 tmpl_message_data_new (CamelMessageInfo
*info
)
123 TmplMessageData
*tmd
;
125 g_return_val_if_fail (info
!= NULL
, NULL
);
127 tmd
= g_new0 (TmplMessageData
, 1);
128 tmd
->subject
= camel_pstring_strdup (tmpl_sanitized_subject (camel_message_info_get_subject (info
)));
129 tmd
->uid
= camel_pstring_strdup (camel_message_info_get_uid (info
));
135 tmpl_message_data_free (gpointer ptr
)
137 TmplMessageData
*tmd
= ptr
;
140 camel_pstring_free (tmd
->subject
);
141 camel_pstring_free (tmd
->uid
);
147 tmpl_message_data_change_subject (TmplMessageData
*tmd
,
148 const gchar
*subject
)
150 g_return_if_fail (tmd
!= NULL
);
152 if (subject
!= tmd
->subject
) {
153 camel_pstring_free (tmd
->subject
);
154 tmd
->subject
= camel_pstring_strdup (tmpl_sanitized_subject (subject
));
159 tmpl_message_data_compare (gconstpointer ptr1
,
162 const TmplMessageData
*tmd1
= ptr1
, *tmd2
= ptr2
;
164 if (!tmd1
|| !tmd2
) {
172 return g_utf8_collate (tmd1
->subject
? tmd1
->subject
: "", tmd2
->subject
? tmd2
->subject
: "");
175 typedef struct _TmplFolderData
{
176 volatile gint ref_count
;
177 GWeakRef
*templates_store_weakref
; /* ETemplatesStore * */
179 gulong changed_handler_id
;
182 /* This might look inefficient, but the rebuild of the menu is called
183 much more often then the remove of a message from the folder, thus
184 it's cheaper to traverse one by one here, then re-sort the content
185 every time the menu is being rebuild. */
186 GSList
*messages
; /* TmplMessageData *, ordered by data->subject */
189 static TmplFolderData
*
190 tmpl_folder_data_new (ETemplatesStore
*templates_store
,
195 g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store
), NULL
);
196 g_return_val_if_fail (CAMEL_IS_FOLDER (folder
), NULL
);
198 tfd
= g_new0 (TmplFolderData
, 1);
200 tfd
->templates_store_weakref
= e_weak_ref_new (templates_store
);
201 tfd
->folder
= g_object_ref (folder
);
202 tfd
->changed_handler_id
= g_signal_connect (folder
, "changed",
203 G_CALLBACK (tmpl_folder_data_folder_changed_cb
), tfd
);
204 g_mutex_init (&tfd
->busy_lock
);
205 tfd
->messages
= NULL
;
210 static TmplFolderData
*
211 tmpl_folder_data_ref (TmplFolderData
*tfd
)
213 g_return_val_if_fail (tfd
!= NULL
, NULL
);
215 g_atomic_int_inc (&tfd
->ref_count
);
221 tmpl_folder_data_unref (gpointer ptr
)
223 TmplFolderData
*tfd
= ptr
;
226 if (!g_atomic_int_dec_and_test (&tfd
->ref_count
))
229 if (tfd
->folder
&& tfd
->changed_handler_id
) {
230 g_signal_handler_disconnect (tfd
->folder
, tfd
->changed_handler_id
);
231 tfd
->changed_handler_id
= 0;
234 if (tfd
->templates_store_weakref
) {
235 e_weak_ref_free (tfd
->templates_store_weakref
);
236 tfd
->templates_store_weakref
= NULL
;
239 g_clear_object (&tfd
->folder
);
241 g_mutex_clear (&tfd
->busy_lock
);
242 g_slist_free_full (tfd
->messages
, tmpl_message_data_free
);
243 tfd
->messages
= NULL
;
250 tmpl_folder_data_lock (TmplFolderData
*tfd
)
252 g_return_if_fail (tfd
!= NULL
);
254 g_mutex_lock (&tfd
->busy_lock
);
258 tmpl_folder_data_unlock (TmplFolderData
*tfd
)
260 g_return_if_fail (tfd
!= NULL
);
262 g_mutex_unlock (&tfd
->busy_lock
);
266 tmpl_folder_data_add_message (TmplFolderData
*tfd
,
267 CamelMessageInfo
*info
)
269 TmplMessageData
*tmd
;
271 g_return_if_fail (tfd
!= NULL
);
272 g_return_if_fail (info
!= NULL
);
274 tmd
= tmpl_message_data_new (info
);
275 g_return_if_fail (tmd
!= NULL
);
277 /* The caller is responsible to call tmpl_folder_data_sort() */
278 tfd
->messages
= g_slist_prepend (tfd
->messages
, tmd
);
281 static TmplMessageData
*
282 tmpl_folder_data_find_message (TmplFolderData
*tfd
,
287 g_return_val_if_fail (tfd
!= NULL
, NULL
);
288 g_return_val_if_fail (uid
!= NULL
, NULL
);
290 for (link
= tfd
->messages
; link
; link
= g_slist_next (link
)) {
291 TmplMessageData
*tmd
= link
->data
;
296 if (uid
== tmd
->uid
|| g_strcmp0 (uid
, tmd
->uid
) == 0)
304 tmpl_folder_data_remove_message (TmplFolderData
*tfd
,
307 TmplMessageData
*tmd
;
309 g_return_val_if_fail (tfd
!= NULL
, FALSE
);
310 g_return_val_if_fail (uid
!= NULL
, FALSE
);
312 tmd
= tmpl_folder_data_find_message (tfd
, uid
);
314 tfd
->messages
= g_slist_remove (tfd
->messages
, tmd
);
315 tmpl_message_data_free (tmd
);
324 tmpl_folder_data_change_message (TmplFolderData
*tfd
,
325 CamelMessageInfo
*info
)
327 TmplMessageData
*tmd
;
328 const gchar
*subject
;
329 gboolean changed
= FALSE
;
331 g_return_val_if_fail (tfd
!= NULL
, FALSE
);
332 g_return_val_if_fail (info
!= NULL
, FALSE
);
334 tmd
= tmpl_folder_data_find_message (tfd
, camel_message_info_get_uid (info
));
336 if (!(camel_message_info_get_flags (info
) & (CAMEL_MESSAGE_JUNK
| CAMEL_MESSAGE_DELETED
))) {
337 tmpl_folder_data_add_message (tfd
, info
);
344 if ((camel_message_info_get_flags (info
) & (CAMEL_MESSAGE_JUNK
| CAMEL_MESSAGE_DELETED
)) != 0) {
345 return tmpl_folder_data_remove_message (tfd
, camel_message_info_get_uid (info
));
348 subject
= tmpl_sanitized_subject (camel_message_info_get_subject (info
));
350 if (g_strcmp0 (subject
, tmd
->subject
) != 0) {
351 tmpl_message_data_change_subject (tmd
, subject
);
352 /* The caller is responsible to call tmpl_folder_data_sort() */
360 tmpl_folder_data_sort (TmplFolderData
*tfd
)
362 g_return_if_fail (tfd
!= NULL
);
364 tfd
->messages
= g_slist_sort (tfd
->messages
, tmpl_message_data_compare
);
368 tmpl_folder_data_compare (gconstpointer ptr1
,
371 const TmplFolderData
*tfd1
= ptr1
, *tfd2
= ptr2
;
372 const gchar
*display_name1
, *display_name2
;
374 if (!tfd1
|| !tfd2
) {
378 return tfd1
? -1 : 1;
381 display_name1
= camel_folder_get_display_name (tfd1
->folder
);
382 display_name2
= camel_folder_get_display_name (tfd2
->folder
);
384 return g_utf8_collate (display_name1
? display_name1
: "", display_name2
? display_name2
: "");
388 tmpl_folder_data_update_done_cb (GObject
*source
,
389 GAsyncResult
*result
,
392 TmplFolderData
*tfd
= user_data
;
393 GError
*local_error
= NULL
;
395 g_return_if_fail (tfd
!= NULL
);
396 g_return_if_fail (g_task_is_valid (result
, source
));
398 if (g_task_propagate_boolean (G_TASK (result
), &local_error
)) {
399 /* Content changed, rebuild menu when needed */
400 ETemplatesStore
*templates_store
;
402 templates_store
= g_weak_ref_get (tfd
->templates_store_weakref
);
403 if (templates_store
) {
404 templates_store_emit_changed (templates_store
);
405 g_object_unref (templates_store
);
407 } else if (local_error
) {
408 g_debug ("%s: Failed with error: %s", G_STRFUNC
, local_error
->message
);
411 g_clear_error (&local_error
);
414 typedef struct _TfdUpdateData
{
416 GPtrArray
*added_uids
;
417 GPtrArray
*changed_uids
;
421 tfd_update_data_free (gpointer ptr
)
423 TfdUpdateData
*tud
= ptr
;
426 tmpl_folder_data_unref (tud
->tfd
);
427 g_ptr_array_free (tud
->added_uids
, TRUE
);
428 g_ptr_array_free (tud
->changed_uids
, TRUE
);
434 tmpl_folder_data_update_sync (TmplFolderData
*tfd
,
435 const GPtrArray
*added_uids
,
436 const GPtrArray
*changed_uids
,
437 GCancellable
*cancellable
)
439 GPtrArray
*all_uids
= NULL
;
440 CamelMessageInfo
*info
;
442 gboolean changed
= FALSE
;
444 g_return_val_if_fail (tfd
!= NULL
, FALSE
);
445 g_return_val_if_fail (CAMEL_IS_FOLDER (tfd
->folder
), FALSE
);
447 if (!added_uids
|| !changed_uids
|| added_uids
->len
+ changed_uids
->len
> 10)
448 camel_folder_summary_prepare_fetch_all (camel_folder_get_folder_summary (tfd
->folder
), NULL
);
450 if (!added_uids
&& !changed_uids
) {
451 all_uids
= camel_folder_summary_get_array (camel_folder_get_folder_summary (tfd
->folder
));
452 added_uids
= all_uids
;
455 tmpl_folder_data_lock (tfd
);
457 for (ii
= 0; added_uids
&& ii
< added_uids
->len
; ii
++) {
458 const gchar
*uid
= added_uids
->pdata
[ii
];
460 info
= camel_folder_summary_get (camel_folder_get_folder_summary (tfd
->folder
), uid
);
462 if (!(camel_message_info_get_flags (info
) & (CAMEL_MESSAGE_JUNK
| CAMEL_MESSAGE_DELETED
))) {
463 /* Sometimes the 'add' notification can come after the 'change',
464 thus use the change_message() which covers both cases. */
465 changed
= tmpl_folder_data_change_message (tfd
, info
) || changed
;
467 changed
= tmpl_folder_data_remove_message (tfd
, camel_message_info_get_uid (info
)) || changed
;
470 g_clear_object (&info
);
474 for (ii
= 0; changed_uids
&& ii
< changed_uids
->len
; ii
++) {
475 const gchar
*uid
= changed_uids
->pdata
[ii
];
477 info
= camel_folder_summary_get (camel_folder_get_folder_summary (tfd
->folder
), uid
);
479 changed
= tmpl_folder_data_change_message (tfd
, info
) || changed
;
480 g_clear_object (&info
);
485 tmpl_folder_data_sort (tfd
);
488 camel_folder_summary_free_array (all_uids
);
490 tmpl_folder_data_unlock (tfd
);
496 tmpl_folder_data_update_thread (GTask
*task
,
497 gpointer source_object
,
499 GCancellable
*cancellable
)
501 TfdUpdateData
*tud
= task_data
;
504 g_return_if_fail (tud
!= NULL
);
505 g_return_if_fail (tud
->tfd
!= NULL
);
506 g_return_if_fail (tud
->added_uids
!= NULL
);
507 g_return_if_fail (tud
->changed_uids
!= NULL
);
509 changed
= tmpl_folder_data_update_sync (tud
->tfd
, tud
->added_uids
, tud
->changed_uids
, cancellable
);
511 g_task_return_boolean (task
, changed
);
515 tmpl_folder_data_schedule_update (TmplFolderData
*tfd
,
516 CamelFolderChangeInfo
*change_info
)
518 ETemplatesStore
*templates_store
;
523 g_return_if_fail (tfd
!= NULL
);
525 templates_store
= g_weak_ref_get (tfd
->templates_store_weakref
);
526 if (!templates_store
)
529 tud
= g_new0 (TfdUpdateData
, 1);
530 tud
->tfd
= tmpl_folder_data_ref (tfd
);
531 tud
->added_uids
= g_ptr_array_new_full (
532 change_info
->uid_added
? change_info
->uid_added
->len
: 0,
533 (GDestroyNotify
) camel_pstring_free
);
534 tud
->changed_uids
= g_ptr_array_new_full (
535 (change_info
->uid_changed
? change_info
->uid_changed
->len
: 0),
536 (GDestroyNotify
) camel_pstring_free
);
538 for (ii
= 0; change_info
->uid_added
&& ii
< change_info
->uid_added
->len
; ii
++) {
539 const gchar
*uid
= change_info
->uid_added
->pdata
[ii
];
542 g_ptr_array_add (tud
->added_uids
, (gpointer
) camel_pstring_strdup (uid
));
545 for (ii
= 0; change_info
->uid_changed
&& ii
< change_info
->uid_changed
->len
; ii
++) {
546 const gchar
*uid
= change_info
->uid_changed
->pdata
[ii
];
549 g_ptr_array_add (tud
->changed_uids
, (gpointer
) camel_pstring_strdup (uid
));
552 task
= g_task_new (NULL
, templates_store
->priv
->cancellable
, tmpl_folder_data_update_done_cb
, tfd
);
553 g_task_set_task_data (task
, tud
, tfd_update_data_free
);
554 g_task_run_in_thread (task
, tmpl_folder_data_update_thread
);
555 g_object_unref (task
);
557 g_object_unref (templates_store
);
561 tmpl_folder_data_folder_changed_cb (CamelFolder
*folder
,
562 CamelFolderChangeInfo
*change_info
,
565 TmplFolderData
*tfd
= user_data
;
567 g_return_if_fail (CAMEL_IS_FOLDER (folder
));
568 g_return_if_fail (change_info
!= NULL
);
569 g_return_if_fail (tfd
!= NULL
);
571 tmpl_folder_data_ref (tfd
);
573 if ((change_info
->uid_added
&& change_info
->uid_added
->len
) ||
574 (change_info
->uid_changed
&& change_info
->uid_changed
->len
)) {
575 tmpl_folder_data_schedule_update (tfd
, change_info
);
576 } else if (change_info
->uid_removed
&& change_info
->uid_removed
->len
) {
577 ETemplatesStore
*templates_store
;
579 templates_store
= g_weak_ref_get (tfd
->templates_store_weakref
);
580 if (templates_store
) {
583 tmpl_folder_data_lock (tfd
);
585 for (ii
= 0; ii
< change_info
->uid_removed
->len
; ii
++) {
586 const gchar
*uid
= change_info
->uid_removed
->pdata
[ii
];
589 tmpl_folder_data_remove_message (tfd
, uid
);
592 tmpl_folder_data_unlock (tfd
);
594 templates_store_emit_changed (templates_store
);
596 g_object_unref (templates_store
);
600 tmpl_folder_data_unref (tfd
);
604 tmpl_store_data_traverse_to_free_cb (GNode
*node
,
607 if (node
&& node
->data
) {
608 tmpl_folder_data_unref (node
->data
);
615 typedef struct _TmplStoreData
{
616 volatile gint ref_count
;
617 GWeakRef
*templates_store_weakref
; /* ETemplatesStore * */
618 GWeakRef
*store_weakref
; /* CamelStore * */
620 gulong folder_created_handler_id
;
621 gulong folder_deleted_handler_id
;
622 gulong folder_renamed_handler_id
;
623 gulong notify_display_name_id
;
626 gchar
*root_folder_path
;
627 gchar
*templates_folder_uri
;
628 gchar
*identity_source_uid
;
629 GNode
*folders
; /* data is TmplFolderData * */
633 tmpl_store_data_set_root_folder_path (TmplStoreData
*tsd
,
634 const gchar
*root_folder_path
);
636 static TmplStoreData
*
637 tmpl_store_data_new (ETemplatesStore
*templates_store
,
639 const gchar
*root_folder_path
,
640 const gchar
*templates_folder_uri
,
641 const gchar
*identity_source_uid
)
645 g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store
), NULL
);
646 g_return_val_if_fail (CAMEL_IS_STORE (store
), NULL
);
647 g_return_val_if_fail (root_folder_path
&& *root_folder_path
, NULL
);
648 g_return_val_if_fail (templates_folder_uri
&& *templates_folder_uri
, NULL
);
650 tsd
= g_new0 (TmplStoreData
, 1);
652 tsd
->templates_store_weakref
= e_weak_ref_new (templates_store
);
653 tsd
->store_weakref
= e_weak_ref_new (store
);
654 g_mutex_init (&tsd
->busy_lock
);
655 tsd
->root_folder_path
= NULL
;
656 tsd
->templates_folder_uri
= g_strdup (templates_folder_uri
);
657 tsd
->identity_source_uid
= g_strdup (identity_source_uid
);
658 tsd
->folders
= g_node_new (NULL
);
660 if (CAMEL_IS_SUBSCRIBABLE (store
)) {
661 tsd
->folder_created_handler_id
= g_signal_connect (store
, "folder-subscribed",
662 G_CALLBACK (tmpl_store_data_folder_created_cb
), tsd
);
663 tsd
->folder_deleted_handler_id
= g_signal_connect (store
, "folder-unsubscribed",
664 G_CALLBACK (tmpl_store_data_folder_deleted_cb
), tsd
);
666 tsd
->folder_created_handler_id
= g_signal_connect (store
, "folder-created",
667 G_CALLBACK (tmpl_store_data_folder_created_cb
), tsd
);
668 tsd
->folder_deleted_handler_id
= g_signal_connect (store
, "folder-deleted",
669 G_CALLBACK (tmpl_store_data_folder_deleted_cb
), tsd
);
672 tsd
->folder_renamed_handler_id
= g_signal_connect (store
, "folder-renamed",
673 G_CALLBACK (tmpl_store_data_folder_renamed_cb
), tsd
);
675 tsd
->notify_display_name_id
= e_signal_connect_notify (store
, "notify::display-name",
676 G_CALLBACK (tmpl_store_data_notify_display_name_cb
), tsd
);
678 tmpl_store_data_set_root_folder_path (tsd
, root_folder_path
);
683 static TmplStoreData
*
684 tmpl_store_data_ref (TmplStoreData
*tsd
)
686 g_return_val_if_fail (tsd
!= NULL
, NULL
);
688 g_atomic_int_inc (&tsd
->ref_count
);
694 tmpl_store_data_unref (gpointer ptr
)
696 TmplStoreData
*tsd
= ptr
;
699 if (!g_atomic_int_dec_and_test (&tsd
->ref_count
))
702 if (tsd
->templates_store_weakref
) {
703 e_weak_ref_free (tsd
->templates_store_weakref
);
704 tsd
->templates_store_weakref
= NULL
;
707 if (tsd
->store_weakref
) {
710 store
= g_weak_ref_get (tsd
->store_weakref
);
712 if (tsd
->folder_created_handler_id
) {
713 g_signal_handler_disconnect (store
, tsd
->folder_created_handler_id
);
714 tsd
->folder_created_handler_id
= 0;
717 if (tsd
->folder_deleted_handler_id
) {
718 g_signal_handler_disconnect (store
, tsd
->folder_deleted_handler_id
);
719 tsd
->folder_deleted_handler_id
= 0;
722 if (tsd
->folder_renamed_handler_id
) {
723 g_signal_handler_disconnect (store
, tsd
->folder_renamed_handler_id
);
724 tsd
->folder_renamed_handler_id
= 0;
727 e_signal_disconnect_notify_handler (store
, &tsd
->notify_display_name_id
);
729 g_clear_object (&store
);
732 e_weak_ref_free (tsd
->store_weakref
);
733 tsd
->store_weakref
= NULL
;
736 g_mutex_clear (&tsd
->busy_lock
);
738 g_free (tsd
->root_folder_path
);
739 tsd
->root_folder_path
= NULL
;
741 g_free (tsd
->templates_folder_uri
);
742 tsd
->templates_folder_uri
= NULL
;
744 g_free (tsd
->identity_source_uid
);
745 tsd
->identity_source_uid
= NULL
;
748 g_node_traverse (tsd
->folders
, G_IN_ORDER
, G_TRAVERSE_ALL
, -1, tmpl_store_data_traverse_to_free_cb
, NULL
);
749 g_node_destroy (tsd
->folders
);
758 tmpl_store_data_lock (TmplStoreData
*tsd
)
760 g_return_if_fail (tsd
!= NULL
);
762 g_mutex_lock (&tsd
->busy_lock
);
766 tmpl_store_data_unlock (TmplStoreData
*tsd
)
768 g_return_if_fail (tsd
!= NULL
);
770 g_mutex_unlock (&tsd
->busy_lock
);
774 tmpl_store_data_compare (gconstpointer ptr1
,
778 const TmplStoreData
*tsd1
= ptr1
, *tsd2
= ptr2
;
779 EMailAccountStore
*account_store
= user_data
;
780 CamelService
*service1
, *service2
;
783 service1
= tsd1
? g_weak_ref_get (tsd1
->store_weakref
) : NULL
;
784 service2
= tsd2
? g_weak_ref_get (tsd2
->store_weakref
) : NULL
;
786 if (account_store
&& service1
&& service2
)
787 res
= e_mail_account_store_compare_services (account_store
, service1
, service2
);
789 res
= g_utf8_collate (service1
? camel_service_get_display_name (service1
) : "",
790 service2
? camel_service_get_display_name (service2
) : "");
792 g_clear_object (&service1
);
793 g_clear_object (&service2
);
799 tmpl_store_data_set_root_folder_path (TmplStoreData
*tsd
,
800 const gchar
*root_folder_path
)
802 g_return_if_fail (tsd
!= NULL
);
803 g_return_if_fail (root_folder_path
&& *root_folder_path
);
805 tmpl_store_data_lock (tsd
);
807 if (g_strcmp0 (tsd
->root_folder_path
, root_folder_path
) != 0) {
810 g_free (tsd
->root_folder_path
);
811 tsd
->root_folder_path
= g_strdup (root_folder_path
);
813 len
= strlen (tsd
->root_folder_path
);
814 if (tsd
->root_folder_path
[len
- 1] == '/')
815 tsd
->root_folder_path
[len
- 1] = '\0';
818 tmpl_store_data_unlock (tsd
);
822 tmpl_store_data_find_parent_node_locked (TmplStoreData
*tsd
,
823 const gchar
*full_name
,
826 GNode
*from_node
, *node
, *parent
;
828 g_return_val_if_fail (tsd
!= NULL
, NULL
);
829 g_return_val_if_fail (full_name
!= NULL
, NULL
);
831 parent
= tsd
->folders
;
832 from_node
= tsd
->folders
;
834 node
= g_node_first_child (from_node
);
838 TmplFolderData
*tfd
= node
->data
;
840 if (tfd
&& tfd
->folder
&&
841 g_str_has_prefix (full_name
, camel_folder_get_full_name (tfd
->folder
)) &&
842 g_strcmp0 (full_name
, camel_folder_get_full_name (tfd
->folder
)) != 0) {
848 node
= g_node_next_sibling (node
);
852 if (for_insert
&& parent
) {
856 TmplFolderData
*tfd
= parent
->data
;
858 if (g_strcmp0 (full_name
, camel_folder_get_full_name (tfd
->folder
)) == 0) {
859 /* The folder is already in the list of folders */
864 for (node
= parent
? parent
->children
: NULL
; node
; node
= node
->next
) {
865 TmplFolderData
*tfd
= node
->data
;
870 if (g_strcmp0 (full_name
, camel_folder_get_full_name (tfd
->folder
)) == 0) {
871 /* The folder is already in the list of folders */
882 tmpl_store_data_find_node_locked (TmplStoreData
*tsd
,
883 const gchar
*full_name
)
885 GNode
*node
, *parent
;
887 g_return_val_if_fail (tsd
!= NULL
, NULL
);
888 g_return_val_if_fail (full_name
!= NULL
, NULL
);
890 parent
= tmpl_store_data_find_parent_node_locked (tsd
, full_name
, FALSE
);
894 for (node
= g_node_first_child (parent
); node
; node
= g_node_next_sibling (node
)) {
895 TmplFolderData
*tfd
= node
->data
;
900 if (tfd
->folder
&& g_strcmp0 (full_name
, camel_folder_get_full_name (tfd
->folder
)) == 0) {
909 tmpl_store_data_find_node_with_folder_locked (TmplStoreData
*tsd
,
914 g_return_val_if_fail (tsd
!= NULL
, NULL
);
915 g_return_val_if_fail (CAMEL_IS_FOLDER (folder
), NULL
);
919 TmplFolderData
*tfd
= node
->data
;
922 if (tfd
&& tfd
->folder
== folder
) {
926 /* Traverse the tree */
927 next
= node
->children
;
933 GNode
*sibl
= next
->next
;
951 tmpl_store_data_update_done_cb (GObject
*source
,
952 GAsyncResult
*result
,
955 TmplStoreData
*tsd
= user_data
;
956 GError
*local_error
= NULL
;
958 g_return_if_fail (tsd
!= NULL
);
959 g_return_if_fail (g_task_is_valid (result
, source
));
961 if (g_task_propagate_boolean (G_TASK (result
), &local_error
)) {
962 /* Content changed, rebuild menu when needed */
963 ETemplatesStore
*templates_store
;
965 templates_store
= g_weak_ref_get (tsd
->templates_store_weakref
);
966 if (templates_store
) {
967 templates_store_emit_changed (templates_store
);
968 g_object_unref (templates_store
);
970 } else if (local_error
) {
971 g_debug ("%s: Failed with error: %s", G_STRFUNC
, local_error
->message
);
974 g_clear_error (&local_error
);
978 tmpl_store_data_initial_setup_thread (GTask
*task
,
979 gpointer source_object
,
981 GCancellable
*cancellable
)
983 ETemplatesStore
*templates_store
;
984 TmplStoreData
*tsd
= task_data
;
986 gboolean changed
= FALSE
;
988 g_return_if_fail (tsd
!= NULL
);
990 templates_store
= g_weak_ref_get (tsd
->templates_store_weakref
);
991 store
= g_weak_ref_get (tsd
->store_weakref
);
992 if (store
&& templates_store
) {
993 CamelFolderInfo
*folder_info
= NULL
, *fi
;
994 gchar
*root_folder_path
;
995 GError
*local_error
= NULL
;
997 tmpl_store_data_lock (tsd
);
998 root_folder_path
= g_strdup (tsd
->root_folder_path
);
999 tmpl_store_data_unlock (tsd
);
1001 if (root_folder_path
) {
1002 folder_info
= camel_store_get_folder_info_sync (
1003 store
, root_folder_path
,
1004 CAMEL_STORE_FOLDER_INFO_RECURSIVE
|
1005 CAMEL_STORE_FOLDER_INFO_SUBSCRIBED
|
1006 CAMEL_STORE_FOLDER_INFO_FAST
, cancellable
, &local_error
);
1009 g_debug ("%s: Failed to get folder info for '%s : %s': %s", G_STRFUNC
,
1010 camel_service_get_display_name (CAMEL_SERVICE (store
)), root_folder_path
, local_error
->message
);
1013 g_clear_error (&local_error
);
1017 while (fi
&& !g_cancellable_is_cancelled (cancellable
)) {
1018 CamelFolderInfo
*next
;
1019 CamelFolder
*folder
;
1021 folder
= camel_store_get_folder_sync (store
, fi
->full_name
, 0, cancellable
, &local_error
);
1025 tmpl_store_data_lock (tsd
);
1027 parent
= tmpl_store_data_find_parent_node_locked (tsd
, fi
->full_name
, TRUE
);
1029 TmplFolderData
*tfd
;
1031 tfd
= tmpl_folder_data_new (templates_store
, folder
);
1033 changed
= tmpl_folder_data_update_sync (tfd
, NULL
, NULL
, cancellable
) || changed
;
1035 g_node_append_data (parent
, tfd
);
1039 tmpl_store_data_unlock (tsd
);
1043 g_debug ("%s: Failed to get folder '%s': %s", G_STRFUNC
, fi
->full_name
, local_error
->message
);
1045 g_clear_object (&folder
);
1046 g_clear_error (&local_error
);
1048 /* Traverse the tree of folders */
1055 CamelFolderInfo
*sibl
= next
->next
;
1061 next
= next
->parent
;
1069 camel_folder_info_free (folder_info
);
1070 g_free (root_folder_path
);
1073 g_clear_object (&templates_store
);
1074 g_clear_object (&store
);
1076 g_task_return_boolean (task
, changed
);
1080 tmpl_store_data_schedule_initial_setup (TmplStoreData
*tsd
)
1082 ETemplatesStore
*templates_store
;
1085 g_return_if_fail (tsd
!= NULL
);
1087 templates_store
= g_weak_ref_get (tsd
->templates_store_weakref
);
1088 if (!templates_store
)
1091 tmpl_store_data_ref (tsd
);
1093 task
= g_task_new (NULL
, templates_store
->priv
->cancellable
, tmpl_store_data_update_done_cb
, tsd
);
1094 g_task_set_task_data (task
, tsd
, tmpl_store_data_unref
);
1095 g_task_run_in_thread (task
, tmpl_store_data_initial_setup_thread
);
1096 g_object_unref (task
);
1098 g_object_unref (templates_store
);
1101 typedef struct _TsdFolderData
{
1104 gchar
*old_fullname
; /* If set, then it's "rename", otherwise it's "create" */
1108 tsd_folder_data_free (gpointer ptr
)
1110 TsdFolderData
*fd
= ptr
;
1113 tmpl_store_data_unref (fd
->tsd
);
1114 g_free (fd
->fullname
);
1115 g_free (fd
->old_fullname
);
1121 tmpl_store_data_folder_thread (GTask
*task
,
1122 gpointer source_object
,
1124 GCancellable
*cancellable
)
1126 ETemplatesStore
*templates_store
;
1127 TsdFolderData
*fd
= task_data
;
1129 gboolean changed
= FALSE
;
1131 g_return_if_fail (fd
!= NULL
);
1132 g_return_if_fail (fd
->tsd
!= NULL
);
1133 g_return_if_fail (fd
->fullname
!= NULL
);
1135 templates_store
= g_weak_ref_get (fd
->tsd
->templates_store_weakref
);
1136 store
= g_weak_ref_get (fd
->tsd
->store_weakref
);
1137 if (store
&& templates_store
) {
1138 GError
*local_error
= NULL
;
1139 CamelFolder
*folder
;
1141 folder
= camel_store_get_folder_sync (store
, fd
->fullname
, 0, cancellable
, &local_error
);
1143 GNode
*parent
= NULL
;
1145 tmpl_store_data_lock (fd
->tsd
);
1147 if (fd
->old_fullname
) {
1150 node
= tmpl_store_data_find_node_locked (fd
->tsd
, fd
->old_fullname
);
1152 /* Sometimes the CamelFolder can be renamed in-place,
1153 thus lookup with the CamelFolder structure as well. */
1154 node
= tmpl_store_data_find_node_with_folder_locked (fd
->tsd
, folder
);
1157 TmplFolderData
*tfd
= node
->data
;
1161 tmpl_folder_data_lock (tfd
);
1163 if (tfd
->folder
!= folder
) {
1164 g_clear_object (&tfd
->folder
);
1165 tfd
->folder
= g_object_ref (folder
);
1168 parent
= tmpl_store_data_find_parent_node_locked (fd
->tsd
, fd
->fullname
, FALSE
);
1169 if (parent
&& node
->parent
!= parent
) {
1170 g_node_unlink (node
);
1171 g_node_append (parent
, node
);
1174 tmpl_folder_data_unlock (tfd
);
1177 parent
= tmpl_store_data_find_parent_node_locked (fd
->tsd
, fd
->fullname
, TRUE
);
1179 TmplFolderData
*tfd
;
1181 tfd
= tmpl_folder_data_new (templates_store
, folder
);
1183 changed
= tmpl_folder_data_update_sync (tfd
, NULL
, NULL
, cancellable
);
1185 g_node_append_data (parent
, tfd
);
1191 GSList
*data
= NULL
, *link
;
1194 for (node
= parent
->children
; node
; node
= node
->next
) {
1196 data
= g_slist_prepend (data
, node
->data
);
1199 data
= g_slist_sort (data
, tmpl_folder_data_compare
);
1201 for (node
= parent
->children
, link
= data
; node
&& link
; node
= node
->next
) {
1203 node
->data
= link
->data
;
1204 link
= g_slist_next (link
);
1208 g_slist_free (data
);
1211 tmpl_store_data_unlock (fd
->tsd
);
1215 g_debug ("%s: Failed to get folder '%s': %s", G_STRFUNC
, fd
->fullname
, local_error
->message
);
1217 g_clear_object (&folder
);
1218 g_clear_error (&local_error
);
1221 g_clear_object (&templates_store
);
1222 g_clear_object (&store
);
1224 g_task_return_boolean (task
, changed
);
1228 tmpl_store_data_folder_created_cb (CamelStore
*store
,
1229 CamelFolderInfo
*folder_info
,
1232 TmplStoreData
*tsd
= user_data
;
1233 ETemplatesStore
*templates_store
;
1235 g_return_if_fail (CAMEL_IS_STORE (store
));
1236 g_return_if_fail (folder_info
!= NULL
);
1237 g_return_if_fail (folder_info
->full_name
!= NULL
);
1238 g_return_if_fail (tsd
!= NULL
);
1240 templates_store
= g_weak_ref_get (tsd
->templates_store_weakref
);
1242 tmpl_store_data_lock (tsd
);
1244 if (templates_store
&& g_str_has_prefix (folder_info
->full_name
, tsd
->root_folder_path
)) {
1247 parent
= tmpl_store_data_find_parent_node_locked (tsd
, folder_info
->full_name
, TRUE
);
1252 fd
= g_new0 (TsdFolderData
, 1);
1253 fd
->tsd
= tmpl_store_data_ref (tsd
);
1254 fd
->fullname
= g_strdup (folder_info
->full_name
);
1255 fd
->old_fullname
= NULL
;
1257 task
= g_task_new (NULL
, templates_store
->priv
->cancellable
, tmpl_store_data_update_done_cb
, tsd
);
1258 g_task_set_task_data (task
, fd
, tsd_folder_data_free
);
1259 g_task_run_in_thread (task
, tmpl_store_data_folder_thread
);
1260 g_object_unref (task
);
1264 tmpl_store_data_unlock (tsd
);
1265 g_clear_object (&templates_store
);
1269 tmpl_store_data_folder_deleted_cb (CamelStore
*store
,
1270 CamelFolderInfo
*folder_info
,
1273 TmplStoreData
*tsd
= user_data
;
1274 ETemplatesStore
*templates_store
;
1275 gboolean changed
= FALSE
;
1277 g_return_if_fail (CAMEL_IS_STORE (store
));
1278 g_return_if_fail (folder_info
!= NULL
);
1279 g_return_if_fail (tsd
!= NULL
);
1281 templates_store
= g_weak_ref_get (tsd
->templates_store_weakref
);
1283 tmpl_store_data_lock (tsd
);
1285 if (templates_store
&& g_str_has_prefix (folder_info
->full_name
, tsd
->root_folder_path
)) {
1288 node
= tmpl_store_data_find_node_locked (tsd
, folder_info
->full_name
);
1290 g_node_traverse (node
, G_IN_ORDER
, G_TRAVERSE_ALL
, -1, tmpl_store_data_traverse_to_free_cb
, NULL
);
1291 g_node_destroy (node
);
1297 tmpl_store_data_unlock (tsd
);
1300 templates_store_emit_changed (templates_store
);
1302 g_clear_object (&templates_store
);
1306 tmpl_store_data_folder_renamed_cb (CamelStore
*store
,
1307 const gchar
*old_name
,
1308 CamelFolderInfo
*folder_info
,
1311 TmplStoreData
*tsd
= user_data
;
1312 ETemplatesStore
*templates_store
;
1313 gboolean changed
= FALSE
;
1315 g_return_if_fail (CAMEL_IS_STORE (store
));
1316 g_return_if_fail (old_name
!= NULL
);
1317 g_return_if_fail (folder_info
!= NULL
);
1318 g_return_if_fail (tsd
!= NULL
);
1320 templates_store
= g_weak_ref_get (tsd
->templates_store_weakref
);
1322 tmpl_store_data_lock (tsd
);
1324 if (templates_store
&& g_str_has_prefix (old_name
, tsd
->root_folder_path
)) {
1325 if (g_str_has_prefix (folder_info
->full_name
, tsd
->root_folder_path
)) {
1329 fd
= g_new0 (TsdFolderData
, 1);
1330 fd
->tsd
= tmpl_store_data_ref (tsd
);
1331 fd
->fullname
= g_strdup (folder_info
->full_name
);
1332 fd
->old_fullname
= g_strdup (old_name
);
1334 task
= g_task_new (NULL
, templates_store
->priv
->cancellable
, tmpl_store_data_update_done_cb
, tsd
);
1335 g_task_set_task_data (task
, fd
, tsd_folder_data_free
);
1336 g_task_run_in_thread (task
, tmpl_store_data_folder_thread
);
1337 g_object_unref (task
);
1341 node
= tmpl_store_data_find_node_locked (tsd
, old_name
);
1343 g_node_traverse (node
, G_IN_ORDER
, G_TRAVERSE_ALL
, -1, tmpl_store_data_traverse_to_free_cb
, NULL
);
1344 g_node_destroy (node
);
1349 } else if (templates_store
&& g_str_has_prefix (folder_info
->full_name
, tsd
->root_folder_path
)) {
1353 fd
= g_new0 (TsdFolderData
, 1);
1354 fd
->tsd
= tmpl_store_data_ref (tsd
);
1355 fd
->fullname
= g_strdup (folder_info
->full_name
);
1356 fd
->old_fullname
= NULL
;
1358 task
= g_task_new (NULL
, templates_store
->priv
->cancellable
, tmpl_store_data_update_done_cb
, tsd
);
1359 g_task_set_task_data (task
, fd
, tsd_folder_data_free
);
1360 g_task_run_in_thread (task
, tmpl_store_data_folder_thread
);
1361 g_object_unref (task
);
1364 tmpl_store_data_unlock (tsd
);
1367 templates_store_emit_changed (templates_store
);
1369 g_clear_object (&templates_store
);
1373 tmpl_store_data_notify_display_name_cb (CamelService
*service
,
1377 TmplStoreData
*tsd
= user_data
;
1378 ETemplatesStore
*templates_store
;
1380 g_return_if_fail (CAMEL_IS_SERVICE (service
));
1381 g_return_if_fail (tsd
!= NULL
);
1383 templates_store
= g_weak_ref_get (tsd
->templates_store_weakref
);
1384 if (templates_store
) {
1385 EMailAccountStore
*account_store
;
1386 gboolean changed
= FALSE
;
1388 account_store
= e_templates_store_ref_account_store (templates_store
);
1390 templates_store_lock (templates_store
);
1392 changed
= templates_store
->priv
->stores
&& templates_store
->priv
->stores
->next
;
1393 templates_store
->priv
->stores
= g_slist_sort_with_data (templates_store
->priv
->stores
,
1394 tmpl_store_data_compare
, account_store
);
1396 templates_store_unlock (templates_store
);
1399 templates_store_emit_changed (templates_store
);
1401 g_object_unref (templates_store
);
1402 g_clear_object (&account_store
);
1407 templates_store_find_custom_templates_root_folder_path (ETemplatesStore
*templates_store
,
1409 EMailSession
*mail_session
,
1410 ESource
**out_identity_source
,
1411 CamelStore
**out_use_store
,
1412 gchar
**out_templates_folder_uri
)
1414 ESource
*identity_source
;
1415 gchar
*root_folder_path
= NULL
;
1417 g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store
), NULL
);
1418 g_return_val_if_fail (CAMEL_IS_STORE (store
), NULL
);
1419 g_return_val_if_fail (out_identity_source
!= NULL
, NULL
);
1420 g_return_val_if_fail (out_use_store
!= NULL
, NULL
);
1421 g_return_val_if_fail (out_templates_folder_uri
!= NULL
, NULL
);
1423 *out_identity_source
= NULL
;
1424 *out_use_store
= NULL
;
1425 *out_templates_folder_uri
= NULL
;
1427 if (g_strcmp0 (E_MAIL_SESSION_LOCAL_UID
, camel_service_get_uid (CAMEL_SERVICE (store
))) == 0) {
1428 *out_templates_folder_uri
= g_strdup (e_mail_session_get_local_folder_uri (mail_session
, E_MAIL_LOCAL_FOLDER_TEMPLATES
));
1429 return g_strdup ("Templates");
1432 identity_source
= em_utils_ref_mail_identity_for_store (e_mail_session_get_registry (mail_session
), store
);
1433 if (identity_source
) {
1434 if (e_source_has_extension (identity_source
, E_SOURCE_EXTENSION_MAIL_COMPOSITION
)) {
1435 ESourceMailComposition
*mail_composition
;
1436 CamelStore
*templates_store
= NULL
;
1437 gchar
*templates_folder
;
1438 GError
*local_error
= NULL
;
1440 mail_composition
= e_source_get_extension (identity_source
, E_SOURCE_EXTENSION_MAIL_COMPOSITION
);
1441 templates_folder
= e_source_mail_composition_dup_templates_folder (mail_composition
);
1443 if (templates_folder
&& *templates_folder
&&
1444 g_strcmp0 (templates_folder
, e_mail_session_get_local_folder_uri (mail_session
, E_MAIL_LOCAL_FOLDER_TEMPLATES
)) != 0 &&
1445 e_mail_folder_uri_parse (CAMEL_SESSION (mail_session
), templates_folder
, &templates_store
, &root_folder_path
, &local_error
)) {
1446 if (g_strcmp0 (E_MAIL_SESSION_LOCAL_UID
, camel_service_get_uid (CAMEL_SERVICE (templates_store
))) == 0 &&
1447 g_strcmp0 (root_folder_path
, "Templates") == 0) {
1448 g_free (root_folder_path
);
1449 root_folder_path
= NULL
;
1451 *out_identity_source
= g_object_ref (identity_source
);
1452 *out_use_store
= g_object_ref (templates_store
);
1453 *out_templates_folder_uri
= g_strdup (templates_folder
);
1456 g_clear_object (&templates_store
);
1460 g_debug ("%s: Failed to parse templates folder URI '%s': %s", G_STRFUNC
, templates_folder
, local_error
->message
);
1461 g_clear_error (&local_error
);
1464 g_free (templates_folder
);
1468 g_clear_object (&identity_source
);
1470 return root_folder_path
;
1474 templates_store_maybe_add_store (ETemplatesStore
*templates_store
,
1477 ESource
*identity_source
= NULL
;
1478 EMailAccountStore
*account_store
;
1479 EMailSession
*mail_session
;
1480 CamelStore
*use_store
= NULL
;
1481 gchar
*root_folder_path
, *templates_folder_uri
= NULL
;
1482 gboolean changed
= FALSE
;
1484 g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store
));
1485 g_return_if_fail (CAMEL_IS_STORE (store
));
1487 account_store
= e_templates_store_ref_account_store (templates_store
);
1491 mail_session
= e_mail_account_store_get_session (account_store
);
1493 templates_store_lock (templates_store
);
1495 root_folder_path
= templates_store_find_custom_templates_root_folder_path (
1496 templates_store
, store
, mail_session
, &identity_source
, &use_store
, &templates_folder_uri
);
1498 if (root_folder_path
) {
1502 for (link
= templates_store
->priv
->stores
; link
; link
= g_slist_next (link
)) {
1503 CamelStore
*tsd_store
;
1510 tsd_store
= g_weak_ref_get (tsd
->store_weakref
);
1511 if (tsd_store
== (use_store
? use_store
: store
) &&
1512 g_strcmp0 (tsd
->root_folder_path
, root_folder_path
) == 0) {
1513 g_clear_object (&tsd_store
);
1517 g_clear_object (&tsd_store
);
1520 /* The store is not in the list of stores yet */
1522 tsd
= tmpl_store_data_new (templates_store
, use_store
? use_store
: store
, root_folder_path
,
1523 templates_folder_uri
, identity_source
? e_source_get_uid (identity_source
) : NULL
);
1525 templates_store
->priv
->stores
= g_slist_insert_sorted_with_data (templates_store
->priv
->stores
,
1526 tsd
, tmpl_store_data_compare
, account_store
);
1528 tmpl_store_data_schedule_initial_setup (tsd
);
1534 templates_store_unlock (templates_store
);
1537 templates_store_emit_changed (templates_store
);
1539 g_free (root_folder_path
);
1540 g_free (templates_folder_uri
);
1541 g_clear_object (&use_store
);
1542 g_clear_object (&identity_source
);
1543 g_clear_object (&account_store
);
1547 templates_store_maybe_remove_store (ETemplatesStore
*templates_store
,
1551 gboolean changed
= FALSE
;
1553 g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store
));
1554 g_return_if_fail (CAMEL_IS_STORE (store
));
1556 templates_store_lock (templates_store
);
1558 for (link
= templates_store
->priv
->stores
; link
&& !changed
; link
= g_slist_next (link
)) {
1559 TmplStoreData
*tsd
= link
->data
;
1560 CamelStore
*other_store
;
1565 other_store
= g_weak_ref_get (tsd
->store_weakref
);
1566 if (other_store
== store
) {
1568 templates_store
->priv
->stores
= g_slist_remove (templates_store
->priv
->stores
, tsd
);
1569 tmpl_store_data_unref (tsd
);
1571 g_object_unref (other_store
);
1575 g_clear_object (&other_store
);
1578 templates_store_unlock (templates_store
);
1581 templates_store_emit_changed (templates_store
);
1585 templates_store_maybe_add_enabled_services (ETemplatesStore
*templates_store
)
1587 EMailAccountStore
*account_store
;
1588 GQueue queue
= G_QUEUE_INIT
;
1590 g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store
));
1591 g_return_if_fail (templates_store
->priv
->stores
== NULL
);
1593 account_store
= e_templates_store_ref_account_store (templates_store
);
1594 g_return_if_fail (account_store
!= NULL
);
1596 e_mail_account_store_queue_enabled_services (account_store
, &queue
);
1598 while (!g_queue_is_empty (&queue
)) {
1599 CamelService
*service
;
1601 service
= g_queue_pop_head (&queue
);
1603 if (CAMEL_IS_STORE (service
))
1604 templates_store_maybe_add_store (templates_store
, CAMEL_STORE (service
));
1607 g_clear_object (&account_store
);
1611 templates_store_service_enabled_cb (EMailAccountStore
*account_store
,
1612 CamelService
*service
,
1615 ETemplatesStore
*templates_store
;
1617 if (!CAMEL_IS_STORE (service
))
1620 templates_store
= g_weak_ref_get (weak_ref
);
1622 if (templates_store
) {
1623 templates_store_maybe_add_store (templates_store
, CAMEL_STORE (service
));
1624 g_object_unref (templates_store
);
1629 templates_store_service_disabled_cb (EMailAccountStore
*account_store
,
1630 CamelService
*service
,
1633 ETemplatesStore
*templates_store
;
1635 if (!CAMEL_IS_STORE (service
))
1638 templates_store
= g_weak_ref_get (weak_ref
);
1640 if (templates_store
) {
1641 templates_store_maybe_remove_store (templates_store
, CAMEL_STORE (service
));
1642 g_object_unref (templates_store
);
1647 templates_store_service_removed_cb (EMailAccountStore
*account_store
,
1648 CamelService
*service
,
1651 ETemplatesStore
*templates_store
;
1653 if (!CAMEL_IS_STORE (service
))
1656 templates_store
= g_weak_ref_get (weak_ref
);
1658 if (templates_store
) {
1659 templates_store_maybe_remove_store (templates_store
, CAMEL_STORE (service
));
1660 g_object_unref (templates_store
);
1665 templates_store_source_changed_cb (ESourceRegistry
*registry
,
1669 ETemplatesStore
*templates_store
;
1671 g_return_if_fail (E_IS_SOURCE (source
));
1673 if (!e_source_has_extension (source
, E_SOURCE_EXTENSION_MAIL_COMPOSITION
))
1676 templates_store
= g_weak_ref_get (weak_ref
);
1678 if (templates_store
) {
1679 TmplStoreData
*corresponding_tsd
= NULL
;
1680 ESourceMailComposition
*mail_composition
;
1681 gboolean rebuild_all
= FALSE
;
1682 gchar
*templates_folder
;
1685 mail_composition
= e_source_get_extension (source
, E_SOURCE_EXTENSION_MAIL_COMPOSITION
);
1686 templates_folder
= e_source_mail_composition_dup_templates_folder (mail_composition
);
1688 templates_store_lock (templates_store
);
1690 for (link
= templates_store
->priv
->stores
; link
; link
= g_slist_next (link
)) {
1691 TmplStoreData
*tsd
= link
->data
;
1696 if (g_strcmp0 (tsd
->identity_source_uid
, e_source_get_uid (source
)) == 0) {
1697 g_warn_if_fail (!corresponding_tsd
);
1698 corresponding_tsd
= tsd
;
1703 if (corresponding_tsd
) {
1704 if (g_strcmp0 (templates_folder
, corresponding_tsd
->templates_folder_uri
) != 0) {
1705 /* Should not happen that often (inefficient, but avoids code complexity). */
1708 } else if (templates_folder
&& *templates_folder
) {
1709 EMailAccountStore
*account_store
;
1710 EMailSession
*mail_session
;
1711 CamelStore
*found_store
= NULL
;
1712 gchar
*root_folder_path
= NULL
;
1713 GError
*local_error
= NULL
;
1715 account_store
= g_weak_ref_get (templates_store
->priv
->account_store_weakref
);
1717 if (account_store
&& (mail_session
= e_mail_account_store_get_session (account_store
)) != NULL
&&
1718 g_strcmp0 (templates_folder
, e_mail_session_get_local_folder_uri (mail_session
, E_MAIL_LOCAL_FOLDER_TEMPLATES
)) != 0 &&
1719 e_mail_folder_uri_parse (CAMEL_SESSION (mail_session
), templates_folder
, &found_store
, &root_folder_path
, &local_error
)) {
1720 if (g_strcmp0 (E_MAIL_SESSION_LOCAL_UID
, camel_service_get_uid (CAMEL_SERVICE (found_store
))) == 0 &&
1721 g_strcmp0 (root_folder_path
, "Templates") == 0) {
1722 g_free (root_folder_path
);
1723 root_folder_path
= NULL
;
1725 /* One of the templates folders had been changed to a real non-default folder;
1726 rebuild everything in this case (inefficient, but avoids code complexity). */
1732 g_debug ("%s: Failed to parse templates folder URI '%s': %s", G_STRFUNC
, templates_folder
, local_error
->message
);
1733 g_clear_error (&local_error
);
1736 g_clear_object (&found_store
);
1737 g_clear_object (&account_store
);
1738 g_free (root_folder_path
);
1742 g_slist_free_full (templates_store
->priv
->stores
, tmpl_store_data_unref
);
1743 templates_store
->priv
->stores
= NULL
;
1746 templates_store_unlock (templates_store
);
1749 templates_store_maybe_add_enabled_services (templates_store
);
1751 g_object_unref (templates_store
);
1752 g_free (templates_folder
);
1757 templates_store_set_account_store (ETemplatesStore
*templates_store
,
1758 EMailAccountStore
*account_store
)
1760 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (account_store
));
1762 g_weak_ref_set (templates_store
->priv
->account_store_weakref
, account_store
);
1766 templates_store_set_property (GObject
*object
,
1768 const GValue
*value
,
1771 switch (property_id
) {
1772 case PROP_ACCOUNT_STORE
:
1773 templates_store_set_account_store (
1774 E_TEMPLATES_STORE (object
),
1775 g_value_get_object (value
));
1779 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1783 templates_store_get_property (GObject
*object
,
1788 switch (property_id
) {
1789 case PROP_ACCOUNT_STORE
:
1790 g_value_take_object (
1792 e_templates_store_ref_account_store (
1793 E_TEMPLATES_STORE (object
)));
1797 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1801 templates_store_dispose (GObject
*object
)
1803 ETemplatesStore
*templates_store
;
1804 EMailAccountStore
*account_store
;
1806 templates_store
= E_TEMPLATES_STORE (object
);
1808 account_store
= e_templates_store_ref_account_store (templates_store
);
1810 if (account_store
) {
1811 if (templates_store
->priv
->service_enabled_handler_id
) {
1812 g_signal_handler_disconnect (account_store
, templates_store
->priv
->service_enabled_handler_id
);
1813 templates_store
->priv
->service_enabled_handler_id
= 0;
1816 if (templates_store
->priv
->service_disabled_handler_id
) {
1817 g_signal_handler_disconnect (account_store
, templates_store
->priv
->service_disabled_handler_id
);
1818 templates_store
->priv
->service_disabled_handler_id
= 0;
1821 if (templates_store
->priv
->service_removed_handler_id
) {
1822 g_signal_handler_disconnect (account_store
, templates_store
->priv
->service_removed_handler_id
);
1823 templates_store
->priv
->service_removed_handler_id
= 0;
1826 if (templates_store
->priv
->source_changed_handler_id
) {
1827 EMailSession
*session
;
1828 ESourceRegistry
*registry
;
1830 session
= e_mail_account_store_get_session (account_store
);
1831 registry
= e_mail_session_get_registry (session
);
1833 g_signal_handler_disconnect (registry
, templates_store
->priv
->source_changed_handler_id
);
1834 templates_store
->priv
->source_changed_handler_id
= 0;
1838 if (templates_store
->priv
->cancellable
) {
1839 g_cancellable_cancel (templates_store
->priv
->cancellable
);
1840 g_clear_object (&templates_store
->priv
->cancellable
);
1843 g_clear_object (&account_store
);
1845 /* Chain up to parent's method. */
1846 G_OBJECT_CLASS (e_templates_store_parent_class
)->dispose (object
);
1850 templates_store_finalize (GObject
*object
)
1852 ETemplatesStore
*templates_store
;
1854 templates_store
= E_TEMPLATES_STORE (object
);
1856 g_slist_free_full (templates_store
->priv
->stores
, tmpl_store_data_unref
);
1857 templates_store
->priv
->stores
= NULL
;
1859 e_weak_ref_free (templates_store
->priv
->account_store_weakref
);
1860 templates_store
->priv
->account_store_weakref
= NULL
;
1862 g_mutex_clear (&templates_store
->priv
->busy_lock
);
1864 /* Chain up to parent's method. */
1865 G_OBJECT_CLASS (e_templates_store_parent_class
)->finalize (object
);
1869 templates_store_constructed (GObject
*object
)
1871 ETemplatesStore
*templates_store
;
1872 ESourceRegistry
*registry
;
1873 EMailAccountStore
*account_store
;
1874 EMailSession
*session
;
1876 templates_store
= E_TEMPLATES_STORE (object
);
1878 /* Chain up to parent's method. */
1879 G_OBJECT_CLASS (e_templates_store_parent_class
)->constructed (object
);
1881 templates_store
->priv
->cancellable
= g_cancellable_new ();
1883 account_store
= e_templates_store_ref_account_store (templates_store
);
1884 g_return_if_fail (account_store
!= NULL
);
1886 session
= e_mail_account_store_get_session (account_store
);
1887 registry
= e_mail_session_get_registry (session
);
1889 templates_store
->priv
->service_enabled_handler_id
= g_signal_connect_data (
1890 account_store
, "service-enabled",
1891 G_CALLBACK (templates_store_service_enabled_cb
),
1892 e_weak_ref_new (templates_store
),
1893 (GClosureNotify
) e_weak_ref_free
, 0);
1895 templates_store
->priv
->service_disabled_handler_id
= g_signal_connect_data (
1896 account_store
, "service-disabled",
1897 G_CALLBACK (templates_store_service_disabled_cb
),
1898 e_weak_ref_new (templates_store
),
1899 (GClosureNotify
) e_weak_ref_free
, 0);
1901 templates_store
->priv
->service_removed_handler_id
= g_signal_connect_data (
1902 account_store
, "service-removed",
1903 G_CALLBACK (templates_store_service_removed_cb
),
1904 e_weak_ref_new (templates_store
),
1905 (GClosureNotify
) e_weak_ref_free
, 0);
1907 templates_store
->priv
->source_changed_handler_id
= g_signal_connect_data (
1908 registry
, "source-changed",
1909 G_CALLBACK (templates_store_source_changed_cb
),
1910 e_weak_ref_new (templates_store
),
1911 (GClosureNotify
) e_weak_ref_free
, 0);
1913 templates_store_maybe_add_enabled_services (templates_store
);
1915 g_clear_object (&account_store
);
1919 e_templates_store_class_init (ETemplatesStoreClass
*class)
1921 GObjectClass
*object_class
;
1923 g_type_class_add_private (class, sizeof (ETemplatesStorePrivate
));
1925 object_class
= G_OBJECT_CLASS (class);
1926 object_class
->set_property
= templates_store_set_property
;
1927 object_class
->get_property
= templates_store_get_property
;
1928 object_class
->dispose
= templates_store_dispose
;
1929 object_class
->finalize
= templates_store_finalize
;
1930 object_class
->constructed
= templates_store_constructed
;
1932 g_object_class_install_property (
1935 g_param_spec_object (
1938 "EMailAccountStore",
1939 E_TYPE_MAIL_ACCOUNT_STORE
,
1941 G_PARAM_CONSTRUCT_ONLY
|
1942 G_PARAM_STATIC_STRINGS
));
1944 signals
[CHANGED
] = g_signal_new (
1946 G_TYPE_FROM_CLASS (class),
1948 G_STRUCT_OFFSET (ETemplatesStoreClass
, changed
),
1950 G_TYPE_NONE
, 0, G_TYPE_NONE
);
1954 e_templates_store_init (ETemplatesStore
*templates_store
)
1956 templates_store
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (templates_store
, E_TYPE_TEMPLATES_STORE
, ETemplatesStorePrivate
);
1958 g_mutex_init (&templates_store
->priv
->busy_lock
);
1959 templates_store
->priv
->account_store_weakref
= e_weak_ref_new (NULL
);
1963 e_templates_store_ref_default (EMailAccountStore
*account_store
)
1965 static gpointer def_templates_store
= NULL
;
1967 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (account_store
), NULL
);
1969 if (def_templates_store
) {
1970 g_object_ref (def_templates_store
);
1972 def_templates_store
= g_object_new (E_TYPE_TEMPLATES_STORE
,
1973 "account-store", account_store
,
1976 g_object_add_weak_pointer (G_OBJECT (def_templates_store
), &def_templates_store
);
1979 return def_templates_store
;
1983 e_templates_store_ref_account_store (ETemplatesStore
*templates_store
)
1985 g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store
), NULL
);
1987 return g_weak_ref_get (templates_store
->priv
->account_store_weakref
);
1991 tmpl_store_data_folder_has_messages_cb (GNode
*node
,
1994 TmplFolderData
*tfd
;
1995 gint
*pmultiple_accounts
= user_data
;
1997 g_return_val_if_fail (node
!= NULL
, TRUE
);
1998 g_return_val_if_fail (pmultiple_accounts
!= NULL
, TRUE
);
2005 if (tfd
->messages
) {
2006 *pmultiple_accounts
= *pmultiple_accounts
+ 1;
2013 typedef struct _TmplActionData
{
2014 ETemplatesStore
*templates_store
; /* not referenced */
2015 CamelFolder
*folder
;
2016 const gchar
*uid
; /* from camel_pstring */
2017 ETemplatesStoreActionFunc action_cb
;
2018 gpointer action_cb_user_data
;
2021 static TmplActionData
*
2022 tmpl_action_data_new (ETemplatesStore
*templates_store
,
2023 CamelFolder
*folder
,
2025 ETemplatesStoreActionFunc action_cb
,
2026 gpointer action_cb_user_data
)
2028 TmplActionData
*tad
;
2030 g_return_val_if_fail (CAMEL_IS_FOLDER (folder
), NULL
);
2031 g_return_val_if_fail (uid
&& *uid
, NULL
);
2033 tad
= g_new0 (TmplActionData
, 1);
2034 tad
->templates_store
= templates_store
;
2035 tad
->folder
= g_object_ref (folder
);
2036 tad
->uid
= camel_pstring_strdup (uid
);
2037 tad
->action_cb
= action_cb
;
2038 tad
->action_cb_user_data
= action_cb_user_data
;
2044 tmpl_action_data_free (gpointer ptr
)
2046 TmplActionData
*tad
= ptr
;
2049 g_clear_object (&tad
->folder
);
2050 camel_pstring_free (tad
->uid
);
2056 templates_store_action_activated_cb (GtkAction
*action
,
2057 TmplActionData
*tad
)
2059 g_return_if_fail (tad
!= NULL
);
2060 g_return_if_fail (tad
->action_cb
!= NULL
);
2062 tad
->action_cb (tad
->templates_store
, tad
->folder
, tad
->uid
, tad
->action_cb_user_data
);
2066 templates_store_add_to_menu_recurse (ETemplatesStore
*templates_store
,
2068 GtkUIManager
*ui_manager
,
2069 GtkActionGroup
*action_group
,
2070 const gchar
*base_menu_path
,
2072 ETemplatesStoreActionFunc action_cb
,
2073 gpointer action_cb_user_data
,
2074 gboolean with_folder_menu
,
2075 guint
*action_count
)
2077 TmplFolderData
*tfd
;
2079 g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store
));
2080 g_return_if_fail (node
!= NULL
);
2085 tmpl_folder_data_lock (tfd
);
2089 gchar
*action_name
, *menu_path
= NULL
;
2090 const gchar
*use_menu_path
;
2093 if (with_folder_menu
) {
2094 action_name
= g_strdup_printf ("templates-menu-%d", *action_count
);
2095 *action_count
= *action_count
+ 1;
2097 action
= gtk_action_new (action_name
, camel_folder_get_display_name (tfd
->folder
), NULL
, NULL
);
2098 gtk_action_group_add_action (action_group
, action
);
2100 gtk_ui_manager_add_ui (ui_manager
, merge_id
, base_menu_path
, action_name
,
2101 action_name
, GTK_UI_MANAGER_MENU
, FALSE
);
2103 menu_path
= g_strdup_printf ("%s/%s", base_menu_path
, action_name
);
2104 use_menu_path
= menu_path
;
2106 g_object_unref (action
);
2107 g_free (action_name
);
2109 use_menu_path
= base_menu_path
;
2112 if (node
->children
) {
2113 templates_store_add_to_menu_recurse (templates_store
, node
->children
,
2114 ui_manager
, action_group
, use_menu_path
, merge_id
,
2115 action_cb
, action_cb_user_data
, TRUE
, action_count
);
2118 for (link
= tfd
->messages
; link
; link
= g_slist_next (link
)) {
2119 TmplMessageData
*tmd
= link
->data
;
2121 if (tmd
&& tmd
->uid
&& tmd
->subject
) {
2122 action_name
= g_strdup_printf ("templates-item-%d", *action_count
);
2123 *action_count
= *action_count
+ 1;
2125 action
= gtk_action_new (action_name
, tmd
->subject
, NULL
, NULL
);
2127 g_signal_connect_data (
2129 G_CALLBACK (templates_store_action_activated_cb
),
2130 tmpl_action_data_new (templates_store
, tfd
->folder
, tmd
->uid
, action_cb
, action_cb_user_data
),
2131 (GClosureNotify
) tmpl_action_data_free
, 0);
2133 gtk_action_group_add_action (action_group
, action
);
2135 gtk_ui_manager_add_ui (
2136 ui_manager
, merge_id
, use_menu_path
, action_name
,
2137 action_name
, GTK_UI_MANAGER_MENUITEM
, FALSE
);
2139 g_object_unref (action
);
2140 g_free (action_name
);
2147 tmpl_folder_data_unlock (tfd
);
2155 e_templates_store_build_menu (ETemplatesStore
*templates_store
,
2156 EShellView
*shell_view
,
2157 GtkUIManager
*ui_manager
,
2158 GtkActionGroup
*action_group
,
2159 const gchar
*base_menu_path
,
2161 ETemplatesStoreActionFunc action_cb
,
2162 gpointer action_cb_user_data
)
2166 gint multiple_accounts
= 0;
2167 guint action_count
= 0;
2168 const gchar
*main_menu_path
= base_menu_path
;
2169 gchar
*tmp_menu_path
= NULL
;
2172 g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store
));
2173 g_return_if_fail (E_IS_SHELL_VIEW (shell_view
));
2174 g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager
));
2175 g_return_if_fail (GTK_IS_ACTION_GROUP (action_group
));
2176 g_return_if_fail (base_menu_path
!= NULL
);
2177 g_return_if_fail (merge_id
!= 0);
2178 g_return_if_fail (action_cb
!= NULL
);
2180 templates_store_lock (templates_store
);
2182 gtk_ui_manager_remove_ui (ui_manager
, merge_id
);
2183 e_action_group_remove_all_actions (action_group
);
2185 for (link
= templates_store
->priv
->stores
; link
&& multiple_accounts
<= 1; link
= g_slist_next (link
)) {
2186 TmplStoreData
*tsd
= link
->data
;
2191 tmpl_store_data_lock (tsd
);
2193 if (tsd
->folders
&& tsd
->folders
->children
) {
2196 store
= g_weak_ref_get (tsd
->store_weakref
);
2198 g_node_traverse (tsd
->folders
, G_PRE_ORDER
, G_TRAVERSE_ALL
, -1,
2199 tmpl_store_data_folder_has_messages_cb
, &multiple_accounts
);
2202 g_clear_object (&store
);
2205 tmpl_store_data_unlock (tsd
);
2208 if (multiple_accounts
> 0) {
2209 action_name
= g_strdup_printf ("templates-menu-%d", action_count
);
2212 action
= gtk_action_new (action_name
, _("Templates"), NULL
, NULL
);
2214 gtk_action_group_add_action (action_group
, action
);
2216 gtk_ui_manager_add_ui (
2217 ui_manager
, merge_id
, base_menu_path
, action_name
,
2218 action_name
, GTK_UI_MANAGER_MENU
, FALSE
);
2220 tmp_menu_path
= g_strdup_printf ("%s/%s", base_menu_path
, action_name
);
2221 main_menu_path
= tmp_menu_path
;
2223 g_object_unref (action
);
2224 g_free (action_name
);
2227 for (link
= templates_store
->priv
->stores
; link
&& multiple_accounts
> 0; link
= g_slist_next (link
)) {
2228 TmplStoreData
*tsd
= link
->data
;
2233 tmpl_store_data_lock (tsd
);
2235 if (tsd
->folders
&& tsd
->folders
->children
) {
2238 store
= g_weak_ref_get (tsd
->store_weakref
);
2240 gchar
*menu_path
= NULL
;
2241 const gchar
*use_menu_path
= main_menu_path
;
2243 if (multiple_accounts
> 1) {
2244 action_name
= g_strdup_printf ("templates-menu-%d", action_count
);
2247 action
= gtk_action_new (action_name
, camel_service_get_display_name (CAMEL_SERVICE (store
)), NULL
, NULL
);
2249 gtk_action_group_add_action (action_group
, action
);
2251 gtk_ui_manager_add_ui (
2252 ui_manager
, merge_id
, main_menu_path
, action_name
,
2253 action_name
, GTK_UI_MANAGER_MENU
, FALSE
);
2255 menu_path
= g_strdup_printf ("%s/%s", main_menu_path
, action_name
);
2256 use_menu_path
= menu_path
;
2258 g_object_unref (action
);
2259 g_free (action_name
);
2262 templates_store_add_to_menu_recurse (templates_store
, tsd
->folders
->children
,
2263 ui_manager
, action_group
, use_menu_path
, merge_id
,
2264 action_cb
, action_cb_user_data
, FALSE
, &action_count
);
2269 g_clear_object (&store
);
2272 tmpl_store_data_unlock (tsd
);
2275 templates_store_unlock (templates_store
);
2277 gtk_ui_manager_ensure_update (ui_manager
);
2279 g_free (tmp_menu_path
);