Bug 793915 - Option to convert UTC time to local time in Reply credits
[evolution.git] / src / plugins / templates / e-templates-store.c
blob1f4017b5750ad29f886b1f0152dda8022daf9fa9
1 /*
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
11 * for more details.
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>
21 #include <gtk/gtk.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;
38 GMutex busy_lock;
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);
46 enum {
47 PROP_0,
48 PROP_ACCOUNT_STORE
51 enum {
52 CHANGED,
53 LAST_SIGNAL
56 static guint signals[LAST_SIGNAL];
58 static void
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);
66 static void
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);
74 static void
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);
82 static void
83 tmpl_folder_data_folder_changed_cb (CamelFolder *folder,
84 CamelFolderChangeInfo *change_info,
85 gpointer user_data);
87 static void
88 tmpl_store_data_folder_created_cb (CamelStore *store,
89 CamelFolderInfo *folder_info,
90 gpointer user_data);
91 static void
92 tmpl_store_data_folder_deleted_cb (CamelStore *store,
93 CamelFolderInfo *folder_info,
94 gpointer user_data);
95 static void
96 tmpl_store_data_folder_renamed_cb (CamelStore *store,
97 const gchar *old_name,
98 CamelFolderInfo *folder_info,
99 gpointer user_data);
101 static void
102 tmpl_store_data_notify_display_name_cb (CamelService *service,
103 GParamSpec *param,
104 gpointer user_data);
106 typedef struct _TmplMessageData {
107 const gchar *subject; /* Allocated by camel-pstring */
108 const gchar *uid; /* Allocated by camel-pstring */
109 } TmplMessageData;
111 static const gchar *
112 tmpl_sanitized_subject (const gchar *subject)
114 if (!subject || !*subject)
115 subject = _("No Title");
117 return subject;
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));
131 return tmd;
134 static void
135 tmpl_message_data_free (gpointer ptr)
137 TmplMessageData *tmd = ptr;
139 if (tmd) {
140 camel_pstring_free (tmd->subject);
141 camel_pstring_free (tmd->uid);
142 g_free (tmd);
146 static void
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));
158 static gint
159 tmpl_message_data_compare (gconstpointer ptr1,
160 gconstpointer ptr2)
162 const TmplMessageData *tmd1 = ptr1, *tmd2 = ptr2;
164 if (!tmd1 || !tmd2) {
165 if (tmd1 == tmd2)
166 return 0;
167 if (tmd1)
168 return -1;
169 return 1;
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 * */
178 CamelFolder *folder;
179 gulong changed_handler_id;
181 GMutex busy_lock;
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 */
187 } TmplFolderData;
189 static TmplFolderData *
190 tmpl_folder_data_new (ETemplatesStore *templates_store,
191 CamelFolder *folder)
193 TmplFolderData *tfd;
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);
199 tfd->ref_count = 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;
207 return tfd;
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);
217 return tfd;
220 static void
221 tmpl_folder_data_unref (gpointer ptr)
223 TmplFolderData *tfd = ptr;
225 if (tfd) {
226 if (!g_atomic_int_dec_and_test (&tfd->ref_count))
227 return;
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;
245 g_free (tfd);
249 static void
250 tmpl_folder_data_lock (TmplFolderData *tfd)
252 g_return_if_fail (tfd != NULL);
254 g_mutex_lock (&tfd->busy_lock);
257 static void
258 tmpl_folder_data_unlock (TmplFolderData *tfd)
260 g_return_if_fail (tfd != NULL);
262 g_mutex_unlock (&tfd->busy_lock);
265 static void
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,
283 const gchar *uid)
285 GSList *link;
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;
293 if (!tmd)
294 continue;
296 if (uid == tmd->uid || g_strcmp0 (uid, tmd->uid) == 0)
297 return tmd;
300 return NULL;
303 static gboolean
304 tmpl_folder_data_remove_message (TmplFolderData *tfd,
305 const gchar *uid)
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);
313 if (tmd) {
314 tfd->messages = g_slist_remove (tfd->messages, tmd);
315 tmpl_message_data_free (tmd);
317 return TRUE;
320 return FALSE;
323 static gboolean
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));
335 if (!tmd) {
336 if (!(camel_message_info_get_flags (info) & (CAMEL_MESSAGE_JUNK | CAMEL_MESSAGE_DELETED))) {
337 tmpl_folder_data_add_message (tfd, info);
338 return TRUE;
341 return FALSE;
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() */
353 changed = TRUE;
356 return changed;
359 static void
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);
367 static gint
368 tmpl_folder_data_compare (gconstpointer ptr1,
369 gconstpointer ptr2)
371 const TmplFolderData *tfd1 = ptr1, *tfd2 = ptr2;
372 const gchar *display_name1, *display_name2;
374 if (!tfd1 || !tfd2) {
375 if (tfd1 == tfd2)
376 return 0;
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 : "");
387 static void
388 tmpl_folder_data_update_done_cb (GObject *source,
389 GAsyncResult *result,
390 gpointer user_data)
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 {
415 TmplFolderData *tfd;
416 GPtrArray *added_uids;
417 GPtrArray *changed_uids;
418 } TfdUpdateData;
420 static void
421 tfd_update_data_free (gpointer ptr)
423 TfdUpdateData *tud = ptr;
425 if (tud) {
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);
429 g_free (tud);
433 static gboolean
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;
441 guint ii;
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);
461 if (info) {
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;
466 } else {
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);
478 if (info) {
479 changed = tmpl_folder_data_change_message (tfd, info) || changed;
480 g_clear_object (&info);
484 if (changed)
485 tmpl_folder_data_sort (tfd);
487 if (all_uids)
488 camel_folder_summary_free_array (all_uids);
490 tmpl_folder_data_unlock (tfd);
492 return changed;
495 static void
496 tmpl_folder_data_update_thread (GTask *task,
497 gpointer source_object,
498 gpointer task_data,
499 GCancellable *cancellable)
501 TfdUpdateData *tud = task_data;
502 gboolean changed;
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);
514 static void
515 tmpl_folder_data_schedule_update (TmplFolderData *tfd,
516 CamelFolderChangeInfo *change_info)
518 ETemplatesStore *templates_store;
519 TfdUpdateData *tud;
520 GTask *task;
521 guint ii;
523 g_return_if_fail (tfd != NULL);
525 templates_store = g_weak_ref_get (tfd->templates_store_weakref);
526 if (!templates_store)
527 return;
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];
541 if (uid && *uid)
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];
548 if (uid && *uid)
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);
560 static void
561 tmpl_folder_data_folder_changed_cb (CamelFolder *folder,
562 CamelFolderChangeInfo *change_info,
563 gpointer user_data)
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) {
581 guint ii;
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];
588 if (uid && *uid)
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);
603 static gboolean
604 tmpl_store_data_traverse_to_free_cb (GNode *node,
605 gpointer user_data)
607 if (node && node->data) {
608 tmpl_folder_data_unref (node->data);
609 node->data = NULL;
612 return FALSE;
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;
625 GMutex busy_lock;
626 gchar *root_folder_path;
627 gchar *templates_folder_uri;
628 gchar *identity_source_uid;
629 GNode *folders; /* data is TmplFolderData * */
630 } TmplStoreData;
632 static void
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,
638 CamelStore *store,
639 const gchar *root_folder_path,
640 const gchar *templates_folder_uri,
641 const gchar *identity_source_uid)
643 TmplStoreData *tsd;
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);
651 tsd->ref_count = 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);
665 } else {
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);
680 return tsd;
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);
690 return tsd;
693 static void
694 tmpl_store_data_unref (gpointer ptr)
696 TmplStoreData *tsd = ptr;
698 if (tsd) {
699 if (!g_atomic_int_dec_and_test (&tsd->ref_count))
700 return;
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) {
708 CamelStore *store;
710 store = g_weak_ref_get (tsd->store_weakref);
711 if (store) {
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;
747 if (tsd->folders) {
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);
750 tsd->folders = NULL;
753 g_free (tsd);
757 static void
758 tmpl_store_data_lock (TmplStoreData *tsd)
760 g_return_if_fail (tsd != NULL);
762 g_mutex_lock (&tsd->busy_lock);
765 static void
766 tmpl_store_data_unlock (TmplStoreData *tsd)
768 g_return_if_fail (tsd != NULL);
770 g_mutex_unlock (&tsd->busy_lock);
773 static gint
774 tmpl_store_data_compare (gconstpointer ptr1,
775 gconstpointer ptr2,
776 gpointer user_data)
778 const TmplStoreData *tsd1 = ptr1, *tsd2 = ptr2;
779 EMailAccountStore *account_store = user_data;
780 CamelService *service1, *service2;
781 gint res;
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);
788 else
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);
795 return res;
798 static void
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) {
808 guint len;
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);
821 static GNode *
822 tmpl_store_data_find_parent_node_locked (TmplStoreData *tsd,
823 const gchar *full_name,
824 gboolean for_insert)
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;
833 while (from_node) {
834 node = g_node_first_child (from_node);
835 from_node = NULL;
837 while (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) {
843 parent = node;
844 from_node = node;
845 break;
848 node = g_node_next_sibling (node);
852 if (for_insert && parent) {
853 GNode *node;
855 if (parent->data) {
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 */
860 parent = NULL;
864 for (node = parent ? parent->children : NULL; node; node = node->next) {
865 TmplFolderData *tfd = node->data;
867 if (!tfd)
868 continue;
870 if (g_strcmp0 (full_name, camel_folder_get_full_name (tfd->folder)) == 0) {
871 /* The folder is already in the list of folders */
872 parent = NULL;
873 break;
878 return parent;
881 static GNode *
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);
891 if (!parent)
892 return NULL;
894 for (node = g_node_first_child (parent); node; node = g_node_next_sibling (node)) {
895 TmplFolderData *tfd = node->data;
897 if (!tfd)
898 continue;
900 if (tfd->folder && g_strcmp0 (full_name, camel_folder_get_full_name (tfd->folder)) == 0) {
901 return node;
905 return NULL;
908 static GNode *
909 tmpl_store_data_find_node_with_folder_locked (TmplStoreData *tsd,
910 CamelFolder *folder)
912 GNode *node;
914 g_return_val_if_fail (tsd != NULL, NULL);
915 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
917 node = tsd->folders;
918 while (node) {
919 TmplFolderData *tfd = node->data;
920 GNode *next;
922 if (tfd && tfd->folder == folder) {
923 return node;
926 /* Traverse the tree */
927 next = node->children;
928 if (!next)
929 next = node->next;
930 if (!next) {
931 next = node->parent;
932 while (next) {
933 GNode *sibl = next->next;
935 if (sibl) {
936 next = sibl;
937 break;
938 } else {
939 next = next->parent;
944 node = next;
947 return NULL;
950 static void
951 tmpl_store_data_update_done_cb (GObject *source,
952 GAsyncResult *result,
953 gpointer user_data)
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);
977 static void
978 tmpl_store_data_initial_setup_thread (GTask *task,
979 gpointer source_object,
980 gpointer task_data,
981 GCancellable *cancellable)
983 ETemplatesStore *templates_store;
984 TmplStoreData *tsd = task_data;
985 CamelStore *store;
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);
1008 if (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);
1016 fi = folder_info;
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);
1022 if (folder) {
1023 GNode *parent;
1025 tmpl_store_data_lock (tsd);
1027 parent = tmpl_store_data_find_parent_node_locked (tsd, fi->full_name, TRUE);
1028 if (parent) {
1029 TmplFolderData *tfd;
1031 tfd = tmpl_folder_data_new (templates_store, folder);
1032 if (tfd) {
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);
1042 if (local_error)
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 */
1049 next = fi->child;
1050 if (!next)
1051 next = fi->next;
1052 if (!next) {
1053 next = fi->parent;
1054 while (next) {
1055 CamelFolderInfo *sibl = next->next;
1057 if (sibl) {
1058 next = sibl;
1059 break;
1060 } else {
1061 next = next->parent;
1066 fi = next;
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);
1079 static void
1080 tmpl_store_data_schedule_initial_setup (TmplStoreData *tsd)
1082 ETemplatesStore *templates_store;
1083 GTask *task;
1085 g_return_if_fail (tsd != NULL);
1087 templates_store = g_weak_ref_get (tsd->templates_store_weakref);
1088 if (!templates_store)
1089 return;
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 {
1102 TmplStoreData *tsd;
1103 gchar *fullname;
1104 gchar *old_fullname; /* If set, then it's "rename", otherwise it's "create" */
1105 } TsdFolderData;
1107 static void
1108 tsd_folder_data_free (gpointer ptr)
1110 TsdFolderData *fd = ptr;
1112 if (fd) {
1113 tmpl_store_data_unref (fd->tsd);
1114 g_free (fd->fullname);
1115 g_free (fd->old_fullname);
1116 g_free (fd);
1120 static void
1121 tmpl_store_data_folder_thread (GTask *task,
1122 gpointer source_object,
1123 gpointer task_data,
1124 GCancellable *cancellable)
1126 ETemplatesStore *templates_store;
1127 TsdFolderData *fd = task_data;
1128 CamelStore *store;
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);
1142 if (folder) {
1143 GNode *parent = NULL;
1145 tmpl_store_data_lock (fd->tsd);
1147 if (fd->old_fullname) {
1148 GNode *node;
1150 node = tmpl_store_data_find_node_locked (fd->tsd, fd->old_fullname);
1151 if (!node) {
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);
1156 if (node) {
1157 TmplFolderData *tfd = node->data;
1159 changed = TRUE;
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);
1176 } else {
1177 parent = tmpl_store_data_find_parent_node_locked (fd->tsd, fd->fullname, TRUE);
1178 if (parent) {
1179 TmplFolderData *tfd;
1181 tfd = tmpl_folder_data_new (templates_store, folder);
1182 if (tfd) {
1183 changed = tmpl_folder_data_update_sync (tfd, NULL, NULL, cancellable);
1185 g_node_append_data (parent, tfd);
1190 if (parent) {
1191 GSList *data = NULL, *link;
1192 GNode *node;
1194 for (node = parent->children; node; node = node->next) {
1195 if (node->data)
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) {
1202 if (node->data) {
1203 node->data = link->data;
1204 link = g_slist_next (link);
1208 g_slist_free (data);
1211 tmpl_store_data_unlock (fd->tsd);
1214 if (local_error)
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);
1227 static void
1228 tmpl_store_data_folder_created_cb (CamelStore *store,
1229 CamelFolderInfo *folder_info,
1230 gpointer user_data)
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)) {
1245 GNode *parent;
1247 parent = tmpl_store_data_find_parent_node_locked (tsd, folder_info->full_name, TRUE);
1248 if (parent) {
1249 TsdFolderData *fd;
1250 GTask *task;
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);
1268 static void
1269 tmpl_store_data_folder_deleted_cb (CamelStore *store,
1270 CamelFolderInfo *folder_info,
1271 gpointer user_data)
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)) {
1286 GNode *node;
1288 node = tmpl_store_data_find_node_locked (tsd, folder_info->full_name);
1289 if (node) {
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);
1293 changed = TRUE;
1297 tmpl_store_data_unlock (tsd);
1299 if (changed)
1300 templates_store_emit_changed (templates_store);
1302 g_clear_object (&templates_store);
1305 static void
1306 tmpl_store_data_folder_renamed_cb (CamelStore *store,
1307 const gchar *old_name,
1308 CamelFolderInfo *folder_info,
1309 gpointer user_data)
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)) {
1326 TsdFolderData *fd;
1327 GTask *task;
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);
1338 } else {
1339 GNode *node;
1341 node = tmpl_store_data_find_node_locked (tsd, old_name);
1342 if (node) {
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);
1346 changed = TRUE;
1349 } else if (templates_store && g_str_has_prefix (folder_info->full_name, tsd->root_folder_path)) {
1350 TsdFolderData *fd;
1351 GTask *task;
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);
1366 if (changed)
1367 templates_store_emit_changed (templates_store);
1369 g_clear_object (&templates_store);
1372 static void
1373 tmpl_store_data_notify_display_name_cb (CamelService *service,
1374 GParamSpec *param,
1375 gpointer user_data)
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);
1398 if (changed)
1399 templates_store_emit_changed (templates_store);
1401 g_object_unref (templates_store);
1402 g_clear_object (&account_store);
1406 static gchar *
1407 templates_store_find_custom_templates_root_folder_path (ETemplatesStore *templates_store,
1408 CamelStore *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;
1450 } else {
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);
1459 if (local_error) {
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;
1473 static void
1474 templates_store_maybe_add_store (ETemplatesStore *templates_store,
1475 CamelStore *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);
1488 if (!account_store)
1489 return;
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) {
1499 TmplStoreData *tsd;
1500 GSList *link;
1502 for (link = templates_store->priv->stores; link; link = g_slist_next (link)) {
1503 CamelStore *tsd_store;
1505 tsd = link->data;
1507 if (!tsd)
1508 continue;
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);
1514 break;
1517 g_clear_object (&tsd_store);
1520 /* The store is not in the list of stores yet */
1521 if (!link) {
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);
1530 changed = TRUE;
1534 templates_store_unlock (templates_store);
1536 if (changed)
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);
1546 static void
1547 templates_store_maybe_remove_store (ETemplatesStore *templates_store,
1548 CamelStore *store)
1550 GSList *link;
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;
1562 if (!tsd)
1563 continue;
1565 other_store = g_weak_ref_get (tsd->store_weakref);
1566 if (other_store == store) {
1567 changed = TRUE;
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);
1572 break;
1575 g_clear_object (&other_store);
1578 templates_store_unlock (templates_store);
1580 if (changed)
1581 templates_store_emit_changed (templates_store);
1584 static void
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);
1610 static void
1611 templates_store_service_enabled_cb (EMailAccountStore *account_store,
1612 CamelService *service,
1613 GWeakRef *weak_ref)
1615 ETemplatesStore *templates_store;
1617 if (!CAMEL_IS_STORE (service))
1618 return;
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);
1628 static void
1629 templates_store_service_disabled_cb (EMailAccountStore *account_store,
1630 CamelService *service,
1631 GWeakRef *weak_ref)
1633 ETemplatesStore *templates_store;
1635 if (!CAMEL_IS_STORE (service))
1636 return;
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);
1646 static void
1647 templates_store_service_removed_cb (EMailAccountStore *account_store,
1648 CamelService *service,
1649 GWeakRef *weak_ref)
1651 ETemplatesStore *templates_store;
1653 if (!CAMEL_IS_STORE (service))
1654 return;
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);
1664 static void
1665 templates_store_source_changed_cb (ESourceRegistry *registry,
1666 ESource *source,
1667 GWeakRef *weak_ref)
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))
1674 return;
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;
1683 GSList *link;
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;
1693 if (!tsd)
1694 continue;
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;
1699 break;
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). */
1706 rebuild_all = TRUE;
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;
1724 } else {
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). */
1727 rebuild_all = TRUE;
1731 if (local_error) {
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);
1741 if (rebuild_all) {
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);
1748 if (rebuild_all)
1749 templates_store_maybe_add_enabled_services (templates_store);
1751 g_object_unref (templates_store);
1752 g_free (templates_folder);
1756 static void
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);
1765 static void
1766 templates_store_set_property (GObject *object,
1767 guint property_id,
1768 const GValue *value,
1769 GParamSpec *pspec)
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));
1776 return;
1779 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1782 static void
1783 templates_store_get_property (GObject *object,
1784 guint property_id,
1785 GValue *value,
1786 GParamSpec *pspec)
1788 switch (property_id) {
1789 case PROP_ACCOUNT_STORE:
1790 g_value_take_object (
1791 value,
1792 e_templates_store_ref_account_store (
1793 E_TEMPLATES_STORE (object)));
1794 return;
1797 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1800 static void
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);
1849 static void
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);
1868 static void
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);
1918 static void
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 (
1933 object_class,
1934 PROP_ACCOUNT_STORE,
1935 g_param_spec_object (
1936 "account-store",
1937 "Account Store",
1938 "EMailAccountStore",
1939 E_TYPE_MAIL_ACCOUNT_STORE,
1940 G_PARAM_READWRITE |
1941 G_PARAM_CONSTRUCT_ONLY |
1942 G_PARAM_STATIC_STRINGS));
1944 signals[CHANGED] = g_signal_new (
1945 "changed",
1946 G_TYPE_FROM_CLASS (class),
1947 G_SIGNAL_RUN_LAST,
1948 G_STRUCT_OFFSET (ETemplatesStoreClass, changed),
1949 NULL, NULL, NULL,
1950 G_TYPE_NONE, 0, G_TYPE_NONE);
1953 static void
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);
1962 ETemplatesStore *
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);
1971 } else {
1972 def_templates_store = g_object_new (E_TYPE_TEMPLATES_STORE,
1973 "account-store", account_store,
1974 NULL);
1976 g_object_add_weak_pointer (G_OBJECT (def_templates_store), &def_templates_store);
1979 return def_templates_store;
1982 EMailAccountStore *
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);
1990 static gboolean
1991 tmpl_store_data_folder_has_messages_cb (GNode *node,
1992 gpointer user_data)
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);
2000 if (!node->data)
2001 return FALSE;
2003 tfd = node->data;
2005 if (tfd->messages) {
2006 *pmultiple_accounts = *pmultiple_accounts + 1;
2007 return TRUE;
2010 return FALSE;
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;
2019 } TmplActionData;
2021 static TmplActionData *
2022 tmpl_action_data_new (ETemplatesStore *templates_store,
2023 CamelFolder *folder,
2024 const gchar *uid,
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;
2040 return tad;
2043 static void
2044 tmpl_action_data_free (gpointer ptr)
2046 TmplActionData *tad = ptr;
2048 if (tad) {
2049 g_clear_object (&tad->folder);
2050 camel_pstring_free (tad->uid);
2051 g_free (tad);
2055 static void
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);
2065 static void
2066 templates_store_add_to_menu_recurse (ETemplatesStore *templates_store,
2067 GNode *node,
2068 GtkUIManager *ui_manager,
2069 GtkActionGroup *action_group,
2070 const gchar *base_menu_path,
2071 guint merge_id,
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);
2082 while (node) {
2083 tfd = node->data;
2084 if (tfd) {
2085 tmpl_folder_data_lock (tfd);
2087 if (tfd->folder) {
2088 GtkAction *action;
2089 gchar *action_name, *menu_path = NULL;
2090 const gchar *use_menu_path;
2091 GSList *link;
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);
2108 } else {
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 (
2128 action, "activate",
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);
2144 g_free (menu_path);
2147 tmpl_folder_data_unlock (tfd);
2150 node = node->next;
2154 void
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,
2160 guint merge_id,
2161 ETemplatesStoreActionFunc action_cb,
2162 gpointer action_cb_user_data)
2164 GSList *link;
2165 GtkAction *action;
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;
2170 gchar *action_name;
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;
2188 if (!tsd)
2189 continue;
2191 tmpl_store_data_lock (tsd);
2193 if (tsd->folders && tsd->folders->children) {
2194 CamelStore *store;
2196 store = g_weak_ref_get (tsd->store_weakref);
2197 if (store) {
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);
2210 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;
2230 if (!tsd)
2231 continue;
2233 tmpl_store_data_lock (tsd);
2235 if (tsd->folders && tsd->folders->children) {
2236 CamelStore *store;
2238 store = g_weak_ref_get (tsd->store_weakref);
2239 if (store) {
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);
2245 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);
2266 g_free (menu_path);
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);