[evolution] I#1512 - Choose account with which to forward message in filter rule



commit 70b7d478732269fbabc4b9c6852eddeac627c128
Author: Milan Crha <mcrha redhat com>
Date:   Fri Jun 11 11:46:27 2021 +0200

    I#1512 - Choose account with which to forward message in filter rule
    
    Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/1512

 .../evolution-util/evolution-util-docs.sgml.in     |   5 +
 po/POTFILES.in                                     |   1 +
 src/e-util/CMakeLists.txt                          |   2 +
 src/e-util/e-filter-label.c                        | 211 +++++++++++++
 src/e-util/e-filter-label.h                        |  69 ++++
 src/e-util/e-mail-identity-combo-box.c             |  46 ++-
 src/e-util/e-mail-identity-combo-box.h             |   5 +
 src/e-util/e-rule-context.c                        |   5 +-
 src/e-util/e-util.h                                |   1 +
 src/libemail-engine/e-mail-session.c               |  74 ++++-
 src/mail/CMakeLists.txt                            |   2 +
 src/mail/em-filter-context.c                       |   8 +-
 src/mail/em-filter-mail-identity-element.c         | 350 +++++++++++++++++++++
 src/mail/em-filter-mail-identity-element.h         |  65 ++++
 src/mail/filtertypes.xml.in                        |   6 +-
 15 files changed, 841 insertions(+), 9 deletions(-)
---
diff --git a/docs/reference/evolution-util/evolution-util-docs.sgml.in 
b/docs/reference/evolution-util/evolution-util-docs.sgml.in
index 20623f090b..d530b8a08c 100644
--- a/docs/reference/evolution-util/evolution-util-docs.sgml.in
+++ b/docs/reference/evolution-util/evolution-util-docs.sgml.in
@@ -87,6 +87,7 @@
     <xi:include href="xml/e-filter-file.xml"/>
     <xi:include href="xml/e-filter-input.xml"/>
     <xi:include href="xml/e-filter-int.xml"/>
+    <xi:include href="xml/e-filter-label.xml"/>
     <xi:include href="xml/e-filter-option.xml"/>
   </chapter>
 
@@ -323,6 +324,10 @@
     <title>Index</title>
     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
   </index>
+  <index id="api-index-3-42" role="3.42">
+    <title>Index of new symbols in 3.42</title>
+    <xi:include href="xml/api-index-3.42.xml"><xi:fallback /></xi:include>
+  </index>
   <index id="api-index-3-40" role="3.40">
     <title>Index of new symbols in 3.40</title>
     <xi:include href="xml/api-index-3.40.xml"><xi:fallback /></xi:include>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 546f5876ef..1eb44ae079 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -357,6 +357,7 @@ src/mail/e-mail-ui-session.c
 src/mail/em-composer-utils.c
 src/mail/em-filter-editor.c
 src/mail/em-filter-editor-folder-element.c
