[evolution] Bug #246530 - Rules editor lacks "Label is (not) 'None'"



commit 304a2c1c91bd21b362090a4368fb460bc697d9b0
Author: Milan Crha <mcrha redhat com>
Date:   Mon Aug 6 16:10:09 2012 +0200

    Bug #246530 - Rules editor lacks "Label is (not) 'None'"

 filter/e-filter-option.c            |   85 +++++++++++++++++----
 filter/e-filter-option.h            |    7 ++-
 mail/filtertypes.xml                |   10 +--
 mail/searchtypes.xml                |   10 +--
 mail/vfoldertypes.xml               |    8 +--
 modules/mail/e-mail-shell-backend.c |  143 ++++++++++++++++++++++++++++++++++-
 modules/mail/e-mail-shell-backend.h |   10 ++-
 7 files changed, 234 insertions(+), 39 deletions(-)
---
diff --git a/filter/e-filter-option.c b/filter/e-filter-option.c
index 2f6c734..dd6d9b8 100644
--- a/filter/e-filter-option.c
+++ b/filter/e-filter-option.c
@@ -45,6 +45,7 @@ free_option (struct _filter_option *opt)
 	g_free (opt->title);
 	g_free (opt->value);
 	g_free (opt->code);
+	g_free (opt->code_gen_func);
 	g_free (opt);
 }
 
@@ -101,6 +102,29 @@ filter_option_get_dynamic_options (EFilterOption *option)
 }
 
 static void
+filter_option_generate_code (EFilterOption *option,
+			     GString *out,
+			     EFilterPart *part)
+{
+	GModule *module;
+	void (*code_gen_func) (EFilterElement *element, GString *out, EFilterPart *part);
+
+	if (!option || !option->current || !option->current->code_gen_func)
+		return;
+
+	module = g_module_open (NULL, G_MODULE_BIND_LAZY);
+
+	if (g_module_symbol (module, option->current->code_gen_func, (gpointer) &code_gen_func)) {
+		code_gen_func (E_FILTER_ELEMENT (option), out, part);
+	} else {
+		g_warning ("optionlist dynamic code function '%s' not found",
+			   option->current->code_gen_func);
+	}
+
+	g_module_close (module);
+}
+
+static void
 filter_option_finalize (GObject *object)
 {
 	EFilterOption *option = E_FILTER_OPTION (object);
@@ -149,7 +173,7 @@ filter_option_xml_create (EFilterElement *element,
 	n = node->children;
 	while (n) {
 		if (!strcmp ((gchar *)n->name, "option")) {
-			gchar *tmp, *value, *title = NULL, *code = NULL;
+			gchar *tmp, *value, *title = NULL, *code = NULL, *code_gen_func = NULL;
 
 			value = (gchar *)xmlGetProp (n, (xmlChar *)"value");
 			work = n->children;
@@ -164,21 +188,43 @@ filter_option_xml_create (EFilterElement *element,
 						xmlFree (tmp);
 					}
 				} else if (!strcmp ((gchar *)work->name, "code")) {
-					if (!code) {
-						if (!(tmp = (gchar *) xmlNodeGetContent (work)))
-							tmp = (gchar *)xmlStrdup ((xmlChar *)"");
+					if (code || code_gen_func) {
+						g_warning ("Element 'code' defined twice in '%s'",
+							element->name);
+					} else {
+						xmlChar *fn;
+
+						/* if element 'code' has attribute 'func', then
+						   the content of the element is ignored and only
+						   the 'func' is used to generate actual rule code;
+						   The function prototype is:
+						   void code_gen_func (EFilterElement *element, GString *out, EFilterPart *part);
+						   where @element is the one on which was called,
+						   @out is GString where to add the code, and
+						   @part is part which contains @element and other options of it.
+						*/
+						fn = xmlGetProp (work, (xmlChar *)"func");
+						if (fn && *fn) {
+							code_gen_func = g_strdup ((const gchar *) fn);
+						} else {
+							if (!(tmp = (gchar *) xmlNodeGetContent (work)))
+								tmp = (gchar *)xmlStrdup ((xmlChar *)"");
+
+							code = g_strdup (tmp);
+							xmlFree (tmp);
+						}
 
-						code = g_strdup (tmp);
-						xmlFree (tmp);
+						xmlFree (fn);
 					}
 				}
 				work = work->next;
 			}
 
-			e_filter_option_add (option, value, title, code, FALSE);
+			e_filter_option_add (option, value, title, code, code_gen_func, FALSE);
 			xmlFree (value);
 			g_free (title);
 			g_free (code);
+			g_free (code_gen_func);
 		} else if (g_str_equal ((gchar *)n->name, "dynamic")) {
 			if (option->dynamic_func) {
 				g_warning (
@@ -217,6 +263,7 @@ filter_option_xml_create (EFilterElement *element,
 								op->value,
 								op->title,
 								op->code,
+								op->code_gen_func,
 								TRUE);
 							free_option (op);
 						}
@@ -295,7 +342,7 @@ filter_option_clone (EFilterElement *element)
 
 		newop = e_filter_option_add (
 			clone_option, op->value,
-			op->title, op->code, op->is_dynamic);
+			op->title, op->code, op->code_gen_func, op->is_dynamic);
 		if (option->current == op)
 			clone_option->current = newop;
 	}
@@ -335,8 +382,8 @@ filter_option_get_widget (EFilterElement *element)
 				break;
 			} else {
 				e_filter_option_add (
-					option, op->value,
-					op->title, op->code, FALSE);
+					option, op->value, op->title,
+					op->code, op->code_gen_func, FALSE);
 			}
 		}
 
