[evolution] I#84 - Filter configuration needs a humanly readable dump



commit 97995de1571917981366477bc28a9e8d99801b1c
Author: Milan Crha <mcrha redhat com>
Date:   Thu Sep 20 18:56:30 2018 +0200

    I#84 - Filter configuration needs a humanly readable dump
    
    Closes https://gitlab.gnome.org/GNOME/evolution/issues/84

 src/e-util/e-filter-code.c                     |  15 ++
 src/e-util/e-filter-color.c                    |  24 +++
 src/e-util/e-filter-datespec.c                 |  50 +++--
 src/e-util/e-filter-element.c                  |  28 ++-
 src/e-util/e-filter-element.h                  |  10 +
 src/e-util/e-filter-file.c                     |  12 ++
 src/e-util/e-filter-input.c                    |  27 +++
 src/e-util/e-filter-int.c                      |  13 ++
 src/e-util/e-filter-option.c                   |  11 ++
 src/e-util/e-filter-part.c                     |  19 ++
 src/e-util/e-filter-part.h                     |   2 +
 src/libemail-engine/em-filter-folder-element.c |  73 ++++++-
 src/libemail-engine/em-filter-folder-element.h |   4 +
 src/mail/em-filter-editor-folder-element.c     |  12 ++
 src/mail/em-filter-editor.c                    | 259 ++++++++++++++++++++++++-
 src/mail/em-filter-source-element.c            |  29 +++
 16 files changed, 569 insertions(+), 19 deletions(-)
---
diff --git a/src/e-util/e-filter-code.c b/src/e-util/e-filter-code.c
index 5fd6779749..0df165fd6d 100644
--- a/src/e-util/e-filter-code.c
+++ b/src/e-util/e-filter-code.c
@@ -60,6 +60,20 @@ filter_code_format_sexp (EFilterElement *element,
 {
 }
 
+static void
+filter_code_describe (EFilterElement *element,
+                     GString *out)
+{
+       EFilterInput *fi = (EFilterInput *) element;
+       GList *link;
+
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_START);
+       for (link = fi->values; link; link = g_list_next (link)) {
+               g_string_append (out, (const gchar *) link->data);
+       }
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_END);
+}
+
 static void
 e_filter_code_class_init (EFilterCodeClass *class)
 {
@@ -68,6 +82,7 @@ e_filter_code_class_init (EFilterCodeClass *class)
        filter_element_class = E_FILTER_ELEMENT_CLASS (class);
        filter_element_class->build_code = filter_code_build_code;
        filter_element_class->format_sexp = filter_code_format_sexp;
+       filter_element_class->describe = filter_code_describe;
 }
 
 static void
diff --git a/src/e-util/e-filter-color.c b/src/e-util/e-filter-color.c
index 24f8e3cf20..5562ae5f5c 100644
--- a/src/e-util/e-filter-color.c
+++ b/src/e-util/e-filter-color.c
@@ -129,6 +129,29 @@ filter_color_format_sexp (EFilterElement *element,
        camel_sexp_encode_string (out, spec);
 }
 
+static void
+filter_color_describe (EFilterElement *element,
+                      GString *out)
+{
+       EFilterColor *fc = E_FILTER_COLOR (element);
+       gchar spec[16];
+
+       #define cnvrt(x) ((255 * (x) / 65535) & 0xFF)
+
+       g_snprintf (
+               spec, sizeof (spec), "#%02x%02x%02x",
+               cnvrt (fc->color.red), cnvrt (fc->color.green), cnvrt (fc->color.blue));
+
+       #undef cnvrt
+
+       g_string_append_c (out, '[');
+       g_string_append (out, spec);
+       g_string_append (out, "] ");
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_COLOR_START);
+       g_string_append (out, spec);
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_COLOR_END);
+}
+
 static void
 e_filter_color_class_init (EFilterColorClass *class)
 {
@@ -140,6 +163,7 @@ e_filter_color_class_init (EFilterColorClass *class)
        filter_element_class->xml_decode = filter_color_xml_decode;
        filter_element_class->get_widget = filter_color_get_widget;
        filter_element_class->format_sexp = filter_color_format_sexp;
+       filter_element_class->describe = filter_color_describe;
 }
 
 static void
diff --git a/src/e-util/e-filter-datespec.c b/src/e-util/e-filter-datespec.c
index a975dca1a5..a538c94701 100644
--- a/src/e-util/e-filter-datespec.c
+++ b/src/e-util/e-filter-datespec.c
@@ -115,52 +115,63 @@ get_best_span (time_t val)
        return 0;
 }
 