+src/mail/em-filter-mail-identity-element.c
 src/mail/em-filter-rule.c
 src/mail/em-folder-properties.c
 src/mail/em-folder-selection-button.c
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index f31f678ff8..03b60d8e4b 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -129,6 +129,7 @@ set(SOURCES
        e-filter-file.c
        e-filter-input.c
        e-filter-int.c
+       e-filter-label.c
        e-filter-option.c
        e-filter-part.c
        e-filter-rule.c
@@ -404,6 +405,7 @@ set(HEADERS
        e-filter-file.h
        e-filter-input.h
        e-filter-int.h
+       e-filter-label.h
        e-filter-option.h
        e-filter-part.h
        e-filter-rule.h
diff --git a/src/e-util/e-filter-label.c b/src/e-util/e-filter-label.c
new file mode 100644
index 0000000000..f6bfcefdd4
--- /dev/null
+++ b/src/e-util/e-filter-label.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2021 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evolution-config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+
+#include "e-filter-part.h"
+
+#include "e-filter-label.h"
+
+struct _EFilterLabelPrivate {
+       gchar *title;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (EFilterLabel, e_filter_label, E_TYPE_FILTER_ELEMENT)
+
+static void
+filter_label_finalize (GObject *object)
+{
+       EFilterLabel *label = E_FILTER_LABEL (object);
+
+       g_free (label->priv->title);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_filter_label_parent_class)->finalize (object);
+}
+
+static gint
+filter_label_eq (EFilterElement *element_a,
+                EFilterElement *element_b)
+{
+       EFilterLabel *label_a = E_FILTER_LABEL (element_a);
+       EFilterLabel *label_b = E_FILTER_LABEL (element_b);
+
+       /* Chain up to parent's method. */
+       if (!E_FILTER_ELEMENT_CLASS (e_filter_label_parent_class)->eq (element_a, element_b))
+               return FALSE;
+
+       return g_strcmp0 (label_a->priv->title, label_b->priv->title) == 0;
+}
+
+static void
+filter_label_xml_create (EFilterElement *element,
+                        xmlNodePtr node)
+{
+       EFilterLabel *label = E_FILTER_LABEL (element);
+       xmlNodePtr n;
+
+       /* Chain up to parent's method. */
+       E_FILTER_ELEMENT_CLASS (e_filter_label_parent_class)->xml_create (element, node);
+
+       n = node->children;
+       while (n) {
+               if (!g_strcmp0 ((gchar *) n->name, "title") ||
+                   !g_strcmp0 ((gchar *) n->name, "_title")) {
+                       if (!label->priv->title) {
+                               xmlChar *tmp;
+
+                               tmp = xmlNodeGetContent (n);
+                               label->priv->title = tmp ? g_strdup ((gchar *) tmp) : NULL;
+                               if (tmp)
+                                       xmlFree (tmp);
+                       }
+               } else if (n->type == XML_ELEMENT_NODE) {
+                       g_warning ("Unknown xml node within 'label': %s\n", n->name);
+               }
+               n = n->next;
+       }
+}
+
+static xmlNodePtr
+filter_label_xml_encode (EFilterElement *element)
+{
+       xmlNodePtr value;
+
+       value = xmlNewNode (NULL, (xmlChar *) "value");
+       xmlSetProp (value, (xmlChar *) "name", (xmlChar *) element->name);
+
+       return value;
+}
+
+static gint
+filter_label_xml_decode (EFilterElement *element,
+                        xmlNodePtr node)
+{
+       xmlFree (element->name);
+       element->name = (gchar *) xmlGetProp (node, (xmlChar *) "name");
+
+       return 0;
+}
+
+static EFilterElement *
+filter_label_clone (EFilterElement *element)
+{
+       EFilterLabel *label = E_FILTER_LABEL (element);
+       EFilterLabel *clone_label;
+       EFilterElement *clone;
+
+       /* Chain up to parent's method. */
+       clone = E_FILTER_ELEMENT_CLASS (e_filter_label_parent_class)->clone (element);
+
+       clone_label = E_FILTER_LABEL (clone);
+       clone_label->priv->title = g_strdup (label->priv->title);
+
+       return clone;
+}
+
+static GtkWidget *
+filter_label_get_widget (EFilterElement *element)
+{
+       EFilterLabel *label = E_FILTER_LABEL (element);
+       GtkWidget *widget;
+
+       widget = gtk_label_new ((label->priv->title && *label->priv->title) ? _(label->priv->title) : "");
+
+       return widget;
+}
+
+static void
+filter_label_build_code (EFilterElement *element,
+                        GString *out,
+                        EFilterPart *part)
+{
+}
+
+static void
+filter_label_format_sexp (EFilterElement *element,
+                          GString *out)
+{
+}
+
+static void
+filter_label_describe (EFilterElement *element,
+                      GString *out)
+{
+       EFilterLabel *label = E_FILTER_LABEL (element);
+
+       if (label->priv->title && *label->priv->title)
+               g_string_append (out, _(label->priv->title));
+}
+
+static void
+e_filter_label_class_init (EFilterLabelClass *class)
+{
+       GObjectClass *object_class;
+       EFilterElementClass *filter_element_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->finalize = filter_label_finalize;
+
+       filter_element_class = E_FILTER_ELEMENT_CLASS (class);
+       filter_element_class->eq = filter_label_eq;
+       filter_element_class->xml_create = filter_label_xml_create;
+       filter_element_class->xml_encode = filter_label_xml_encode;
+       filter_element_class->xml_decode = filter_label_xml_decode;
+       filter_element_class->clone = filter_label_clone;
+       filter_element_class->get_widget = filter_label_get_widget;
+       filter_element_class->build_code = filter_label_build_code;
+       filter_element_class->format_sexp = filter_label_format_sexp;
+       filter_element_class->describe = filter_label_describe;
+}
+
+static void
+e_filter_label_init (EFilterLabel *label)
+{
+       label->priv = e_filter_label_get_instance_private (label);
+}
+
+EFilterElement *
+e_filter_label_new (void)
+{
+       return g_object_new (E_TYPE_FILTER_LABEL, NULL);
+}
+
+void
+e_filter_label_set_title (EFilterLabel *label,
+                         const gchar *title)
+{
+       g_return_if_fail (E_IS_FILTER_LABEL (label));
+
+       if (label->priv->title != title) {
+               g_free (label->priv->title);
+               label->priv->title = g_strdup (title);
+       }
+}
+
+const gchar *
+e_filter_label_get_title (EFilterLabel *label)
+{
+       g_return_val_if_fail (E_IS_FILTER_LABEL (label), NULL);
+
+       return label->priv->title;
+}
diff --git a/src/e-util/e-filter-label.h b/src/e-util/e-filter-label.h
new file mode 100644
index 0000000000..865f041295
--- /dev/null
+++ b/src/e-util/e-filter-label.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_FILTER_LABEL_H
+#define E_FILTER_LABEL_H
+
+#include <e-util/e-filter-element.h>
+
+/* Standard GObject macros */
+#define E_TYPE_FILTER_LABEL \
+       (e_filter_label_get_type ())
+#define E_FILTER_LABEL(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_FILTER_LABEL, EFilterLabel))
+#define E_FILTER_LABEL_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_FILTER_LABEL, EFilterLabelClass))
+#define E_IS_FILTER_LABEL(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_FILTER_LABEL))
+#define E_IS_FILTER_LABEL_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_FILTER_LABEL))
+#define E_FILTER_LABEL_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_FILTER_LABEL, EFilterLabelClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EFilterLabel EFilterLabel;
+typedef struct _EFilterLabelClass EFilterLabelClass;
+typedef struct _EFilterLabelPrivate EFilterLabelPrivate;
+
+struct _EFilterLabel {
+       EFilterElement parent;
+       EFilterLabelPrivate *priv;
+};
+
+struct _EFilterLabelClass {
+       EFilterElementClass parent_class;
+};
+
+GType          e_filter_label_get_type (void) G_GNUC_CONST;
+EFilterElement *e_filter_label_new             (void);
+void           e_filter_label_set_title        (EFilterLabel *option,
+                                                const gchar *title);
+const gchar *  e_filter_label_get_title        (EFilterLabel *option);
+
+G_END_DECLS
+
+#endif /* E_FILTER_LABEL_H */
diff --git a/src/e-util/e-mail-identity-combo-box.c b/src/e-util/e-mail-identity-combo-box.c
index 4bd6bb5ef1..88c78a543f 100644
--- a/src/e-util/e-mail-identity-combo-box.c
+++ b/src/e-util/e-mail-identity-combo-box.c
@@ -45,6 +45,8 @@ struct _EMailIdentityComboBoxPrivate {
        gulong source_changed_handler_id;
        gulong source_removed_handler_id;
 
+       gchar *none_title;
+
        gboolean allow_none;
        gboolean allow_aliases;
 
@@ -256,6 +258,7 @@ mail_identity_combo_box_dispose (GObject *object)
                priv->refresh_idle_id = 0;
        }
 
+       g_clear_pointer (&priv->none_title, g_free);
        g_clear_object (&priv->registry);
 
        /* Chain up to parent's dispose() method. */
@@ -691,7 +694,7 @@ e_mail_identity_combo_box_refresh (EMailIdentityComboBox *combo_box)
 
                gtk_list_store_set (
                        GTK_LIST_STORE (tree_model), &iter,
-                       E_MAIL_IDENTITY_COMBO_BOX_COLUMN_DISPLAY_NAME, _("None"),
+                       E_MAIL_IDENTITY_COMBO_BOX_COLUMN_DISPLAY_NAME, 
e_mail_identity_combo_box_get_none_title (combo_box),
                        E_MAIL_IDENTITY_COMBO_BOX_COLUMN_UID, "",
                        E_MAIL_IDENTITY_COMBO_BOX_COLUMN_COMBO_ID, "",
                        -1);
@@ -776,6 +779,47 @@ e_mail_identity_combo_box_set_allow_none (EMailIdentityComboBox *combo_box,
        e_mail_identity_combo_box_refresh (combo_box);
 }
 
+/**
+ * e_mail_identity_combo_box_get_none_title:
+ * @combo_box: an #EMailIdentityComboBox
+ *
+ * Returns: what title the none item should have
+ *
+ * Since: 3.42
+ **/
+const gchar *
+e_mail_identity_combo_box_get_none_title (EMailIdentityComboBox *combo_box)
+{
+       g_return_val_if_fail (E_IS_MAIL_IDENTITY_COMBO_BOX (combo_box), NULL);
+
+       if (combo_box->priv->none_title)
+               return combo_box->priv->none_title;
+
+       return _("None");
+}
+
+/**
+ * e_mail_identity_combo_box_set_none_title:
+ * @combo_box: an #EMailIdentityComboBox
+ * @none_title: (nullable): a title to use, or %NULL
+ *
+ * Set what title the none item should have. This is a user visible string, thus
+ * it should be localized. Use %NULL to reset to the default "None" title.
+ *
+ * Since: 3.42
+ **/
+void
+e_mail_identity_combo_box_set_none_title (EMailIdentityComboBox *combo_box,
+                                         const gchar *none_title)
+{
+       g_return_if_fail (E_IS_MAIL_IDENTITY_COMBO_BOX (combo_box));
+
+       if (combo_box->priv->none_title != none_title) {
+               g_free (combo_box->priv->none_title);
+               combo_box->priv->none_title = g_strdup (none_title);
+       }
+}
+
 /**
  * e_mail_identity_combo_box_get_allow_aliases:
  * @combo_box: an #EMailIdentityComboBox
diff --git a/src/e-util/e-mail-identity-combo-box.h b/src/e-util/e-mail-identity-combo-box.h
index 36d75e6bed..e926190e0b 100644
--- a/src/e-util/e-mail-identity-combo-box.h
+++ b/src/e-util/e-mail-identity-combo-box.h
@@ -87,6 +87,11 @@ gboolean     e_mail_identity_combo_box_get_allow_none
 void           e_mail_identity_combo_box_set_allow_none
                                        (EMailIdentityComboBox *combo_box,
                                         gboolean allow_none);
+const gchar *  e_mail_identity_combo_box_get_none_title
+                                       (EMailIdentityComboBox *combo_box);
+void           e_mail_identity_combo_box_set_none_title
+                                       (EMailIdentityComboBox *combo_box,
+                                        const gchar *none_title);
 gboolean       e_mail_identity_combo_box_get_allow_aliases
                                        (EMailIdentityComboBox *combo_box);
 void           e_mail_identity_combo_box_set_allow_aliases
diff --git a/src/e-util/e-rule-context.c b/src/e-util/e-rule-context.c
index 8d73fcf1f7..4f72f7ad35 100644
--- a/src/e-util/e-rule-context.c
+++ b/src/e-util/e-rule-context.c
@@ -45,6 +45,7 @@
 #include "e-filter-file.h"
 #include "e-filter-input.h"
 #include "e-filter-int.h"
+#include "e-filter-label.h"
 #include "e-filter-option.h"
 #include "e-filter-rule.h"
 #include "e-rule-context.h"
@@ -460,7 +461,9 @@ static EFilterElement *
 rule_context_new_element (ERuleContext *context,
                           const gchar *type)
 {
-       if (!strcmp (type, "string")) {
+       if (!strcmp (type, "label")) {
+               return (EFilterElement *) e_filter_label_new ();
+       } else if (!strcmp (type, "string")) {
                return (EFilterElement *) e_filter_input_new ();
        } else if (!strcmp (type, "address")) {
                /* FIXME: temporary ... need real address type */
diff --git a/src/e-util/e-util.h b/src/e-util/e-util.h
index d6a50b85d6..c430fed8bd 100644
--- a/src/e-util/e-util.h
+++ b/src/e-util/e-util.h
@@ -114,6 +114,7 @@
 #include <e-util/e-filter-file.h>
 #include <e-util/e-filter-input.h>
 #include <e-util/e-filter-int.h>
+#include <e-util/e-filter-label.h>
 #include <e-util/e-filter-option.h>
 #include <e-util/e-filter-part.h>
 #include <e-util/e-filter-rule.h>
diff --git a/src/libemail-engine/e-mail-session.c b/src/libemail-engine/e-mail-session.c
index 8c7d410fc3..e1dbe548ff 100644
--- a/src/libemail-engine/e-mail-session.c
+++ b/src/libemail-engine/e-mail-session.c
@@ -1523,6 +1523,67 @@ mail_session_forget_password (CamelSession *session,
        return TRUE;
 }
 
+/* Expects 'forward_with' encoded as: "identity_uid|alias_name|alias_address" with '\\' and '|' being 
backslash-escaped */
+static ESource *
+mail_session_decode_forward_with (ESourceRegistry *registry,
+                                 const gchar *forward_with,
+                                 gchar **out_alias_name,
+                                 gchar **out_alias_address)
+{
+       ESource *source = NULL;
+       GString *str;
+       gint step;
+       const gchar *ptr;
+
+       if (!forward_with || !*forward_with)
+               return NULL;
+
+       str = g_string_sized_new (strlen (forward_with));
+
+       for (step = 0, ptr = forward_with; *ptr; ptr++) {
+               if (*ptr == '\\' && ptr[1]) {
+                       g_string_append_c (str, ptr[1]);
+                       ptr++;
+                       g_string_append_c (str, *ptr);
+               } else if (*ptr == '|') {
+                       if (step == 0) { /* identity_uid */
+                               source = e_source_registry_ref_source (registry, str->str);
+                               if (!source)
+                                       break;
+                       } else if (step == 1) { /* alias_name */
+                               if (str->len)
+                                       *out_alias_name = g_strdup (str->str);
+                       } else if (step == 2) { /* alias_address */
+                               if (str->len)
+                                       *out_alias_address = g_strdup (str->str);
+                       }
+
+                       g_string_truncate (str, 0);
+                       step++;
+
+                       if (step == 3)
+                               break;
+               } else {
+                       g_string_append_c (str, *ptr);
+               }
+       }
+
+       /* When the string doesn't end with the '|' */
+       if (step < 3 && str->len) {
+               if (step == 0) { /* identity_uid */
+                       source = e_source_registry_ref_source (registry, str->str);
+               } else if (step == 1) { /* alias_name */
+                       *out_alias_name = g_strdup (str->str);
+               } else if (step == 2) { /* alias_address */
+                       *out_alias_address = g_strdup (str->str);
+               }
+       }
+
+       g_string_free (str, TRUE);
+
+       return source;
+}
+
 static gboolean
 mail_session_forward_to_sync (CamelSession *session,
                               CamelFolder *folder,
@@ -1569,9 +1630,16 @@ mail_session_forward_to_sync (CamelSession *session,
 
        registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
 
-       /* This returns a new ESource reference. */
-       source = em_utils_guess_mail_identity_with_recipients (
-               registry, message, folder, NULL, &alias_name, &alias_address);
+       source = mail_session_decode_forward_with (registry,
+               camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Forward-With"),
+               &alias_name, &alias_address);
+
+       if (!source) {
+               /* This returns a new ESource reference. */
+               source = em_utils_guess_mail_identity_with_recipients (
+                       registry, message, folder, NULL, &alias_name, &alias_address);
+       }
+
        if (source == NULL) {
                g_set_error (
                        error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
diff --git a/src/mail/CMakeLists.txt b/src/mail/CMakeLists.txt
index 5d08d13928..0cd0a9a842 100644
--- a/src/mail/CMakeLists.txt
+++ b/src/mail/CMakeLists.txt
@@ -100,6 +100,7 @@ set(SOURCES
        em-filter-context.c
        em-filter-editor.c
        em-filter-editor-folder-element.c
+       em-filter-mail-identity-element.c
        em-filter-rule.c
        em-filter-source-element.c
        em-folder-properties.c
@@ -191,6 +192,7 @@ set(HEADERS
        em-filter-context.h
        em-filter-editor.h
        em-filter-editor-folder-element.h
+       em-filter-mail-identity-element.h
        em-filter-rule.h
        em-filter-source-element.h
        em-folder-properties.h
diff --git a/src/mail/em-filter-context.c b/src/mail/em-filter-context.c
index 445327fd81..cd62dfe721 100644
--- a/src/mail/em-filter-context.c
+++ b/src/mail/em-filter-context.c
@@ -26,10 +26,9 @@
 
 #include "em-filter-context.h"
 #include "em-filter-rule.h"
-#include "em-filter-source-element.h"
-
-/* For poking into filter-folder guts */
 #include "em-filter-editor-folder-element.h"
+#include "em-filter-mail-identity-element.h"
+#include "em-filter-source-element.h"
 
 #define EM_FILTER_CONTEXT_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -238,6 +237,9 @@ filter_context_new_element (ERuleContext *context,
        if (strcmp (type, "source") == 0)
                return em_filter_source_element_new (priv->session);
 
+       if (strcmp (type, "mail-identity") == 0)
+               return em_filter_mail_identity_element_new (e_mail_session_get_registry (priv->session));
+
        return E_RULE_CONTEXT_CLASS (em_filter_context_parent_class)->
                new_element (context, type);
 }
diff --git a/src/mail/em-filter-mail-identity-element.c b/src/mail/em-filter-mail-identity-element.c
new file mode 100644
index 0000000000..3370c7ed79
--- /dev/null
+++ b/src/mail/em-filter-mail-identity-element.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2021 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evolution-config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+
+#include "e-util/e-util.h"
+#include "em-filter-mail-identity-element.h"
+
+struct _EMFilterMailIdentityElementPrivate {
+       ESourceRegistry *registry;
+       gchar *display_name;
+       gchar *identity_uid;
+       gchar *alias_name;
+       gchar *alias_address;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (EMFilterMailIdentityElement, em_filter_mail_identity_element, 
E_TYPE_FILTER_ELEMENT)
+
+static void
+filter_mail_identity_take_value (EMFilterMailIdentityElement *mail_identity,
+                                gchar *display_name,
+                                gchar *identity_uid,
+                                gchar *alias_name,
+                                gchar *alias_address)
+{
+       if (mail_identity->priv->display_name != display_name) {
+               g_free (mail_identity->priv->display_name);
+               mail_identity->priv->display_name = display_name;
+       } else {
+               g_free (display_name);
+       }
+
+       if (mail_identity->priv->identity_uid != identity_uid) {
+               g_free (mail_identity->priv->identity_uid);
+               mail_identity->priv->identity_uid = identity_uid;
+       } else {
+               g_free (identity_uid);
+       }
+
+       if (mail_identity->priv->alias_name != alias_name) {
+               g_free (mail_identity->priv->alias_name);
+               mail_identity->priv->alias_name = alias_name;
+       } else {
+               g_free (alias_name);
+       }
+
+       if (mail_identity->priv->alias_address != alias_address) {
+               g_free (mail_identity->priv->alias_address);
+               mail_identity->priv->alias_address = alias_address;
+       } else {
+               g_free (alias_address);
+       }
+}
+
+static void
+filter_mail_identity_element_finalize (GObject *object)
+{
+       EMFilterMailIdentityElement *mail_identity = EM_FILTER_MAIL_IDENTITY_ELEMENT (object);
+
+       g_clear_object (&mail_identity->priv->registry);
+       g_free (mail_identity->priv->display_name);
+       g_free (mail_identity->priv->identity_uid);
+       g_free (mail_identity->priv->alias_name);
+       g_free (mail_identity->priv->alias_address);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (em_filter_mail_identity_element_parent_class)->finalize (object);
+}
+
+static gint
+filter_mail_identity_element_eq (EFilterElement *element_a,
+                                EFilterElement *element_b)
+{
+       EMFilterMailIdentityElement *mail_identity_a = EM_FILTER_MAIL_IDENTITY_ELEMENT (element_a);
+       EMFilterMailIdentityElement *mail_identity_b = EM_FILTER_MAIL_IDENTITY_ELEMENT (element_b);
+
+       /* Chain up to parent's method. */
+       if (!E_FILTER_ELEMENT_CLASS (em_filter_mail_identity_element_parent_class)->eq (element_a, element_b))
+               return FALSE;
+
+       return g_strcmp0 (mail_identity_a->priv->display_name, mail_identity_b->priv->display_name) == 0 &&
+              g_strcmp0 (mail_identity_a->priv->identity_uid, mail_identity_b->priv->identity_uid) == 0 &&
+              g_strcmp0 (mail_identity_a->priv->alias_name, mail_identity_b->priv->alias_name) == 0 &&
+              g_strcmp0 (mail_identity_a->priv->alias_address, mail_identity_b->priv->alias_address) == 0;
+}
+
+static void
+filter_mail_identity_element_xml_create (EFilterElement *element,
+                                        xmlNodePtr node)
+{
+       xmlNodePtr n;
+
+       /* Chain up to parent's method. */
+       E_FILTER_ELEMENT_CLASS (em_filter_mail_identity_element_parent_class)->xml_create (element, node);
+
+       n = node->children;
+       while (n) {
+               if (n->type == XML_ELEMENT_NODE) {
+                       g_warning ("Unknown xml node within 'label': %s\n", n->name);
+               }
+               n = n->next;
+       }
+}
+
+static xmlNodePtr
+filter_mail_identity_element_xml_encode (EFilterElement *element)
+{
+       EMFilterMailIdentityElement *mail_identity = EM_FILTER_MAIL_IDENTITY_ELEMENT (element);
+       xmlNodePtr value;
+
+       value = xmlNewNode (NULL, (xmlChar *) "value");
+       xmlSetProp (value, (xmlChar *) "name", (xmlChar *) element->name);
+
+       if (mail_identity->priv->display_name)
+               xmlSetProp (value, (xmlChar *) "display-name", (xmlChar *) mail_identity->priv->display_name);
+
+       if (mail_identity->priv->identity_uid)
+               xmlSetProp (value, (xmlChar *) "identity-uid", (xmlChar *) mail_identity->priv->identity_uid);
+
+       if (mail_identity->priv->alias_name)
+               xmlSetProp (value, (xmlChar *) "alias-name", (xmlChar *) mail_identity->priv->alias_name);
+
+       if (mail_identity->priv->alias_address)
+               xmlSetProp (value, (xmlChar *) "alias-address", (xmlChar *) 
mail_identity->priv->alias_address);
+
+       return value;
+}
+
+static gint
+filter_mail_identity_element_xml_decode (EFilterElement *element,
+                                        xmlNodePtr node)
+{
+       EMFilterMailIdentityElement *mail_identity = EM_FILTER_MAIL_IDENTITY_ELEMENT (element);
+       xmlChar *x_display_name, *x_identity_uid, *x_alias_name, *x_alias_address;
+
+       xmlFree (element->name);
+       element->name = (gchar *) xmlGetProp (node, (xmlChar *) "name");
+
+       x_display_name = xmlGetProp (node, (xmlChar *) "display-name");
+       x_identity_uid = xmlGetProp (node, (xmlChar *) "identity-uid");
+       x_alias_name = xmlGetProp (node, (xmlChar *) "alias-name");
+       x_alias_address = xmlGetProp (node, (xmlChar *) "alias-address");
+
+       filter_mail_identity_take_value (mail_identity,
+               g_strdup ((gchar *) x_display_name),
+               g_strdup ((gchar *) x_identity_uid),
+               g_strdup ((gchar *) x_alias_name),
+               g_strdup ((gchar *) x_alias_address));
+
+       g_clear_pointer (&x_display_name, xmlFree);
+       g_clear_pointer (&x_identity_uid, xmlFree);
+       g_clear_pointer (&x_alias_name, xmlFree);
+       g_clear_pointer (&x_alias_address, xmlFree);
+
+       return 0;
+}
+
+static EFilterElement *
+filter_mail_identity_element_clone (EFilterElement *element)
+{
+       EMFilterMailIdentityElement *mail_identity = EM_FILTER_MAIL_IDENTITY_ELEMENT (element);
+       EMFilterMailIdentityElement *clone_mail_identity;
+       EFilterElement *clone;
+
+       /* Chain up to parent's method. */
+       clone = E_FILTER_ELEMENT_CLASS (em_filter_mail_identity_element_parent_class)->clone (element);
+
+       clone_mail_identity = EM_FILTER_MAIL_IDENTITY_ELEMENT (clone);
+       clone_mail_identity->priv->display_name = g_strdup (mail_identity->priv->display_name);
+       clone_mail_identity->priv->identity_uid = g_strdup (mail_identity->priv->identity_uid);
+       clone_mail_identity->priv->alias_name = g_strdup (mail_identity->priv->alias_name);
+       clone_mail_identity->priv->alias_address = g_strdup (mail_identity->priv->alias_address);
+
+       if (mail_identity->priv->registry)
+               clone_mail_identity->priv->registry = g_object_ref (mail_identity->priv->registry);
+
+       return clone;
+}
+
+static void
+filter_mail_identity_element_changed_cb (GtkComboBox *combo_box,
+                                        gpointer user_data)
+{
+       EMFilterMailIdentityElement *mail_identity = user_data;
+       GtkTreeIter iter;
+       gchar *display_name = NULL, *identity_uid = NULL, *alias_name = NULL, *alias_address = NULL;
+
+       g_return_if_fail (EM_IS_FILTER_MAIL_IDENTITY_ELEMENT (mail_identity));
+
+       if (!e_mail_identity_combo_box_get_active_uid (E_MAIL_IDENTITY_COMBO_BOX (combo_box), &identity_uid, 
&alias_name, &alias_address)) {
+               identity_uid = NULL;
+               alias_name = NULL;
+               alias_address = NULL;
+       }
+
+       if (gtk_combo_box_get_active_iter (combo_box, &iter)) {
+               GtkTreeModel *model;
+
+               model = gtk_combo_box_get_model (combo_box);
+               gtk_tree_model_get (model, &iter,
+                       E_MAIL_IDENTITY_COMBO_BOX_COLUMN_DISPLAY_NAME, &display_name,
+                       -1);
+       }
+
+       filter_mail_identity_take_value (mail_identity, display_name, identity_uid, alias_name, 
alias_address);
+}
+
+static GtkWidget *
+filter_mail_identity_element_get_widget (EFilterElement *element)
+{
+       EMFilterMailIdentityElement *mail_identity = EM_FILTER_MAIL_IDENTITY_ELEMENT (element);
+       EMailIdentityComboBox *combo_box;
+       GtkWidget *widget;
+
+       widget = e_mail_identity_combo_box_new (mail_identity->priv->registry);
+       combo_box = E_MAIL_IDENTITY_COMBO_BOX (widget);
+       e_mail_identity_combo_box_set_none_title (combo_box, _("Default Account"));
+       e_mail_identity_combo_box_set_allow_none (combo_box, TRUE);
+       e_mail_identity_combo_box_set_allow_aliases (combo_box, TRUE);
+
+       g_signal_connect_object (combo_box, "changed",
+               G_CALLBACK (filter_mail_identity_element_changed_cb), mail_identity, 0);
+
+       if (mail_identity->priv->identity_uid) {
+               e_mail_identity_combo_box_set_active_uid (combo_box,
+                       mail_identity->priv->identity_uid,
+                       mail_identity->priv->alias_name,
+                       mail_identity->priv->alias_address);
+       } else {
+               e_mail_identity_combo_box_set_active_uid (combo_box, "", NULL, NULL);
+       }
+
+       return widget;
+}
+
+static void
+filter_mail_identity_element_add_value (GString *str,
+                                       const gchar *value)
+{
+       const gchar *pp;
+
+       if (!value)
+               return;
+
+       for (pp = value; *pp; pp++) {
+               if (*pp == '\\' || *pp == '|')
+                       g_string_append_c (str, '\\');
+               g_string_append_c (str, *pp);
+       }
+}
+
+static void
+filter_mail_identity_element_format_sexp (EFilterElement *element,
+                                         GString *out)
+{
+       EMFilterMailIdentityElement *mail_identity = EM_FILTER_MAIL_IDENTITY_ELEMENT (element);
+       GString *value = NULL;
+
+       if (mail_identity->priv->identity_uid && *mail_identity->priv->identity_uid) {
+               /* Encode the value as: "identity_uid|alias_name|alias_value" */
+               value = g_string_sized_new (strlen (mail_identity->priv->identity_uid) * 2);
+
+               filter_mail_identity_element_add_value (value, mail_identity->priv->identity_uid);
+               g_string_append_c (value, '|');
+               filter_mail_identity_element_add_value (value, mail_identity->priv->alias_name);
+               g_string_append_c (value, '|');
+               filter_mail_identity_element_add_value (value, mail_identity->priv->alias_address);
+       }
+
+       camel_sexp_encode_string (out, value ? value->str : NULL);
+
+       if (value)
+               g_string_free (value, TRUE);
+}
+
+static void
+filter_mail_identity_element_describe (EFilterElement *element,
+                                      GString *out)
+{
+       EMFilterMailIdentityElement *mail_identity = EM_FILTER_MAIL_IDENTITY_ELEMENT (element);
+
+       if (mail_identity->priv->display_name && *mail_identity->priv->display_name)
+               g_string_append (out, mail_identity->priv->display_name);
+}
+
+static void
+em_filter_mail_identity_element_class_init (EMFilterMailIdentityElementClass *class)
+{
+       GObjectClass *object_class;
+       EFilterElementClass *filter_element_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->finalize = filter_mail_identity_element_finalize;
+
+       filter_element_class = E_FILTER_ELEMENT_CLASS (class);
+       filter_element_class->eq = filter_mail_identity_element_eq;
+       filter_element_class->xml_create = filter_mail_identity_element_xml_create;
+       filter_element_class->xml_encode = filter_mail_identity_element_xml_encode;
+       filter_element_class->xml_decode = filter_mail_identity_element_xml_decode;
+       filter_element_class->clone = filter_mail_identity_element_clone;
+       filter_element_class->get_widget = filter_mail_identity_element_get_widget;
+       filter_element_class->format_sexp = filter_mail_identity_element_format_sexp;
+       filter_element_class->describe = filter_mail_identity_element_describe;
+}
+
+static void
+em_filter_mail_identity_element_init (EMFilterMailIdentityElement *mail_identity)
+{
+       mail_identity->priv = em_filter_mail_identity_element_get_instance_private (mail_identity);
+}
+
+EFilterElement *
+em_filter_mail_identity_element_new (ESourceRegistry *registry)
+{
+       EMFilterMailIdentityElement *mail_identity;
+
+       g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+       mail_identity = g_object_new (EM_TYPE_FILTER_MAIL_IDENTITY_ELEMENT, NULL);
+       mail_identity->priv->registry = g_object_ref (registry);
+
+       return E_FILTER_ELEMENT (mail_identity);
+}
+
+ESourceRegistry *
+em_filter_mail_identity_element_get_registry (EMFilterMailIdentityElement *mail_identity)
+{
+       g_return_val_if_fail (EM_IS_FILTER_MAIL_IDENTITY_ELEMENT (mail_identity), NULL);
+
+       return mail_identity->priv->registry;
+}
diff --git a/src/mail/em-filter-mail-identity-element.h b/src/mail/em-filter-mail-identity-element.h
new file mode 100644
index 0000000000..19d1f4a38a
--- /dev/null
+++ b/src/mail/em-filter-mail-identity-element.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef EM_FILTER_MAIL_IDENTITY_ELEMENT_H
+#define EM_FILTER_MAIL_IDENTITY_ELEMENT_H
+
+#include <libedataserver/libedataserver.h>
+#include <e-util/e-util.h>
+
+/* Standard GObject macros */
+#define EM_TYPE_FILTER_MAIL_IDENTITY_ELEMENT \
+       (em_filter_mail_identity_element_get_type ())
+#define EM_FILTER_MAIL_IDENTITY_ELEMENT(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), EM_TYPE_FILTER_MAIL_IDENTITY_ELEMENT, EMFilterMailIdentityElement))
+#define EM_FILTER_MAIL_IDENTITY_ELEMENT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), EM_TYPE_FILTER_MAIL_IDENTITY_ELEMENT, EMFilterMailIdentityElementClass))
+#define EM_IS_FILTER_MAIL_IDENTITY_ELEMENT(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), EM_TYPE_FILTER_MAIL_IDENTITY_ELEMENT))
+#define EM_IS_FILTER_MAIL_IDENTITY_ELEMENT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), EM_TYPE_FILTER_MAIL_IDENTITY_ELEMENT))
+#define EM_FILTER_MAIL_IDENTITY_ELEMENT_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), EM_TYPE_FILTER_MAIL_IDENTITY_ELEMENT, EMFilterMailIdentityElementClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMFilterMailIdentityElement EMFilterMailIdentityElement;
+typedef struct _EMFilterMailIdentityElementClass EMFilterMailIdentityElementClass;
+typedef struct _EMFilterMailIdentityElementPrivate EMFilterMailIdentityElementPrivate;
+
+struct _EMFilterMailIdentityElement {
+       EFilterElement parent;
+       EMFilterMailIdentityElementPrivate *priv;
+};
+
+struct _EMFilterMailIdentityElementClass {
+       EFilterElementClass parent_class;
+};
+
+GType          em_filter_mail_identity_element_get_type        (void) G_GNUC_CONST;
+EFilterElement *em_filter_mail_identity_element_new            (ESourceRegistry *registry);
+ESourceRegistry *
+               em_filter_mail_identity_element_get_registry    (EMFilterMailIdentityElement *mail_identity);
+
+G_END_DECLS
+
+#endif /* EM_FILTER_MAIL_IDENTITY_ELEMENT_H */
diff --git a/src/mail/filtertypes.xml.in b/src/mail/filtertypes.xml.in
index d963bc65af..c5cec3f601 100644
--- a/src/mail/filtertypes.xml.in
+++ b/src/mail/filtertypes.xml.in
@@ -806,8 +806,12 @@
 
  <part name="forward">
   <_title>Forward to</_title>
-  <code>(forward-to ${address})</code>
+  <code>(forward-to ${address} ${from})</code>
   <input type="address" name="address" allow-empty="false"/>
+  <input type="label" name="with">
+    <_title>with</_title>
+  </input>
+  <input type="mail-identity" name="from"/>
  </part>
 </actionset>
 </filterdescription>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]