@@ -346,8 +393,8 @@ filter_option_get_widget (EFilterElement *element)
 
 			if (op) {
 				e_filter_option_add (
-					option, op->value,
-					op->title, op->code, TRUE);
+					option, op->value, op->title,
+					op->code, op->code_gen_func, TRUE);
 				free_option (op);
 			}
 		}
@@ -360,8 +407,8 @@ filter_option_get_widget (EFilterElement *element)
 
 			if (!op->is_dynamic)
 				e_filter_option_add (
-					option, op->value,
-					op->title, op->code, FALSE);
+					option, op->value, op->title,
+					op->code, op->code_gen_func, FALSE);
 		}
 
 		if (old_cur)
@@ -402,8 +449,11 @@ filter_option_build_code (EFilterElement *element,
 {
 	EFilterOption *option = E_FILTER_OPTION (element);
 
-	if (option->current && option->current->code)
+	if (option->current && option->current->code_gen_func) {
+		filter_option_generate_code (option, out, part);
+	} else if (option->current && option->current->code) {
 		e_filter_part_expand_code (part, option->current->code, out);
+	}
 }
 
 static void
@@ -464,6 +514,7 @@ e_filter_option_add (EFilterOption *option,
                      const gchar *value,
                      const gchar *title,
                      const gchar *code,
+		     const gchar *code_gen_func,
                      gboolean is_dynamic)
 {
 	struct _filter_option *op;
@@ -471,10 +522,14 @@ e_filter_option_add (EFilterOption *option,
 	g_return_val_if_fail (E_IS_FILTER_OPTION (option), NULL);
 	g_return_val_if_fail (find_option (option, value) == NULL, NULL);
 
+	if (code_gen_func && !*code_gen_func)
+		code_gen_func = NULL;
+
 	op = g_malloc (sizeof (*op));
 	op->title = g_strdup (title);
 	op->value = g_strdup (value);
 	op->code = g_strdup (code);
+	op->code_gen_func = g_strdup (code_gen_func);
 	op->is_dynamic = is_dynamic;
 
 	option->options = g_list_append (option->options, op);
diff --git a/filter/e-filter-option.h b/filter/e-filter-option.h
index 45d426b..84b98bd 100644
--- a/filter/e-filter-option.h
+++ b/filter/e-filter-option.h
@@ -55,8 +55,12 @@ struct _filter_option {
 	gchar *title;		/* button title */
 	gchar *value;		/* value, if it has one */
 	gchar *code;		/* used to string code segments together */
+	gchar *code_gen_func;	/* function to generate the code;
+				   either @code or @code_gen_func is non-NULL,
+				   never both */
 
-	gboolean is_dynamic;	/* whether is the option dynamic, FALSE if static */
+	gboolean is_dynamic;	/* whether is the option dynamic, FALSE if static;
+				   dynamic means "generated by EFilterOption::dynamic_func" */
 };
 
 struct _EFilterOption {
@@ -84,6 +88,7 @@ struct _filter_option *
 						 const gchar *name,
 						 const gchar *title,
 						 const gchar *code,
+						 const gchar *code_gen_func,
 						 gboolean is_dynamic);
 void		e_filter_option_remove_all	(EFilterOption *option);
 
diff --git a/mail/filtertypes.xml b/mail/filtertypes.xml
index c655c32..02bea7f 100644
--- a/mail/filtertypes.xml
+++ b/mail/filtertypes.xml
@@ -648,15 +648,11 @@
   <input type="optionlist" name="label-type">
    <option value="is">
     <title>is</title>
-    <code>
-       (match-all (or (= (user-tag "label") ${versus}) (user-flag (+ "$Label" ${versus})) (user-flag ${versus})))
-    </code>
+    <code func="e_mail_labels_get_filter_code"/>
    </option>
    <option value="is-not">
     <title>is not</title>
-    <code>
-       (match-all (not (or (= (user-tag "label") ${versus}) (user-flag (+ "$Label" ${versus})) (user-flag ${versus}))))
-    </code>
+    <code func="e_mail_labels_get_filter_code"/>
    </option>
   </input>
   <input type="optionlist" name="versus">
@@ -947,7 +943,7 @@
   <title>Set Label</title>
   <code>(set-label ${label})</code>
   <input type="optionlist" name="label">
-     <dynamic func="e_mail_labels_get_filter_options"/>
+     <dynamic func="e_mail_labels_get_filter_options_without_none"/>
   </input>
  </part>
  <part name="colour">
diff --git a/mail/searchtypes.xml b/mail/searchtypes.xml
index 0173066..bf933d8 100644
--- a/mail/searchtypes.xml
+++ b/mail/searchtypes.xml
@@ -644,16 +644,12 @@
   <input type="optionlist" name="label-type">
    <option value="is">
     <title>is</title>
-    <code>
-       (match-all (or (= (user-tag "label") ${versus}) (user-flag (+ "$Label" ${versus})) (user-flag ${versus})))
-    </code>
+    <code func="e_mail_labels_get_filter_code"/>
    </option>
    <option value="is-not">
     <title>is not</title>
-    <code>
-       (match-all (not (or (= (user-tag "label") ${versus}) (user-flag (+ "$Label" ${versus})) (user-flag ${versus}))))
-    </code>
-   </option>
+    <code func="e_mail_labels_get_filter_code"/>
+    </option>
   </input>
   <input type="optionlist" name="versus">
      <dynamic func="e_mail_labels_get_filter_options"/>
diff --git a/mail/vfoldertypes.xml b/mail/vfoldertypes.xml
index 5f8bbd1..9195271 100644
--- a/mail/vfoldertypes.xml
+++ b/mail/vfoldertypes.xml
@@ -640,15 +640,11 @@
   <input type="optionlist" name="label-type">
    <option value="is">
     <title>is</title>
-    <code>
-       (match-all (or (= (user-tag "label") ${versus}) (user-flag (+ "$Label" ${versus})) (user-flag ${versus})))
-    </code>
+    <code func="e_mail_labels_get_filter_code"/>
    </option>
    <option value="is-not">
     <title>is not</title>
-    <code>
-       (match-all (not (or (= (user-tag "label") ${versus}) (user-flag (+ "$Label" ${versus})) (user-flag ${versus}))))
-    </code>
+    <code func="e_mail_labels_get_filter_code"/>
    </option>
   </input>
   <input type="optionlist" name="versus">
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
index 6fab84b..2079d28 100644
--- a/modules/mail/e-mail-shell-backend.c
+++ b/modules/mail/e-mail-shell-backend.c
@@ -871,8 +871,8 @@ e_mail_shell_backend_edit_account (EMailShellBackend *mail_shell_backend,
 #include "filter/e-filter-option.h"
 #include "shell/e-shell-settings.h"
 
-GSList *
-e_mail_labels_get_filter_options (void)
+static GSList *
+mail_labels_get_filter_options (gboolean include_none)
 {
 	EShell *shell;
 	EShellBackend *shell_backend;
@@ -892,6 +892,16 @@ e_mail_labels_get_filter_options (void)
 	label_store = e_mail_ui_session_get_label_store (
 		E_MAIL_UI_SESSION (session));
 
+	if (include_none) {
+		struct _filter_option *option;
+
+		option = g_new0 (struct _filter_option, 1);
+		/* Translators: The first item in the list, to be able to set rule: [Label] [is/is-not] [None] */
+		option->title = g_strdup (C_("label", "None"));
+		option->value = g_strdup ("");
+		list = g_slist_prepend (list, option);
+	}
+
 	model = GTK_TREE_MODEL (label_store);
 	valid = gtk_tree_model_get_iter_first (model, &iter);
 
@@ -923,6 +933,135 @@ e_mail_labels_get_filter_options (void)
 	return g_slist_reverse (list);
 }
 
+
+GSList *
+e_mail_labels_get_filter_options (void)
+{
+	return mail_labels_get_filter_options (TRUE);
+}
+
+GSList *
+e_mail_labels_get_filter_options_without_none (void)
+{
+	return mail_labels_get_filter_options (FALSE);
+}
+
+static const gchar *
+get_filter_option_value (EFilterPart *part,
+			 const gchar *name)
+{
+	EFilterElement *elem;
+	EFilterOption *opt;
+
+	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_OPTION (elem), NULL);
+
+	opt = E_FILTER_OPTION (elem);
+	return e_filter_option_get_current (opt);
+}
+
+static void
+append_one_label_expr (GString *out,
+		       const gchar *versus)
+{
+	GString *encoded;
+
+	g_return_if_fail (out != NULL);
+	g_return_if_fail (versus != NULL);
+
+	encoded = g_string_new ("");
+	camel_sexp_encode_string (encoded, versus);
+
+	g_string_append_printf (out,
+		" (= (user-tag \"label\") %s) (user-flag (+ \"$Label\" %s)) (user-flag %s)",
+		encoded->str, encoded->str, encoded->str);
+
+	g_string_free (encoded, TRUE);
+}
+
+void
+e_mail_labels_get_filter_code (EFilterElement *element,
+			       GString *out,
+			       EFilterPart *part)
+{
+	const gchar *label_type, *versus;
+	gboolean is_not;
+
+	label_type = get_filter_option_value (part, "label-type");
+	versus = get_filter_option_value (part, "versus");
+
+	g_return_if_fail (label_type != NULL);
+	g_return_if_fail (versus != NULL);
+
+	is_not = g_str_equal (label_type, "is-not");
+
+	if (!g_str_equal (label_type, "is") && !is_not) {
+		g_warning ("%s: Unknown label-type: '%s'", G_STRFUNC, label_type);
+		return;
+	}
+
+	/* the 'None' item has 'is-not' inverted */
+	if (!*versus)
+		is_not = !is_not;
+
+	g_string_append (out, " (match-all (");
+	if (is_not)
+		g_string_append (out, " not (");
+	g_string_append (out, "or");
+
+	/* the 'None' item; "is None" means "has not set any label" */
+	if (!*versus) {
+		EShell *shell;
+		EShellBackend *shell_backend;
+		EMailBackend *backend;
+		EMailSession *session;
+		EMailLabelListStore *label_store;
+		GtkTreeModel *model;
+		GtkTreeIter iter;
+		gboolean valid;
+		
+		shell = e_shell_get_default ();
+		shell_backend = e_shell_get_backend_by_name (shell, "mail");
+
+		backend = E_MAIL_BACKEND (shell_backend);
+		session = e_mail_backend_get_session (backend);
+		label_store = e_mail_ui_session_get_label_store (E_MAIL_UI_SESSION (session));
+
+		model = GTK_TREE_MODEL (label_store);
+		valid = gtk_tree_model_get_iter_first (model, &iter);
+
+		while (valid) {
+			gchar *tag;
+
+			tag = e_mail_label_list_store_get_tag (label_store, &iter);
+
+			if (g_str_has_prefix (tag, "$Label")) {
+				gchar *tmp = tag;
+
+				tag = g_strdup (tag + 6);
+
+				g_free (tmp);
+			}
+
+			append_one_label_expr (out, tag);
+
+			g_free (tag);
+
+			valid = gtk_tree_model_iter_next (model, &iter);
+		}
+	} else {
+		append_one_label_expr (out, versus);
+	}
+
+	if (is_not)
+		g_string_append (out, ")");
+	g_string_append (out, " ))");
+}
+
 static void
 message_parsed_cb (GObject *source_object,
                    GAsyncResult *res,
diff --git a/modules/mail/e-mail-shell-backend.h b/modules/mail/e-mail-shell-backend.h
index 0b76ea3..5b2da24 100644
--- a/modules/mail/e-mail-shell-backend.h
+++ b/modules/mail/e-mail-shell-backend.h
@@ -23,6 +23,8 @@
 #define E_MAIL_SHELL_BACKEND_H
 
 #include <mail/e-mail-backend.h>
+#include <filter/e-filter-element.h>
+#include <filter/e-filter-rule.h>
 
 /* Standard GObject macros */
 #define E_TYPE_MAIL_SHELL_BACKEND \
@@ -71,7 +73,13 @@ void		e_mail_shell_backend_edit_account
 						 ESource *mail_account);
 
 /* XXX Find a better place for this function. */
-GSList *	e_mail_labels_get_filter_options (void);
+GSList *	e_mail_labels_get_filter_options
+						(void);
+GSList *	e_mail_labels_get_filter_options_without_none
+						(void);
+void		e_mail_labels_get_filter_code	(EFilterElement *element,
+						 GString *out,
+						 EFilterPart *part);
 
 G_END_DECLS
 



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