-/* sets button label */
 static void
-set_button (EFilterDatespec *fds)
+describe_to_buffer (EFilterDatespec *fds,
+                   gchar *buf,
+                   gint buf_size,
+                   gboolean with_fallback)
 {
-       gchar buf[128];
-       gchar *label = buf;
-
        switch (fds->type) {
        case FDST_UNKNOWN:
-               label = _("<click here to select a date>");
+               if (with_fallback)
+                       g_snprintf (buf, buf_size, _("<click here to select a date>"));
+               else
+                       g_snprintf (buf, buf_size, "%s", "");
                break;
        case FDST_NOW:
-               label = _("now");
+               g_snprintf (buf, buf_size, _("now"));
                break;
        case FDST_SPECIFIED: {
                struct tm tm;
 
                localtime_r (&fds->value, &tm);
                /* strftime for date filter display, only needs to show a day date (i.e. no time) */
-               strftime (buf, sizeof (buf), _("%d-%b-%Y"), &tm);
+               strftime (buf, buf_size, _("%d-%b-%Y"), &tm);
                break; }
        case FDST_X_AGO:
                if (fds->value == 0)
-                       label = _("now");
+                       g_snprintf (buf, buf_size, _("now"));
                else {
                        gint span, count;
 
                        span = get_best_span (fds->value);
                        count = fds->value / timespans[span].seconds;
-                       sprintf (buf, ngettext (timespans[span].past_singular, timespans[span].past_plural, 
count), count);
+                       g_snprintf (buf, buf_size, ngettext (timespans[span].past_singular, 
timespans[span].past_plural, count), count);
                }
                break;
        case FDST_X_FUTURE:
                if (fds->value == 0)
-                       label = _("now");
+                       g_snprintf (buf, buf_size, _("now"));
                else {
                        gint span, count;
 
                        span = get_best_span (fds->value);
                        count = fds->value / timespans[span].seconds;
-                       sprintf (buf, ngettext (timespans[span].future_singular, 
timespans[span].future_plural, count), count);
+                       g_snprintf (buf, buf_size, ngettext (timespans[span].future_singular, 
timespans[span].future_plural, count), count);
                }
                break;
        }
+}
+
+/* sets button label */
+static void
+set_button (EFilterDatespec *fds)
+{
+       gchar buf[128];
+
+       describe_to_buffer (fds, buf, sizeof (buf), TRUE);
 
-       gtk_label_set_text ((GtkLabel *) fds->priv->label_button, label);
+       gtk_label_set_text ((GtkLabel *) fds->priv->label_button, buf);
 }
 
 static void
@@ -473,6 +484,18 @@ filter_datespec_format_sexp (EFilterElement *element,
        }
 }
 
+static void
+filter_datespec_describe (EFilterElement *element,
+                         GString *out)
+{
+       EFilterDatespec *fds = E_FILTER_DATESPEC (element);
+       gchar buf[128];
+
+       describe_to_buffer (fds, buf, sizeof (buf), FALSE);
+
+       g_string_append (out, buf);
+}
+
 static void
 e_filter_datespec_class_init (EFilterDatespecClass *class)
 {
@@ -487,6 +510,7 @@ e_filter_datespec_class_init (EFilterDatespecClass *class)
        filter_element_class->xml_decode = filter_datespec_xml_decode;
        filter_element_class->get_widget = filter_datespec_get_widget;
        filter_element_class->format_sexp = filter_datespec_format_sexp;
+       filter_element_class->describe = filter_datespec_describe;
 }
 
 static void
diff --git a/src/e-util/e-filter-element.c b/src/e-util/e-filter-element.c
index 05532118d0..416abff8be 100644
--- a/src/e-util/e-filter-element.c
+++ b/src/e-util/e-filter-element.c
@@ -355,13 +355,12 @@ e_filter_element_clone (EFilterElement *element)
 }
 
 /**
- * filter_element_get_widget:
+ * e_filter_element_get_widget:
  * @fe: filter element
- * @node: xml node
  *
  * Create a widget to represent this element.
  *
- * Return value:
+ * Returns: (transfer full): a new GtkWidget
  **/
 GtkWidget *
 e_filter_element_get_widget (EFilterElement *element)
@@ -453,3 +452,26 @@ e_filter_element_copy_value (EFilterElement *dst_element,
 
        class->copy_value (dst_element, src_element);
 }
