[evolution] Bug 681353 - Use per-account Templates folder setting
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Bug 681353 - Use per-account Templates folder setting
- Date: Wed, 4 May 2016 14:14:41 +0000 (UTC)
commit 10b70ae20da97a621c3c802b4546f77f66345b86
Author: Milan Crha <mcrha redhat com>
Date: Wed May 4 16:13:08 2016 +0200
Bug 681353 - Use per-account Templates folder setting
composer/mail-composer.error.xml | 5 +
mail/e-mail-config-defaults-page.c | 41 +-
mail/em-folder-tree-model.c | 52 +-
plugins/templates/Makefile.am | 5 +-
plugins/templates/e-templates-store.c | 2282 +++++++++++++++++++++++++++++++++
plugins/templates/e-templates-store.h | 95 ++
plugins/templates/templates.c | 475 +++-----
po/POTFILES.in | 1 +
8 files changed, 2592 insertions(+), 364 deletions(-)
---
diff --git a/composer/mail-composer.error.xml b/composer/mail-composer.error.xml
index 27e00fd..722d9ad 100644
--- a/composer/mail-composer.error.xml
+++ b/composer/mail-composer.error.xml
@@ -102,4 +102,9 @@
<button _label="Lose _formatting" response="GTK_RESPONSE_YES"/>
</error>
+ <error id="failed-save-template" type="error">
+ <_primary>An error occurred while saving to your Templates folder.</_primary>
+ <_secondary>The reported error was "{0}". The message has most likely not been
saved.</_secondary>
+ </error>
+
</error-list>
diff --git a/mail/e-mail-config-defaults-page.c b/mail/e-mail-config-defaults-page.c
index 5c2cf71..3bbd045 100644
--- a/mail/e-mail-config-defaults-page.c
+++ b/mail/e-mail-config-defaults-page.c
@@ -43,6 +43,7 @@ struct _EMailConfigDefaultsPagePrivate {
GtkWidget *drafts_button; /* not referenced */
GtkWidget *sent_button; /* not referenced */
GtkWidget *archive_button; /* not referenced */
+ GtkWidget *templates_button; /* not referenced */
GtkWidget *replies_toggle; /* not referenced */
GtkWidget *trash_toggle; /* not referenced */
GtkWidget *junk_toggle; /* not referenced */
@@ -275,6 +276,11 @@ mail_config_defaults_page_restore_folders (EMailConfigDefaultsPage *page)
folder_uri = e_mail_session_get_local_folder_uri (session, type);
em_folder_selection_button_set_folder_uri (button, folder_uri);
+ type = E_MAIL_LOCAL_FOLDER_TEMPLATES;
+ button = EM_FOLDER_SELECTION_BUTTON (page->priv->templates_button);
+ folder_uri = e_mail_session_get_local_folder_uri (session, type);
+ em_folder_selection_button_set_folder_uri (button, folder_uri);
+
if (gtk_widget_is_sensitive (page->priv->sent_button)) {
type = E_MAIL_LOCAL_FOLDER_SENT;
button = EM_FOLDER_SELECTION_BUTTON (page->priv->sent_button);
@@ -775,6 +781,7 @@ mail_config_defaults_page_constructed (GObject *object)
GEnumValue *enum_value;
EMdnResponsePolicy policy;
CamelProvider *provider = NULL;
+ CamelStore *store;
const gchar *extension_name;
const gchar *text;
gchar *markup;
@@ -922,8 +929,36 @@ mail_config_defaults_page_constructed (GObject *object)
G_BINDING_BIDIRECTIONAL |
G_BINDING_SYNC_CREATE);
+ text = _("_Templates Folder:");
+ widget = gtk_label_new_with_mnemonic (text);
+ gtk_widget_set_margin_left (widget, 12);
+ gtk_size_group_add_widget (size_group, widget);
+ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+ gtk_grid_attach (GTK_GRID (container), widget, 0, 5, 1, 1);
+ gtk_widget_show (widget);
+
+ label = GTK_LABEL (widget);
+
+ text = _("Choose a folder to use for template messages to.");
+ widget = em_folder_selection_button_new (session, "", text);
+ store = mail_config_defaults_page_ref_store (page);
+ if (store)
+ em_folder_selection_button_set_store (EM_FOLDER_SELECTION_BUTTON (widget), store);
+ g_clear_object (&store);
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_label_set_mnemonic_widget (label, widget);
+ gtk_grid_attach (GTK_GRID (container), widget, 1, 5, 1, 1);
+ page->priv->templates_button = widget; /* not referenced */
+ gtk_widget_show (widget);
+
+ e_binding_bind_object_text_property (
+ composition_ext, "templates-folder",
+ widget, "folder-uri",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
- gtk_grid_attach (GTK_GRID (container), hbox, 1, 7, 1, 1);
+ gtk_grid_attach (GTK_GRID (container), hbox, 1, 8, 1, 1);
gtk_widget_show (hbox);
widget = gtk_button_new_with_mnemonic (_("_Restore Defaults"));
@@ -965,7 +1000,7 @@ mail_config_defaults_page_constructed (GObject *object)
_("Choose a folder for deleted messages."),
"real-trash-path", "use-real-trash-path");
if (widget != NULL) {
- gtk_grid_attach (GTK_GRID (container), widget, 0, 5, 2, 1);
+ gtk_grid_attach (GTK_GRID (container), widget, 0, 6, 2, 1);
gtk_widget_show (widget);
}
@@ -975,7 +1010,7 @@ mail_config_defaults_page_constructed (GObject *object)
_("Choose a folder for junk messages."),
"real-junk-path", "use-real-junk-path");
if (widget != NULL) {
- gtk_grid_attach (GTK_GRID (container), widget, 0, 6, 2, 1);
+ gtk_grid_attach (GTK_GRID (container), widget, 0, 7, 2, 1);
gtk_widget_show (widget);
}
diff --git a/mail/em-folder-tree-model.c b/mail/em-folder-tree-model.c
index 6bce0e9..37480d9 100644
--- a/mail/em-folder-tree-model.c
+++ b/mail/em-folder-tree-model.c
@@ -1056,17 +1056,20 @@ em_folder_tree_model_set_session (EMFolderTreeModel *model,
g_object_notify (G_OBJECT (model), "session");
}
-/* Helper for em_folder_tree_model_set_folder_info() */
static void
-folder_tree_model_get_drafts_folder_uri (ESourceRegistry *registry,
- CamelStore *store,
- gchar **drafts_folder_uri)
+folder_tree_model_get_special_folders_uri (ESourceRegistry *registry,
+ CamelStore *store,
+ gchar **drafts_folder_uri,
+ gchar **templates_folder_uri,
+ gchar **sent_folder_uri)
{
ESource *source;
const gchar *extension_name;
/* In case we fail... */
*drafts_folder_uri = NULL;
+ *templates_folder_uri = NULL;
+ *sent_folder_uri = NULL;
source = em_utils_ref_mail_identity_for_store (registry, store);
if (source == NULL)
@@ -1078,37 +1081,17 @@ folder_tree_model_get_drafts_folder_uri (ESourceRegistry *registry,
extension = e_source_get_extension (source, extension_name);
- *drafts_folder_uri =
- e_source_mail_composition_dup_drafts_folder (extension);
+ *drafts_folder_uri = e_source_mail_composition_dup_drafts_folder (extension);
+ *templates_folder_uri = e_source_mail_composition_dup_templates_folder (extension);
}
- g_object_unref (source);
-}
-
-/* Helper for em_folder_tree_model_set_folder_info() */
-static void
-folder_tree_model_get_sent_folder_uri (ESourceRegistry *registry,
- CamelStore *store,
- gchar **sent_folder_uri)
-{
- ESource *source;
- const gchar *extension_name;
-
- /* In case we fail... */
- *sent_folder_uri = NULL;
-
- source = em_utils_ref_mail_identity_for_store (registry, store);
- if (source == NULL)
- return;
-
extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
if (e_source_has_extension (source, extension_name)) {
ESourceMailSubmission *extension;
extension = e_source_get_extension (source, extension_name);
- *sent_folder_uri =
- e_source_mail_submission_dup_sent_folder (extension);
+ *sent_folder_uri = e_source_mail_submission_dup_sent_folder (extension);
}
g_object_unref (source);
@@ -1240,13 +1223,11 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model,
if ((flags & CAMEL_FOLDER_TYPE_MASK) == 0) {
gchar *drafts_folder_uri;
+ gchar *templates_folder_uri;
gchar *sent_folder_uri;
- folder_tree_model_get_drafts_folder_uri (
- registry, store, &drafts_folder_uri);
-
- folder_tree_model_get_sent_folder_uri (
- registry, store, &sent_folder_uri);
+ folder_tree_model_get_special_folders_uri (registry, store,
+ &drafts_folder_uri, &templates_folder_uri, &sent_folder_uri);
if (!folder_is_drafts && drafts_folder_uri != NULL) {
folder_is_drafts = e_mail_folder_uri_equal (
@@ -1254,6 +1235,12 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model,
uri, drafts_folder_uri);
}
+ if (!folder_is_templates && templates_folder_uri != NULL) {
+ folder_is_templates = e_mail_folder_uri_equal (
+ CAMEL_SESSION (session),
+ uri, templates_folder_uri);
+ }
+
if (sent_folder_uri != NULL) {
if (e_mail_folder_uri_equal (
CAMEL_SESSION (session),
@@ -1263,6 +1250,7 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model,
}
g_free (drafts_folder_uri);
+ g_free (templates_folder_uri);
g_free (sent_folder_uri);
}
diff --git a/plugins/templates/Makefile.am b/plugins/templates/Makefile.am
index 18d25ea..18e3f99 100644
--- a/plugins/templates/Makefile.am
+++ b/plugins/templates/Makefile.am
@@ -14,7 +14,10 @@ liborg_gnome_templates_la_CPPFLAGS = \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
-liborg_gnome_templates_la_SOURCES = templates.c
+liborg_gnome_templates_la_SOURCES = \
+ templates.c \
+ e-templates-store.h \
+ e-templates-store.c
liborg_gnome_templates_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) $(CODE_COVERAGE_LDFLAGS)
diff --git a/plugins/templates/e-templates-store.c b/plugins/templates/e-templates-store.c
new file mode 100644
index 0000000..15a815d
--- /dev/null
+++ b/plugins/templates/e-templates-store.c
@@ -0,0 +1,2282 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <camel/camel.h>
+#include <libedataserver/libedataserver.h>
+
+#include "shell/e-shell-view.h"
+
+#include "e-templates-store.h"
+
+struct _ETemplatesStorePrivate {
+ GWeakRef *account_store_weakref; /* EMailAccountStore * */
+
+ gulong service_enabled_handler_id;
+ gulong service_disabled_handler_id;
+ gulong service_removed_handler_id;
+ gulong source_changed_handler_id;
+
+ GMutex busy_lock;
+ GCancellable *cancellable;
+ GSList *stores; /* TmplStoreData *, sorted by account_store options; those with set templates dir */
+ guint menu_refresh_idle_id;
+};
+
+G_DEFINE_TYPE (ETemplatesStore, e_templates_store, G_TYPE_OBJECT);
+
+enum {
+ PROP_0,
+ PROP_ACCOUNT_STORE
+};
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static void
+templates_store_lock (ETemplatesStore *templates_store)
+{
+ g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store));
+
+ g_mutex_lock (&templates_store->priv->busy_lock);
+}
+
+static void
+templates_store_unlock (ETemplatesStore *templates_store)
+{
+ g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store));
+
+ g_mutex_unlock (&templates_store->priv->busy_lock);
+}
+
+static void
+templates_store_emit_changed (ETemplatesStore *templates_store)
+{
+ g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store));
+
+ g_signal_emit (templates_store, signals[CHANGED], 0, NULL);
+}
+
+static void
+tmpl_folder_data_folder_changed_cb (CamelFolder *folder,
+ CamelFolderChangeInfo *change_info,
+ gpointer user_data);
+
+static void
+tmpl_store_data_folder_created_cb (CamelStore *store,
+ CamelFolderInfo *folder_info,
+ gpointer user_data);
+static void
+tmpl_store_data_folder_deleted_cb (CamelStore *store,
+ CamelFolderInfo *folder_info,
+ gpointer user_data);
+static void
+tmpl_store_data_folder_renamed_cb (CamelStore *store,
+ const gchar *old_name,
+ CamelFolderInfo *folder_info,
+ gpointer user_data);
+
+static void
+tmpl_store_data_notify_display_name_cb (CamelService *service,
+ GParamSpec *param,
+ gpointer user_data);
+
+typedef struct _TmplMessageData {
+ const gchar *subject; /* Allocated by camel-pstring */
+ const gchar *uid; /* Allocated by camel-pstring */
+} TmplMessageData;
+
+static const gchar *
+tmpl_sanitized_subject (const gchar *subject)
+{
+ if (!subject || !*subject)
+ subject = _("No Title");
+
+ return subject;
+}
+
+static TmplMessageData *
+tmpl_message_data_new (CamelMessageInfo *info)
+{
+ TmplMessageData *tmd;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ tmd = g_new0 (TmplMessageData, 1);
+ tmd->subject = camel_pstring_strdup (tmpl_sanitized_subject (camel_message_info_subject (info)));
+ tmd->uid = camel_pstring_strdup (camel_message_info_uid (info));
+
+ return tmd;
+}
+
+static void
+tmpl_message_data_free (gpointer ptr)
+{
+ TmplMessageData *tmd = ptr;
+
+ if (tmd) {
+ camel_pstring_free (tmd->subject);
+ camel_pstring_free (tmd->uid);
+ g_free (tmd);
+ }
+}
+
+static void
+tmpl_message_data_change_subject (TmplMessageData *tmd,
+ const gchar *subject)
+{
+ g_return_if_fail (tmd != NULL);
+
+ if (subject != tmd->subject) {
+ camel_pstring_free (tmd->subject);
+ tmd->subject = camel_pstring_strdup (tmpl_sanitized_subject (subject));
+ }
+}
+
+static gint
+tmpl_message_data_compare (gconstpointer ptr1,
+ gconstpointer ptr2)
+{
+ const TmplMessageData *tmd1 = ptr1, *tmd2 = ptr2;
+
+ if (!tmd1 || !tmd2) {
+ if (tmd1 == tmd2)
+ return 0;
+ if (tmd1)
+ return -1;
+ return 1;
+ }
+
+ return g_utf8_collate (tmd1->subject ? tmd1->subject : "", tmd2->subject ? tmd2->subject : "");
+}
+
+typedef struct _TmplFolderData {
+ volatile gint ref_count;
+ GWeakRef *templates_store_weakref; /* ETemplatesStore * */
+ CamelFolder *folder;
+ gulong changed_handler_id;
+
+ GMutex busy_lock;
+ /* This might look inefficient, but the rebuild of the menu is called
+ much more often then the remove of a message from the folder, thus
+ it's cheaper to traverse one by one here, then re-sort the content
+ every time the menu is being rebuild. */
+ GSList *messages; /* TmplMessageData *, ordered by data->subject */
+} TmplFolderData;
+
+static TmplFolderData *
+tmpl_folder_data_new (ETemplatesStore *templates_store,
+ CamelFolder *folder)
+{
+ TmplFolderData *tfd;
+
+ g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store), NULL);
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+
+ tfd = g_new0 (TmplFolderData, 1);
+ tfd->ref_count = 1;
+ tfd->templates_store_weakref = e_weak_ref_new (templates_store);
+ tfd->folder = g_object_ref (folder);
+ tfd->changed_handler_id = g_signal_connect (folder, "changed",
+ G_CALLBACK (tmpl_folder_data_folder_changed_cb), tfd);
+ g_mutex_init (&tfd->busy_lock);
+ tfd->messages = NULL;
+
+ return tfd;
+}
+
+static TmplFolderData *
+tmpl_folder_data_ref (TmplFolderData *tfd)
+{
+ g_return_val_if_fail (tfd != NULL, NULL);
+
+ g_atomic_int_inc (&tfd->ref_count);
+
+ return tfd;
+}
+
+static void
+tmpl_folder_data_unref (gpointer ptr)
+{
+ TmplFolderData *tfd = ptr;
+
+ if (tfd) {
+ if (!g_atomic_int_dec_and_test (&tfd->ref_count))
+ return;
+
+ if (tfd->folder && tfd->changed_handler_id) {
+ g_signal_handler_disconnect (tfd->folder, tfd->changed_handler_id);
+ tfd->changed_handler_id = 0;
+ }
+
+ if (tfd->templates_store_weakref) {
+ e_weak_ref_free (tfd->templates_store_weakref);
+ tfd->templates_store_weakref = NULL;
+ }
+
+ g_clear_object (&tfd->folder);
+
+ g_mutex_clear (&tfd->busy_lock);
+ g_slist_free_full (tfd->messages, tmpl_message_data_free);
+ tfd->messages = NULL;
+
+ g_free (tfd);
+ }
+}
+
+static void
+tmpl_folder_data_lock (TmplFolderData *tfd)
+{
+ g_return_if_fail (tfd != NULL);
+
+ g_mutex_lock (&tfd->busy_lock);
+}
+
+static void
+tmpl_folder_data_unlock (TmplFolderData *tfd)
+{
+ g_return_if_fail (tfd != NULL);
+
+ g_mutex_unlock (&tfd->busy_lock);
+}
+
+static void
+tmpl_folder_data_add_message (TmplFolderData *tfd,
+ CamelMessageInfo *info)
+{
+ TmplMessageData *tmd;
+
+ g_return_if_fail (tfd != NULL);
+ g_return_if_fail (info != NULL);
+
+ tmd = tmpl_message_data_new (info);
+ g_return_if_fail (tmd != NULL);
+
+ /* The caller is responsible to call tmpl_folder_data_sort() */
+ tfd->messages = g_slist_prepend (tfd->messages, tmd);
+}
+
+static TmplMessageData *
+tmpl_folder_data_find_message (TmplFolderData *tfd,
+ const gchar *uid)
+{
+ GSList *link;
+
+ g_return_val_if_fail (tfd != NULL, NULL);
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ for (link = tfd->messages; link; link = g_slist_next (link)) {
+ TmplMessageData *tmd = link->data;
+
+ if (!tmd)
+ continue;
+
+ if (uid == tmd->uid || g_strcmp0 (uid, tmd->uid) == 0)
+ return tmd;
+ }
+
+ return NULL;
+}
+
+static gboolean
+tmpl_folder_data_remove_message (TmplFolderData *tfd,
+ const gchar *uid)
+{
+ TmplMessageData *tmd;
+
+ g_return_val_if_fail (tfd != NULL, FALSE);
+ g_return_val_if_fail (uid != NULL, FALSE);
+
+ tmd = tmpl_folder_data_find_message (tfd, uid);
+ if (tmd) {
+ tfd->messages = g_slist_remove (tfd->messages, tmd);
+ tmpl_message_data_free (tmd);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+tmpl_folder_data_change_message (TmplFolderData *tfd,
+ CamelMessageInfo *info)
+{
+ TmplMessageData *tmd;
+ const gchar *subject;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (tfd != NULL, FALSE);
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ tmd = tmpl_folder_data_find_message (tfd, camel_message_info_uid (info));
+ if (!tmd) {
+ if (!(camel_message_info_flags (info) & (CAMEL_MESSAGE_JUNK | CAMEL_MESSAGE_DELETED))) {
+ tmpl_folder_data_add_message (tfd, info);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ if ((camel_message_info_flags (info) & (CAMEL_MESSAGE_JUNK | CAMEL_MESSAGE_DELETED)) != 0) {
+ return tmpl_folder_data_remove_message (tfd, camel_message_info_uid (info));
+ }
+
+ subject = tmpl_sanitized_subject (camel_message_info_subject (info));
+
+ if (g_strcmp0 (subject, tmd->subject) != 0) {
+ tmpl_message_data_change_subject (tmd, subject);
+ /* The caller is responsible to call tmpl_folder_data_sort() */
+ changed = TRUE;
+ }
+
+ return changed;
+}
+
+static void
+tmpl_folder_data_sort (TmplFolderData *tfd)
+{
+ g_return_if_fail (tfd != NULL);
+
+ tfd->messages = g_slist_sort (tfd->messages, tmpl_message_data_compare);
+}
+
+static gint
+tmpl_folder_data_compare (gconstpointer ptr1,
+ gconstpointer ptr2)
+{
+ const TmplFolderData *tfd1 = ptr1, *tfd2 = ptr2;
+ const gchar *display_name1, *display_name2;
+
+ if (!tfd1 || !tfd2) {
+ if (tfd1 == tfd2)
+ return 0;
+
+ return tfd1 ? -1 : 1;
+ }
+
+ display_name1 = camel_folder_get_display_name (tfd1->folder);
+ display_name2 = camel_folder_get_display_name (tfd2->folder);
+
+ return g_utf8_collate (display_name1 ? display_name1 : "", display_name2 ? display_name2 : "");
+}
+
+static void
+tmpl_folder_data_update_done_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ TmplFolderData *tfd = user_data;
+ GError *local_error = NULL;
+
+ g_return_if_fail (tfd != NULL);
+ g_return_if_fail (g_task_is_valid (result, source));
+
+ if (g_task_propagate_boolean (G_TASK (result), &local_error)) {
+ /* Content changed, rebuild menu when needed */
+ ETemplatesStore *templates_store;
+
+ templates_store = g_weak_ref_get (tfd->templates_store_weakref);
+ if (templates_store) {
+ templates_store_emit_changed (templates_store);
+ g_object_unref (templates_store);
+ }
+ } else if (local_error) {
+ g_debug ("%s: Failed with error: %s", G_STRFUNC, local_error->message);
+ }
+
+ g_clear_error (&local_error);
+}
+
+typedef struct _TfdUpdateData {
+ TmplFolderData *tfd;
+ GPtrArray *added_uids;
+ GPtrArray *changed_uids;
+} TfdUpdateData;
+
+static void
+tfd_update_data_free (gpointer ptr)
+{
+ TfdUpdateData *tud = ptr;
+
+ if (tud) {
+ tmpl_folder_data_unref (tud->tfd);
+ g_ptr_array_free (tud->added_uids, TRUE);
+ g_ptr_array_free (tud->changed_uids, TRUE);
+ g_free (tud);
+ }
+}
+
+static gboolean
+tmpl_folder_data_update_sync (TmplFolderData *tfd,
+ const GPtrArray *added_uids,
+ const GPtrArray *changed_uids,
+ GCancellable *cancellable)
+{
+ GPtrArray *all_uids = NULL;
+ CamelMessageInfo *info;
+ guint ii;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (tfd != NULL, FALSE);
+ g_return_val_if_fail (CAMEL_IS_FOLDER (tfd->folder), FALSE);
+
+ if (!added_uids || !changed_uids || added_uids->len + changed_uids->len > 10)
+ camel_folder_summary_prepare_fetch_all (tfd->folder->summary, NULL);
+
+ if (!added_uids && !changed_uids) {
+ all_uids = camel_folder_summary_get_array (tfd->folder->summary);
+ added_uids = all_uids;
+ }
+
+ tmpl_folder_data_lock (tfd);
+
+ for (ii = 0; added_uids && ii < added_uids->len; ii++) {
+ const gchar *uid = added_uids->pdata[ii];
+
+ info = camel_folder_summary_get (tfd->folder->summary, uid);
+ if (info) {
+ if (!(camel_message_info_flags (info) & (CAMEL_MESSAGE_JUNK |
CAMEL_MESSAGE_DELETED))) {
+ /* Sometimes the 'add' notification can come after the 'change',
+ thus use the change_message() which covers both cases. */
+ changed = tmpl_folder_data_change_message (tfd, info) || changed;
+ } else {
+ changed = tmpl_folder_data_remove_message (tfd, camel_message_info_uid
(info)) || changed;
+ }
+
+ camel_message_info_unref (info);
+ }
+ }
+
+ for (ii = 0; changed_uids && ii < changed_uids->len; ii++) {
+ const gchar *uid = changed_uids->pdata[ii];
+
+ info = camel_folder_summary_get (tfd->folder->summary, uid);
+ if (info) {
+ changed = tmpl_folder_data_change_message (tfd, info) || changed;
+ camel_message_info_unref (info);
+ }
+ }
+
+ if (changed)
+ tmpl_folder_data_sort (tfd);
+
+ if (all_uids)
+ camel_folder_summary_free_array (all_uids);
+
+ tmpl_folder_data_unlock (tfd);
+
+ return changed;
+}
+
+static void
+tmpl_folder_data_update_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ TfdUpdateData *tud = task_data;
+ gboolean changed;
+
+ g_return_if_fail (tud != NULL);
+ g_return_if_fail (tud->tfd != NULL);
+ g_return_if_fail (tud->added_uids != NULL);
+ g_return_if_fail (tud->changed_uids != NULL);
+
+ changed = tmpl_folder_data_update_sync (tud->tfd, tud->added_uids, tud->changed_uids, cancellable);
+
+ g_task_return_boolean (task, changed);
+}
+
+static void
+tmpl_folder_data_schedule_update (TmplFolderData *tfd,
+ CamelFolderChangeInfo *change_info)
+{
+ ETemplatesStore *templates_store;
+ TfdUpdateData *tud;
+ GTask *task;
+ guint ii;
+
+ g_return_if_fail (tfd != NULL);
+
+ templates_store = g_weak_ref_get (tfd->templates_store_weakref);
+ if (!templates_store)
+ return;
+
+ tud = g_new0 (TfdUpdateData, 1);
+ tud->tfd = tmpl_folder_data_ref (tfd);
+ tud->added_uids = g_ptr_array_new_full (
+ change_info->uid_added ? change_info->uid_added->len : 0,
+ (GDestroyNotify) camel_pstring_free);
+ tud->changed_uids = g_ptr_array_new_full (
+ (change_info->uid_changed ? change_info->uid_changed->len : 0),
+ (GDestroyNotify) camel_pstring_free);
+
+ for (ii = 0; change_info->uid_added && ii < change_info->uid_added->len; ii++) {
+ const gchar *uid = change_info->uid_added->pdata[ii];
+
+ if (uid && *uid)
+ g_ptr_array_add (tud->added_uids, (gpointer) camel_pstring_strdup (uid));
+ }
+
+ for (ii = 0; change_info->uid_changed && ii < change_info->uid_changed->len; ii++) {
+ const gchar *uid = change_info->uid_changed->pdata[ii];
+
+ if (uid && *uid)
+ g_ptr_array_add (tud->changed_uids, (gpointer) camel_pstring_strdup (uid));
+ }
+
+ task = g_task_new (NULL, templates_store->priv->cancellable, tmpl_folder_data_update_done_cb, tfd);
+ g_task_set_task_data (task, tud, tfd_update_data_free);
+ g_task_run_in_thread (task, tmpl_folder_data_update_thread);
+ g_object_unref (task);
+
+ g_object_unref (templates_store);
+}
+
+static void
+tmpl_folder_data_folder_changed_cb (CamelFolder *folder,
+ CamelFolderChangeInfo *change_info,
+ gpointer user_data)
+{
+ TmplFolderData *tfd = user_data;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (change_info != NULL);
+ g_return_if_fail (tfd != NULL);
+
+ tmpl_folder_data_ref (tfd);
+
+ if ((change_info->uid_added && change_info->uid_added->len) ||
+ (change_info->uid_changed && change_info->uid_changed->len)) {
+ tmpl_folder_data_schedule_update (tfd, change_info);
+ } else if (change_info->uid_removed && change_info->uid_removed->len) {
+ ETemplatesStore *templates_store;
+
+ templates_store = g_weak_ref_get (tfd->templates_store_weakref);
+ if (templates_store) {
+ guint ii;
+
+ tmpl_folder_data_lock (tfd);
+
+ for (ii = 0; ii < change_info->uid_removed->len; ii++) {
+ const gchar *uid = change_info->uid_removed->pdata[ii];
+
+ if (uid && *uid)
+ tmpl_folder_data_remove_message (tfd, uid);
+ }
+
+ tmpl_folder_data_unlock (tfd);
+
+ templates_store_emit_changed (templates_store);
+
+ g_object_unref (templates_store);
+ }
+ }
+
+ tmpl_folder_data_unref (tfd);
+}
+
+static gboolean
+tmpl_store_data_traverse_to_free_cb (GNode *node,
+ gpointer user_data)
+{
+ if (node && node->data) {
+ tmpl_folder_data_unref (node->data);
+ node->data = NULL;
+ }
+
+ return FALSE;
+}
+
+typedef struct _TmplStoreData {
+ volatile gint ref_count;
+ GWeakRef *templates_store_weakref; /* ETemplatesStore * */
+ GWeakRef *store_weakref; /* CamelStore * */
+
+ gulong folder_created_handler_id;
+ gulong folder_deleted_handler_id;
+ gulong folder_renamed_handler_id;
+ gulong notify_display_name_id;
+
+ GMutex busy_lock;
+ gchar *root_folder_path;
+ gchar *templates_folder_uri;
+ gchar *identity_source_uid;
+ GNode *folders; /* data is TmplFolderData * */
+} TmplStoreData;
+
+static void
+tmpl_store_data_set_root_folder_path (TmplStoreData *tsd,
+ const gchar *root_folder_path);
+
+static TmplStoreData *
+tmpl_store_data_new (ETemplatesStore *templates_store,
+ CamelStore *store,
+ const gchar *root_folder_path,
+ const gchar *templates_folder_uri,
+ const gchar *identity_source_uid)
+{
+ TmplStoreData *tsd;
+
+ g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store), NULL);
+ g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+ g_return_val_if_fail (root_folder_path && *root_folder_path, NULL);
+ g_return_val_if_fail (templates_folder_uri && *templates_folder_uri, NULL);
+
+ tsd = g_new0 (TmplStoreData, 1);
+ tsd->ref_count = 1;
+ tsd->templates_store_weakref = e_weak_ref_new (templates_store);
+ tsd->store_weakref = e_weak_ref_new (store);
+ g_mutex_init (&tsd->busy_lock);
+ tsd->root_folder_path = NULL;
+ tsd->templates_folder_uri = g_strdup (templates_folder_uri);
+ tsd->identity_source_uid = g_strdup (identity_source_uid);
+ tsd->folders = g_node_new (NULL);
+
+ if (CAMEL_IS_SUBSCRIBABLE (store)) {
+ tsd->folder_created_handler_id = g_signal_connect (store, "folder-subscribed",
+ G_CALLBACK (tmpl_store_data_folder_created_cb), tsd);
+ tsd->folder_deleted_handler_id = g_signal_connect (store, "folder-unsubscribed",
+ G_CALLBACK (tmpl_store_data_folder_deleted_cb), tsd);
+ } else {
+ tsd->folder_created_handler_id = g_signal_connect (store, "folder-created",
+ G_CALLBACK (tmpl_store_data_folder_created_cb), tsd);
+ tsd->folder_deleted_handler_id = g_signal_connect (store, "folder-deleted",
+ G_CALLBACK (tmpl_store_data_folder_deleted_cb), tsd);
+ }
+
+ tsd->folder_renamed_handler_id = g_signal_connect (store, "folder-renamed",
+ G_CALLBACK (tmpl_store_data_folder_renamed_cb), tsd);
+
+ tsd->notify_display_name_id = e_signal_connect_notify (store, "notify::display-name",
+ G_CALLBACK (tmpl_store_data_notify_display_name_cb), tsd);
+
+ tmpl_store_data_set_root_folder_path (tsd, root_folder_path);
+
+ return tsd;
+}
+
+static TmplStoreData *
+tmpl_store_data_ref (TmplStoreData *tsd)
+{
+ g_return_val_if_fail (tsd != NULL, NULL);
+
+ g_atomic_int_inc (&tsd->ref_count);
+
+ return tsd;
+}
+
+static void
+tmpl_store_data_unref (gpointer ptr)
+{
+ TmplStoreData *tsd = ptr;
+
+ if (tsd) {
+ if (!g_atomic_int_dec_and_test (&tsd->ref_count))
+ return;
+
+ if (tsd->templates_store_weakref) {
+ e_weak_ref_free (tsd->templates_store_weakref);
+ tsd->templates_store_weakref = NULL;
+ }
+
+ if (tsd->store_weakref) {
+ CamelStore *store;
+
+ store = g_weak_ref_get (tsd->store_weakref);
+ if (store) {
+ if (tsd->folder_created_handler_id) {
+ g_signal_handler_disconnect (store, tsd->folder_created_handler_id);
+ tsd->folder_created_handler_id = 0;
+ }
+
+ if (tsd->folder_deleted_handler_id) {
+ g_signal_handler_disconnect (store, tsd->folder_deleted_handler_id);
+ tsd->folder_deleted_handler_id = 0;
+ }
+
+ if (tsd->folder_renamed_handler_id) {
+ g_signal_handler_disconnect (store, tsd->folder_renamed_handler_id);
+ tsd->folder_renamed_handler_id = 0;
+ }
+
+ e_signal_disconnect_notify_handler (store, &tsd->notify_display_name_id);
+
+ g_clear_object (&store);
+ }
+
+ e_weak_ref_free (tsd->store_weakref);
+ tsd->store_weakref = NULL;
+ }
+
+ g_mutex_clear (&tsd->busy_lock);
+
+ g_free (tsd->root_folder_path);
+ tsd->root_folder_path = NULL;
+
+ g_free (tsd->templates_folder_uri);
+ tsd->templates_folder_uri = NULL;
+
+ g_free (tsd->identity_source_uid);
+ tsd->identity_source_uid = NULL;
+
+ if (tsd->folders) {
+ g_node_traverse (tsd->folders, G_IN_ORDER, G_TRAVERSE_ALL, -1,
tmpl_store_data_traverse_to_free_cb, NULL);
+ g_node_destroy (tsd->folders);
+ tsd->folders = NULL;
+ }
+
+ g_free (tsd);
+ }
+}
+
+static void
+tmpl_store_data_lock (TmplStoreData *tsd)
+{
+ g_return_if_fail (tsd != NULL);
+
+ g_mutex_lock (&tsd->busy_lock);
+}
+
+static void
+tmpl_store_data_unlock (TmplStoreData *tsd)
+{
+ g_return_if_fail (tsd != NULL);
+
+ g_mutex_unlock (&tsd->busy_lock);
+}
+
+static gint
+tmpl_store_data_compare (gconstpointer ptr1,
+ gconstpointer ptr2,
+ gpointer user_data)
+{
+ const TmplStoreData *tsd1 = ptr1, *tsd2 = ptr2;
+ EMailAccountStore *account_store = user_data;
+ CamelService *service1, *service2;
+ gint res;
+
+ service1 = tsd1 ? g_weak_ref_get (tsd1->store_weakref) : NULL;
+ service2 = tsd2 ? g_weak_ref_get (tsd2->store_weakref) : NULL;
+
+ if (account_store && service1 && service2)
+ res = e_mail_account_store_compare_services (account_store, service1, service2);
+ else
+ res = g_utf8_collate (service1 ? camel_service_get_display_name (service1) : "",
+ service2 ? camel_service_get_display_name (service2) : "");
+
+ g_clear_object (&service1);
+ g_clear_object (&service2);
+
+ return res;
+}
+
+static void
+tmpl_store_data_set_root_folder_path (TmplStoreData *tsd,
+ const gchar *root_folder_path)
+{
+ g_return_if_fail (tsd != NULL);
+ g_return_if_fail (root_folder_path && *root_folder_path);
+
+ tmpl_store_data_lock (tsd);
+
+ if (g_strcmp0 (tsd->root_folder_path, root_folder_path) != 0) {
+ guint len;
+
+ g_free (tsd->root_folder_path);
+ tsd->root_folder_path = g_strdup (root_folder_path);
+
+ len = strlen (tsd->root_folder_path);
+ if (tsd->root_folder_path[len - 1] == '/')
+ tsd->root_folder_path[len - 1] = '\0';
+ }
+
+ tmpl_store_data_unlock (tsd);
+}
+
+static GNode *
+tmpl_store_data_find_parent_node_locked (TmplStoreData *tsd,
+ const gchar *full_name,
+ gboolean for_insert)
+{
+ GNode *from_node, *node, *parent;
+
+ g_return_val_if_fail (tsd != NULL, NULL);
+ g_return_val_if_fail (full_name != NULL, NULL);
+
+ parent = tsd->folders;
+ from_node = tsd->folders;
+ while (from_node) {
+ node = g_node_first_child (from_node);
+ from_node = NULL;
+
+ while (node) {
+ TmplFolderData *tfd = node->data;
+
+ if (tfd && tfd->folder &&
+ g_str_has_prefix (full_name, camel_folder_get_full_name (tfd->folder)) &&
+ g_strcmp0 (full_name, camel_folder_get_full_name (tfd->folder)) != 0) {
+ parent = node;
+ from_node = node;
+ break;
+ }
+
+ node = g_node_next_sibling (node);
+ }
+ }
+
+ if (for_insert && parent) {
+ GNode *node;
+
+ if (parent->data) {
+ TmplFolderData *tfd = parent->data;
+
+ if (g_strcmp0 (full_name, camel_folder_get_full_name (tfd->folder)) == 0) {
+ /* The folder is already in the list of folders */
+ parent = NULL;
+ }
+ }
+
+ for (node = parent ? parent->children : NULL; node; node = node->next) {
+ TmplFolderData *tfd = node->data;
+
+ if (!tfd)
+ continue;
+
+ if (g_strcmp0 (full_name, camel_folder_get_full_name (tfd->folder)) == 0) {
+ /* The folder is already in the list of folders */
+ parent = NULL;
+ break;
+ }
+ }
+ }
+
+ return parent;
+}
+
+static GNode *
+tmpl_store_data_find_node_locked (TmplStoreData *tsd,
+ const gchar *full_name)
+{
+ GNode *node, *parent;
+
+ g_return_val_if_fail (tsd != NULL, NULL);
+ g_return_val_if_fail (full_name != NULL, NULL);
+
+ parent = tmpl_store_data_find_parent_node_locked (tsd, full_name, FALSE);
+ if (!parent)
+ return NULL;
+
+ for (node = g_node_first_child (parent); node; node = g_node_next_sibling (node)) {
+ TmplFolderData *tfd = node->data;
+
+ if (!tfd)
+ continue;
+
+ if (tfd->folder && g_strcmp0 (full_name, camel_folder_get_full_name (tfd->folder)) == 0) {
+ return node;
+ }
+ }
+
+ return NULL;
+}
+
+static GNode *
+tmpl_store_data_find_node_with_folder_locked (TmplStoreData *tsd,
+ CamelFolder *folder)
+{
+ GNode *node;
+
+ g_return_val_if_fail (tsd != NULL, NULL);
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+
+ node = tsd->folders;
+ while (node) {
+ TmplFolderData *tfd = node->data;
+ GNode *next;
+
+ if (tfd && tfd->folder == folder) {
+ return node;
+ }
+
+ /* Traverse the tree */
+ next = node->children;
+ if (!next)
+ next = node->next;
+ if (!next) {
+ next = node->parent;
+ while (next) {
+ GNode *sibl = next->next;
+
+ if (sibl) {
+ next = sibl;
+ break;
+ } else {
+ next = next->parent;
+ }
+ }
+ }
+
+ node = next;
+ }
+
+ return NULL;
+}
+
+static void
+tmpl_store_data_update_done_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ TmplStoreData *tsd = user_data;
+ GError *local_error = NULL;
+
+ g_return_if_fail (tsd != NULL);
+ g_return_if_fail (g_task_is_valid (result, source));
+
+ if (g_task_propagate_boolean (G_TASK (result), &local_error)) {
+ /* Content changed, rebuild menu when needed */
+ ETemplatesStore *templates_store;
+
+ templates_store = g_weak_ref_get (tsd->templates_store_weakref);
+ if (templates_store) {
+ templates_store_emit_changed (templates_store);
+ g_object_unref (templates_store);
+ }
+ } else if (local_error) {
+ g_debug ("%s: Failed with error: %s", G_STRFUNC, local_error->message);
+ }
+
+ g_clear_error (&local_error);
+}
+
+static void
+tmpl_store_data_initial_setup_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ ETemplatesStore *templates_store;
+ TmplStoreData *tsd = task_data;
+ CamelStore *store;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (tsd != NULL);
+
+ templates_store = g_weak_ref_get (tsd->templates_store_weakref);
+ store = g_weak_ref_get (tsd->store_weakref);
+ if (store && templates_store) {
+ CamelFolderInfo *folder_info = NULL, *fi;
+ gchar *root_folder_path;
+ GError *local_error = NULL;
+
+ tmpl_store_data_lock (tsd);
+ root_folder_path = g_strdup (tsd->root_folder_path);
+ tmpl_store_data_unlock (tsd);
+
+ if (root_folder_path) {
+ folder_info = camel_store_get_folder_info_sync (
+ store, root_folder_path,
+ CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+ CAMEL_STORE_FOLDER_INFO_SUBSCRIBED |
+ CAMEL_STORE_FOLDER_INFO_FAST, cancellable, &local_error);
+
+ if (local_error) {
+ g_debug ("%s: Failed to get folder info for '%s : %s': %s", G_STRFUNC,
+ camel_service_get_display_name (CAMEL_SERVICE (store)),
root_folder_path, local_error->message);
+ }
+
+ g_clear_error (&local_error);
+ }
+
+ fi = folder_info;
+ while (fi && !g_cancellable_is_cancelled (cancellable)) {
+ CamelFolderInfo *next;
+ CamelFolder *folder;
+
+ folder = camel_store_get_folder_sync (store, fi->full_name, 0, cancellable,
&local_error);
+ if (folder) {
+ GNode *parent;
+
+ tmpl_store_data_lock (tsd);
+
+ parent = tmpl_store_data_find_parent_node_locked (tsd, fi->full_name, TRUE);
+ if (parent) {
+ TmplFolderData *tfd;
+
+ tfd = tmpl_folder_data_new (templates_store, folder);
+ if (tfd) {
+ changed = tmpl_folder_data_update_sync (tfd, NULL, NULL,
cancellable) || changed;
+
+ g_node_append_data (parent, tfd);
+ }
+ }
+
+ tmpl_store_data_unlock (tsd);
+ }
+
+ if (local_error)
+ g_debug ("%s: Failed to get folder '%s': %s", G_STRFUNC, fi->full_name,
local_error->message);
+
+ g_clear_object (&folder);
+ g_clear_error (&local_error);
+
+ /* Traverse the tree of folders */
+ next = fi->child;
+ if (!next)
+ next = fi->next;
+ if (!next) {
+ next = fi->parent;
+ while (next) {
+ CamelFolderInfo *sibl = next->next;
+
+ if (sibl) {
+ next = sibl;
+ break;
+ } else {
+ next = next->parent;
+ }
+ }
+ }
+
+ fi = next;
+ }
+
+ camel_folder_info_free (folder_info);
+ g_free (root_folder_path);
+ }
+
+ g_clear_object (&templates_store);
+ g_clear_object (&store);
+
+ g_task_return_boolean (task, changed);
+}
+
+static void
+tmpl_store_data_schedule_initial_setup (TmplStoreData *tsd)
+{
+ ETemplatesStore *templates_store;
+ GTask *task;
+
+ g_return_if_fail (tsd != NULL);
+
+ templates_store = g_weak_ref_get (tsd->templates_store_weakref);
+ if (!templates_store)
+ return;
+
+ tmpl_store_data_ref (tsd);
+
+ task = g_task_new (NULL, templates_store->priv->cancellable, tmpl_store_data_update_done_cb, tsd);
+ g_task_set_task_data (task, tsd, tmpl_store_data_unref);
+ g_task_run_in_thread (task, tmpl_store_data_initial_setup_thread);
+ g_object_unref (task);
+
+ g_object_unref (templates_store);
+}
+
+typedef struct _TsdFolderData {
+ TmplStoreData *tsd;
+ gchar *fullname;
+ gchar *old_fullname; /* If set, then it's "rename", otherwise it's "create" */
+} TsdFolderData;
+
+static void
+tsd_folder_data_free (gpointer ptr)
+{
+ TsdFolderData *fd = ptr;
+
+ if (fd) {
+ tmpl_store_data_unref (fd->tsd);
+ g_free (fd->fullname);
+ g_free (fd->old_fullname);
+ g_free (fd);
+ }
+}
+
+static void
+tmpl_store_data_folder_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ ETemplatesStore *templates_store;
+ TsdFolderData *fd = task_data;
+ CamelStore *store;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (fd != NULL);
+ g_return_if_fail (fd->tsd != NULL);
+ g_return_if_fail (fd->fullname != NULL);
+
+ templates_store = g_weak_ref_get (fd->tsd->templates_store_weakref);
+ store = g_weak_ref_get (fd->tsd->store_weakref);
+ if (store && templates_store) {
+ GError *local_error = NULL;
+ CamelFolder *folder;
+
+ folder = camel_store_get_folder_sync (store, fd->fullname, 0, cancellable, &local_error);
+ if (folder) {
+ GNode *parent = NULL;
+
+ tmpl_store_data_lock (fd->tsd);
+
+ if (fd->old_fullname) {
+ GNode *node;
+
+ node = tmpl_store_data_find_node_locked (fd->tsd, fd->old_fullname);
+ if (!node) {
+ /* Sometimes the CamelFolder can be renamed in-place,
+ thus lookup with the CamelFolder structure as well. */
+ node = tmpl_store_data_find_node_with_folder_locked (fd->tsd, folder);
+ }
+ if (node) {
+ TmplFolderData *tfd = node->data;
+
+ changed = TRUE;
+
+ tmpl_folder_data_lock (tfd);
+
+ if (tfd->folder != folder) {
+ g_clear_object (&tfd->folder);
+ tfd->folder = g_object_ref (folder);
+ }
+
+ parent = tmpl_store_data_find_parent_node_locked (fd->tsd,
fd->fullname, FALSE);
+ if (parent && node->parent != parent) {
+ g_node_unlink (node);
+ g_node_append (parent, node);
+ }
+
+ tmpl_folder_data_unlock (tfd);
+ }
+ } else {
+ parent = tmpl_store_data_find_parent_node_locked (fd->tsd, fd->fullname,
TRUE);
+ if (parent) {
+ TmplFolderData *tfd;
+
+ tfd = tmpl_folder_data_new (templates_store, folder);
+ if (tfd) {
+ changed = tmpl_folder_data_update_sync (tfd, NULL, NULL,
cancellable);
+
+ g_node_append_data (parent, tfd);
+ }
+ }
+ }
+
+ if (parent) {
+ GSList *data = NULL, *link;
+ GNode *node;
+
+ for (node = parent->children; node; node = node->next) {
+ if (node->data)
+ data = g_slist_prepend (data, node->data);
+ }
+
+ data = g_slist_sort (data, tmpl_folder_data_compare);
+
+ for (node = parent->children, link = data; node && link; node = node->next) {
+ if (node->data) {
+ node->data = link->data;
+ link = g_slist_next (link);
+ }
+ }
+
+ g_slist_free (data);
+ }
+
+ tmpl_store_data_unlock (fd->tsd);
+ }
+
+ if (local_error)
+ g_debug ("%s: Failed to get folder '%s': %s", G_STRFUNC, fd->fullname,
local_error->message);
+
+ g_clear_object (&folder);
+ g_clear_error (&local_error);
+ }
+
+ g_clear_object (&templates_store);
+ g_clear_object (&store);
+
+ g_task_return_boolean (task, changed);
+}
+
+static void
+tmpl_store_data_folder_created_cb (CamelStore *store,
+ CamelFolderInfo *folder_info,
+ gpointer user_data)
+{
+ TmplStoreData *tsd = user_data;
+ ETemplatesStore *templates_store;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+ g_return_if_fail (folder_info != NULL);
+ g_return_if_fail (folder_info->full_name != NULL);
+ g_return_if_fail (tsd != NULL);
+
+ templates_store = g_weak_ref_get (tsd->templates_store_weakref);
+
+ tmpl_store_data_lock (tsd);
+
+ if (templates_store && g_str_has_prefix (folder_info->full_name, tsd->root_folder_path)) {
+ GNode *parent;
+
+ parent = tmpl_store_data_find_parent_node_locked (tsd, folder_info->full_name, TRUE);
+ if (parent) {
+ TsdFolderData *fd;
+ GTask *task;
+
+ fd = g_new0 (TsdFolderData, 1);
+ fd->tsd = tmpl_store_data_ref (tsd);
+ fd->fullname = g_strdup (folder_info->full_name);
+ fd->old_fullname = NULL;
+
+ task = g_task_new (NULL, templates_store->priv->cancellable,
tmpl_store_data_update_done_cb, tsd);
+ g_task_set_task_data (task, fd, tsd_folder_data_free);
+ g_task_run_in_thread (task, tmpl_store_data_folder_thread);
+ g_object_unref (task);
+ }
+ }
+
+ tmpl_store_data_unlock (tsd);
+ g_clear_object (&templates_store);
+}
+
+static void
+tmpl_store_data_folder_deleted_cb (CamelStore *store,
+ CamelFolderInfo *folder_info,
+ gpointer user_data)
+{
+ TmplStoreData *tsd = user_data;
+ ETemplatesStore *templates_store;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+ g_return_if_fail (folder_info != NULL);
+ g_return_if_fail (tsd != NULL);
+
+ templates_store = g_weak_ref_get (tsd->templates_store_weakref);
+
+ tmpl_store_data_lock (tsd);
+
+ if (templates_store && g_str_has_prefix (folder_info->full_name, tsd->root_folder_path)) {
+ GNode *node;
+
+ node = tmpl_store_data_find_node_locked (tsd, folder_info->full_name);
+ if (node) {
+ g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_ALL, -1,
tmpl_store_data_traverse_to_free_cb, NULL);
+ g_node_destroy (node);
+
+ changed = TRUE;
+ }
+ }
+
+ tmpl_store_data_unlock (tsd);
+
+ if (changed)
+ templates_store_emit_changed (templates_store);
+
+ g_clear_object (&templates_store);
+}
+
+static void
+tmpl_store_data_folder_renamed_cb (CamelStore *store,
+ const gchar *old_name,
+ CamelFolderInfo *folder_info,
+ gpointer user_data)
+{
+ TmplStoreData *tsd = user_data;
+ ETemplatesStore *templates_store;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (CAMEL_IS_STORE (store));
+ g_return_if_fail (old_name != NULL);
+ g_return_if_fail (folder_info != NULL);
+ g_return_if_fail (tsd != NULL);
+
+ templates_store = g_weak_ref_get (tsd->templates_store_weakref);
+
+ tmpl_store_data_lock (tsd);
+
+ if (templates_store && g_str_has_prefix (old_name, tsd->root_folder_path)) {
+ if (g_str_has_prefix (folder_info->full_name, tsd->root_folder_path)) {
+ TsdFolderData *fd;
+ GTask *task;
+
+ fd = g_new0 (TsdFolderData, 1);
+ fd->tsd = tmpl_store_data_ref (tsd);
+ fd->fullname = g_strdup (folder_info->full_name);
+ fd->old_fullname = g_strdup (old_name);
+
+ task = g_task_new (NULL, templates_store->priv->cancellable,
tmpl_store_data_update_done_cb, tsd);
+ g_task_set_task_data (task, fd, tsd_folder_data_free);
+ g_task_run_in_thread (task, tmpl_store_data_folder_thread);
+ g_object_unref (task);
+ } else {
+ GNode *node;
+
+ node = tmpl_store_data_find_node_locked (tsd, old_name);
+ if (node) {
+ g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_ALL, -1,
tmpl_store_data_traverse_to_free_cb, NULL);
+ g_node_destroy (node);
+
+ changed = TRUE;
+ }
+ }
+ } else if (templates_store && g_str_has_prefix (folder_info->full_name, tsd->root_folder_path)) {
+ TsdFolderData *fd;
+ GTask *task;
+
+ fd = g_new0 (TsdFolderData, 1);
+ fd->tsd = tmpl_store_data_ref (tsd);
+ fd->fullname = g_strdup (folder_info->full_name);
+ fd->old_fullname = NULL;
+
+ task = g_task_new (NULL, templates_store->priv->cancellable, tmpl_store_data_update_done_cb,
tsd);
+ g_task_set_task_data (task, fd, tsd_folder_data_free);
+ g_task_run_in_thread (task, tmpl_store_data_folder_thread);
+ g_object_unref (task);
+ }
+
+ tmpl_store_data_unlock (tsd);
+
+ if (changed)
+ templates_store_emit_changed (templates_store);
+
+ g_clear_object (&templates_store);
+}
+
+static void
+tmpl_store_data_notify_display_name_cb (CamelService *service,
+ GParamSpec *param,
+ gpointer user_data)
+{
+ TmplStoreData *tsd = user_data;
+ ETemplatesStore *templates_store;
+
+ g_return_if_fail (CAMEL_IS_SERVICE (service));
+ g_return_if_fail (tsd != NULL);
+
+ templates_store = g_weak_ref_get (tsd->templates_store_weakref);
+ if (templates_store) {
+ EMailAccountStore *account_store;
+ gboolean changed = FALSE;
+
+ account_store = e_templates_store_ref_account_store (templates_store);
+
+ templates_store_lock (templates_store);
+
+ changed = templates_store->priv->stores && templates_store->priv->stores->next;
+ templates_store->priv->stores = g_slist_sort_with_data (templates_store->priv->stores,
+ tmpl_store_data_compare, account_store);
+
+ templates_store_unlock (templates_store);
+
+ if (changed)
+ templates_store_emit_changed (templates_store);
+
+ g_object_unref (templates_store);
+ g_clear_object (&account_store);
+ }
+}
+
+static gchar *
+templates_store_find_custom_templates_root_folder_path (ETemplatesStore *templates_store,
+ CamelStore *store,
+ EMailSession *mail_session,
+ ESource **out_identity_source,
+ CamelStore **out_use_store,
+ gchar **out_templates_folder_uri)
+{
+ ESource *identity_source;
+ gchar *root_folder_path = NULL;
+
+ g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store), NULL);
+ g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+ g_return_val_if_fail (out_identity_source != NULL, NULL);
+ g_return_val_if_fail (out_use_store != NULL, NULL);
+ g_return_val_if_fail (out_templates_folder_uri != NULL, NULL);
+
+ *out_identity_source = NULL;
+ *out_use_store = NULL;
+ *out_templates_folder_uri = NULL;
+
+ if (g_strcmp0 (E_MAIL_SESSION_LOCAL_UID, camel_service_get_uid (CAMEL_SERVICE (store))) == 0) {
+ *out_templates_folder_uri = g_strdup (e_mail_session_get_local_folder_uri (mail_session,
E_MAIL_LOCAL_FOLDER_TEMPLATES));
+ return g_strdup ("Templates");
+ }
+
+ identity_source = em_utils_ref_mail_identity_for_store (e_mail_session_get_registry (mail_session),
store);
+ if (identity_source) {
+ if (e_source_has_extension (identity_source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) {
+ ESourceMailComposition *mail_composition;
+ CamelStore *templates_store = NULL;
+ gchar *templates_folder;
+ GError *local_error = NULL;
+
+ mail_composition = e_source_get_extension (identity_source,
E_SOURCE_EXTENSION_MAIL_COMPOSITION);
+ templates_folder = e_source_mail_composition_dup_templates_folder (mail_composition);
+
+ if (templates_folder && *templates_folder &&
+ g_strcmp0 (templates_folder, e_mail_session_get_local_folder_uri (mail_session,
E_MAIL_LOCAL_FOLDER_TEMPLATES)) != 0 &&
+ e_mail_folder_uri_parse (CAMEL_SESSION (mail_session), templates_folder,
&templates_store, &root_folder_path, &local_error)) {
+ if (g_strcmp0 (E_MAIL_SESSION_LOCAL_UID, camel_service_get_uid (CAMEL_SERVICE
(templates_store))) == 0 &&
+ g_strcmp0 (root_folder_path, "Templates") == 0) {
+ g_free (root_folder_path);
+ root_folder_path = NULL;
+ } else {
+ *out_identity_source = g_object_ref (identity_source);
+ *out_use_store = g_object_ref (templates_store);
+ *out_templates_folder_uri = g_strdup (templates_folder);
+ }
+
+ g_clear_object (&templates_store);
+ }
+
+ if (local_error) {
+ g_debug ("%s: Failed to parse templates folder URI '%s': %s", G_STRFUNC,
templates_folder, local_error->message);
+ g_clear_error (&local_error);
+ }
+
+ g_free (templates_folder);
+ }
+ }
+
+ g_clear_object (&identity_source);
+
+ return root_folder_path;
+}
+
+static void
+templates_store_maybe_add_store (ETemplatesStore *templates_store,
+ CamelStore *store)
+{
+ ESource *identity_source = NULL;
+ EMailAccountStore *account_store;
+ EMailSession *mail_session;
+ CamelStore *use_store = NULL;
+ gchar *root_folder_path, *templates_folder_uri = NULL;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store));
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ account_store = e_templates_store_ref_account_store (templates_store);
+ if (!account_store)
+ return;
+
+ mail_session = e_mail_account_store_get_session (account_store);
+
+ templates_store_lock (templates_store);
+
+ root_folder_path = templates_store_find_custom_templates_root_folder_path (
+ templates_store, store, mail_session, &identity_source, &use_store, &templates_folder_uri);
+
+ if (root_folder_path) {
+ TmplStoreData *tsd;
+ GSList *link;
+
+ for (link = templates_store->priv->stores; link; link = g_slist_next (link)) {
+ CamelStore *tsd_store;
+
+ tsd = link->data;
+
+ if (!tsd)
+ continue;
+
+ tsd_store = g_weak_ref_get (tsd->store_weakref);
+ if (tsd_store == (use_store ? use_store : store) &&
+ g_strcmp0 (tsd->root_folder_path, root_folder_path) == 0) {
+ g_clear_object (&tsd_store);
+ break;
+ }
+
+ g_clear_object (&tsd_store);
+ }
+
+ /* The store is not in the list of stores yet */
+ if (!link) {
+ tsd = tmpl_store_data_new (templates_store, use_store ? use_store : store,
root_folder_path,
+ templates_folder_uri, identity_source ? e_source_get_uid (identity_source) :
NULL);
+
+ templates_store->priv->stores = g_slist_insert_sorted_with_data
(templates_store->priv->stores,
+ tsd, tmpl_store_data_compare, account_store);
+
+ tmpl_store_data_schedule_initial_setup (tsd);
+
+ changed = TRUE;
+ }
+ }
+
+ templates_store_unlock (templates_store);
+
+ if (changed)
+ templates_store_emit_changed (templates_store);
+
+ g_free (root_folder_path);
+ g_free (templates_folder_uri);
+ g_clear_object (&use_store);
+ g_clear_object (&identity_source);
+ g_clear_object (&account_store);
+}
+
+static void
+templates_store_maybe_remove_store (ETemplatesStore *templates_store,
+ CamelStore *store)
+{
+ GSList *link;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store));
+ g_return_if_fail (CAMEL_IS_STORE (store));
+
+ templates_store_lock (templates_store);
+
+ for (link = templates_store->priv->stores; link && !changed; link = g_slist_next (link)) {
+ TmplStoreData *tsd = link->data;
+ CamelStore *other_store;
+
+ if (!tsd)
+ continue;
+
+ other_store = g_weak_ref_get (tsd->store_weakref);
+ if (other_store == store) {
+ changed = TRUE;
+ templates_store->priv->stores = g_slist_remove (templates_store->priv->stores, tsd);
+ tmpl_store_data_unref (tsd);
+
+ g_object_unref (other_store);
+ break;
+ }
+
+ g_clear_object (&other_store);
+ }
+
+ templates_store_unlock (templates_store);
+
+ if (changed)
+ templates_store_emit_changed (templates_store);
+}
+
+static void
+templates_store_maybe_add_enabled_services (ETemplatesStore *templates_store)
+{
+ EMailAccountStore *account_store;
+ GQueue queue = G_QUEUE_INIT;
+
+ g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store));
+ g_return_if_fail (templates_store->priv->stores == NULL);
+
+ account_store = e_templates_store_ref_account_store (templates_store);
+ g_return_if_fail (account_store != NULL);
+
+ e_mail_account_store_queue_enabled_services (account_store, &queue);
+
+ while (!g_queue_is_empty (&queue)) {
+ CamelService *service;
+
+ service = g_queue_pop_head (&queue);
+
+ if (CAMEL_IS_STORE (service))
+ templates_store_maybe_add_store (templates_store, CAMEL_STORE (service));
+ }
+
+ g_clear_object (&account_store);
+}
+
+static void
+templates_store_service_enabled_cb (EMailAccountStore *account_store,
+ CamelService *service,
+ GWeakRef *weak_ref)
+{
+ ETemplatesStore *templates_store;
+
+ if (!CAMEL_IS_STORE (service))
+ return;
+
+ templates_store = g_weak_ref_get (weak_ref);
+
+ if (templates_store) {
+ templates_store_maybe_add_store (templates_store, CAMEL_STORE (service));
+ g_object_unref (templates_store);
+ }
+}
+
+static void
+templates_store_service_disabled_cb (EMailAccountStore *account_store,
+ CamelService *service,
+ GWeakRef *weak_ref)
+{
+ ETemplatesStore *templates_store;
+
+ if (!CAMEL_IS_STORE (service))
+ return;
+
+ templates_store = g_weak_ref_get (weak_ref);
+
+ if (templates_store) {
+ templates_store_maybe_remove_store (templates_store, CAMEL_STORE (service));
+ g_object_unref (templates_store);
+ }
+}
+
+static void
+templates_store_service_removed_cb (EMailAccountStore *account_store,
+ CamelService *service,
+ GWeakRef *weak_ref)
+{
+ ETemplatesStore *templates_store;
+
+ if (!CAMEL_IS_STORE (service))
+ return;
+
+ templates_store = g_weak_ref_get (weak_ref);
+
+ if (templates_store) {
+ templates_store_maybe_remove_store (templates_store, CAMEL_STORE (service));
+ g_object_unref (templates_store);
+ }
+}
+
+static void
+templates_store_source_changed_cb (ESourceRegistry *registry,
+ ESource *source,
+ GWeakRef *weak_ref)
+{
+ ETemplatesStore *templates_store;
+
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ if (!e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION))
+ return;
+
+ templates_store = g_weak_ref_get (weak_ref);
+
+ if (templates_store) {
+ TmplStoreData *corresponding_tsd = NULL;
+ ESourceMailComposition *mail_composition;
+ gboolean rebuild_all = FALSE;
+ gchar *templates_folder;
+ GSList *link;
+
+ mail_composition = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION);
+ templates_folder = e_source_mail_composition_dup_templates_folder (mail_composition);
+
+ templates_store_lock (templates_store);
+
+ for (link = templates_store->priv->stores; link; link = g_slist_next (link)) {
+ TmplStoreData *tsd = link->data;
+
+ if (!tsd)
+ continue;
+
+ if (g_strcmp0 (tsd->identity_source_uid, e_source_get_uid (source)) == 0) {
+ g_warn_if_fail (!corresponding_tsd);
+ corresponding_tsd = tsd;
+ break;
+ }
+ }
+
+ if (corresponding_tsd) {
+ if (g_strcmp0 (templates_folder, corresponding_tsd->templates_folder_uri) != 0) {
+ /* Should not happen that often (inefficient, but avoids code complexity). */
+ rebuild_all = TRUE;
+ }
+ } else if (templates_folder && *templates_folder) {
+ EMailAccountStore *account_store;
+ EMailSession *mail_session;
+ CamelStore *found_store = NULL;
+ gchar *root_folder_path = NULL;
+ GError *local_error = NULL;
+
+ account_store = g_weak_ref_get (templates_store->priv->account_store_weakref);
+
+ if (account_store && (mail_session = e_mail_account_store_get_session
(account_store)) != NULL &&
+ g_strcmp0 (templates_folder, e_mail_session_get_local_folder_uri (mail_session,
E_MAIL_LOCAL_FOLDER_TEMPLATES)) != 0 &&
+ e_mail_folder_uri_parse (CAMEL_SESSION (mail_session), templates_folder,
&found_store, &root_folder_path, &local_error)) {
+ if (g_strcmp0 (E_MAIL_SESSION_LOCAL_UID, camel_service_get_uid (CAMEL_SERVICE
(found_store))) == 0 &&
+ g_strcmp0 (root_folder_path, "Templates") == 0) {
+ g_free (root_folder_path);
+ root_folder_path = NULL;
+ } else {
+ /* One of the templates folders had been changed to a real
non-default folder;
+ rebuild everything in this case (inefficient, but avoids code
complexity). */
+ rebuild_all = TRUE;
+ }
+ }
+
+ if (local_error) {
+ g_debug ("%s: Failed to parse templates folder URI '%s': %s", G_STRFUNC,
templates_folder, local_error->message);
+ g_clear_error (&local_error);
+ }
+
+ g_clear_object (&found_store);
+ g_clear_object (&account_store);
+ g_free (root_folder_path);
+ }
+
+ if (rebuild_all) {
+ g_slist_free_full (templates_store->priv->stores, tmpl_store_data_unref);
+ templates_store->priv->stores = NULL;
+ }
+
+ templates_store_unlock (templates_store);
+
+ if (rebuild_all)
+ templates_store_maybe_add_enabled_services (templates_store);
+
+ g_object_unref (templates_store);
+ g_free (templates_folder);
+ }
+}
+
+static void
+templates_store_set_account_store (ETemplatesStore *templates_store,
+ EMailAccountStore *account_store)
+{
+ g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (account_store));
+
+ g_weak_ref_set (templates_store->priv->account_store_weakref, account_store);
+}
+
+static void
+templates_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACCOUNT_STORE:
+ templates_store_set_account_store (
+ E_TEMPLATES_STORE (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+templates_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACCOUNT_STORE:
+ g_value_take_object (
+ value,
+ e_templates_store_ref_account_store (
+ E_TEMPLATES_STORE (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+templates_store_dispose (GObject *object)
+{
+ ETemplatesStore *templates_store;
+ EMailAccountStore *account_store;
+
+ templates_store = E_TEMPLATES_STORE (object);
+
+ account_store = e_templates_store_ref_account_store (templates_store);
+
+ if (account_store) {
+ if (templates_store->priv->service_enabled_handler_id) {
+ g_signal_handler_disconnect (account_store,
templates_store->priv->service_enabled_handler_id);
+ templates_store->priv->service_enabled_handler_id = 0;
+ }
+
+ if (templates_store->priv->service_disabled_handler_id) {
+ g_signal_handler_disconnect (account_store,
templates_store->priv->service_disabled_handler_id);
+ templates_store->priv->service_disabled_handler_id = 0;
+ }
+
+ if (templates_store->priv->service_removed_handler_id) {
+ g_signal_handler_disconnect (account_store,
templates_store->priv->service_removed_handler_id);
+ templates_store->priv->service_removed_handler_id = 0;
+ }
+
+ if (templates_store->priv->source_changed_handler_id) {
+ EMailSession *session;
+ ESourceRegistry *registry;
+
+ session = e_mail_account_store_get_session (account_store);
+ registry = e_mail_session_get_registry (session);
+
+ g_signal_handler_disconnect (registry,
templates_store->priv->source_changed_handler_id);
+ templates_store->priv->source_changed_handler_id = 0;
+ }
+ }
+
+ if (templates_store->priv->cancellable) {
+ g_cancellable_cancel (templates_store->priv->cancellable);
+ g_clear_object (&templates_store->priv->cancellable);
+ }
+
+ g_clear_object (&account_store);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_templates_store_parent_class)->dispose (object);
+}
+
+static void
+templates_store_finalize (GObject *object)
+{
+ ETemplatesStore *templates_store;
+
+ templates_store = E_TEMPLATES_STORE (object);
+
+ g_slist_free_full (templates_store->priv->stores, tmpl_store_data_unref);
+ templates_store->priv->stores = NULL;
+
+ e_weak_ref_free (templates_store->priv->account_store_weakref);
+ templates_store->priv->account_store_weakref = NULL;
+
+ g_mutex_clear (&templates_store->priv->busy_lock);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_templates_store_parent_class)->finalize (object);
+}
+
+static void
+templates_store_constructed (GObject *object)
+{
+ ETemplatesStore *templates_store;
+ ESourceRegistry *registry;
+ EMailAccountStore *account_store;
+ EMailSession *session;
+
+ templates_store = E_TEMPLATES_STORE (object);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_templates_store_parent_class)->constructed (object);
+
+ templates_store->priv->cancellable = g_cancellable_new ();
+
+ account_store = e_templates_store_ref_account_store (templates_store);
+ g_return_if_fail (account_store != NULL);
+
+ session = e_mail_account_store_get_session (account_store);
+ registry = e_mail_session_get_registry (session);
+
+ templates_store->priv->service_enabled_handler_id = g_signal_connect_data (
+ account_store, "service-enabled",
+ G_CALLBACK (templates_store_service_enabled_cb),
+ e_weak_ref_new (templates_store),
+ (GClosureNotify) e_weak_ref_free, 0);
+
+ templates_store->priv->service_disabled_handler_id = g_signal_connect_data (
+ account_store, "service-disabled",
+ G_CALLBACK (templates_store_service_disabled_cb),
+ e_weak_ref_new (templates_store),
+ (GClosureNotify) e_weak_ref_free, 0);
+
+ templates_store->priv->service_removed_handler_id = g_signal_connect_data (
+ account_store, "service-removed",
+ G_CALLBACK (templates_store_service_removed_cb),
+ e_weak_ref_new (templates_store),
+ (GClosureNotify) e_weak_ref_free, 0);
+
+ templates_store->priv->source_changed_handler_id = g_signal_connect_data (
+ registry, "source-changed",
+ G_CALLBACK (templates_store_source_changed_cb),
+ e_weak_ref_new (templates_store),
+ (GClosureNotify) e_weak_ref_free, 0);
+
+ templates_store_maybe_add_enabled_services (templates_store);
+
+ g_clear_object (&account_store);
+}
+
+static void
+e_templates_store_class_init (ETemplatesStoreClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (ETemplatesStorePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = templates_store_set_property;
+ object_class->get_property = templates_store_get_property;
+ object_class->dispose = templates_store_dispose;
+ object_class->finalize = templates_store_finalize;
+ object_class->constructed = templates_store_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACCOUNT_STORE,
+ g_param_spec_object (
+ "account-store",
+ "Account Store",
+ "EMailAccountStore",
+ E_TYPE_MAIL_ACCOUNT_STORE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ signals[CHANGED] = g_signal_new (
+ "changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ETemplatesStoreClass, changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0, G_TYPE_NONE);
+}
+
+static void
+e_templates_store_init (ETemplatesStore *templates_store)
+{
+ templates_store->priv = G_TYPE_INSTANCE_GET_PRIVATE (templates_store, E_TYPE_TEMPLATES_STORE,
ETemplatesStorePrivate);
+
+ g_mutex_init (&templates_store->priv->busy_lock);
+ templates_store->priv->account_store_weakref = e_weak_ref_new (NULL);
+}
+
+ETemplatesStore *
+e_templates_store_ref_default (EMailAccountStore *account_store)
+{
+ static gpointer def_templates_store = NULL;
+
+ g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (account_store), NULL);
+
+ if (def_templates_store) {
+ g_object_ref (def_templates_store);
+ } else {
+ def_templates_store = g_object_new (E_TYPE_TEMPLATES_STORE,
+ "account-store", account_store,
+ NULL);
+
+ g_object_add_weak_pointer (G_OBJECT (def_templates_store), &def_templates_store);
+ }
+
+ return def_templates_store;
+}
+
+EMailAccountStore *
+e_templates_store_ref_account_store (ETemplatesStore *templates_store)
+{
+ g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store), NULL);
+
+ return g_weak_ref_get (templates_store->priv->account_store_weakref);
+}
+
+static gboolean
+tmpl_store_data_folder_has_messages_cb (GNode *node,
+ gpointer user_data)
+{
+ TmplFolderData *tfd;
+ gint *pmultiple_accounts = user_data;
+
+ g_return_val_if_fail (node != NULL, TRUE);
+ g_return_val_if_fail (pmultiple_accounts != NULL, TRUE);
+
+ if (!node->data)
+ return FALSE;
+
+ tfd = node->data;
+
+ if (tfd->messages) {
+ *pmultiple_accounts = *pmultiple_accounts + 1;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+typedef struct _TmplActionData {
+ ETemplatesStore *templates_store; /* not referenced */
+ CamelFolder *folder;
+ const gchar *uid; /* from camel_pstring */
+ ETemplatesStoreActionFunc action_cb;
+ gpointer action_cb_user_data;
+} TmplActionData;
+
+static TmplActionData *
+tmpl_action_data_new (ETemplatesStore *templates_store,
+ CamelFolder *folder,
+ const gchar *uid,
+ ETemplatesStoreActionFunc action_cb,
+ gpointer action_cb_user_data)
+{
+ TmplActionData *tad;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ g_return_val_if_fail (uid && *uid, NULL);
+
+ tad = g_new0 (TmplActionData, 1);
+ tad->templates_store = templates_store;
+ tad->folder = g_object_ref (folder);
+ tad->uid = camel_pstring_strdup (uid);
+ tad->action_cb = action_cb;
+ tad->action_cb_user_data = action_cb_user_data;
+
+ return tad;
+}
+
+static void
+tmpl_action_data_free (gpointer ptr)
+{
+ TmplActionData *tad = ptr;
+
+ if (tad) {
+ g_clear_object (&tad->folder);
+ camel_pstring_free (tad->uid);
+ g_free (tad);
+ }
+}
+
+static void
+templates_store_action_activated_cb (GtkAction *action,
+ TmplActionData *tad)
+{
+ g_return_if_fail (tad != NULL);
+ g_return_if_fail (tad->action_cb != NULL);
+
+ tad->action_cb (tad->templates_store, tad->folder, tad->uid, tad->action_cb_user_data);
+}
+
+static void
+templates_store_add_to_menu_recurse (ETemplatesStore *templates_store,
+ GNode *node,
+ GtkUIManager *ui_manager,
+ GtkActionGroup *action_group,
+ const gchar *base_menu_path,
+ guint merge_id,
+ ETemplatesStoreActionFunc action_cb,
+ gpointer action_cb_user_data,
+ gboolean with_folder_menu,
+ guint *action_count)
+{
+ TmplFolderData *tfd;
+
+ g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store));
+ g_return_if_fail (node != NULL);
+
+ while (node) {
+ tfd = node->data;
+ if (tfd) {
+ tmpl_folder_data_lock (tfd);
+
+ if (tfd->folder) {
+ GtkAction *action;
+ gchar *action_name, *menu_path = NULL;
+ const gchar *use_menu_path;
+ GSList *link;
+
+ if (with_folder_menu) {
+ action_name = g_strdup_printf ("templates-menu-%d", *action_count);
+ *action_count = *action_count + 1;
+
+ action = gtk_action_new (action_name, camel_folder_get_display_name
(tfd->folder), NULL, NULL);
+ gtk_action_group_add_action (action_group, action);
+
+ gtk_ui_manager_add_ui (ui_manager, merge_id, base_menu_path,
action_name,
+ action_name, GTK_UI_MANAGER_MENU, FALSE);
+
+ menu_path = g_strdup_printf ("%s/%s", base_menu_path, action_name);
+ use_menu_path = menu_path;
+
+ g_object_unref (action);
+ g_free (action_name);
+ } else {
+ use_menu_path = base_menu_path;
+ }
+
+ if (node->children) {
+ templates_store_add_to_menu_recurse (templates_store, node->children,
+ ui_manager, action_group, use_menu_path, merge_id,
+ action_cb, action_cb_user_data, TRUE, action_count);
+ }
+
+ for (link = tfd->messages; link; link = g_slist_next (link)) {
+ TmplMessageData *tmd = link->data;
+
+ if (tmd && tmd->uid && tmd->subject) {
+ action_name = g_strdup_printf ("templates-item-%d",
*action_count);
+ *action_count = *action_count + 1;
+
+ action = gtk_action_new (action_name, tmd->subject, NULL,
NULL);
+
+ g_signal_connect_data (
+ action, "activate",
+ G_CALLBACK (templates_store_action_activated_cb),
+ tmpl_action_data_new (templates_store, tfd->folder,
tmd->uid, action_cb, action_cb_user_data),
+ (GClosureNotify) tmpl_action_data_free, 0);
+
+ gtk_action_group_add_action (action_group, action);
+
+ gtk_ui_manager_add_ui (
+ ui_manager, merge_id, use_menu_path, action_name,
+ action_name, GTK_UI_MANAGER_MENUITEM, FALSE);
+
+ g_object_unref (action);
+ g_free (action_name);
+ }
+ }
+
+ g_free (menu_path);
+ }
+
+ tmpl_folder_data_unlock (tfd);
+ }
+
+ node = node->next;
+ }
+}
+
+void
+e_templates_store_build_menu (ETemplatesStore *templates_store,
+ EShellView *shell_view,
+ GtkUIManager *ui_manager,
+ GtkActionGroup *action_group,
+ const gchar *base_menu_path,
+ guint merge_id,
+ ETemplatesStoreActionFunc action_cb,
+ gpointer action_cb_user_data)
+{
+ GSList *link;
+ GtkAction *action;
+ gint multiple_accounts = 0;
+ guint action_count = 0;
+ const gchar *main_menu_path = base_menu_path;
+ gchar *tmp_menu_path = NULL;
+ gchar *action_name;
+
+ g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store));
+ g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
+ g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager));
+ g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
+ g_return_if_fail (base_menu_path != NULL);
+ g_return_if_fail (merge_id != 0);
+ g_return_if_fail (action_cb != NULL);
+
+ templates_store_lock (templates_store);
+
+ gtk_ui_manager_remove_ui (ui_manager, merge_id);
+ e_action_group_remove_all_actions (action_group);
+
+ for (link = templates_store->priv->stores; link && multiple_accounts <= 1; link = g_slist_next
(link)) {
+ TmplStoreData *tsd = link->data;
+
+ if (!tsd)
+ continue;
+
+ tmpl_store_data_lock (tsd);
+
+ if (tsd->folders && tsd->folders->children) {
+ CamelStore *store;
+
+ store = g_weak_ref_get (tsd->store_weakref);
+ if (store) {
+ g_node_traverse (tsd->folders, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ tmpl_store_data_folder_has_messages_cb, &multiple_accounts);
+ }
+
+ g_clear_object (&store);
+ }
+
+ tmpl_store_data_unlock (tsd);
+ }
+
+ if (multiple_accounts > 0) {
+ action_name = g_strdup_printf ("templates-menu-%d", action_count);
+ action_count++;
+
+ action = gtk_action_new (action_name, _("Templates"), NULL, NULL);
+
+ gtk_action_group_add_action (action_group, action);
+
+ gtk_ui_manager_add_ui (
+ ui_manager, merge_id, base_menu_path, action_name,
+ action_name, GTK_UI_MANAGER_MENU, FALSE);
+
+ tmp_menu_path = g_strdup_printf ("%s/%s", base_menu_path, action_name);
+ main_menu_path = tmp_menu_path;
+
+ g_object_unref (action);
+ g_free (action_name);
+ }
+
+ for (link = templates_store->priv->stores; link && multiple_accounts > 0; link = g_slist_next (link))
{
+ TmplStoreData *tsd = link->data;
+
+ if (!tsd)
+ continue;
+
+ tmpl_store_data_lock (tsd);
+
+ if (tsd->folders && tsd->folders->children) {
+ CamelStore *store;
+
+ store = g_weak_ref_get (tsd->store_weakref);
+ if (store) {
+ gchar *menu_path = NULL;
+ const gchar *use_menu_path = main_menu_path;
+
+ if (multiple_accounts > 1) {
+ action_name = g_strdup_printf ("templates-menu-%d", action_count);
+ action_count++;
+
+ action = gtk_action_new (action_name, camel_service_get_display_name
(CAMEL_SERVICE (store)), NULL, NULL);
+
+ gtk_action_group_add_action (action_group, action);
+
+ gtk_ui_manager_add_ui (
+ ui_manager, merge_id, main_menu_path, action_name,
+ action_name, GTK_UI_MANAGER_MENU, FALSE);
+
+ menu_path = g_strdup_printf ("%s/%s", main_menu_path, action_name);
+ use_menu_path = menu_path;
+
+ g_object_unref (action);
+ g_free (action_name);
+ }
+
+ templates_store_add_to_menu_recurse (templates_store, tsd->folders->children,
+ ui_manager, action_group, use_menu_path, merge_id,
+ action_cb, action_cb_user_data, FALSE, &action_count);
+
+ g_free (menu_path);
+ }
+
+ g_clear_object (&store);
+ }
+
+ tmpl_store_data_unlock (tsd);
+ }
+
+ templates_store_unlock (templates_store);
+
+ gtk_ui_manager_ensure_update (ui_manager);
+
+ g_free (tmp_menu_path);
+}
diff --git a/plugins/templates/e-templates-store.h b/plugins/templates/e-templates-store.h
new file mode 100644
index 0000000..76019c5
--- /dev/null
+++ b/plugins/templates/e-templates-store.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_TEMPLATES_STORE_H
+#define E_TEMPLATES_STORE_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <camel/camel.h>
+#include <libedataserver/libedataserver.h>
+#include <libemail-engine/libemail-engine.h>
+#include <mail/e-mail-account-store.h>
+#include <shell/e-shell-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_TEMPLATES_STORE \
+ (e_templates_store_get_type ())
+#define E_TEMPLATES_STORE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_TEMPLATES_STORE, ETemplatesStore))
+#define E_TEMPLATES_STORE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_TEMPLATES_STORE, ETemplatesStoreClass))
+#define E_IS_TEMPLATES_STORE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_TEMPLATES_STORE))
+#define E_IS_TEMPLATES_STORE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_TEMPLATES_STORE))
+#define E_TEMPLATES_STORE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_TEMPLATES_STORE, ETemplatesStoreClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ETemplatesStore ETemplatesStore;
+typedef struct _ETemplatesStoreClass ETemplatesStoreClass;
+typedef struct _ETemplatesStorePrivate ETemplatesStorePrivate;
+
+/**
+ * ETemplatesStore:
+ *
+ * Contains only private data that should be read and manipulated using
+ * the functions below.
+ **/
+struct _ETemplatesStore {
+ GObject parent;
+ ETemplatesStorePrivate *priv;
+};
+
+struct _ETemplatesStoreClass {
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*changed) (ETemplatesStore *templates_store);
+};
+
+typedef void (* ETemplatesStoreActionFunc) (ETemplatesStore *templates_store,
+ CamelFolder *folder,
+ const gchar *message_uid,
+ gpointer user_data);
+
+GType e_templates_store_get_type (void) G_GNUC_CONST;
+ETemplatesStore *
+ e_templates_store_ref_default (EMailAccountStore *account_store);
+EMailAccountStore *
+ e_templates_store_ref_account_store
+ (ETemplatesStore *templates_store);
+void e_templates_store_build_menu (ETemplatesStore *templates_store,
+ EShellView *shell_view,
+ GtkUIManager *ui_manager,
+ GtkActionGroup *action_group,
+ const gchar *base_menu_path,
+ guint merge_id,
+ ETemplatesStoreActionFunc action_cb,
+ gpointer action_cb_user_data);
+
+G_END_DECLS
+
+#endif /* E_TEMPLATES_STORE_H */
diff --git a/plugins/templates/templates.c b/plugins/templates/templates.c
index 5953f41..57229dd 100644
--- a/plugins/templates/templates.c
+++ b/plugins/templates/templates.c
@@ -34,12 +34,15 @@
#include <shell/e-shell-view.h>
#include <mail/e-mail-reader.h>
+#include <mail/e-mail-ui-session.h>
#include <mail/em-composer-utils.h>
#include <mail/em-utils.h>
#include <mail/message-list.h>
#include <composer/e-msg-composer.h>
+#include "e-templates-store.h"
+
#define CONF_KEY_TEMPLATE_PLACEHOLDERS "template-placeholders"
typedef struct _AsyncContext AsyncContext;
@@ -78,6 +81,31 @@ gboolean init_shell_actions (GtkUIManager *ui_manager,
gint e_plugin_lib_enable (EPlugin *plugin,
gboolean enabled);
+#define TEMPLATES_DATA_KEY "templates::data"
+
+typedef struct _TemplatesData {
+ ETemplatesStore *templates_store;
+ gulong changed_handler_id;
+ gboolean changed;
+ guint merge_id;
+} TemplatesData;
+
+static void
+templates_data_free (gpointer ptr)
+{
+ TemplatesData *td = ptr;
+
+ if (td) {
+ if (td->templates_store && td->changed_handler_id) {
+ g_signal_handler_disconnect (td->templates_store, td->changed_handler_id);
+ td->changed_handler_id = 0;
+ }
+
+ g_clear_object (&td->templates_store);
+ g_free (td);
+ }
+}
+
/* Thanks to attachment reminder plugin for this*/
static void commit_changes (UIData *ui);
@@ -90,20 +118,9 @@ static void value_cell_edited_callback (GtkCellRendererText *cell, gchar *path_
static gboolean clue_foreach_check_isempty (GtkTreeModel *model, GtkTreePath
*path, GtkTreeIter *iter, UIData *ui);
-static void templates_folder_msg_changed_cb (CamelFolder *folder,
- CamelFolderChangeInfo *change_info,
- EShellWindow *shell_window);
-
static gboolean plugin_enabled;
static void
-disconnect_signals_on_dispose (gpointer object_with_signal,
- GObject *signal_data)
-{
- g_signal_handlers_disconnect_by_data (object_with_signal, signal_data);
-}
-
-static void
async_context_free (AsyncContext *context)
{
if (context->activity != NULL)
@@ -1032,19 +1049,20 @@ template_got_source_message (CamelFolder *folder,
}
static void
-action_reply_with_template_cb (GtkAction *action,
- EShellView *shell_view)
+action_reply_with_template_cb (ETemplatesStore *templates_store,
+ CamelFolder *template_folder,
+ const gchar *template_message_uid,
+ gpointer user_data)
{
EActivity *activity;
AsyncContext *context;
GCancellable *cancellable;
CamelFolder *folder;
- CamelFolder *template_folder;
+ EShellView *shell_view = user_data;
EShellContent *shell_content;
EMailReader *reader;
GPtrArray *uids;
const gchar *message_uid;
- const gchar *template_message_uid;
shell_content = e_shell_view_get_shell_content (shell_view);
reader = E_MAIL_READER (shell_content);
@@ -1053,11 +1071,6 @@ action_reply_with_template_cb (GtkAction *action,
g_return_if_fail (uids != NULL && uids->len == 1);
message_uid = g_ptr_array_index (uids, 0);
- template_folder = g_object_get_data (
- G_OBJECT (action), "template-folder");
- template_message_uid = g_object_get_data (
- G_OBJECT (action), "template-uid");
-
activity = e_mail_reader_new_activity (reader);
cancellable = e_activity_get_cancellable (activity);
@@ -1087,196 +1100,86 @@ action_reply_with_template_cb (GtkAction *action,
g_ptr_array_unref (uids);
}
-static gint
-compare_ptr_array_uids_by_subject (gconstpointer ptr1,
- gconstpointer ptr2,
- gpointer user_data)
+static gchar *
+get_account_templates_folder_uri (EMsgComposer *composer)
{
- CamelFolderSummary *summary = user_data;
- CamelMessageInfo *mi1, *mi2;
- const gchar * const *puid1 = ptr1, * const *puid2 = ptr2;
- const gchar *subject1, *subject2;
- gint res;
-
- if (!puid1 || !*puid1) {
- if (!puid2 || !*puid2)
- return 0;
-
- return -1;
- } else if (!puid2 || !*puid2) {
- return 1;
- }
+ EComposerHeaderTable *table;
+ ESource *source;
+ const gchar *identity_uid;
+ gchar *templates_folder_uri = NULL;
- mi1 = camel_folder_summary_get (summary, *puid1);
- mi2 = camel_folder_summary_get (summary, *puid2);
+ g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
- if (!mi1) {
- if (!mi2)
- return 0;
+ table = e_msg_composer_get_header_table (composer);
+ identity_uid = e_composer_header_table_get_identity_uid (table);
+ source = e_composer_header_table_ref_source (table, identity_uid);
- camel_message_info_unref (mi2);
- return -1;
- } else if (!mi2) {
- camel_message_info_unref (mi1);
- return 1;
- }
+ /* Get the selected identity's preferred Templates folder. */
+ if (source != NULL) {
+ ESourceMailComposition *extension;
+ const gchar *extension_name;
- subject1 = camel_message_info_subject (mi1);
- subject2 = camel_message_info_subject (mi2);
+ extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
+ extension = e_source_get_extension (source, extension_name);
+ templates_folder_uri = e_source_mail_composition_dup_templates_folder (extension);
- if (!subject1)
- subject1 = "";
- if (!subject2)
- subject2 = "";
+ g_object_unref (source);
+ }
- res = g_utf8_collate (subject1, subject2);
+ return templates_folder_uri;
+}
- camel_message_info_unref (mi1);
- camel_message_info_unref (mi2);
+typedef struct _SaveTemplateAsyncData {
+ EMsgComposer *composer;
+ EMailSession *session;
+ CamelMimeMessage *message;
+ CamelMessageInfo *info;
+ gchar *templates_folder_uri;
+} SaveTemplateAsyncData;
- return res;
+static void
+save_template_async_data_free (gpointer ptr)
+{
+ SaveTemplateAsyncData *sta = ptr;
+
+ if (sta) {
+ g_clear_object (&sta->composer);
+ g_clear_object (&sta->session);
+ g_clear_object (&sta->message);
+ camel_message_info_unref (sta->info);
+ g_free (sta->templates_folder_uri);
+ g_free (sta);
+ }
}
static void
-build_template_menus_recurse (CamelStore *local_store,
- GtkUIManager *ui_manager,
- GtkActionGroup *action_group,
- const gchar *menu_path,
- guint *action_count,
- guint merge_id,
- CamelFolderInfo *folder_info,
- EShellView *shell_view)
+save_template_thread (EAlertSinkThreadJobData *job_data,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
{
- EShellWindow *shell_window;
-
- shell_window = e_shell_view_get_shell_window (shell_view);
-
- while (folder_info != NULL) {
- CamelFolder *folder;
- GPtrArray *uids;
- GtkAction *action;
- const gchar *action_label;
- const gchar *display_name;
- gchar *action_name;
- gchar *path;
- guint ii;
-
- display_name = folder_info->display_name;
-
- /* FIXME Not passing a GCancellable or GError here. */
- folder = camel_store_get_folder_sync (
- local_store, folder_info->full_name, 0, NULL, NULL);
-
- action_name = g_strdup_printf (
- "templates-menu-%d", *action_count);
- *action_count = *action_count + 1;
-
- /* To avoid having a Templates dir, we ignore the top level */
- if (g_str_has_suffix (display_name, "Templates"))
- action_label = _("Templates");
- else
- action_label = display_name;
-
- action = gtk_action_new (
- action_name, action_label, NULL, NULL);
-
- gtk_action_group_add_action (action_group, action);
-
- gtk_ui_manager_add_ui (
- ui_manager, merge_id, menu_path, action_name,
- action_name, GTK_UI_MANAGER_MENU, FALSE);
-
- /* Disconnect previous connection to avoid possible multiple calls because
- * folder is a persistent structure */
- if (g_signal_handlers_disconnect_by_func (
- folder, G_CALLBACK (templates_folder_msg_changed_cb), shell_window))
- g_object_weak_unref (G_OBJECT (shell_window), disconnect_signals_on_dispose, folder);
- g_signal_connect (
- folder, "changed",
- G_CALLBACK (templates_folder_msg_changed_cb),
- shell_window);
- g_object_weak_ref (G_OBJECT (shell_window), disconnect_signals_on_dispose, folder);
-
- path = g_strdup_printf ("%s/%s", menu_path, action_name);
-
- g_object_unref (action);
- g_free (action_name);
-
- /* Add submenus, if any. */
- if (folder_info->child != NULL)
- build_template_menus_recurse (
- local_store,
- ui_manager, action_group,
- path, action_count, merge_id,
- folder_info->child, shell_view);
-
- if (!folder) {
- g_free (path);
- folder_info = folder_info->next;
- continue;
- }
-
- /* Get the UIDs for this folder and add them to the menu. */
- uids = camel_folder_get_uids (folder);
- if (uids && folder->summary)
- g_ptr_array_sort_with_data (uids, compare_ptr_array_uids_by_subject, folder->summary);
-
- for (ii = 0; uids && ii < uids->len; ii++) {
- CamelMimeMessage *template;
- const gchar *uid = uids->pdata[ii];
- guint32 flags;
-
- /* If the UIDs is marked for deletion, skip it. */
- flags = camel_folder_get_message_flags (folder, uid);
- if (flags & CAMEL_MESSAGE_DELETED)
- continue;
-
- /* FIXME Not passing a GCancellable or GError here. */
- template = camel_folder_get_message_sync (
- folder, uid, NULL, NULL);
-
- /* FIXME Do something more intelligent with errors. */
- if (template == NULL)
- continue;
-
- action_label =
- camel_mime_message_get_subject (template);
- if (action_label == NULL || *action_label == '\0')
- action_label = _("No Title");
-
- action_name = g_strdup_printf (
- "templates-item-%d", *action_count);
- *action_count = *action_count + 1;
-
- action = gtk_action_new (
- action_name, action_label, NULL, NULL);
-
- g_object_set_data (G_OBJECT (action), "template-uid", (gpointer) uid);
-
- g_object_set_data (G_OBJECT (action), "template-folder", folder);
-
- g_signal_connect (
- action, "activate",
- G_CALLBACK (action_reply_with_template_cb),
- shell_view);
-
- gtk_action_group_add_action (action_group, action);
-
- gtk_ui_manager_add_ui (
- ui_manager, merge_id, path, action_name,
- action_name, GTK_UI_MANAGER_MENUITEM, FALSE);
-
- g_object_unref (action);
- g_free (action_name);
- g_object_unref (template);
- }
-
- camel_folder_free_uids (folder, uids);
- g_object_unref (folder);
- g_free (path);
+ SaveTemplateAsyncData *sta = user_data;
+ CamelFolder *templates_folder = NULL;
+
+ if (sta->templates_folder_uri && *sta->templates_folder_uri) {
+ templates_folder = e_mail_session_uri_to_folder_sync (sta->session,
+ sta->templates_folder_uri, 0, cancellable, error);
+ if (!templates_folder)
+ return;
+ }
- folder_info = folder_info->next;
+ if (!templates_folder) {
+ e_mail_session_append_to_local_folder_sync (
+ sta->session, E_MAIL_LOCAL_FOLDER_TEMPLATES,
+ sta->message, sta->info,
+ NULL, cancellable, error);
+ } else {
+ e_mail_folder_append_message_sync (
+ templates_folder, sta->message, sta->info,
+ NULL, cancellable, error);
}
+
+ g_clear_object (&templates_folder);
}
static void
@@ -1287,8 +1190,11 @@ got_message_draft_cb (EMsgComposer *composer,
EShellBackend *shell_backend;
EMailBackend *backend;
EMailSession *session;
+ EHTMLEditor *html_editor;
CamelMimeMessage *message;
CamelMessageInfo *info;
+ SaveTemplateAsyncData *sta;
+ EActivity *activity;
GError *error = NULL;
message = e_msg_composer_get_message_draft_finish (
@@ -1325,16 +1231,24 @@ got_message_draft_cb (EMsgComposer *composer,
* which flags to modify. In this case, ~0 means all flags.
* So it clears all the flags and then sets SEEN and DRAFT. */
camel_message_info_set_flags (
- info, CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DRAFT, ~0);
+ info, CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DRAFT |
+ (camel_mime_message_has_attachment (message) ? CAMEL_MESSAGE_ATTACHMENTS : 0), ~0);
+
+ sta = g_new0 (SaveTemplateAsyncData, 1);
+ sta->composer = g_object_ref (composer);
+ sta->session = g_object_ref (session);
+ sta->message = message;
+ sta->info = info;
+ sta->templates_folder_uri = get_account_templates_folder_uri (composer);
- /* FIXME Should submit an EActivity for this
- * operation, same as saving to Outbox. */
- e_mail_session_append_to_local_folder (
- session, E_MAIL_LOCAL_FOLDER_TEMPLATES,
- message, info, G_PRIORITY_DEFAULT,
- NULL, (GAsyncReadyCallback) NULL, NULL);
+ html_editor = e_msg_composer_get_editor (composer);
- g_object_unref (message);
+ activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (html_editor),
+ _("Saving message template"),
+ "mail-composer:failed-save-template",
+ NULL, save_template_thread, sta, save_template_async_data_free);
+
+ g_clear_object (&activity);
}
static void
@@ -1358,73 +1272,33 @@ static GtkActionEntry composer_entries[] = {
};
static void
-build_menu (EShellWindow *shell_window,
- GtkActionGroup *action_group)
-{
- EShellView *shell_view;
- EShellBackend *shell_backend;
- EMailBackend *backend;
- EMailSession *session;
- CamelFolder *folder;
- CamelStore *local_store;
- CamelFolderInfo *folder_info;
- GtkUIManager *ui_manager;
- guint merge_id;
- guint action_count = 0;
- const gchar *full_name;
-
- ui_manager = e_shell_window_get_ui_manager (shell_window);
- shell_view = e_shell_window_get_shell_view (shell_window, "mail");
- shell_backend = e_shell_view_get_shell_backend (shell_view);
-
- backend = E_MAIL_BACKEND (shell_backend);
- session = e_mail_backend_get_session (backend);
- local_store = e_mail_session_get_local_store (session);
-
- merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (action_group), "merge-id"));
-
- /* Now recursively build template submenus in the pop-up menu. */
- folder = e_mail_session_get_local_folder (
- session, E_MAIL_LOCAL_FOLDER_TEMPLATES);
- full_name = camel_folder_get_full_name (folder);
-
- /* FIXME Not passing a GCancellable or GError here. */
- folder_info = camel_store_get_folder_info_sync (
- local_store, full_name,
- CAMEL_STORE_FOLDER_INFO_RECURSIVE |
- CAMEL_STORE_FOLDER_INFO_FAST, NULL, NULL);
-
- build_template_menus_recurse (
- local_store, ui_manager, action_group,
- "/mail-message-popup/mail-message-templates",
- &action_count, merge_id, folder_info,
- shell_view);
-
- camel_folder_info_free (folder_info);
-}
-
-static void
-update_actions_cb (EShellView *shell_view,
- GtkActionGroup *action_group)
+templates_update_actions_cb (EShellView *shell_view,
+ GtkActionGroup *action_group)
{
- GList *list;
- gint length;
+ TemplatesData *td;
if (!plugin_enabled)
return;
- list = gtk_action_group_list_actions (action_group);
- length = g_list_length (list);
+ td = g_object_get_data (G_OBJECT (shell_view), TEMPLATES_DATA_KEY);
+ if (td) {
+ if (td->changed) {
+ EShellWindow *shell_window;
+ GtkUIManager *ui_manager;
+
+ td->changed = FALSE;
- if (!length) {
- EShellWindow *shell_window = e_shell_view_get_shell_window (shell_view);
- build_menu (shell_window, action_group);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ ui_manager = e_shell_window_get_ui_manager (shell_window);
+
+ e_templates_store_build_menu (td->templates_store, shell_view, ui_manager,
action_group,
+ "/mail-message-popup/mail-message-templates", td->merge_id,
+ action_reply_with_template_cb, shell_view);
+ }
}
gtk_action_group_set_sensitive (action_group, TRUE);
gtk_action_group_set_visible (action_group, TRUE);
-
- g_list_free (list);
}
gboolean
@@ -1444,49 +1318,14 @@ init_composer_actions (GtkUIManager *ui_manager,
}
static void
-rebuild_template_menu (EShellWindow *shell_window)
+templates_store_changed_cb (ETemplatesStore *templates_store,
+ gpointer user_data)
{
- GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
- guint merge_id;
-
- ui_manager = e_shell_window_get_ui_manager (shell_window);
-
- action_group = e_lookup_action_group (ui_manager, "templates");
- merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (action_group), "merge-id"));
+ TemplatesData *td = user_data;
- gtk_ui_manager_remove_ui (ui_manager, merge_id);
- e_action_group_remove_all_actions (action_group);
- gtk_ui_manager_ensure_update (ui_manager);
+ g_return_if_fail (td != NULL);
- build_menu (shell_window, action_group);
-}
-
-static void
-templates_folder_msg_changed_cb (CamelFolder *folder,
- CamelFolderChangeInfo *change_info,
- EShellWindow *shell_window)
-{
- rebuild_template_menu (shell_window);
-}
-
-static void
-templates_folder_changed_cb (CamelStore *store,
- CamelFolderInfo *folder_info,
- EShellWindow *shell_window)
-{
- if (folder_info->full_name && strstr (folder_info->full_name, _("Templates")) != NULL)
- rebuild_template_menu (shell_window);
-}
-
-static void
-templates_folder_renamed_cb (CamelStore *store,
- const gchar *old_name,
- CamelFolderInfo *folder_info,
- EShellWindow *shell_window)
-{
- if (folder_info->full_name && strstr (folder_info->full_name, _("Templates")) != NULL)
- rebuild_template_menu (shell_window);
+ td->changed = TRUE;
}
static void
@@ -1498,48 +1337,28 @@ mail_shell_view_created_cb (EShellWindow *shell_window,
EShellBackend *shell_backend;
GtkUIManager *ui_manager;
GtkActionGroup *action_group;
- CamelFolder *folder;
- CamelStore *local_store;
- guint merge_id;
+ TemplatesData *td;
ui_manager = e_shell_window_get_ui_manager (shell_window);
e_shell_window_add_action_group (shell_window, "templates");
action_group = e_lookup_action_group (ui_manager, "templates");
- merge_id = gtk_ui_manager_new_merge_id (ui_manager);
-
- g_object_set_data (
- G_OBJECT (action_group), "merge-id",
- GUINT_TO_POINTER (merge_id));
-
shell_backend = e_shell_view_get_shell_backend (shell_view);
backend = E_MAIL_BACKEND (shell_backend);
session = e_mail_backend_get_session (backend);
- local_store = e_mail_session_get_local_store (session);
- folder = e_mail_session_get_local_folder (
- session, E_MAIL_LOCAL_FOLDER_TEMPLATES);
-
- g_signal_connect (
- folder, "changed",
- G_CALLBACK (templates_folder_msg_changed_cb), shell_window);
- g_signal_connect (
- local_store, "folder-created",
- G_CALLBACK (templates_folder_changed_cb), shell_window);
- g_signal_connect (
- local_store, "folder-deleted",
- G_CALLBACK (templates_folder_changed_cb), shell_window);
- g_signal_connect (
- local_store, "folder-renamed",
- G_CALLBACK (templates_folder_renamed_cb), shell_window);
+ td = g_new0 (TemplatesData, 1);
+ td->templates_store = e_templates_store_ref_default (e_mail_ui_session_get_account_store
(E_MAIL_UI_SESSION (session)));
+ td->changed_handler_id = g_signal_connect (td->templates_store, "changed", G_CALLBACK
(templates_store_changed_cb), td);
+ td->merge_id = gtk_ui_manager_new_merge_id (ui_manager);
+ td->changed = TRUE;
- g_object_weak_ref (G_OBJECT (shell_window), disconnect_signals_on_dispose, folder);
- g_object_weak_ref (G_OBJECT (shell_window), disconnect_signals_on_dispose, local_store);
+ g_object_set_data_full (G_OBJECT (shell_view), TEMPLATES_DATA_KEY, td, templates_data_free);
g_signal_connect (
shell_view, "update-actions",
- G_CALLBACK (update_actions_cb), action_group);
+ G_CALLBACK (templates_update_actions_cb), action_group);
}
gboolean
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 815837e..0f7ff2c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -504,6 +504,7 @@ plugins/save-calendar/ical-format.c
plugins/save-calendar/org-gnome-save-calendar.eplug.xml
plugins/save-calendar/rdf-format.c
plugins/save-calendar/save-calendar.c
+plugins/templates/e-templates-store.c
plugins/templates/org-gnome-templates.eplug.xml
plugins/templates/templates.c
shell/e-shell-backend.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]