[evolution] Bug 550796 - Implement free form filter expression
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Bug 550796 - Implement free form filter expression
- Date: Tue, 9 Dec 2014 08:20:24 +0000 (UTC)
commit aab2dd62548d71372cd2946307e4e92dc19e2d12
Author: Milan Crha <mcrha redhat com>
Date: Tue Dec 9 09:19:20 2014 +0100
Bug 550796 - Implement free form filter expression
e-util/e-filter-input.c | 88 +++++-
e-util/e-filter-input.h | 1 +
mail/Makefile.am | 2 +
mail/e-mail-free-form-exp.c | 557 ++++++++++++++++++++++++++++++
mail/e-mail-free-form-exp.h | 33 ++
mail/filtertypes.xml.in | 7 +
mail/searchtypes.xml.in | 17 +
mail/vfoldertypes.xml.in | 7 +
modules/mail/e-mail-shell-view-actions.c | 7 +
modules/mail/e-mail-shell-view-actions.h | 2 +
modules/mail/e-mail-shell-view-private.h | 1 +
modules/mail/e-mail-shell-view.c | 2 +
po/POTFILES.in | 1 +
ui/evolution-mail.ui | 1 +
14 files changed, 724 insertions(+), 2 deletions(-)
---
diff --git a/e-util/e-filter-input.c b/e-util/e-filter-input.c
index 6dbcc7e..1bfa029 100644
--- a/e-util/e-filter-input.c
+++ b/e-util/e-filter-input.c
@@ -30,9 +30,11 @@
#include <gtk/gtk.h>
#include <glib/gi18n.h>
+#include <gmodule.h>
#include "e-alert.h"
#include "e-filter-input.h"
+#include "e-filter-part.h"
G_DEFINE_TYPE (
EFilterInput,
@@ -59,6 +61,7 @@ filter_input_finalize (GObject *object)
EFilterInput *input = E_FILTER_INPUT (object);
xmlFree (input->type);
+ g_free (input->code_gen_func);
g_list_foreach (input->values, (GFunc) g_free, NULL);
g_list_free (input->values);
@@ -164,6 +167,9 @@ filter_input_eq (EFilterElement *element_a,
if (link_a != NULL || link_b != NULL)
return FALSE;
+ if (g_strcmp0 (input_a->code_gen_func, input_b->code_gen_func) != 0)
+ return FALSE;
+
return input_a->allow_empty == input_b->allow_empty;
}
@@ -172,8 +178,12 @@ filter_input_xml_create (EFilterElement *element,
xmlNodePtr node)
{
EFilterInput *input = E_FILTER_INPUT (element);
+ xmlNodePtr n;
gchar *allow_empty;
+ g_free (input->code_gen_func);
+ input->code_gen_func = NULL;
+
/* Chain up to parent's method. */
E_FILTER_ELEMENT_CLASS (e_filter_input_parent_class)->xml_create (element, node);
@@ -181,13 +191,29 @@ filter_input_xml_create (EFilterElement *element,
input->allow_empty = !allow_empty || g_strcmp0 (allow_empty, "true") == 0;
xmlFree (allow_empty);
+
+ for (n = node->children; n; n = n->next) {
+ if (g_str_equal (n->name, "code")) {
+ xmlChar *func = xmlGetProp (n, (xmlChar *) "func");
+
+ if (func && *func) {
+ if (input->code_gen_func)
+ g_free (input->code_gen_func);
+ input->code_gen_func = g_strdup ((gchar *) func);
+ }
+
+ if (func)
+ xmlFree (func);
+ break;
+ }
+ }
}
static xmlNodePtr
filter_input_xml_encode (EFilterElement *element)
{
EFilterInput *input = E_FILTER_INPUT (element);
- xmlNodePtr value;
+ xmlNodePtr value, cur;
GList *link;
const gchar *type;
@@ -200,7 +226,6 @@ filter_input_xml_encode (EFilterElement *element)
for (link = input->values; link != NULL; link = g_list_next (link)) {
xmlChar *str = link->data;
- xmlNodePtr cur;
cur = xmlNewChild (value, NULL, (xmlChar *) type, NULL);
@@ -256,6 +281,34 @@ filter_input_xml_decode (EFilterElement *element,
return 0;
}
+static EFilterElement *
+filter_input_clone (EFilterElement *element)
+{
+ EFilterInput *input = E_FILTER_INPUT (element);
+ EFilterInput *clone_input;
+ EFilterElement *clone;
+ GList *link;
+
+ /* Chain up to parent's clone() method. */
+ clone = E_FILTER_ELEMENT_CLASS (e_filter_input_parent_class)->clone (element);
+
+ clone_input = E_FILTER_INPUT (clone);
+
+ if (clone_input->type)
+ xmlFree (clone_input->type);
+ clone_input->type = input->type ? (gchar *) xmlStrdup ((const xmlChar *) input->type) : NULL;
+ clone_input->allow_empty = input->allow_empty;
+ clone_input->code_gen_func = g_strdup (input->code_gen_func);
+
+ for (link = input->values; link != NULL; link = g_list_next (link)) {
+ clone_input->values = g_list_prepend (clone_input->values, g_strdup (link->data));
+ }
+
+ clone_input->values = g_list_reverse (clone_input->values);
+
+ return clone;
+}
+
static GtkWidget *
filter_input_get_widget (EFilterElement *element)
{
@@ -281,11 +334,39 @@ filter_input_format_sexp (EFilterElement *element,
EFilterInput *input = E_FILTER_INPUT (element);
GList *link;
+ if (input->code_gen_func)
+ return;
+
for (link = input->values; link != NULL; link = g_list_next (link))
camel_sexp_encode_string (out, link->data);
}
static void
+filter_input_build_code (EFilterElement *element,
+ GString *out,
+ EFilterPart *part)
+{
+ EFilterInput *input = E_FILTER_INPUT (element);
+ GModule *module;
+ void (*code_gen_func) (EFilterElement *element, GString *out, EFilterPart *part);
+
+ if (!input->code_gen_func)
+ return;
+
+ module = g_module_open (NULL, G_MODULE_BIND_LAZY);
+
+ if (g_module_symbol (module, input->code_gen_func, (gpointer) &code_gen_func)) {
+ code_gen_func (E_FILTER_ELEMENT (input), out, part);
+ } else {
+ g_warning (
+ "input dynamic code function '%s' not found",
+ input->code_gen_func);
+ }
+
+ g_module_close (module);
+}
+
+static void
e_filter_input_class_init (EFilterInputClass *class)
{
GObjectClass *object_class;
@@ -300,8 +381,10 @@ e_filter_input_class_init (EFilterInputClass *class)
filter_element_class->xml_create = filter_input_xml_create;
filter_element_class->xml_encode = filter_input_xml_encode;
filter_element_class->xml_decode = filter_input_xml_decode;
+ filter_element_class->clone = filter_input_clone;
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;
}
static void
@@ -309,6 +392,7 @@ e_filter_input_init (EFilterInput *input)
{
input->values = g_list_prepend (NULL, g_strdup (""));
input->allow_empty = TRUE;
+ input->code_gen_func = NULL;
}
/**
diff --git a/e-util/e-filter-input.h b/e-util/e-filter-input.h
index 456da41..ea0470b 100644
--- a/e-util/e-filter-input.h
+++ b/e-util/e-filter-input.h
@@ -61,6 +61,7 @@ struct _EFilterInput {
gchar *type; /* name of type */
GList *values; /* strings */
gboolean allow_empty; /* whether can have empty value */
+ gchar *code_gen_func; /* function name to build the 'code' */
};
struct _EFilterInputClass {
diff --git a/mail/Makefile.am b/mail/Makefile.am
index d0b7220..58cd98d 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -86,6 +86,7 @@ mailinclude_HEADERS = \
e-mail-enumtypes.h \
e-mail-folder-create-dialog.h \
e-mail-folder-pane.h \
+ e-mail-free-form-exp.h \
e-mail-junk-options.h \
e-mail-label-action.h \
e-mail-label-dialog.h \
@@ -163,6 +164,7 @@ libevolution_mail_la_SOURCES = \
e-mail-enumtypes.c \
e-mail-folder-create-dialog.c \
e-mail-folder-pane.c \
+ e-mail-free-form-exp.c \
e-mail-junk-options.c \
e-mail-label-action.c \
e-mail-label-dialog.c \
diff --git a/mail/e-mail-free-form-exp.c b/mail/e-mail-free-form-exp.c
new file mode 100644
index 0000000..fb7864b
--- /dev/null
+++ b/mail/e-mail-free-form-exp.c
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2014 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <string.h>
+
+#include <camel/camel.h>
+#include <e-util/e-util.h>
+#include <libedataserver/libedataserver.h>
+
+#include "e-mail-free-form-exp.h"
+
+static gchar *
+mail_ffe_build_header_sexp (const gchar *word,
+ const gchar *options,
+ const gchar * const *header_names)
+{
+ GString *sexp = NULL, *encoded_word;
+ const gchar *compare_type = NULL;
+ gint ii;
+
+ g_return_val_if_fail (header_names != NULL, NULL);
+ g_return_val_if_fail (header_names[0] != NULL, NULL);
+
+ if (!word)
+ return NULL;
+
+ if (options) {
+ struct _KnownOptions {
+ const gchar *compare_type;
+ const gchar *alt_name;
+ } known_options[] = {
+ { "contains", "c" },
+ { "has-words", "w" },
+ { "matches", "m" },
+ { "starts-with", "sw" },
+ { "ends-with", "ew" },
+ { "soundex", "se" },
+ { "regex", "r" },
+ { "full-regex", "fr" }
+ };
+
+ for (ii = 0; ii < G_N_ELEMENTS (known_options); ii++) {
+ if (g_ascii_strcasecmp (options, known_options[ii].compare_type) == 0 ||
+ (known_options[ii].alt_name && g_ascii_strcasecmp (options,
known_options[ii].alt_name) == 0)) {
+ compare_type = known_options[ii].compare_type;
+ break;
+ }
+ }
+ }
+
+ if (!compare_type)
+ compare_type = "contains";
+
+ encoded_word = g_string_new ("");
+ camel_sexp_encode_string (encoded_word, word);
+
+ if (!header_names[1]) {
+ if (!sexp)
+ sexp = g_string_new ("");
+ } else if (!sexp) {
+ sexp = g_string_new ("(or ");
+ } else {
+ g_string_append (sexp, "(or ");
+ }
+
+ for (ii = 0; header_names[ii]; ii++) {
+ g_string_append_printf (sexp, "(match-all (header-%s \"%s\" %s))", compare_type,
header_names[ii], encoded_word->str);
+ }
+
+ if (header_names[1])
+ g_string_append (sexp, ")");
+
+ g_string_free (encoded_word, TRUE);
+
+ return sexp ? g_string_free (sexp, FALSE) : NULL;
+}
+
+static gchar *
+mail_ffe_recips (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ const gchar *header_names[] = { "To", "Cc", "Subject", NULL };
+
+ /* Include Subject only in the default expression. */
+ if (!hint)
+ header_names[2] = NULL;
+
+ return mail_ffe_build_header_sexp (word, options, header_names);
+}
+
+static gchar *
+mail_ffe_from (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ const gchar *header_names[] = { "From", NULL };
+
+ return mail_ffe_build_header_sexp (word, options, header_names);
+}
+
+static gchar *
+mail_ffe_to (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ const gchar *header_names[] = { "To", NULL };
+
+ return mail_ffe_build_header_sexp (word, options, header_names);
+}
+
+static gchar *
+mail_ffe_cc (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ const gchar *header_names[] = { "Cc", NULL };
+
+ return mail_ffe_build_header_sexp (word, options, header_names);
+}
+
+static gchar *
+mail_ffe_subject (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ const gchar *header_names[] = { "Subject", NULL };
+
+ return mail_ffe_build_header_sexp (word, options, header_names);
+}
+
+static gchar *
+mail_ffe_header (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ const gchar *header_names[] = { NULL, NULL };
+ const gchar *equal;
+ gchar *header_name, *sexp;
+
+ equal = word ? strchr (word, '=') : NULL;
+ if (!equal)
+ return NULL;
+
+ header_name = g_strndup (word, equal - word);
+ header_names[0] = header_name;
+
+ sexp = mail_ffe_build_header_sexp (equal + 1, options, header_names);
+
+ g_free (header_name);
+
+ return sexp;
+}
+
+static gchar *
+mail_ffe_exists (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ GString *encoded_word;
+ gchar *sexp;
+
+ if (!word)
+ return NULL;
+
+ encoded_word = g_string_new ("");
+ camel_sexp_encode_string (encoded_word, word);
+
+ sexp = g_strdup_printf ("(match-all (header-exists %s))", encoded_word->str);
+
+ g_string_free (encoded_word, TRUE);
+
+ return sexp;
+}
+
+static gchar *
+mail_ffe_tag (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ GString *encoded_word;
+ gchar *sexp;
+
+ if (!word)
+ return NULL;
+
+ encoded_word = g_string_new ("");
+ camel_sexp_encode_string (encoded_word, word);
+
+ sexp = g_strdup_printf ("(match-all (not (= (user-tag %s) \"\")))", encoded_word->str);
+
+ g_string_free (encoded_word, TRUE);
+
+ return sexp;
+}
+
+static gchar *
+mail_ffe_flag (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ const gchar *system_flags[] = {
+ /* Translators: This is a name of a flag, the same as all strings in the 'ffe' context.
+ The translated value should not contain spaces. */
+ NC_("ffe", "Answered"),
+ NC_("ffe", "Deleted"),
+ NC_("ffe", "Draft"),
+ NC_("ffe", "Flagged"),
+ NC_("ffe", "Seen"),
+ NC_("ffe", "Attachment")
+ };
+ GString *encoded_word;
+ gchar *sexp = NULL;
+ gint ii;
+
+ if (!word)
+ return NULL;
+
+ encoded_word = g_string_new ("");
+ camel_sexp_encode_string (encoded_word, word);
+
+ for (ii = 0; ii < G_N_ELEMENTS (system_flags); ii++) {
+ if (g_ascii_strcasecmp (word, system_flags[ii]) == 0 ||
+ g_ascii_strcasecmp (word, g_dpgettext2 (NULL, "ffe", system_flags[ii])) == 0) {
+ sexp = g_strdup_printf ("(match-all (system-flag \"%s\"))", system_flags[ii]);
+ break;
+ }
+ }
+
+ if (!sexp)
+ sexp = g_strdup_printf ("(match-all (not (= (user-tag %s) \"\")))", encoded_word->str);
+
+ g_string_free (encoded_word, TRUE);
+
+ return sexp;
+}
+
+static gchar *
+mail_ffe_label (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ GString *encoded_word;
+ gchar *sexp;
+
+ if (!word)
+ return NULL;
+
+ encoded_word = g_string_new ("");
+ camel_sexp_encode_string (encoded_word, word);
+
+ sexp = g_strdup_printf ("(match-all (or ((= (user-tag \"label\") %s) (user-flag (+ \"$Label\" %s))
(user-flag %s)))",
+ encoded_word->str, encoded_word->str, encoded_word->str);
+
+ g_string_free (encoded_word, TRUE);
+
+ return sexp;
+}
+
+static gchar *
+mail_ffe_size (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ GString *encoded_word;
+ gchar *sexp;
+ const gchar *cmp = "=";
+
+ if (!word)
+ return NULL;
+
+ if (options) {
+ if (g_ascii_strcasecmp (options, "<") == 0 ||
+ g_ascii_strcasecmp (options, ">") == 0)
+ cmp = options;
+ }
+
+ encoded_word = g_string_new ("");
+ camel_sexp_encode_string (encoded_word, word);
+
+ sexp = g_strdup_printf ("(match-all (%s (get-size) (cast-int %s)))", cmp, encoded_word->str);
+
+ g_string_free (encoded_word, TRUE);
+
+ return sexp;
+}
+
+static gchar *
+mail_ffe_score (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ GString *encoded_word;
+ gchar *sexp;
+ const gchar *cmp = "=";
+
+ if (!word)
+ return NULL;
+
+ if (options) {
+ if (g_ascii_strcasecmp (options, "<") == 0 ||
+ g_ascii_strcasecmp (options, ">") == 0)
+ cmp = options;
+ }
+
+ encoded_word = g_string_new ("");
+ camel_sexp_encode_string (encoded_word, word);
+
+ sexp = g_strdup_printf ("(match-all (%s (cast-int (user-tag \"score\")) (cast-int %s)))", cmp,
encoded_word->str);
+
+ g_string_free (encoded_word, TRUE);
+
+ return sexp;
+}
+
+static gchar *
+mail_ffe_body (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ GString *encoded_word;
+ gchar *sexp;
+ const gchar *cmp = "contains";
+
+ if (!word)
+ return NULL;
+
+ if (options) {
+ if (g_ascii_strcasecmp (options, "regex") == 0 ||
+ g_ascii_strcasecmp (options, "re") == 0 ||
+ g_ascii_strcasecmp (options, "r") == 0)
+ cmp = "regex";
+ }
+
+ encoded_word = g_string_new ("");
+ camel_sexp_encode_string (encoded_word, word);
+
+ sexp = g_strdup_printf ("(match-all (body-%s %s))", cmp, encoded_word->str);
+
+ g_string_free (encoded_word, TRUE);
+
+ return sexp;
+}
+
+static gboolean
+mail_ffe_decode_date_time (const gchar *word,
+ GTimeVal *tv)
+{
+ struct tm tm;
+
+ g_return_val_if_fail (word != NULL, FALSE);
+ g_return_val_if_fail (tv != NULL, FALSE);
+
+ /* YYYY-MM-DD */
+ if (strlen (word) == 10 && word[4] == '-' && word[7] == '-') {
+ gint yy, mm, dd;
+
+ yy = atoi (word);
+ mm = atoi (word + 5);
+ dd = atoi (word + 8);
+
+ if (g_date_valid_dmy (dd, mm, yy)) {
+ GDate *date;
+
+ date = g_date_new_dmy (dd, mm, yy);
+ g_date_to_struct_tm (date, &tm);
+ g_date_free (date);
+
+ tv->tv_sec = mktime (&tm);
+ tv->tv_usec = 0;
+
+ return TRUE;
+ }
+ }
+
+ if (g_time_val_from_iso8601 (word, tv))
+ return TRUE;
+
+ if (e_time_parse_date_and_time (word, &tm) == E_TIME_PARSE_OK ||
+ e_time_parse_date (word, &tm) == E_TIME_PARSE_OK) {
+ tv->tv_sec = mktime (&tm);
+ tv->tv_usec = 0;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+mail_ffe_process_date (const gchar *get_date_fnc,
+ const gchar *word,
+ const gchar *options)
+{
+ gint64 rel_days;
+ gchar *endptr = NULL;
+ const gchar *op = ">";
+ GTimeVal tv;
+
+ g_return_val_if_fail (get_date_fnc != NULL, NULL);
+
+ if (options) {
+ if (g_ascii_strcasecmp (options, "<") == 0) {
+ op = "<";
+ } else if (g_ascii_strcasecmp (options, "=") == 0) {
+ op = "=";
+ } else if (g_ascii_strcasecmp (options, ">") == 0) {
+ op = ">";
+ }
+ }
+
+ rel_days = g_ascii_strtoll (word, &endptr, 10);
+ if (rel_days != 0 && endptr && !*endptr) {
+ return g_strdup_printf ("(match-all (%s (%s) (%s (get-current-date) %" G_GINT64_FORMAT ")))",
op, get_date_fnc,
+ rel_days < 0 ? "+" : "-", (rel_days < 0 ? -1 : 1) * rel_days * 24 * 60 * 60);
+ }
+
+ if (!mail_ffe_decode_date_time (word, &tv))
+ return g_strdup_printf ("(match-all (%s (%s) (get-current-date)))", op, get_date_fnc);
+
+ return g_strdup_printf ("(match-all (%s (%s) %" G_GINT64_FORMAT "))", op, get_date_fnc, (gint64)
tv.tv_sec);
+}
+
+static gchar *
+mail_ffe_sent (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ if (!word)
+ return NULL;
+
+ return mail_ffe_process_date ("get-sent-date", word, options);
+}
+
+static gchar *
+mail_ffe_received (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ if (!word)
+ return NULL;
+
+ return mail_ffe_process_date ("get-received-date", word, options);
+}
+
+static gchar *
+mail_ffe_attachment (const gchar *word,
+ const gchar *options,
+ const gchar *hint)
+{
+ gboolean is_neg = FALSE;
+
+ if (!word)
+ return NULL;
+
+ if (g_ascii_strcasecmp (word, "no") == 0 ||
+ g_ascii_strcasecmp (word, "false") == 0 ||
+ g_ascii_strcasecmp (word, C_("ffe", "no")) == 0 ||
+ g_ascii_strcasecmp (word, C_("ffe", "false")) == 0 ||
+ g_ascii_strcasecmp (word, "0") == 0) {
+ is_neg = TRUE;
+ }
+
+ return g_strdup_printf ("(match-all %s(system-flag \"Attachment\")%s)", is_neg ? "(not " : "", is_neg
? ")" : "");
+}
+
+static const EFreeFormExpSymbol mail_ffe_symbols[] = {
+ { "", "1", mail_ffe_recips },
+ { "from:f", NULL, mail_ffe_from },
+ { "to:t", NULL, mail_ffe_to },
+ { "cc:c:", NULL, mail_ffe_cc },
+ { "recips:r", NULL, mail_ffe_recips },
+ { "subject:s", NULL, mail_ffe_subject },
+ { "header:h", NULL, mail_ffe_header },
+ { "exists:e", NULL, mail_ffe_exists },
+ { "tag", NULL, mail_ffe_tag },
+ { "flag", NULL, mail_ffe_flag },
+ { "label:l", NULL, mail_ffe_label },
+ { "size:sz", NULL, mail_ffe_size },
+ { "score:sc", NULL, mail_ffe_score },
+ { "body:b", NULL, mail_ffe_body },
+ { "sent", NULL, mail_ffe_sent },
+ { "received:rcv", NULL, mail_ffe_received },
+ { "attachment:a", NULL, mail_ffe_attachment },
+ { NULL, NULL, NULL}
+};
+
+static gchar *
+get_filter_input_value (EFilterPart *part,
+ const gchar *name)
+{
+ EFilterElement *elem;
+ EFilterInput *input;
+ GString *value;
+ GList *link;
+
+ g_return_val_if_fail (part != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ elem = e_filter_part_find_element (part, name);
+ g_return_val_if_fail (elem != NULL, NULL);
+ g_return_val_if_fail (E_IS_FILTER_INPUT (elem), NULL);
+
+ input = E_FILTER_INPUT (elem);
+ value = g_string_new ("");
+
+ for (link = input->values; link; link = g_list_next (link)) {
+ const gchar *val = link->data;
+
+ if (val && *val) {
+ if (value->len > 0)
+ g_string_append_c (value, ' ');
+ g_string_append (value, val);
+ }
+ }
+
+ return g_string_free (value, FALSE);
+}
+
+void
+e_mail_free_form_exp_to_sexp (EFilterElement *element,
+ GString *out,
+ EFilterPart *part)
+{
+ gchar *ffe, *sexp;
+
+ ffe = get_filter_input_value (part, "ffe");
+ g_return_if_fail (ffe != NULL);
+
+ sexp = e_free_form_exp_to_sexp (ffe, mail_ffe_symbols);
+ if (sexp)
+ g_string_append (out, sexp);
+
+ g_free (sexp);
+ g_free (ffe);
+}
diff --git a/mail/e-mail-free-form-exp.h b/mail/e-mail-free-form-exp.h
new file mode 100644
index 0000000..5192202
--- /dev/null
+++ b/mail/e-mail-free-form-exp.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_MAIL_FREE_FORM_EXP_H
+#define E_MAIL_FREE_FORM_EXP_H
+
+#include <glib.h>
+
+#include <e-util/e-util.h>
+
+G_BEGIN_DECLS
+
+void e_mail_free_form_exp_to_sexp (EFilterElement *element,
+ GString *out,
+ EFilterPart *part);
+
+G_END_DECLS
+
+#endif /* E_MAIL_FREE_FORM_EXP_H */
diff --git a/mail/filtertypes.xml.in b/mail/filtertypes.xml.in
index 25be780..ec2e70e 100644
--- a/mail/filtertypes.xml.in
+++ b/mail/filtertypes.xml.in
@@ -581,6 +581,13 @@
<input type="code" name="code"/>
</part>
+ <part name="mail-free-form-exp">
+ <_title>Free Form Expression</_title>
+ <input type="string" name="ffe">
+ <code func="e_mail_free_form_exp_to_sexp"/>
+ </input>
+ </part>
+
<part name="sent-date">
<_title>Date sent</_title>
<input type="optionlist" name="date-spec-type">
diff --git a/mail/searchtypes.xml.in b/mail/searchtypes.xml.in
index ef116f4..f5cb719 100644
--- a/mail/searchtypes.xml.in
+++ b/mail/searchtypes.xml.in
@@ -656,6 +656,13 @@
<input type="code" name="code"/>
</part>
+ <part name="mail-free-form-exp">
+ <_title>Free Form Expression</_title>
+ <input type="string" name="ffe">
+ <code func="e_mail_free_form_exp_to_sexp"/>
+ </input>
+ </part>
+
<part name="sent-date">
<_title>Date sent</_title>
<input type="optionlist" name="date-spec-type">
@@ -1012,5 +1019,15 @@
<sources/>
</rule>
+ <rule grouping="any" source="demand">
+ <_title>Free form expression</_title>
+ <partset>
+ <part name="mail-free-form-exp">
+ <value name="ffe" type="string"/>
+ </part>
+ </partset>
+ <sources/>
+ </rule>
+
</ruleset>
</filterdescription>
diff --git a/mail/vfoldertypes.xml.in b/mail/vfoldertypes.xml.in
index 51460c7..8910073 100644
--- a/mail/vfoldertypes.xml.in
+++ b/mail/vfoldertypes.xml.in
@@ -652,6 +652,13 @@
<input type="code" name="code"/>
</part>
+ <part name="mail-free-form-exp">
+ <_title>Free Form Expression</_title>
+ <input type="string" name="ffe">
+ <code func="e_mail_free_form_exp_to_sexp"/>
+ </input>
+ </part>
+
<part name="sent-date">
<_title>Date sent</_title>
<input type="optionlist" name="date-spec-type">
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
index 28da400..b1be490 100644
--- a/modules/mail/e-mail-shell-view-actions.c
+++ b/modules/mail/e-mail-shell-view-actions.c
@@ -2090,6 +2090,13 @@ static GtkRadioActionEntry mail_search_entries[] = {
NULL, /* XXX Add a tooltip! */
MAIL_SEARCH_BODY_CONTAINS },
+ { "mail-search-free-form-expr",
+ NULL,
+ N_("Free form expression"),
+ NULL,
+ NULL, /* XXX Add a tooltip! */
+ MAIL_SEARCH_FREE_FORM_EXPR },
+
{ "mail-search-message-contains",
NULL,
N_("Message contains"),
diff --git a/modules/mail/e-mail-shell-view-actions.h b/modules/mail/e-mail-shell-view-actions.h
index ad54d89..aa75a2b 100644
--- a/modules/mail/e-mail-shell-view-actions.h
+++ b/modules/mail/e-mail-shell-view-actions.h
@@ -246,6 +246,8 @@
E_SHELL_WINDOW_ACTION ((window), "mail-scope-current-message")
#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_BODY_CONTAINS(window) \
E_SHELL_WINDOW_ACTION ((window), "mail-search-body-contains")
+#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_FREE_FORM_EXPR(window) \
+ E_SHELL_WINDOW_ACTION ((window), "mail-search-free-form-expr")
#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_MESSAGE_CONTAINS(window) \
E_SHELL_WINDOW_ACTION ((window), "mail-search-message-contains")
#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_RECIPIENTS_CONTAIN(window) \
diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h
index bf12feb..b0a7933 100644
--- a/modules/mail/e-mail-shell-view-private.h
+++ b/modules/mail/e-mail-shell-view-private.h
@@ -96,6 +96,7 @@ enum {
MAIL_SEARCH_SUBJECT_CONTAINS,
MAIL_SEARCH_SENDER_CONTAINS,
MAIL_SEARCH_BODY_CONTAINS,
+ MAIL_SEARCH_FREE_FORM_EXPR,
MAIL_NUM_SEARCH_RULES
};
diff --git a/modules/mail/e-mail-shell-view.c b/modules/mail/e-mail-shell-view.c
index 7477e68..8dd6b0f 100644
--- a/modules/mail/e-mail-shell-view.c
+++ b/modules/mail/e-mail-shell-view.c
@@ -448,6 +448,8 @@ mail_shell_view_execute_search (EShellView *shell_view)
element = e_filter_part_find_element (part, "sender");
else if (strcmp (part->name, "to") == 0)
element = e_filter_part_find_element (part, "recipient");
+ else if (strcmp (part->name, "mail-free-form-exp") == 0)
+ element = e_filter_part_find_element (part, "ffe");
if (strcmp (part->name, "body") == 0) {
struct _camel_search_words *words;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 195c269..978d245 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -346,6 +346,7 @@ mail/e-mail-config-welcome-page.c
mail/e-mail-config-window.c
mail/e-mail-display.c
mail/e-mail-folder-create-dialog.c
+mail/e-mail-free-form-exp.c
mail/e-mail-junk-options.c
mail/e-mail-label-dialog.c
mail/e-mail-label-list-store.c
diff --git a/ui/evolution-mail.ui b/ui/evolution-mail.ui
index e3d5dca..70fd30d 100644
--- a/ui/evolution-mail.ui
+++ b/ui/evolution-mail.ui
@@ -148,6 +148,7 @@
<menuitem action='mail-search-subject-contains'/>
<menuitem action='mail-search-sender-contains'/>
<menuitem action='mail-search-body-contains'/>
+ <menuitem action='mail-search-free-form-expr'/>
<separator/>
<menuitem action='search-advanced'/>
</popup>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]