+
+/**
+ * e_filter_element_describe:
+ * @fe: filter element
+ * @out: a #GString to add the description to
+ *
+ * Describes the @element in a human-readable way.
+ **/
+void
+e_filter_element_describe (EFilterElement *element,
+                          GString *out)
+{
+       EFilterElementClass *klass;
+
+       g_return_if_fail (E_IS_FILTER_ELEMENT (element));
+       g_return_if_fail (out != NULL);
+
+       klass = E_FILTER_ELEMENT_GET_CLASS (element);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->describe != NULL);
+
+       klass->describe (element, out);
+}
diff --git a/src/e-util/e-filter-element.h b/src/e-util/e-filter-element.h
index 881a92c9c9..e3e39d816a 100644
--- a/src/e-util/e-filter-element.h
+++ b/src/e-util/e-filter-element.h
@@ -52,6 +52,12 @@
        (G_TYPE_INSTANCE_GET_CLASS \
        ((obj), E_TYPE_FILTER_ELEMENT, EFilterElementClass))
 
+/* Keep the values in sync with their escaped values ("&#1;" and so on) in em-filter-editor.c  */
+#define E_FILTER_ELEMENT_DESCIPTION_VALUE_START        '\1'
+#define E_FILTER_ELEMENT_DESCIPTION_VALUE_END  '\2'
+#define E_FILTER_ELEMENT_DESCIPTION_COLOR_START        '\3'
+#define E_FILTER_ELEMENT_DESCIPTION_COLOR_END  '\4'
+
 G_BEGIN_DECLS
 
 struct _EFilterPart;
@@ -92,6 +98,8 @@ struct _EFilterElementClass {
                                                 struct _EFilterPart *part);
        void            (*format_sexp)          (EFilterElement *element,
                                                 GString *out);
+       void            (*describe)             (EFilterElement *element,
+                                                GString *out);
 };
 
 GType          e_filter_element_get_type       (void) G_GNUC_CONST;
@@ -116,6 +124,8 @@ void                e_filter_element_build_code     (EFilterElement *element,
                                                 struct _EFilterPart *part);
 void           e_filter_element_format_sexp    (EFilterElement *element,
                                                 GString *out);
+void           e_filter_element_describe       (EFilterElement *element,
+                                                GString *out);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-filter-file.c b/src/e-util/e-filter-file.c
index 9cdc2b0ff2..536ddbdcf5 100644
--- a/src/e-util/e-filter-file.c
+++ b/src/e-util/e-filter-file.c
@@ -200,6 +200,17 @@ filter_file_format_sexp (EFilterElement *element,
        camel_sexp_encode_string (out, file->path);
 }
 
+static void
+filter_file_describe (EFilterElement *element,
+                     GString *out)
+{
+       EFilterFile *file = E_FILTER_FILE (element);
+
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_START);
+       g_string_append (out, file->path);
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_END);
+}
+
 static void
 e_filter_file_class_init (EFilterFileClass *class)
 {
@@ -216,6 +227,7 @@ e_filter_file_class_init (EFilterFileClass *class)
        filter_element_class->xml_decode = filter_file_xml_decode;
        filter_element_class->get_widget = filter_file_get_widget;
        filter_element_class->format_sexp = filter_file_format_sexp;
+       filter_element_class->describe = filter_file_describe;
 }
 
 static void
diff --git a/src/e-util/e-filter-input.c b/src/e-util/e-filter-input.c
index 9dbc8560f8..33769beff4 100644
--- a/src/e-util/e-filter-input.c
+++ b/src/e-util/e-filter-input.c
@@ -367,6 +367,32 @@ filter_input_build_code (EFilterElement *element,
        g_module_close (module);
 }
 
+static void
+filter_input_describe (EFilterElement *element,
+                      GString *out)
+{
+       EFilterInput *input = E_FILTER_INPUT (element);
+       GList *link;
+       gboolean added = FALSE;
+
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_START);
+
+       for (link = input->values; link; link = g_list_next (link)) {
+               const gchar *value = link->data;
+
+               if (value && *value) {
+                       if (added)
+                               g_string_append_c (out, ' ');
+                       else
+                               added = TRUE;
+
+                       g_string_append (out, value);
+               }
+       }
+
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_END);
+}
+
 static void
 e_filter_input_class_init (EFilterInputClass *class)
 {
@@ -386,6 +412,7 @@ e_filter_input_class_init (EFilterInputClass *class)
        filter_element_class->get_widget = filter_input_get_widget;
        filter_element_class->format_sexp = filter_input_format_sexp;
        filter_element_class->build_code = filter_input_build_code;
+       filter_element_class->describe = filter_input_describe;
 }
 
 static void
diff --git a/src/e-util/e-filter-int.c b/src/e-util/e-filter-int.c
index 9127079a57..df9f3be062 100644
--- a/src/e-util/e-filter-int.c
+++ b/src/e-util/e-filter-int.c
@@ -170,6 +170,18 @@ filter_int_format_sexp (EFilterElement *element,
                g_string_append_printf (out, "%d", filter_int->val);
 }
 
+static void
+filter_int_describe (EFilterElement *element,
+                    GString *out)
+{
+       EFilterInt *filter_int = E_FILTER_INT (element);
+
+       g_string_append_printf (out, "%c%d%c",
+               E_FILTER_ELEMENT_DESCIPTION_VALUE_START,
+               filter_int->val,
+               E_FILTER_ELEMENT_DESCIPTION_VALUE_END);
+}
+
 static void
 e_filter_int_class_init (EFilterIntClass *class)
 {
@@ -186,6 +198,7 @@ e_filter_int_class_init (EFilterIntClass *class)
        filter_element_class->xml_decode = filter_int_xml_decode;
        filter_element_class->get_widget = filter_int_get_widget;
        filter_element_class->format_sexp = filter_int_format_sexp;
+       filter_element_class->describe = filter_int_describe;
 }
 
 static void
diff --git a/src/e-util/e-filter-option.c b/src/e-util/e-filter-option.c
index cd4ca4e771..949cab50ec 100644
--- a/src/e-util/e-filter-option.c
+++ b/src/e-util/e-filter-option.c
@@ -465,6 +465,16 @@ filter_option_format_sexp (EFilterElement *element,
                camel_sexp_encode_string (out, option->current->value);
 }
 
+static void
+filter_option_describe (EFilterElement *element,
+                       GString *out)
+{
+       EFilterOption *option = E_FILTER_OPTION (element);
+
+       if (option->current)
+               g_string_append (out, _(option->current->title));
+}
+
 static void
 e_filter_option_class_init (EFilterOptionClass *class)
 {
@@ -483,6 +493,7 @@ e_filter_option_class_init (EFilterOptionClass *class)
        filter_element_class->get_widget = filter_option_get_widget;
        filter_element_class->build_code = filter_option_build_code;
        filter_element_class->format_sexp = filter_option_format_sexp;
+       filter_element_class->describe = filter_option_describe;
 }
 
 static void
diff --git a/src/e-util/e-filter-part.c b/src/e-util/e-filter-part.c
index 4fb2eb90be..4dc5065944 100644
--- a/src/e-util/e-filter-part.c
+++ b/src/e-util/e-filter-part.c
@@ -353,6 +353,25 @@ e_filter_part_get_widget (EFilterPart *part)
        return hbox;
 }
 
+void
+e_filter_part_describe (EFilterPart *part,
+                       GString *out)
+{
+       GList *link;
+
+       g_return_if_fail (E_IS_FILTER_PART (part));
+       g_return_if_fail (out != NULL);
+
+       g_string_append (out, _(part->title));
+
+       for (link = part->elements; link != NULL; link = g_list_next (link)) {
+               EFilterElement *element = link->data;
+
+               g_string_append_c (out, ' ');
+               e_filter_element_describe (element, out);
+       }
+}
+
 /**
  * e_filter_part_build_code:
  * @part:
diff --git a/src/e-util/e-filter-part.h b/src/e-util/e-filter-part.h
index 4eed142083..071986319e 100644
--- a/src/e-util/e-filter-part.h
+++ b/src/e-util/e-filter-part.h
@@ -93,6 +93,8 @@ void          e_filter_part_copy_values       (EFilterPart *dst_part,
 EFilterElement *e_filter_part_find_element     (EFilterPart *part,
                                                 const gchar *name);
 GtkWidget *    e_filter_part_get_widget        (EFilterPart *part);
+void           e_filter_part_describe          (EFilterPart *part,
+                                                GString *out);
 void           e_filter_part_build_code        (EFilterPart *part,
                                                 GString *out);
 void           e_filter_part_expand_code       (EFilterPart *part,
diff --git a/src/libemail-engine/em-filter-folder-element.c b/src/libemail-engine/em-filter-folder-element.c
index fec0e0ba3d..d2e355e84f 100644
--- a/src/libemail-engine/em-filter-folder-element.c
+++ b/src/libemail-engine/em-filter-folder-element.c
@@ -23,13 +23,14 @@
 
 #include "evolution-config.h"
 
-#include "em-filter-folder-element.h"
-
 #include <string.h>
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 
+#include "e-mail-folder-utils.h"
+#include "em-filter-folder-element.h"
+
 #define EM_FILTER_FOLDER_ELEMENT_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), EM_TYPE_FILTER_FOLDER_ELEMENT, EMFilterFolderElementPrivate))
@@ -167,6 +168,21 @@ filter_folder_element_copy_value (EFilterElement *de,
                em_filter_folder_element_parent_class)->copy_value (de, se);
        }
 }
+
+static void
+filter_folder_element_describe (EFilterElement *fe,
+                               GString *out)
+{
+       EMFilterFolderElement *ff = (EMFilterFolderElement *) fe;
+
+       if (!ff->priv->uri)
+               return;
+
+       /* This might not be usually used, do some special processing
+          for it and call em_filter_folder_element_describe() instead */
+       g_string_append (out, ff->priv->uri);
+}
+
 static void
 em_filter_folder_element_class_init (EMFilterFolderElementClass *class)
 {
@@ -187,6 +203,7 @@ em_filter_folder_element_class_init (EMFilterFolderElementClass *class)
        filter_element_class->build_code = filter_folder_element_build_code;
        filter_element_class->format_sexp = filter_folder_element_format_sexp;
        filter_element_class->copy_value = filter_folder_element_copy_value;
+       filter_element_class->describe = filter_folder_element_describe;
 }
 
 static void
@@ -221,3 +238,55 @@ em_filter_folder_element_set_uri (EMFilterFolderElement *element,
        element->priv->uri = g_strdup (uri);
 }
 
+void
+em_filter_folder_element_describe (EMFilterFolderElement *element,
+                                  CamelSession *session,
+                                  GString *out)
+{
+       g_return_if_fail (EM_IS_FILTER_FOLDER_ELEMENT (element));
+       g_return_if_fail (CAMEL_IS_SESSION (session));
+       g_return_if_fail (out != NULL);
+
+       if (element->priv->uri) {
+               gchar *full_name = NULL;
+               const gchar *use_name = element->priv->uri;
+               CamelStore *store = NULL;
+               gchar *folder_name = NULL;
+
+               if (e_mail_folder_uri_parse (session, element->priv->uri, &store, &folder_name, NULL)) {
+                       CamelFolder *folder;
+
+                       folder = camel_store_get_folder_sync (store, folder_name, 0, NULL, NULL);
+                       if (folder) {
+                               const gchar *service_display_name;
+
+                               service_display_name = camel_service_get_display_name (CAMEL_SERVICE (store));
+
+                               if (CAMEL_IS_VEE_FOLDER (folder) && (
+                                   g_strcmp0 (folder_name, CAMEL_VTRASH_NAME) == 0 ||
+                                   g_strcmp0 (folder_name, CAMEL_VJUNK_NAME) == 0)) {
+                                       full_name = g_strdup_printf ("%s/%s", service_display_name, 
camel_folder_get_display_name (folder));
+                               } else {
+                                       full_name = g_strdup_printf ("%s/%s", service_display_name, 
folder_name);
+                               }
+
+                               g_clear_object (&folder);
+                       }
+
+                       if (!full_name)
+                               full_name = g_strdup_printf ("%s/%s", camel_service_get_display_name 
(CAMEL_SERVICE (store)), folder_name);
+
+                       if (full_name)
+                               use_name = full_name;
+
+                       g_clear_object (&store);
+                       g_free (folder_name);
+               }
+
+               g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_START);
+               g_string_append (out, use_name);
+               g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_END);
+
+               g_free (full_name);
+       }
+}
diff --git a/src/libemail-engine/em-filter-folder-element.h b/src/libemail-engine/em-filter-folder-element.h
index 9e24242e38..5350f4ee66 100644
--- a/src/libemail-engine/em-filter-folder-element.h
+++ b/src/libemail-engine/em-filter-folder-element.h
@@ -71,6 +71,10 @@ const gchar *        em_filter_folder_element_get_uri
 void           em_filter_folder_element_set_uri
                                                (EMFilterFolderElement *element,
                                                 const gchar *uri);
+void           em_filter_folder_element_describe
+                                               (EMFilterFolderElement *element,
+                                                CamelSession *session,
+                                                GString *out);
 
 G_END_DECLS
 
diff --git a/src/mail/em-filter-editor-folder-element.c b/src/mail/em-filter-editor-folder-element.c
index 246210c413..5d7a2f84fc 100644
--- a/src/mail/em-filter-editor-folder-element.c
+++ b/src/mail/em-filter-editor-folder-element.c
@@ -163,6 +163,17 @@ filter_editor_folder_element_get_widget (EFilterElement *fe)
        return button;
 }
 
+static void
+filter_editor_folder_element_describe (EFilterElement *fe,
+                                      GString *out)
+{
+       EMFilterEditorFolderElement *ff = (EMFilterEditorFolderElement *) fe;
+       EMailSession *mail_session;
+
+       mail_session = em_filter_editor_folder_element_get_session (ff);
+       em_filter_folder_element_describe (EM_FILTER_FOLDER_ELEMENT (ff), CAMEL_SESSION (mail_session), out);
+}
+
 static void
 em_filter_editor_folder_element_class_init (EMFilterEditorFolderElementClass *class)
 {
@@ -178,6 +189,7 @@ em_filter_editor_folder_element_class_init (EMFilterEditorFolderElementClass *cl
 
        filter_element_class = E_FILTER_ELEMENT_CLASS (class);
        filter_element_class->get_widget = filter_editor_folder_element_get_widget;
+       filter_element_class->describe = filter_editor_folder_element_describe;
 
        g_object_class_install_property (
                object_class,
diff --git a/src/mail/em-filter-editor.c b/src/mail/em-filter-editor.c
index e20983b371..8e1653dc7e 100644
--- a/src/mail/em-filter-editor.c
+++ b/src/mail/em-filter-editor.c
@@ -25,6 +25,7 @@
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 
+#include "shell/e-shell.h"
 #include "e-util/e-util.h"
 #include "e-util/e-util-private.h"
 
@@ -33,6 +34,242 @@
 
 G_DEFINE_TYPE (EMFilterEditor, em_filter_editor, E_TYPE_RULE_EDITOR)
 
+static void
+emfe_show_html (GtkWindow *parent,
+               const gchar *html)
+{
+       GtkWidget *dialog, *widget, *container;
+
+       dialog = gtk_dialog_new_with_buttons (_("Description of Filters"), parent,
+               GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+               _("_Close"), GTK_RESPONSE_CLOSE,
+               NULL);
+       gtk_window_set_default_size (GTK_WINDOW (dialog), 480, 410);
+       gtk_window_set_position (GTK_WINDOW (dialog), parent ? GTK_WIN_POS_CENTER_ON_PARENT : 
GTK_WIN_POS_CENTER);
+
+       container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_widget_set_vexpand (widget, TRUE);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
+       gtk_widget_show (widget);
+
+       container = widget;
+
+       widget = e_web_view_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               "editable", FALSE,
+               NULL);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+
+       e_web_view_load_string (E_WEB_VIEW (widget), html);
+
+       gtk_dialog_run (GTK_DIALOG (dialog));
+
+       gtk_widget_destroy (dialog);
+}
+
+/*  Some parts require special processing, which cannot access session from their place */
+static void
+emfe_describe_part (EFilterPart *part,
+                   GString *out,
+                   CamelSession *session)
+{
+       GList *link;
+
+       g_return_if_fail (E_IS_FILTER_PART (part));
+       g_return_if_fail (out != NULL);
+
+       g_string_append (out, _(part->title));
+
+       for (link = part->elements; link != NULL; link = g_list_next (link)) {
+               EFilterElement *element = link->data;
+
+               g_string_append_c (out, ' ');
+
+               if (EM_IS_FILTER_FOLDER_ELEMENT (element)) {
+                       em_filter_folder_element_describe (EM_FILTER_FOLDER_ELEMENT (element), session, out);
+               } else {
+                       e_filter_element_describe (element, out);
+               }
+       }
+}
+
+static void
+emfe_describe_filters_cb (GtkWidget *button,
+                         gpointer user_data)
+{
+       EShell *shell;
+       EShellBackend *shell_backend;
+       ESourceRegistry *registry;
+       EMFilterEditor *fe = user_data;
+       ERuleContext *context;
+       EFilterRule *rule = NULL;
+       CamelSession *session = NULL;
+       GString *description;
+       gchar *html;
+       const gchar *source;
+
+       g_return_if_fail (EM_IS_FILTER_EDITOR (fe));
+
+       context = E_RULE_EDITOR (fe)->context;
+       source = E_RULE_EDITOR (fe)->source;
+
+       shell = e_shell_get_default ();
+       shell_backend = e_shell_get_backend_by_name (shell, "mail");
+       if (shell_backend)
+               g_object_get (shell_backend, "session", &session, NULL);
+
+       registry = e_shell_get_registry (shell);
+
+       description = g_string_sized_new (2048);
+
+       while (rule = e_rule_context_next_rule (context, rule, source), rule) {
+               GList *link;
+               gchar *account, *rule_name;
+
+               account = g_strdup (em_filter_rule_get_account_uid (EM_FILTER_RULE (rule)));
+               if (account && *account) {
+                       ESource *source;
+
+                       source = e_source_registry_ref_source (registry, account);
+                       if (source) {
+                               g_free (account);
+                               account = e_source_dup_display_name (source);
+                               g_object_unref (source);
+                       }
+               } else {
+                       g_free (account);
+                       account = NULL;
+               }
+
+               if (description->len)
+                       g_string_append_c (description, '\n');
+
+               rule_name = g_strdup_printf ("%c%s%c",
+                       E_FILTER_ELEMENT_DESCIPTION_VALUE_START,
+                       rule->name,
+                       E_FILTER_ELEMENT_DESCIPTION_VALUE_END);
+
+               if (account) {
+                       /* Translators: The first '%s' is replaced with the rule name;
+                          the second '%s' with 'enabled' or 'disabled' word;
+                          the third '%s' is replaced with the account name */
+                       g_string_append_printf (description, _("%s (%s, for account %s)"), rule_name, 
rule->enabled ? _("enabled") : _("disabled"), account);
+               } else {
+                       /* Translators: The first '%s' is replaced with the rule name;
+                          the second '%s' with 'enabled' or 'disabled' word */
+                       g_string_append_printf (description, _("%s (%s, for any account)"), rule_name, 
rule->enabled ? _("enabled") : _("disabled"));
+               }
+
+               g_string_append (description, "\n");
+
+               g_free (rule_name);
+               g_free (account);
+
+               g_string_append (description, "   ");
+
+               switch (rule->grouping) {
+               case E_FILTER_GROUP_ALL:
+                       g_string_append (description, _("If all the following conditions are met"));
+                       break;
+               case E_FILTER_GROUP_ANY:
+                       g_string_append (description, _("If any of the following conditions are met"));
+                       break;
+               }
+               g_string_append_c (description, '\n');
+
+               for (link = rule->parts; link; link = g_list_next (link)) {
+                       EFilterPart *part = link->data;
+
+                       if (!part)
+                               continue;
+
+                       g_string_append (description, "      ");
+                       emfe_describe_part (part, description, session);
+                       g_string_append_c (description, '\n');
+               }
+
+               g_string_append (description, "   ");
+               g_string_append (description, _("Then"));
+               g_string_append_c (description, '\n');
+
+               for (link = em_filter_rule_get_actions (EM_FILTER_RULE (rule)); link; link = g_list_next 
(link)) {
+                       EFilterPart *part = link->data;
+
+                       if (!part)
+                               continue;
+
+                       g_string_append (description, "      ");
+                       emfe_describe_part (part, description, session);
+                       g_string_append_c (description, '\n');
+               }
+       }
+
+       html = camel_text_to_html (description->str, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | 
CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES, 0);
+       g_string_free (description, TRUE);
+
+       description = e_str_replace_string (html, "&#1;", "<b>"); /* E_FILTER_ELEMENT_DESCIPTION_VALUE_START 
*/
+       g_string_prepend (description, "<div style=\"white-space: nowrap;\">");
+       g_string_append (description, "</div>");
+       g_free (html);
+       html = g_string_free (description, FALSE);
+
+       #define replace_in_html(_find, _replace) \
+               description = e_str_replace_string (html, _find, _replace); \
+               g_free (html); \
+               html = g_string_free (description, FALSE);
+
+       replace_in_html ("&#2;", "</b>"); /* E_FILTER_ELEMENT_DESCIPTION_VALUE_END */
+
+       if (strstr (html, "&#3;") && strstr (html, "&#4;")) {
+               replace_in_html ("&#3;", "<span style=\"background-color:"); /* 
E_FILTER_ELEMENT_DESCIPTION_COLOR_START */
+               replace_in_html ("&#4;", 
";\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>"); /* 
E_FILTER_ELEMENT_DESCIPTION_COLOR_END */
+       }
+
+       #undef replace_in_html
+
+       emfe_show_html (GTK_WINDOW (fe), html);
+
+       g_clear_object (&session);
+       g_free (html);
+}
+
+static void
+emfe_update_describe_sensitive (GtkTreeModel *model,
+                               GtkWidget *button)
+{
+       GtkTreeIter iter;
+
+       gtk_widget_set_sensitive (button, gtk_tree_model_get_iter_first (model, &iter));
+}
+
+static void
+emfe_rules_model_row_inserted_cb (GtkTreeModel *tree_model,
+                                 GtkTreePath *path,
+                                 GtkTreeIter *iter,
+                                 gpointer user_data)
+{
+       emfe_update_describe_sensitive (tree_model, user_data);
+}
+
+static void
+emfe_rules_model_row_deleted_cb (GtkTreeModel *tree_model,
+                                GtkTreePath *path,
+                                gpointer user_data)
+{
+       emfe_update_describe_sensitive (tree_model, user_data);
+}
+
 static EFilterRule *
 filter_editor_create_rule (ERuleEditor *rule_editor)
 {
@@ -131,9 +368,10 @@ em_filter_editor_construct (EMFilterEditor *fe,
                             GtkBuilder *builder,
                             const EMFilterSource *source_names)
 {
-       GtkWidget *combobox;
+       GtkWidget *combobox, *action_area, *button, *tree_view;
        gint i;
        GtkTreeViewColumn *column;
+       GtkTreeModel *model;
        GtkTreeIter iter;
        GtkListStore *store;
        GSList *sources = NULL;
@@ -165,4 +403,23 @@ em_filter_editor_construct (EMFilterEditor *fe,
        /* Show the Enabled column, we support it here */
        column = gtk_tree_view_get_column (GTK_TREE_VIEW (E_RULE_EDITOR (fe)->list), 0);
        gtk_tree_view_column_set_visible (column, TRUE);
+
+       action_area = gtk_dialog_get_action_area (GTK_DIALOG (fe));
+       button = gtk_button_new_with_mnemonic (_("De_scribe Filters…"));
+       gtk_widget_show (button);
+       gtk_box_pack_start (GTK_BOX (action_area), button, FALSE, TRUE, 0);
+       gtk_box_reorder_child (GTK_BOX (action_area), button, 0);
+
+       if (GTK_IS_BUTTON_BOX (action_area))
+               gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (action_area), button, TRUE);
+
+       g_signal_connect (button, "clicked", G_CALLBACK (emfe_describe_filters_cb), fe);
+
+       tree_view = e_builder_get_widget (builder, "rule_tree_view");
+       model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+
+       g_signal_connect_object (model, "row-inserted", G_CALLBACK (emfe_rules_model_row_inserted_cb), 
button, 0);
+       g_signal_connect_object (model, "row-deleted", G_CALLBACK (emfe_rules_model_row_deleted_cb), button, 
0);
+
+       emfe_update_describe_sensitive (model, button);
 }
diff --git a/src/mail/em-filter-source-element.c b/src/mail/em-filter-source-element.c
index dc32d538c5..04ab2f3a98 100644
--- a/src/mail/em-filter-source-element.c
+++ b/src/mail/em-filter-source-element.c
@@ -403,6 +403,34 @@ filter_source_element_format_sexp (EFilterElement *fe,
        camel_sexp_encode_string (out, fs->priv->active_id);
 }
 
+static void
+filter_source_element_describe (EFilterElement *fe,
+                               GString *out)
+{
+       EMFilterSourceElement *fs = (EMFilterSourceElement *) fe;
+       EMailSession *mail_session;
+       ESourceRegistry *registry;
+       ESource *source;
+
+       if (!fs->priv->active_id)
+               return;
+
+       mail_session = em_filter_source_element_get_session (fs);
+       registry = e_mail_session_get_registry (mail_session);
+       source = e_source_registry_ref_source (registry, fs->priv->active_id);
+
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_START);
+
+       if (source) {
+               g_string_append (out, e_source_get_display_name (source));
+               g_object_unref (source);
+       } else {
+               g_string_append (out, fs->priv->active_id);
+       }
+
+       g_string_append_c (out, E_FILTER_ELEMENT_DESCIPTION_VALUE_END);
+}
+
 static void
 em_filter_source_element_class_init (EMFilterSourceElementClass *class)
 {
@@ -425,6 +453,7 @@ em_filter_source_element_class_init (EMFilterSourceElementClass *class)
        filter_element_class->get_widget = filter_source_element_get_widget;
        filter_element_class->build_code = filter_source_element_build_code;
        filter_element_class->format_sexp = filter_source_element_format_sexp;
+       filter_element_class->describe = filter_source_element_describe;
 
        g_object_class_install_property (
                object_class,


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