aboutsummaryrefslogtreecommitdiffstats
path: root/filter
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2009-10-25 21:17:23 +0800
committerMatthew Barnes <mbarnes@redhat.com>2009-10-27 21:25:01 +0800
commit2b16aef84141800099f859e72d05e1e6bf8e02dd (patch)
treeb7168d8e9a4157a1f6733b0c667dd6239c5e0055 /filter
parent58166e645971a4812fef23702f45cacc8e64e419 (diff)
downloadgsoc2013-evolution-2b16aef84141800099f859e72d05e1e6bf8e02dd.tar.gz
gsoc2013-evolution-2b16aef84141800099f859e72d05e1e6bf8e02dd.tar.zst
gsoc2013-evolution-2b16aef84141800099f859e72d05e1e6bf8e02dd.zip
Cleanup and rename filter classes.
Diffstat (limited to 'filter')
-rw-r--r--filter/Makefile.am72
-rw-r--r--filter/e-filter-code.c126
-rw-r--r--filter/e-filter-code.h68
-rw-r--r--filter/e-filter-color.c180
-rw-r--r--filter/e-filter-color.h70
-rw-r--r--filter/e-filter-datespec.c (renamed from filter/filter-datespec.c)376
-rw-r--r--filter/e-filter-datespec.h87
-rw-r--r--filter/e-filter-element.c472
-rw-r--r--filter/e-filter-element.h118
-rw-r--r--filter/e-filter-file.c278
-rw-r--r--filter/e-filter-file.h74
-rw-r--r--filter/e-filter-input.c322
-rw-r--r--filter/e-filter-input.h74
-rw-r--r--filter/e-filter-int.c261
-rw-r--r--filter/e-filter-int.h77
-rw-r--r--filter/e-filter-option.c514
-rw-r--r--filter/e-filter-option.h92
-rw-r--r--filter/e-filter-part.c532
-rw-r--r--filter/e-filter-part.h104
-rw-r--r--filter/e-filter-rule.c1116
-rw-r--r--filter/e-filter-rule.h161
-rw-r--r--filter/e-rule-context.c988
-rw-r--r--filter/e-rule-context.h215
-rw-r--r--filter/e-rule-editor.c912
-rw-r--r--filter/e-rule-editor.h122
-rw-r--r--filter/filter-code.c136
-rw-r--r--filter/filter-code.h56
-rw-r--r--filter/filter-colour.c219
-rw-r--r--filter/filter-colour.h58
-rw-r--r--filter/filter-datespec.h75
-rw-r--r--filter/filter-element.c336
-rw-r--r--filter/filter-element.h93
-rw-r--r--filter/filter-file.c292
-rw-r--r--filter/filter-file.h67
-rw-r--r--filter/filter-input.c360
-rw-r--r--filter/filter-input.h62
-rw-r--r--filter/filter-int.c270
-rw-r--r--filter/filter-int.h62
-rw-r--r--filter/filter-option.c497
-rw-r--r--filter/filter-option.h75
-rw-r--r--filter/filter-part.c541
-rw-r--r--filter/filter-part.h89
-rw-r--r--filter/filter-rule.c1044
-rw-r--r--filter/filter-rule.h134
-rw-r--r--filter/rule-context.c952
-rw-r--r--filter/rule-context.h149
-rw-r--r--filter/rule-editor.c880
-rw-r--r--filter/rule-editor.h104
48 files changed, 7175 insertions, 6787 deletions
diff --git a/filter/Makefile.am b/filter/Makefile.am
index 48cfa5cc7a..d05842738b 100644
--- a/filter/Makefile.am
+++ b/filter/Makefile.am
@@ -13,44 +13,44 @@ libfilter_la_CPPFLAGS = \
filterincludedir = $(privincludedir)/filter
filterinclude_HEADERS = \
- filter-code.h \
- filter-colour.h \
- filter-datespec.h \
- filter-element.h \
- filter-file.h \
- filter-input.h \
- filter-int.h \
- filter-option.h \
- filter-part.h \
- filter-rule.h \
- rule-context.h \
- rule-editor.h
+ e-filter-code.h \
+ e-filter-color.h \
+ e-filter-datespec.h \
+ e-filter-element.h \
+ e-filter-file.h \
+ e-filter-input.h \
+ e-filter-int.h \
+ e-filter-option.h \
+ e-filter-part.h \
+ e-filter-rule.h \
+ e-rule-context.h \
+ e-rule-editor.h
libfilter_la_SOURCES = \
- filter-code.c \
- filter-code.h \
- filter-colour.c \
- filter-colour.h \
- filter-datespec.c \
- filter-datespec.h \
- filter-element.c \
- filter-element.h \
- filter-file.c \
- filter-file.h \
- filter-input.c \
- filter-input.h \
- filter-int.c \
- filter-int.h \
- filter-option.c \
- filter-option.h \
- filter-part.c \
- filter-part.h \
- filter-rule.c \
- filter-rule.h \
- rule-context.c \
- rule-context.h \
- rule-editor.c \
- rule-editor.h
+ e-filter-code.c \
+ e-filter-code.h \
+ e-filter-color.c \
+ e-filter-color.h \
+ e-filter-datespec.c \
+ e-filter-datespec.h \
+ e-filter-element.c \
+ e-filter-element.h \
+ e-filter-file.c \
+ e-filter-file.h \
+ e-filter-input.c \
+ e-filter-input.h \
+ e-filter-int.c \
+ e-filter-int.h \
+ e-filter-option.c \
+ e-filter-option.h \
+ e-filter-part.c \
+ e-filter-part.h \
+ e-filter-rule.c \
+ e-filter-rule.h \
+ e-rule-context.c \
+ e-rule-context.h \
+ e-rule-editor.c \
+ e-rule-editor.h
libfilter_la_LDFLAGS = $(NO_UNDEFINED)
diff --git a/filter/e-filter-code.c b/filter/e-filter-code.c
new file mode 100644
index 0000000000..7c00baf8d8
--- /dev/null
+++ b/filter/e-filter-code.c
@@ -0,0 +1,126 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-filter-code.h"
+
+static gpointer parent_class;
+
+/* here, the string IS the code */
+static void
+filter_code_build_code (EFilterElement *element,
+ GString *out,
+ struct _EFilterPart *part)
+{
+ GList *l;
+ EFilterInput *fi = (EFilterInput *)element;
+ gboolean is_rawcode = fi && fi->type && g_str_equal (fi->type, "rawcode");
+
+ if (!is_rawcode)
+ g_string_append(out, "(match-all ");
+
+ l = fi->values;
+ while (l) {
+ g_string_append(out, (gchar *)l->data);
+ l = g_list_next(l);
+ }
+
+ if (!is_rawcode)
+ g_string_append (out, ")");
+}
+
+/* and we have no value */
+static void
+filter_code_format_sexp (EFilterElement *element,
+ GString *out)
+{
+}
+
+static void
+filter_code_class_init (EFilterCodeClass *class)
+{
+ EFilterElementClass *filter_element_class;
+
+ parent_class = g_type_class_peek_parent (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;
+}
+
+static void
+filter_code_init (EFilterCode *code)
+{
+ EFilterInput *input = E_FILTER_INPUT (code);
+
+ input->type = (gchar *) xmlStrdup ((xmlChar *) "code");
+}
+
+GType
+e_filter_code_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterCodeClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_code_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterCode),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) filter_code_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_FILTER_INPUT, "EFilterCode", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * filter_code_new:
+ *
+ * Create a new EFilterCode object.
+ *
+ * Return value: A new #EFilterCode object.
+ **/
+EFilterCode *
+e_filter_code_new (gboolean raw_code)
+{
+ EFilterCode *fc = g_object_new (E_TYPE_FILTER_CODE, NULL, NULL);
+
+ if (fc && raw_code) {
+ xmlFree (((EFilterInput *) fc)->type);
+ ((EFilterInput *) fc)->type = (gchar *)xmlStrdup ((xmlChar *)"rawcode");
+ }
+
+ return fc;
+}
diff --git a/filter/e-filter-code.h b/filter/e-filter-code.h
new file mode 100644
index 0000000000..6a903a5dcb
--- /dev/null
+++ b/filter/e-filter-code.h
@@ -0,0 +1,68 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_CODE_H
+#define E_FILTER_CODE_H
+
+#include "e-filter-input.h"
+
+/* Standard GObject macros */
+#define E_TYPE_FILTER_CODE \
+ (e_filter_code_get_type ())
+#define E_FILTER_CODE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_CODE, EFilterCode))
+#define E_FILTER_CODE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_CODE, EFilterCodeClass))
+#define E_IS_FILTER_CODE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_CODE))
+#define E_IS_FILTER_CODE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_CODE))
+#define E_FILTER_CODE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_CODE, EFilterCodeClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EFilterCode EFilterCode;
+typedef struct _EFilterCodeClass EFilterCodeClass;
+typedef struct _EFilterCodePrivate EFilterCodePrivate;
+
+struct _EFilterCode {
+ EFilterInput parent;
+ EFilterCodePrivate *priv;
+};
+
+struct _EFilterCodeClass {
+ EFilterInputClass parent_class;
+};
+
+GType e_filter_code_get_type (void);
+EFilterCode * e_filter_code_new (gboolean raw_code);
+
+G_END_DECLS
+
+#endif /* E_FILTER_CODE_H */
diff --git a/filter/e-filter-color.c b/filter/e-filter-color.c
new file mode 100644
index 0000000000..c4a36607cb
--- /dev/null
+++ b/filter/e-filter-color.c
@@ -0,0 +1,180 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <libedataserver/e-sexp.h>
+
+#include "e-filter-color.h"
+
+static gpointer parent_class;
+
+static void
+set_color (GtkColorButton *color_button, EFilterColor *fc)
+{
+ gtk_color_button_get_color (color_button, &fc->color);
+}
+
+static gint
+filter_color_eq (EFilterElement *element_a,
+ EFilterElement *element_b)
+{
+ EFilterColor *color_a = E_FILTER_COLOR (element_a);
+ EFilterColor *color_b = E_FILTER_COLOR (element_b);
+
+ return E_FILTER_ELEMENT_CLASS (parent_class)->eq (element_a, element_b)
+ && gdk_color_equal (&color_a->color, &color_b->color);
+}
+
+static xmlNodePtr
+filter_color_xml_encode (EFilterElement *element)
+{
+ EFilterColor *fc = E_FILTER_COLOR (element);
+ xmlNodePtr value;
+ gchar spec[16];
+
+ g_snprintf (spec, sizeof (spec), "#%04x%04x%04x",
+ fc->color.red, fc->color.green, fc->color.blue);
+
+ value = xmlNewNode(NULL, (xmlChar *)"value");
+ xmlSetProp(value, (xmlChar *)"type", (xmlChar *)"colour");
+ xmlSetProp(value, (xmlChar *)"name", (xmlChar *)element->name);
+ xmlSetProp(value, (xmlChar *)"spec", (xmlChar *)spec);
+
+ return value;
+}
+
+static gint
+filter_color_xml_decode (EFilterElement *element,
+ xmlNodePtr node)
+{
+ EFilterColor *fc = E_FILTER_COLOR (element);
+ xmlChar *prop;
+
+ xmlFree (element->name);
+ element->name = (gchar *)xmlGetProp(node, (xmlChar *)"name");
+
+ prop = xmlGetProp(node, (xmlChar *)"spec");
+ if (prop != NULL) {
+ gdk_color_parse((gchar *)prop, &fc->color);
+ xmlFree (prop);
+ } else {
+ /* try reading the old RGB properties */
+ prop = xmlGetProp(node, (xmlChar *)"red");
+ sscanf((gchar *)prop, "%" G_GINT16_MODIFIER "x", &fc->color.red);
+ xmlFree (prop);
+ prop = xmlGetProp(node, (xmlChar *)"green");
+ sscanf((gchar *)prop, "%" G_GINT16_MODIFIER "x", &fc->color.green);
+ xmlFree (prop);
+ prop = xmlGetProp(node, (xmlChar *)"blue");
+ sscanf((gchar *)prop, "%" G_GINT16_MODIFIER "x", &fc->color.blue);
+ xmlFree (prop);
+ }
+
+ return 0;
+}
+
+static GtkWidget *
+filter_color_get_widget (EFilterElement *element)
+{
+ EFilterColor *fc = E_FILTER_COLOR (element);
+ GtkWidget *color_button;
+
+ color_button = gtk_color_button_new_with_color (&fc->color);
+ gtk_widget_show (color_button);
+
+ g_signal_connect (
+ G_OBJECT (color_button), "color_set",
+ G_CALLBACK (set_color), element);
+
+ return color_button;
+}
+
+static void
+filter_color_format_sexp (EFilterElement *element,
+ GString *out)
+{
+ EFilterColor *fc = E_FILTER_COLOR (element);
+ gchar spec[16];
+
+ g_snprintf (spec, sizeof (spec), "#%04x%04x%04x",
+ fc->color.red, fc->color.green, fc->color.blue);
+ e_sexp_encode_string (out, spec);
+}
+
+static void
+filter_color_class_init (EFilterColorClass *class)
+{
+ EFilterElementClass *filter_element_class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ filter_element_class = E_FILTER_ELEMENT_CLASS (class);
+ filter_element_class->eq = filter_color_eq;
+ filter_element_class->xml_encode = filter_color_xml_encode;
+ 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;
+}
+
+GType
+e_filter_color_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterColorClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_color_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterColor),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_FILTER_ELEMENT, "EFilterColor", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * filter_color_new:
+ *
+ * Create a new EFilterColor object.
+ *
+ * Return value: A new #EFilterColor object.
+ **/
+EFilterColor *
+e_filter_color_new (void)
+{
+ return g_object_new (E_TYPE_FILTER_COLOR, NULL);
+}
diff --git a/filter/e-filter-color.h b/filter/e-filter-color.h
new file mode 100644
index 0000000000..cf75bc13ce
--- /dev/null
+++ b/filter/e-filter-color.h
@@ -0,0 +1,70 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_COLOR_H
+#define E_FILTER_COLOR_H
+
+#include "e-filter-element.h"
+
+/* Standard GObject macros */
+#define E_TYPE_FILTER_COLOR \
+ (e_filter_color_get_type ())
+#define E_FILTER_COLOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_COLOR, EFilterColor))
+#define E_FILTER_COLOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_COLOR, EFilterColorClass))
+#define E_IS_FILTER_COLOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_COLOR))
+#define E_IS_FILTER_COLOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_COLOR))
+#define E_FILTER_COLOR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_COLOR, EFilterColorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EFilterColor EFilterColor;
+typedef struct _EFilterColorClass EFilterColorClass;
+typedef struct _EFilterColorPrivate EFilterColorPrivate;
+
+struct _EFilterColor {
+ EFilterElement parent;
+ EFilterColorPrivate *priv;
+
+ GdkColor color;
+};
+
+struct _EFilterColorClass {
+ EFilterElementClass parent_class;
+};
+
+GType e_filter_color_get_type (void);
+EFilterColor * e_filter_color_new (void);
+
+G_END_DECLS
+
+#endif /* E_FILTER_COLOR_H */
diff --git a/filter/filter-datespec.c b/filter/e-filter-datespec.c
index a0e981d111..1a34cce525 100644
--- a/filter/filter-datespec.c
+++ b/filter/e-filter-datespec.c
@@ -33,34 +33,25 @@
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <glade/glade.h>
+#include <libedataserver/e-sexp.h>
-#include "filter-datespec.h"
-#include "libedataserver/e-sexp.h"
#include "e-util/e-error.h"
#include "e-util/e-util-private.h"
+#include "e-filter-datespec.h"
+#include "e-filter-part.h"
+
#ifdef G_OS_WIN32
#define localtime_r(tp,tmp) memcpy(tmp,localtime(tp),sizeof(struct tm))
#endif
-#define d(x)
-
-static gboolean validate (FilterElement *fe, GtkWindow *error_parent);
-static gint date_eq (FilterElement *fe, FilterElement *cm);
-static void xml_create (FilterElement *fe, xmlNodePtr node);
-static xmlNodePtr xml_encode (FilterElement *fe);
-static gint xml_decode (FilterElement *fe, xmlNodePtr node);
-static GtkWidget *get_widget (FilterElement *fe);
-static void build_code (FilterElement *fe, GString *out, struct _FilterPart *fds);
-static void format_sexp (FilterElement *, GString *);
+#define E_FILTER_DATESPEC_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_FILTER_DATESPEC, EFilterDatespecPrivate))
-static void filter_datespec_class_init (FilterDatespecClass *klass);
-static void filter_datespec_init (FilterDatespec *fd);
-static void filter_datespec_finalise (GObject *obj);
-
-#define PRIV(x) (((FilterDatespec *)(x))->priv)
+#define d(x)
-typedef struct _timespan {
+typedef struct {
guint32 seconds;
const gchar *past_singular;
const gchar *past_plural;
@@ -92,171 +83,14 @@ static const timespan timespans[] = {
#define DAY_INDEX 3
-struct _FilterDatespecPrivate {
+struct _EFilterDatespecPrivate {
GtkWidget *label_button;
GtkWidget *notebook_type, *combobox_type, *calendar_specify, *spin_relative, *combobox_relative, *combobox_past_future;
- FilterDatespec_type type;
+ EFilterDatespecType type;
gint span;
};
-static FilterElementClass *parent_class;
-
-GType
-filter_datespec_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterDatespecClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_datespec_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterDatespec),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_datespec_init,
- };
-
- type = g_type_register_static (FILTER_TYPE_ELEMENT, "FilterDatespec", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_datespec_class_init (FilterDatespecClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- FilterElementClass *fe_class = FILTER_ELEMENT_CLASS (klass);
-
- parent_class = g_type_class_ref (FILTER_TYPE_ELEMENT);
-
- object_class->finalize = filter_datespec_finalise;
-
- /* override methods */
- fe_class->validate = validate;
- fe_class->eq = date_eq;
- fe_class->xml_create = xml_create;
- fe_class->xml_encode = xml_encode;
- fe_class->xml_decode = xml_decode;
- fe_class->get_widget = get_widget;
- fe_class->build_code = build_code;
- fe_class->format_sexp = format_sexp;
-}
-
-static void
-filter_datespec_init (FilterDatespec *fd)
-{
- fd->priv = g_malloc0 (sizeof (*fd->priv));
- fd->type = FDST_UNKNOWN;
-}
-
-static void
-filter_datespec_finalise (GObject *obj)
-{
- FilterDatespec *fd = (FilterDatespec *) obj;
-
- g_free (fd->priv);
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_datespec_new:
- *
- * Create a new FilterDatespec object.
- *
- * Return value: A new #FilterDatespec object.
- **/
-FilterDatespec *
-filter_datespec_new (void)
-{
- return (FilterDatespec *) g_object_new (FILTER_TYPE_DATESPEC, NULL, NULL);
-}
-
-static gboolean
-validate (FilterElement *fe, GtkWindow *error_parent)
-{
- FilterDatespec *fds = (FilterDatespec *) fe;
- gboolean valid;
-
- valid = fds->type != FDST_UNKNOWN;
- if (!valid) {
- e_error_run (error_parent, "filter:no-date", NULL);
- }
-
- return valid;
-}
-
-static gint
-date_eq (FilterElement *fe, FilterElement *cm)
-{
- FilterDatespec *fd = (FilterDatespec *)fe, *cd = (FilterDatespec *)cm;
-
- return FILTER_ELEMENT_CLASS (parent_class)->eq(fe, cm)
- && (fd->type == cd->type)
- && (fd->value == cd->value);
-}
-
-static void
-xml_create (FilterElement *fe, xmlNodePtr node)
-{
- /* parent implementation */
- FILTER_ELEMENT_CLASS (parent_class)->xml_create (fe, node);
-}
-
-static xmlNodePtr
-xml_encode (FilterElement *fe)
-{
- xmlNodePtr value, work;
- FilterDatespec *fds = (FilterDatespec *)fe;
- gchar str[32];
-
- d(printf ("Encoding datespec as xml\n"));
-
- value = xmlNewNode (NULL, (const guchar *)"value");
- xmlSetProp (value, (const guchar *)"name", (guchar *)fe->name);
- xmlSetProp (value, (const guchar *)"type", (const guchar *)"datespec");
-
- work = xmlNewChild (value, NULL, (const guchar *)"datespec", NULL);
- sprintf (str, "%d", fds->type);
- xmlSetProp (work, (const guchar *)"type", (guchar *)str);
- sprintf (str, "%d", (gint)fds->value);
- xmlSetProp (work, (const guchar *)"value", (guchar *)str);
-
- return value;
-}
-
-static gint
-xml_decode (FilterElement *fe, xmlNodePtr node)
-{
- FilterDatespec *fds = (FilterDatespec *)fe;
- xmlNodePtr n;
- xmlChar *val;
-
- d(printf ("Decoding datespec from xml %p\n", fe));
-
- xmlFree (fe->name);
- fe->name = (gchar *)xmlGetProp (node, (const guchar *)"name");
-
- n = node->children;
- while (n) {
- if (!strcmp ((gchar *)n->name, "datespec")) {
- val = xmlGetProp (n, (const guchar *)"type");
- fds->type = atoi ((gchar *)val);
- xmlFree (val);
- val = xmlGetProp (n, (const guchar *)"value");
- fds->value = atoi ((gchar *)val);
- xmlFree (val);
- break;
- }
- n = n->next;
- }
-
- return 0;
-}
+static gpointer parent_class;
static gint
get_best_span (time_t val)
@@ -273,7 +107,7 @@ get_best_span (time_t val)
/* sets button label */
static void
-set_button (FilterDatespec *fds)
+set_button (EFilterDatespec *fds)
{
gchar buf[128];
gchar *label = buf;
@@ -320,9 +154,9 @@ set_button (FilterDatespec *fds)
}
static void
-get_values (FilterDatespec *fds)
+get_values (EFilterDatespec *fds)
{
- struct _FilterDatespecPrivate *p = PRIV(fds);
+ EFilterDatespecPrivate *p = E_FILTER_DATESPEC_GET_PRIVATE (fds);
switch (fds->priv->type) {
case FDST_SPECIFIED: {
@@ -353,11 +187,11 @@ get_values (FilterDatespec *fds)
}
static void
-set_values (FilterDatespec *fds)
+set_values (EFilterDatespec *fds)
{
gint note_type;
- struct _FilterDatespecPrivate *p = PRIV(fds);
+ EFilterDatespecPrivate *p = E_FILTER_DATESPEC_GET_PRIVATE (fds);
p->type = fds->type==FDST_UNKNOWN ? FDST_NOW : fds->type;
@@ -396,20 +230,20 @@ set_values (FilterDatespec *fds)
}
static void
-set_combobox_type (GtkComboBox *combobox, FilterDatespec *fds)
+set_combobox_type (GtkComboBox *combobox, EFilterDatespec *fds)
{
fds->priv->type = gtk_combo_box_get_active (combobox);
gtk_notebook_set_current_page ((GtkNotebook*) fds->priv->notebook_type, fds->priv->type);
}
static void
-set_combobox_relative (GtkComboBox *combobox, FilterDatespec *fds)
+set_combobox_relative (GtkComboBox *combobox, EFilterDatespec *fds)
{
fds->priv->span = gtk_combo_box_get_active (combobox);
}
static void
-set_combobox_past_future (GtkComboBox *combobox, FilterDatespec *fds)
+set_combobox_past_future (GtkComboBox *combobox, EFilterDatespec *fds)
{
if (gtk_combo_box_get_active (combobox) == 0)
fds->type = fds->priv->type = FDST_X_AGO;
@@ -418,9 +252,9 @@ set_combobox_past_future (GtkComboBox *combobox, FilterDatespec *fds)
}
static void
-button_clicked (GtkButton *button, FilterDatespec *fds)
+button_clicked (GtkButton *button, EFilterDatespec *fds)
{
- struct _FilterDatespecPrivate *p = PRIV(fds);
+ EFilterDatespecPrivate *p = E_FILTER_DATESPEC_GET_PRIVATE (fds);
GtkWidget *content_area;
GtkWidget *toplevel;
GtkDialog *dialog;
@@ -465,10 +299,92 @@ button_clicked (GtkButton *button, FilterDatespec *fds)
gtk_widget_destroy ((GtkWidget *)dialog);
}
+static gboolean
+filter_datespec_validate (EFilterElement *element,
+ GtkWindow *error_parent)
+{
+ EFilterDatespec *fds = E_FILTER_DATESPEC (element);
+ gboolean valid;
+
+ valid = fds->type != FDST_UNKNOWN;
+ if (!valid) {
+ e_error_run (error_parent, "filter:no-date", NULL);
+ }
+
+ return valid;
+}
+
+static gint
+filter_datespec_eq (EFilterElement *element_a,
+ EFilterElement *element_b)
+{
+ EFilterDatespec *datespec_a = E_FILTER_DATESPEC (element_a);
+ EFilterDatespec *datespec_b = E_FILTER_DATESPEC (element_b);
+
+ /* Chain up to parent's eq() method. */
+ if (!E_FILTER_ELEMENT_CLASS (parent_class)->eq (element_a, element_b))
+ return FALSE;
+
+ return (datespec_a->type == datespec_b->type) &&
+ (datespec_a->value == datespec_b->value);
+}
+
+static xmlNodePtr
+filter_datespec_xml_encode (EFilterElement *element)
+{
+ xmlNodePtr value, work;
+ EFilterDatespec *fds = E_FILTER_DATESPEC (element);
+ gchar str[32];
+
+ d(printf ("Encoding datespec as xml\n"));
+
+ value = xmlNewNode (NULL, (xmlChar *)"value");
+ xmlSetProp (value, (xmlChar *)"name", (xmlChar *)element->name);
+ xmlSetProp (value, (xmlChar *)"type", (xmlChar *)"datespec");
+
+ work = xmlNewChild (value, NULL, (xmlChar *)"datespec", NULL);
+ sprintf (str, "%d", fds->type);
+ xmlSetProp (work, (xmlChar *)"type", (xmlChar *)str);
+ sprintf (str, "%d", (gint)fds->value);
+ xmlSetProp (work, (xmlChar *)"value", (xmlChar *)str);
+
+ return value;
+}
+
+static gint
+filter_datespec_xml_decode (EFilterElement *element,
+ xmlNodePtr node)
+{
+ EFilterDatespec *fds = E_FILTER_DATESPEC (element);
+ xmlNodePtr n;
+ xmlChar *val;
+
+ d(printf ("Decoding datespec from xml %p\n", element));
+
+ xmlFree (element->name);
+ element->name = (gchar *)xmlGetProp (node, (xmlChar *)"name");
+
+ n = node->children;
+ while (n) {
+ if (!strcmp ((gchar *)n->name, "datespec")) {
+ val = xmlGetProp (n, (xmlChar *)"type");
+ fds->type = atoi ((gchar *)val);
+ xmlFree (val);
+ val = xmlGetProp (n, (xmlChar *)"value");
+ fds->value = atoi ((gchar *)val);
+ xmlFree (val);
+ break;
+ }
+ n = n->next;
+ }
+
+ return 0;
+}
+
static GtkWidget *
-get_widget (FilterElement *fe)
+filter_datespec_get_widget (EFilterElement *element)
{
- FilterDatespec *fds = (FilterDatespec *)fe;
+ EFilterDatespec *fds = E_FILTER_DATESPEC (element);
GtkWidget *button;
fds->priv->label_button = gtk_label_new ("");
@@ -486,15 +402,10 @@ get_widget (FilterElement *fe)
}
static void
-build_code (FilterElement *fe, GString *out, struct _FilterPart *fp)
-{
- return;
-}
-
-static void
-format_sexp (FilterElement *fe, GString *out)
+filter_datespec_format_sexp (EFilterElement *element,
+ GString *out)
{
- FilterDatespec *fds = (FilterDatespec *)fe;
+ EFilterDatespec *fds = E_FILTER_DATESPEC (element);
switch (fds->type) {
case FDST_UNKNOWN:
@@ -507,10 +418,75 @@ format_sexp (FilterElement *fe, GString *out)
g_string_append_printf (out, "%d", (gint) fds->value);
break;
case FDST_X_AGO:
- g_string_append_printf (out, "(- (get-current-date) %d)", (gint) fds->value);
+ g_string_append_printf (
+ out, "(- (get-current-date) %d)", (gint) fds->value);
break;
case FDST_X_FUTURE:
- g_string_append_printf (out, "(+ (get-current-date) %d)", (gint) fds->value);
+ g_string_append_printf (
+ out, "(+ (get-current-date) %d)", (gint) fds->value);
break;
}
}
+
+static void
+filter_datespec_class_init (EFilterDatespecClass *class)
+{
+ EFilterElementClass *filter_element_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EFilterDatespecPrivate));
+
+ filter_element_class = E_FILTER_ELEMENT_CLASS (class);
+ filter_element_class->validate = filter_datespec_validate;
+ filter_element_class->eq = filter_datespec_eq;
+ filter_element_class->xml_encode = filter_datespec_xml_encode;
+ 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;
+}
+
+static void
+filter_datespec_init (EFilterDatespec *datespec)
+{
+ datespec->priv = E_FILTER_DATESPEC_GET_PRIVATE (datespec);
+ datespec->type = FDST_UNKNOWN;
+}
+
+GType
+e_filter_datespec_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterDatespecClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_datespec_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterDatespec),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) filter_datespec_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_FILTER_ELEMENT, "EFilterDatespec", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * filter_datespec_new:
+ *
+ * Create a new EFilterDatespec object.
+ *
+ * Return value: A new #EFilterDatespec object.
+ **/
+EFilterDatespec *
+e_filter_datespec_new (void)
+{
+ return g_object_new (E_TYPE_FILTER_DATESPEC, NULL);
+}
diff --git a/filter/e-filter-datespec.h b/filter/e-filter-datespec.h
new file mode 100644
index 0000000000..aed978a09a
--- /dev/null
+++ b/filter/e-filter-datespec.h
@@ -0,0 +1,87 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_DATESPEC_H
+#define E_FILTER_DATESPEC_H
+
+#include <time.h>
+#include "e-filter-element.h"
+
+/* Standard GObject types */
+#define E_TYPE_FILTER_DATESPEC \
+ (e_filter_datespec_get_type ())
+#define E_FILTER_DATESPEC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_DATESPEC, EFilterDatespec))
+#define E_FILTER_DATESPEC_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_DATESPEC, EFilterDatespecClass))
+#define E_IS_FILTER_DATESPEC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_DATESPEC))
+#define E_IS_FILTER_DATESPEC_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_DATESPEC))
+#define E_FILTER_DATESPEC_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_DATESPEC, EFilterDatespecClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EFilterDatespec EFilterDatespec;
+typedef struct _EFilterDatespecClass EFilterDatespecClass;
+typedef struct _EFilterDatespecPrivate EFilterDatespecPrivate;
+
+typedef enum {
+ FDST_UNKNOWN = -1,
+ FDST_NOW,
+ FDST_SPECIFIED,
+ FDST_X_AGO,
+ FDST_X_FUTURE
+} EFilterDatespecType;
+
+struct _EFilterDatespec {
+ EFilterElement parent;
+ EFilterDatespecPrivate *priv;
+
+ EFilterDatespecType type;
+
+ /* either a timespan, an absolute time, or 0
+ * depending on type -- the above mapping to
+ * (X_FUTURE, X_AGO, SPECIFIED, NOW)
+ */
+
+ time_t value;
+};
+
+struct _EFilterDatespecClass {
+ EFilterElementClass parent_class;
+};
+
+GType e_filter_datespec_get_type (void);
+EFilterDatespec *
+ e_filter_datespec_new (void);
+
+G_END_DECLS
+
+#endif /* E_FILTER_DATESPEC_H */
diff --git a/filter/e-filter-element.c b/filter/e-filter-element.c
new file mode 100644
index 0000000000..707692ec3f
--- /dev/null
+++ b/filter/e-filter-element.c
@@ -0,0 +1,472 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "e-filter-element.h"
+#include "e-filter-part.h"
+
+struct _element_type {
+ gchar *name;
+
+ EFilterElementFunc create;
+ gpointer data;
+};
+
+static gpointer parent_class;
+
+static gboolean
+filter_element_validate (EFilterElement *element,
+ GtkWindow *error_parent)
+{
+ return TRUE;
+}
+
+static gint
+filter_element_eq (EFilterElement *element_a,
+ EFilterElement *element_b)
+{
+ return (g_strcmp0 (element_a->name, element_b->name) == 0);
+}
+
+static void
+filter_element_xml_create (EFilterElement *element,
+ xmlNodePtr node)
+{
+ element->name = (gchar *)xmlGetProp (node, (xmlChar *) "name");
+}
+
+static EFilterElement *
+filter_element_clone (EFilterElement *element)
+{
+ EFilterElement *clone;
+ xmlNodePtr node;
+
+ clone = g_object_new (G_OBJECT_TYPE (element), NULL);
+
+ node = e_filter_element_xml_encode (element);
+ e_filter_element_xml_decode (clone, node);
+ xmlFreeNodeList (node);
+
+ return clone;
+}
+
+/* This is somewhat hackish, implement all the base cases in here */
+#include "e-filter-input.h"
+#include "e-filter-option.h"
+#include "e-filter-code.h"
+#include "e-filter-color.h"
+#include "e-filter-datespec.h"
+#include "e-filter-int.h"
+#include "e-filter-file.h"
+
+static void
+filter_element_copy_value (EFilterElement *dst_element,
+ EFilterElement *src_element)
+{
+ if (E_IS_FILTER_INPUT (src_element)) {
+ EFilterInput *src_input;
+
+ src_input = E_FILTER_INPUT (src_element);
+
+ if (E_IS_FILTER_INPUT (dst_element)) {
+ EFilterInput *dst_input;
+
+ dst_input = E_FILTER_INPUT (dst_element);
+
+ if (src_input->values)
+ e_filter_input_set_value (
+ dst_input,
+ src_input->values->data);
+
+ } else if (E_IS_FILTER_INT (dst_element)) {
+ EFilterInt *dst_int;
+
+ dst_int = E_FILTER_INT (dst_element);
+
+ dst_int->val = atoi (src_input->values->data);
+ }
+
+ } else if (E_IS_FILTER_COLOR (src_element)) {
+ EFilterColor *src_color;
+
+ src_color = E_FILTER_COLOR (src_element);
+
+ if (E_IS_FILTER_COLOR (dst_element)) {
+ EFilterColor *dst_color;
+
+ dst_color = E_FILTER_COLOR (dst_element);
+
+ dst_color->color = src_color->color;
+ }
+
+ } else if (E_IS_FILTER_DATESPEC (src_element)) {
+ EFilterDatespec *src_datespec;
+
+ src_datespec = E_FILTER_DATESPEC (src_element);
+
+ if (E_IS_FILTER_DATESPEC (dst_element)) {
+ EFilterDatespec *dst_datespec;
+
+ dst_datespec = E_FILTER_DATESPEC (dst_element);
+
+ dst_datespec->type = src_datespec->type;
+ dst_datespec->value = src_datespec->value;
+ }
+
+ } else if (E_IS_FILTER_INT (src_element)) {
+ EFilterInt *src_int;
+
+ src_int = E_FILTER_INT (src_element);
+
+ if (E_IS_FILTER_INT (dst_element)) {
+ EFilterInt *dst_int;
+
+ dst_int = E_FILTER_INT (dst_element);
+
+ dst_int->val = src_int->val;
+
+ } else if (E_IS_FILTER_INPUT (dst_element)) {
+ EFilterInput *dst_input;
+ gchar *values;
+
+ dst_input = E_FILTER_INPUT (dst_element);
+
+ values = g_strdup_printf ("%d", src_int->val);
+ e_filter_input_set_value (dst_input, values);
+ g_free (values);
+ }
+
+ } else if (E_IS_FILTER_OPTION (src_element)) {
+ EFilterOption *src_option;
+
+ src_option = E_FILTER_OPTION (src_element);
+
+ if (E_IS_FILTER_OPTION (dst_element)) {
+ EFilterOption *dst_option;
+
+ dst_option = E_FILTER_OPTION (dst_element);
+
+ if (src_option->current)
+ e_filter_option_set_current (
+ dst_option,
+ src_option->current->value);
+ }
+ }
+}
+
+static void
+filter_element_finalize (GObject *object)
+{
+ EFilterElement *element = E_FILTER_ELEMENT (object);
+
+ xmlFree (element->name);
+
+ /* Chain up to parent's finalize () method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+filter_element_class_init (EFilterElementClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = filter_element_finalize;
+
+ class->validate = filter_element_validate;
+ class->eq = filter_element_eq;
+ class->xml_create = filter_element_xml_create;
+ class->clone = filter_element_clone;
+ class->copy_value = filter_element_copy_value;
+}
+
+GType
+e_filter_element_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterElementClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_element_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterElement),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ G_TYPE_OBJECT, "EFilterElement", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * filter_element_new:
+ *
+ * Create a new EFilterElement object.
+ *
+ * Return value: A new #EFilterElement object.
+ **/
+EFilterElement *
+e_filter_element_new (void)
+{
+ return g_object_new (E_TYPE_FILTER_ELEMENT, NULL);
+}
+
+gboolean
+e_filter_element_validate (EFilterElement *element,
+ GtkWindow *error_parent)
+{
+ EFilterElementClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_ELEMENT (element), FALSE);
+
+ /* Warn but proceed if no parent window was given. */
+ if (error_parent != NULL)
+ g_return_val_if_fail (GTK_IS_WINDOW (error_parent), FALSE);
+ else
+ g_warning ("%s() called with no parent window", G_STRFUNC);
+
+ class = E_FILTER_ELEMENT_GET_CLASS (element);
+ g_return_val_if_fail (class->validate != NULL, FALSE);
+
+ return class->validate (element, error_parent);
+}
+
+gint
+e_filter_element_eq (EFilterElement *element_a,
+ EFilterElement *element_b)
+{
+ EFilterElementClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_ELEMENT (element_a), FALSE);
+ g_return_val_if_fail (E_IS_FILTER_ELEMENT (element_b), FALSE);
+
+ /* The elements must be the same type. */
+ if (G_OBJECT_TYPE (element_a) != G_OBJECT_TYPE (element_b))
+ return FALSE;
+
+ class = E_FILTER_ELEMENT_GET_CLASS (element_a);
+ g_return_val_if_fail (class->eq != NULL, FALSE);
+
+ return class->eq (element_a, element_b);
+}
+
+/**
+ * filter_element_xml_create:
+ * @fe: filter element
+ * @node: xml node
+ *
+ * Create a new filter element based on an xml definition of
+ * that element.
+ **/
+void
+e_filter_element_xml_create (EFilterElement *element,
+ xmlNodePtr node)
+{
+ EFilterElementClass *class;
+
+ g_return_if_fail (E_IS_FILTER_ELEMENT (element));
+ g_return_if_fail (node != NULL);
+
+ class = E_FILTER_ELEMENT_GET_CLASS (element);
+ g_return_if_fail (class->xml_create != NULL);
+
+ class->xml_create (element, node);
+}
+
+/**
+ * filter_element_xml_encode:
+ * @fe: filter element
+ *
+ * Encode the values of a filter element into xml format.
+ *
+ * Return value:
+ **/
+xmlNodePtr
+e_filter_element_xml_encode (EFilterElement *element)
+{
+ EFilterElementClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_ELEMENT (element), NULL);
+
+ class = E_FILTER_ELEMENT_GET_CLASS (element);
+ g_return_val_if_fail (class->xml_encode != NULL, NULL);
+
+ return class->xml_encode (element);
+}
+
+/**
+ * filter_element_xml_decode:
+ * @fe: filter element
+ * @node: xml node
+ *
+ * Decode the values of a fitler element from xml format.
+ *
+ * Return value:
+ **/
+gint
+e_filter_element_xml_decode (EFilterElement *element,
+ xmlNodePtr node)
+{
+ EFilterElementClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_ELEMENT (element), FALSE);
+ g_return_val_if_fail (node != NULL, FALSE);
+
+ class = E_FILTER_ELEMENT_GET_CLASS (element);
+ g_return_val_if_fail (class->xml_decode != NULL, FALSE);
+
+ return class->xml_decode (element, node);
+}
+
+/**
+ * filter_element_clone:
+ * @fe: filter element
+ *
+ * Clones the EFilterElement @fe.
+ *
+ * Return value:
+ **/
+EFilterElement *
+e_filter_element_clone (EFilterElement *element)
+{
+ EFilterElementClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_ELEMENT (element), NULL);
+
+ class = E_FILTER_ELEMENT_GET_CLASS (element);
+ g_return_val_if_fail (class->clone != NULL, NULL);
+
+ return class->clone (element);
+}
+
+/**
+ * filter_element_get_widget:
+ * @fe: filter element
+ * @node: xml node
+ *
+ * Create a widget to represent this element.
+ *
+ * Return value:
+ **/
+GtkWidget *
+e_filter_element_get_widget (EFilterElement *element)
+{
+ EFilterElementClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_ELEMENT (element), NULL);
+
+ class = E_FILTER_ELEMENT_GET_CLASS (element);
+ g_return_val_if_fail (class->get_widget != NULL, NULL);
+
+ return class->get_widget (element);
+}
+
+/**
+ * filter_element_build_code:
+ * @fe: filter element
+ * @out: output buffer
+ * @ff:
+ *
+ * Add the code representing this element to the output string @out.
+ **/
+void
+e_filter_element_build_code (EFilterElement *element,
+ GString *out,
+ EFilterPart *part)
+{
+ EFilterElementClass *class;
+
+ g_return_if_fail (E_IS_FILTER_ELEMENT (element));
+ g_return_if_fail (out != NULL);
+ g_return_if_fail (E_IS_FILTER_PART (part));
+
+ class = E_FILTER_ELEMENT_GET_CLASS (element);
+
+ /* This method is optional. */
+ if (class->build_code != NULL)
+ class->build_code (element, out, part);
+}
+
+/**
+ * filter_element_format_sexp:
+ * @fe: filter element
+ * @out: output buffer
+ *
+ * Format the value(s) of this element in a method suitable for the context of
+ * sexp where it is used. Usually as space separated, double-quoted strings.
+ **/
+void
+e_filter_element_format_sexp (EFilterElement *element,
+ GString *out)
+{
+ EFilterElementClass *class;
+
+ g_return_if_fail (E_IS_FILTER_ELEMENT (element));
+ g_return_if_fail (out != NULL);
+
+ class = E_FILTER_ELEMENT_GET_CLASS (element);
+ g_return_if_fail (class->format_sexp != NULL);
+
+ class->format_sexp (element, out);
+}
+
+void
+e_filter_element_set_data (EFilterElement *element,
+ gpointer data)
+{
+ g_return_if_fail (E_IS_FILTER_ELEMENT (element));
+
+ element->data = data;
+}
+
+/* only copies the value, not the name/type */
+void
+e_filter_element_copy_value (EFilterElement *dst_element,
+ EFilterElement *src_element)
+{
+ EFilterElementClass *class;
+
+ g_return_if_fail (E_IS_FILTER_ELEMENT (dst_element));
+ g_return_if_fail (E_IS_FILTER_ELEMENT (src_element));
+
+ class = E_FILTER_ELEMENT_GET_CLASS (dst_element);
+ g_return_if_fail (class->copy_value != NULL);
+
+ class->copy_value (dst_element, src_element);
+}
diff --git a/filter/e-filter-element.h b/filter/e-filter-element.h
new file mode 100644
index 0000000000..589d3a48ca
--- /dev/null
+++ b/filter/e-filter-element.h
@@ -0,0 +1,118 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_ELEMENT_H
+#define E_FILTER_ELEMENT_H
+
+#include <gtk/gtk.h>
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#define E_TYPE_FILTER_ELEMENT \
+ (e_filter_element_get_type ())
+#define E_FILTER_ELEMENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_ELEMENT, EFilterElement))
+#define E_FILTER_ELEMENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_ELEMENT, EFilterElementClass))
+#define E_IS_FILTER_ELEMENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_ELEMENT))
+#define E_IS_FILTER_ELEMENT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_ELEMENT))
+#define E_FILTER_ELEMENT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_ELEMENT, EFilterElementClass))
+
+G_BEGIN_DECLS
+
+struct _EFilterPart;
+
+typedef struct _EFilterElement EFilterElement;
+typedef struct _EFilterElementClass EFilterElementClass;
+typedef struct _EFilterElementPrivate EFilterElementPrivate;
+
+typedef EFilterElement * (*EFilterElementFunc) (gpointer data);
+
+struct _EFilterElement {
+ GObject parent;
+ EFilterElementPrivate *priv;
+
+ gchar *name;
+ gpointer data;
+};
+
+struct _EFilterElementClass {
+ GObjectClass parent_class;
+
+ gboolean (*validate) (EFilterElement *element,
+ GtkWindow *error_parent);
+ gint (*eq) (EFilterElement *element_a,
+ EFilterElement *element_b);
+
+ void (*xml_create) (EFilterElement *element,
+ xmlNodePtr node);
+ xmlNodePtr (*xml_encode) (EFilterElement *element);
+ gint (*xml_decode) (EFilterElement *element,
+ xmlNodePtr node);
+
+ EFilterElement *(*clone) (EFilterElement *element);
+ void (*copy_value) (EFilterElement *dst_element,
+ EFilterElement *src_element);
+
+ GtkWidget * (*get_widget) (EFilterElement *element);
+ void (*build_code) (EFilterElement *element,
+ GString *out,
+ struct _EFilterPart *part);
+ void (*format_sexp) (EFilterElement *element,
+ GString *out);
+};
+
+GType e_filter_element_get_type (void);
+EFilterElement *e_filter_element_new (void);
+void e_filter_element_set_data (EFilterElement *element,
+ gpointer data);
+gboolean e_filter_element_validate (EFilterElement *element,
+ GtkWindow *error_parent);
+gint e_filter_element_eq (EFilterElement *element_a,
+ EFilterElement *element_b);
+void e_filter_element_xml_create (EFilterElement *element,
+ xmlNodePtr node);
+xmlNodePtr e_filter_element_xml_encode (EFilterElement *element);
+gint e_filter_element_xml_decode (EFilterElement *element,
+ xmlNodePtr node);
+EFilterElement *e_filter_element_clone (EFilterElement *element);
+void e_filter_element_copy_value (EFilterElement *dst_element,
+ EFilterElement *src_element);
+GtkWidget * e_filter_element_get_widget (EFilterElement *element);
+void e_filter_element_build_code (EFilterElement *element,
+ GString *out,
+ struct _EFilterPart *part);
+void e_filter_element_format_sexp (EFilterElement *element,
+ GString *out);
+
+G_END_DECLS
+
+#endif /* E_FILTER_ELEMENT_H */
diff --git a/filter/e-filter-file.c b/filter/e-filter-file.c
new file mode 100644
index 0000000000..6364475a2e
--- /dev/null
+++ b/filter/e-filter-file.c
@@ -0,0 +1,278 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <libedataserver/e-sexp.h>
+
+#include "e-util/e-error.h"
+
+#include "e-filter-file.h"
+#include "e-filter-part.h"
+
+static gpointer parent_class;
+
+static void
+filter_file_filename_changed (GtkFileChooser *file_chooser,
+ EFilterElement *element)
+{
+ EFilterFile *file = E_FILTER_FILE (element);
+ const gchar *path;
+
+ path = gtk_file_chooser_get_filename (file_chooser);
+
+ g_free (file->path);
+ file->path = g_strdup (path);
+}
+
+static void
+filter_file_finalize (GObject *object)
+{
+ EFilterFile *file = E_FILTER_FILE (object);
+
+ xmlFree (file->type);
+ g_free (file->path);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+filter_file_validate (EFilterElement *element,
+ GtkWindow *error_parent)
+{
+ EFilterFile *file = E_FILTER_FILE (element);
+
+ if (!file->path) {
+ e_error_run (error_parent, "filter:no-file", NULL);
+ return FALSE;
+ }
+
+ /* FIXME: do more to validate command-lines? */
+
+ if (g_strcmp0 (file->type, "file") == 0) {
+ if (!g_file_test (file->path, G_FILE_TEST_IS_REGULAR)) {
+ e_error_run (
+ error_parent, "filter:bad-file",
+ file->path, NULL);
+ return FALSE;
+ }
+ } else if (g_strcmp0 (file->type, "command") == 0) {
+ /* Only requirements so far is that the
+ * command can't be an empty string. */
+ return (file->path[0] != '\0');
+ }
+
+ return TRUE;
+}
+
+static gint
+filter_file_eq (EFilterElement *element_a,
+ EFilterElement *element_b)
+{
+ EFilterFile *file_a = E_FILTER_FILE (element_a);
+ EFilterFile *file_b = E_FILTER_FILE (element_b);
+
+ /* Chain up to parent's eq() method. */
+ if (!E_FILTER_ELEMENT_CLASS (parent_class)->eq (element_a, element_b))
+ return FALSE;
+
+ if (g_strcmp0 (file_a->path, file_b->path) != 0)
+ return FALSE;
+
+ if (g_strcmp0 (file_a->type, file_b->type) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static xmlNodePtr
+filter_file_xml_encode (EFilterElement *element)
+{
+ EFilterFile *file = E_FILTER_FILE (element);
+ xmlNodePtr cur, value;
+ const gchar *type;
+
+ type = file->type ? file->type : "file";
+
+ value = xmlNewNode (NULL, (xmlChar *)"value");
+ xmlSetProp (value, (xmlChar *) "name", (xmlChar *) element->name);
+ xmlSetProp (value, (xmlChar *) "type", (xmlChar *) type);
+
+ cur = xmlNewChild (value, NULL, (xmlChar *)type, NULL);
+ xmlNodeSetContent (cur, (xmlChar *)file->path);
+
+ return value;
+}
+
+static gint
+filter_file_xml_decode (EFilterElement *element,
+ xmlNodePtr node)
+{
+ EFilterFile *file = E_FILTER_FILE (element);
+ gchar *name, *str, *type;
+ xmlNodePtr child;
+
+ name = (gchar *)xmlGetProp (node, (xmlChar *) "name");
+ type = (gchar *)xmlGetProp (node, (xmlChar *) "type");
+
+ xmlFree (element->name);
+ element->name = name;
+
+ xmlFree (file->type);
+ file->type = type;
+
+ g_free (file->path);
+ file->path = NULL;
+
+ child = node->children;
+ while (child != NULL) {
+ if (!strcmp ((gchar *)child->name, type)) {
+ str = (gchar *)xmlNodeGetContent (child);
+ file->path = g_strdup (str ? str : "");
+ xmlFree (str);
+
+ break;
+ } else if (child->type == XML_ELEMENT_NODE) {
+ g_warning (
+ "Unknown node type '%s' encountered "
+ "decoding a %s\n", child->name, type);
+ }
+
+ child = child->next;
+ }
+
+ return 0;
+}
+
+static GtkWidget *
+filter_file_get_widget (EFilterElement *element)
+{
+ EFilterFile *file = E_FILTER_FILE (element);
+ GtkWidget *widget;
+
+ widget = gtk_file_chooser_button_new (
+ _("Choose a File"), GTK_FILE_CHOOSER_ACTION_OPEN);
+ gtk_file_chooser_set_filename (
+ GTK_FILE_CHOOSER (widget), file->path);
+ g_signal_connect (
+ widget, "selection-changed",
+ G_CALLBACK (filter_file_filename_changed), element);
+
+ return widget;
+}
+
+static void
+filter_file_format_sexp (EFilterElement *element,
+ GString *out)
+{
+ EFilterFile *file = E_FILTER_FILE (element);
+
+ e_sexp_encode_string (out, file->path);
+}
+
+static void
+filter_file_class_init (EFilterFileClass *class)
+{
+ GObjectClass *object_class;
+ EFilterElementClass *filter_element_class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = filter_file_finalize;
+
+ filter_element_class = E_FILTER_ELEMENT_CLASS (class);
+ filter_element_class->validate = filter_file_validate;
+ filter_element_class->eq = filter_file_eq;
+ filter_element_class->xml_encode = filter_file_xml_encode;
+ 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;
+}
+
+GType
+e_filter_file_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterFileClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_file_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterFile),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_FILTER_ELEMENT, "EFilterFile", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * filter_file_new:
+ *
+ * Create a new EFilterFile object.
+ *
+ * Return value: A new #EFilterFile object.
+ **/
+EFilterFile *
+e_filter_file_new (void)
+{
+ return g_object_new (E_TYPE_FILTER_FILE, NULL);
+}
+
+EFilterFile *
+e_filter_file_new_type_name (const gchar *type)
+{
+ EFilterFile *file;
+
+ file = e_filter_file_new ();
+ file->type = (gchar *) xmlStrdup ((xmlChar *)type);
+
+ return file;
+}
+
+void
+e_filter_file_set_path (EFilterFile *file,
+ const gchar *path)
+{
+ g_return_if_fail (E_IS_FILTER_FILE (file));
+
+ g_free (file->path);
+ file->path = g_strdup (path);
+}
diff --git a/filter/e-filter-file.h b/filter/e-filter-file.h
new file mode 100644
index 0000000000..6f7946e8e7
--- /dev/null
+++ b/filter/e-filter-file.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_FILE_H
+#define E_FILTER_FILE_H
+
+#include "e-filter-element.h"
+
+/* Standard GObject macros */
+#define E_TYPE_FILTER_FILE \
+ (e_filter_file_get_type ())
+#define E_FILTER_FILE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_FILE, EFilterFile))
+#define E_FILTER_FILE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_FILE, EFilterFileClass))
+#define E_IS_FILTER_FILE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_FILE))
+#define E_IS_FILTER_FILE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_FILE))
+#define E_FILTER_FILE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_FILE, EFilterFileClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EFilterFile EFilterFile;
+typedef struct _EFilterFileClass EFilterFileClass;
+typedef struct _EFilterFilePrivate EFilterFilePrivate;
+
+struct _EFilterFile {
+ EFilterElement parent;
+ EFilterFilePrivate *priv;
+
+ gchar *type;
+ gchar *path;
+};
+
+struct _EFilterFileClass {
+ EFilterElementClass parent_class;
+};
+
+GType e_filter_file_get_type (void);
+EFilterFile * e_filter_file_new (void);
+EFilterFile * e_filter_file_new_type_name (const gchar *type);
+void e_filter_file_set_path (EFilterFile *file,
+ const gchar *path);
+
+G_END_DECLS
+
+#endif /* E_FILTER_FILE_H */
diff --git a/filter/e-filter-input.c b/filter/e-filter-input.c
new file mode 100644
index 0000000000..a8d6123905
--- /dev/null
+++ b/filter/e-filter-input.c
@@ -0,0 +1,322 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include <libedataserver/e-sexp.h>
+
+#include "e-util/e-error.h"
+
+#include "e-filter-input.h"
+
+static gpointer parent_class;
+
+static void
+filter_input_entry_changed (GtkEntry *entry,
+ EFilterElement *element)
+{
+ EFilterInput *input = E_FILTER_INPUT (element);
+ const gchar *text;
+
+ g_list_foreach (input->values, (GFunc) g_free, NULL);
+ g_list_free (input->values);
+
+ text = gtk_entry_get_text (entry);
+ input->values = g_list_append (NULL, g_strdup (text));
+}
+
+static void
+filter_input_finalize (GObject *object)
+{
+ EFilterInput *input = E_FILTER_INPUT (object);
+
+ xmlFree (input->type);
+
+ g_list_foreach (input->values, (GFunc)g_free, NULL);
+ g_list_free (input->values);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+filter_input_validate (EFilterElement *element,
+ GtkWindow *error_parent)
+{
+ EFilterInput *input = E_FILTER_INPUT (element);
+ gboolean valid = TRUE;
+
+ if (input->values && !strcmp (input->type, "regex")) {
+ const gchar *pattern;
+ regex_t regexpat;
+ gint regerr;
+
+ pattern = input->values->data;
+
+ if ((regerr = regcomp (&regexpat, pattern, REG_EXTENDED | REG_NEWLINE | REG_ICASE))) {
+ gsize reglen;
+ gchar *regmsg;
+
+ /* regerror gets called twice to get the full error string
+ length to do proper posix error reporting */
+ reglen = regerror (regerr, &regexpat, 0, 0);
+ regmsg = g_malloc0 (reglen + 1);
+ regerror (regerr, &regexpat, regmsg, reglen);
+
+ e_error_run (error_parent, "filter:bad-regexp", pattern, regmsg, NULL);
+ g_free (regmsg);
+
+ valid = FALSE;
+ }
+
+ regfree (&regexpat);
+ }
+
+ return valid;
+}
+
+static gint
+filter_input_eq (EFilterElement *element_a,
+ EFilterElement *element_b)
+{
+ EFilterInput *input_a = E_FILTER_INPUT (element_a);
+ EFilterInput *input_b = E_FILTER_INPUT (element_b);
+ GList *link_a;
+ GList *link_b;
+
+ /* Chain up to parent's eq() method. */
+ if (!E_FILTER_ELEMENT_CLASS (parent_class)->eq (element_a, element_b))
+ return FALSE;
+
+ if (g_strcmp0 (input_a->type, input_b->type) != 0)
+ return FALSE;
+
+ link_a = input_a->values;
+ link_b = input_b->values;
+
+ while (link_a != NULL && link_b != NULL) {
+ if (g_strcmp0 (link_a->data, link_b->data) != 0)
+ return FALSE;
+
+ link_a = g_list_next (link_a);
+ link_b = g_list_next (link_b);
+ }
+
+ if (link_a != NULL || link_b != NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+static xmlNodePtr
+filter_input_xml_encode (EFilterElement *element)
+{
+ EFilterInput *input = E_FILTER_INPUT (element);
+ xmlNodePtr value;
+ GList *link;
+ const gchar *type;
+
+ type = input->type ? input->type : "string";
+
+ value = xmlNewNode (NULL, (xmlChar *) "value");
+ xmlSetProp (value, (xmlChar *) "name", (xmlChar *) element->name);
+ xmlSetProp (value, (xmlChar *) "type", (xmlChar *) type);
+
+ for (link = input->values; link != NULL; link = g_list_next (link)) {
+ xmlChar *str = link->data;
+ xmlNodePtr cur;
+
+ cur = xmlNewChild (value, NULL, (xmlChar *)type, NULL);
+
+ str = xmlEncodeEntitiesReentrant (NULL, str);
+ xmlNodeSetContent (cur, str);
+ xmlFree (str);
+ }
+
+ return value;
+}
+
+static gint
+filter_input_xml_decode (EFilterElement *element, xmlNodePtr node)
+{
+ EFilterInput *input = (EFilterInput *)element;
+ gchar *name, *str, *type;
+ xmlNodePtr child;
+
+ g_list_foreach (input->values, (GFunc) g_free, NULL);
+ g_list_free (input->values);
+ input->values = NULL;
+
+ name = (gchar *) xmlGetProp (node, (xmlChar *) "name");
+ type = (gchar *) xmlGetProp (node, (xmlChar *) "type");
+
+ xmlFree (element->name);
+ element->name = name;
+
+ xmlFree (input->type);
+ input->type = type;
+
+ child = node->children;
+ while (child != NULL) {
+ if (!strcmp ((gchar *)child->name, type)) {
+ if (!(str = (gchar *)xmlNodeGetContent (child)))
+ str = (gchar *)xmlStrdup ((xmlChar *)"");
+
+ input->values = g_list_append (input->values, g_strdup (str));
+ xmlFree (str);
+ } else if (child->type == XML_ELEMENT_NODE) {
+ g_warning (
+ "Unknown node type '%s' encountered "
+ "decoding a %s\n", child->name, type);
+ }
+ child = child->next;
+ }
+
+ return 0;
+}
+
+static GtkWidget *
+filter_input_get_widget (EFilterElement *element)
+{
+ EFilterInput *input = E_FILTER_INPUT (element);
+ GtkWidget *entry;
+
+ entry = gtk_entry_new ();
+ if (input->values && input->values->data)
+ gtk_entry_set_text (
+ GTK_ENTRY (entry), input->values->data);
+
+ g_signal_connect (
+ entry, "changed",
+ G_CALLBACK (filter_input_entry_changed), element);
+
+ return entry;
+}
+
+static void
+filter_input_format_sexp (EFilterElement *element,
+ GString *out)
+{
+ EFilterInput *input = E_FILTER_INPUT (element);
+ GList *link;
+
+ for (link = input->values; link != NULL; link = g_list_next (link))
+ e_sexp_encode_string (out, link->data);
+}
+
+static void
+filter_input_class_init (EFilterInputClass *class)
+{
+ GObjectClass *object_class;
+ EFilterElementClass *filter_element_class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = filter_input_finalize;
+
+ filter_element_class = E_FILTER_ELEMENT_CLASS (class);
+ filter_element_class->validate = filter_input_validate;
+ filter_element_class->eq = filter_input_eq;
+ filter_element_class->xml_encode = filter_input_xml_encode;
+ filter_element_class->xml_decode = filter_input_xml_decode;
+ filter_element_class->get_widget = filter_input_get_widget;
+ filter_element_class->format_sexp = filter_input_format_sexp;
+}
+
+static void
+filter_input_init (EFilterInput *input)
+{
+ input->values = g_list_prepend (NULL, g_strdup (""));
+}
+
+GType
+e_filter_input_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterInputClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_input_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterInput),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) filter_input_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_FILTER_ELEMENT, "EFilterInput", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * filter_input_new:
+ *
+ * Create a new EFilterInput object.
+ *
+ * Return value: A new #EFilterInput object.
+ **/
+EFilterInput *
+e_filter_input_new (void)
+{
+ return g_object_new (E_TYPE_FILTER_INPUT, NULL);
+}
+
+EFilterInput *
+e_filter_input_new_type_name (const gchar *type)
+{
+ EFilterInput *input;
+
+ input = e_filter_input_new ();
+ input->type = (gchar *) xmlStrdup ((xmlChar *) type);
+
+ return input;
+}
+
+void
+e_filter_input_set_value (EFilterInput *input,
+ const gchar *value)
+{
+ g_return_if_fail (E_IS_FILTER_INPUT (input));
+
+ g_list_foreach (input->values, (GFunc) g_free, NULL);
+ g_list_free (input->values);
+
+ input->values = g_list_append (NULL, g_strdup (value));
+}
diff --git a/filter/e-filter-input.h b/filter/e-filter-input.h
new file mode 100644
index 0000000000..07239c92cb
--- /dev/null
+++ b/filter/e-filter-input.h
@@ -0,0 +1,74 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_INPUT_H
+#define E_FILTER_INPUT_H
+
+#include "e-filter-element.h"
+
+/* Standard GObject macros */
+#define E_TYPE_FILTER_INPUT \
+ (e_filter_input_get_type ())
+#define E_FILTER_INPUT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_INPUT, EFilterInput))
+#define E_FILTER_INPUT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_INPUT, EFilterInputClass))
+#define E_IS_FILTER_INPUT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_INPUT))
+#define E_IS_FILTER_INPUT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_INPUT))
+#define E_FILTER_INPUT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_INPUT, EFilterInputClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EFilterInput EFilterInput;
+typedef struct _EFilterInputClass EFilterInputClass;
+typedef struct _EFilterInputPrivate EFilterInputPrivate;
+
+struct _EFilterInput {
+ EFilterElement parent;
+ EFilterInputPrivate *priv;
+
+ gchar *type; /* name of type */
+ GList *values; /* strings */
+};
+
+struct _EFilterInputClass {
+ EFilterElementClass parent_class;
+};
+
+GType e_filter_input_get_type (void);
+EFilterInput * e_filter_input_new (void);
+EFilterInput * e_filter_input_new_type_name (const gchar *type);
+void e_filter_input_set_value (EFilterInput *input,
+ const gchar *value);
+
+G_END_DECLS
+
+#endif /* E_FILTER_INPUT_H */
diff --git a/filter/e-filter-int.c b/filter/e-filter-int.c
new file mode 100644
index 0000000000..f3fef9dc89
--- /dev/null
+++ b/filter/e-filter-int.c
@@ -0,0 +1,261 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <gtk/gtk.h>
+
+#include <libedataserver/e-sexp.h>
+
+#include "e-filter-int.h"
+
+static gpointer parent_class;
+
+static void
+filter_int_spin_changed (GtkSpinButton *spin_button,
+ EFilterElement *element)
+{
+ EFilterInt *filter_int = E_FILTER_INT (element);
+
+ filter_int->val = gtk_spin_button_get_value_as_int (spin_button);
+}
+
+static void
+filter_int_finalize (GObject *object)
+{
+ EFilterInt *filter_int = E_FILTER_INT (object);
+
+ g_free (filter_int->type);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gint
+filter_int_eq (EFilterElement *element_a,
+ EFilterElement *element_b)
+{
+ EFilterInt *filter_int_a = E_FILTER_INT (element_a);
+ EFilterInt *filter_int_b = E_FILTER_INT (element_b);
+
+ /* Chain up to parent's eq() method. */
+ if (!E_FILTER_ELEMENT_CLASS (parent_class)->eq (element_a, element_b))
+ return FALSE;
+
+ return (filter_int_a->val == filter_int_b->val);
+}
+
+static EFilterElement *
+filter_int_clone (EFilterElement *element)
+{
+ EFilterInt *filter_int = E_FILTER_INT (element);
+ EFilterInt *clone;
+
+ clone = e_filter_int_new_type (
+ filter_int->type, filter_int->min, filter_int->max);
+ clone->val = filter_int->val;
+
+ E_FILTER_ELEMENT (clone)->name = g_strdup (element->name);
+
+ return E_FILTER_ELEMENT (clone);
+}
+
+static xmlNodePtr
+filter_int_xml_encode (EFilterElement *element)
+{
+ EFilterInt *filter_int = E_FILTER_INT (element);
+ xmlNodePtr value;
+ gchar intval[32];
+ const gchar *type;
+
+ type = filter_int->type ? filter_int->type : "integer";
+
+ value = xmlNewNode (NULL, (xmlChar *)"value");
+ xmlSetProp (value, (xmlChar *) "name", (xmlChar *) element->name);
+ xmlSetProp (value, (xmlChar *) "type", (xmlChar *) type);
+
+ sprintf (intval, "%d", filter_int->val);
+ xmlSetProp (value, (xmlChar *)type, (xmlChar *)intval);
+
+ return value;
+}
+
+static gint
+filter_int_xml_decode (EFilterElement *element,
+ xmlNodePtr node)
+{
+ EFilterInt *filter_int = E_FILTER_INT (element);
+ gchar *name, *type;
+ gchar *intval;
+
+ name = (gchar *)xmlGetProp (node, (xmlChar *)"name");
+ xmlFree (element->name);
+ element->name = name;
+
+ type = (gchar *)xmlGetProp (node, (xmlChar *)"type");
+ g_free (filter_int->type);
+ filter_int->type = g_strdup (type);
+ xmlFree (type);
+
+ intval = (gchar *)xmlGetProp (node, (xmlChar *)(filter_int->type ? filter_int->type : "integer"));
+ if (intval) {
+ filter_int->val = atoi (intval);
+ xmlFree (intval);
+ } else {
+ filter_int->val = 0;
+ }
+
+ return 0;
+}
+
+static GtkWidget *
+filter_int_get_widget (EFilterElement *element)
+{
+ EFilterInt *filter_int = E_FILTER_INT (element);
+ GtkWidget *widget;
+ GtkObject *adjustment;
+
+ adjustment = gtk_adjustment_new (
+ 0.0, (gfloat) filter_int->min,
+ (gfloat) filter_int->max, 1.0, 1.0, 0);
+ widget = gtk_spin_button_new (
+ GTK_ADJUSTMENT (adjustment),
+ filter_int->max > filter_int->min + 1000 ? 5.0 : 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (widget), TRUE);
+
+ if (filter_int->val)
+ gtk_spin_button_set_value (
+ GTK_SPIN_BUTTON (widget), (gfloat) filter_int->val);
+
+ g_signal_connect (
+ widget, "value-changed",
+ G_CALLBACK (filter_int_spin_changed), element);
+
+ return widget;
+}
+
+static void
+filter_int_format_sexp (EFilterElement *element,
+ GString *out)
+{
+ EFilterInt *filter_int = E_FILTER_INT (element);
+
+ if (filter_int->val < 0)
+ /* See #364731 #457523 C6*/
+ g_string_append_printf (out, "(- %d)", (filter_int->val * -1));
+ else
+ g_string_append_printf (out, "%d", filter_int->val);
+}
+
+static void
+filter_int_class_init (EFilterIntClass *class)
+{
+ GObjectClass *object_class;
+ EFilterElementClass *filter_element_class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = filter_int_finalize;
+
+ filter_element_class = E_FILTER_ELEMENT_CLASS (class);
+ filter_element_class->eq = filter_int_eq;
+ filter_element_class->clone = filter_int_clone;
+ filter_element_class->xml_encode = filter_int_xml_encode;
+ 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;
+}
+
+static void
+filter_int_init (EFilterInt *filter_int)
+{
+ filter_int->min = 0;
+ filter_int->max = G_MAXINT;
+}
+
+GType
+e_filter_int_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterIntClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_int_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterInt),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) filter_int_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_FILTER_ELEMENT, "EFilterInt", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * filter_int_new:
+ *
+ * Create a new EFilterInt object.
+ *
+ * Return value: A new #EFilterInt object.
+ **/
+EFilterInt *
+e_filter_int_new (void)
+{
+ return g_object_new (E_TYPE_FILTER_INT, NULL);
+}
+
+EFilterInt *
+e_filter_int_new_type (const gchar *type,
+ gint min,
+ gint max)
+{
+ EFilterInt *filter_int;
+
+ filter_int = e_filter_int_new ();
+
+ filter_int->type = g_strdup (type);
+ filter_int->min = min;
+ filter_int->max = max;
+
+ return filter_int;
+}
+
+void
+e_filter_int_set_value (EFilterInt *filter_int,
+ gint value)
+{
+ g_return_if_fail (E_IS_FILTER_INT (filter_int));
+
+ filter_int->val = value;
+}
diff --git a/filter/e-filter-int.h b/filter/e-filter-int.h
new file mode 100644
index 0000000000..c9f613c9a3
--- /dev/null
+++ b/filter/e-filter-int.h
@@ -0,0 +1,77 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_INT_H
+#define E_FILTER_INT_H
+
+#include "e-filter-element.h"
+
+/* Standard GObject macros */
+#define E_TYPE_FILTER_INT \
+ (e_filter_int_get_type ())
+#define E_FILTER_INT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_INT, EFilterInt))
+#define E_FILTER_INT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_INT, EFilterIntClass))
+#define E_IS_FILTER_INT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_INT))
+#define E_IS_FILTER_INT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_INT))
+#define E_FILTER_INT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_INT, EFilterIntClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EFilterInt EFilterInt;
+typedef struct _EFilterIntClass EFilterIntClass;
+typedef struct _EFilterIntPrivate EFilterIntPrivate;
+
+struct _EFilterInt {
+ EFilterElement parent;
+ EFilterIntPrivate *priv;
+
+ gchar *type;
+ gint val;
+ gint min;
+ gint max;
+};
+
+struct _EFilterIntClass {
+ EFilterElementClass parent_class;
+};
+
+GType e_filter_int_get_type (void);
+EFilterInt * e_filter_int_new (void);
+EFilterInt * e_filter_int_new_type (const gchar *type,
+ gint min,
+ gint max);
+void e_filter_int_set_value (EFilterInt *f_int,
+ gint value);
+
+G_END_DECLS
+
+#endif /* E_FILTER_INT_H */
diff --git a/filter/e-filter-option.c b/filter/e-filter-option.c
new file mode 100644
index 0000000000..3765238a80
--- /dev/null
+++ b/filter/e-filter-option.c
@@ -0,0 +1,514 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <gmodule.h>
+
+#include <libedataserver/e-sexp.h>
+
+#include "e-filter-option.h"
+#include "e-filter-part.h"
+
+static gpointer parent_class;
+
+static void
+free_option (struct _filter_option *opt)
+{
+ g_free (opt->title);
+ g_free (opt->value);
+ g_free (opt->code);
+ g_free (opt);
+}
+
+static struct _filter_option *
+find_option (EFilterOption *option,
+ const gchar *name)
+{
+ GList *link;
+
+ for (link = option->options; link != NULL; link = g_list_next (link)) {
+ struct _filter_option *opt = link->data;
+
+ if (strcmp (name, opt->value) == 0)
+ return opt;
+ }
+
+ return NULL;
+}
+
+static void
+filter_option_combobox_changed (GtkComboBox *combo_box,
+ EFilterElement *element)
+{
+ EFilterOption *option = E_FILTER_OPTION (element);
+ gint active;
+
+ active = gtk_combo_box_get_active (combo_box);
+ option->current = g_list_nth_data (option->options, active);
+}
+
+static GSList *
+filter_option_get_dynamic_options (EFilterOption *option)
+{
+ GModule *module;
+ GSList *(*get_func)(void);
+ GSList *res = NULL;
+
+ if (!option || !option->dynamic_func)
+ return res;
+
+ module = g_module_open (NULL, G_MODULE_BIND_LAZY);
+
+ if (g_module_symbol (module, option->dynamic_func, (gpointer) &get_func)) {
+ res = get_func ();
+ } else {
+ g_warning ("optionlist dynamic fill function '%s' not found", option->dynamic_func);
+ }
+
+ g_module_close (module);
+
+ return res;
+}
+
+static void
+filter_option_finalize (GObject *object)
+{
+ EFilterOption *option = E_FILTER_OPTION (object);
+
+ g_list_foreach (option->options, (GFunc) free_option, NULL);
+ g_list_free (option->options);
+
+ g_free (option->dynamic_func);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gint
+filter_option_eq (EFilterElement *element_a,
+ EFilterElement *element_b)
+{
+ EFilterOption *option_a = E_FILTER_OPTION (element_a);
+ EFilterOption *option_b = E_FILTER_OPTION (element_b);
+
+ /* Chain up to parent's eq() method. */
+ if (!E_FILTER_ELEMENT_CLASS (parent_class)->eq (element_a, element_b))
+ return FALSE;
+
+ if (option_a->current == NULL && option_b->current == NULL)
+ return TRUE;
+
+ if (option_a->current == NULL || option_b->current == NULL)
+ return FALSE;
+
+ return (g_strcmp0 (option_a->current->value, option_b->current->value) == 0);
+}
+
+static void
+filter_option_xml_create (EFilterElement *element,
+ xmlNodePtr node)
+{
+ EFilterOption *option = E_FILTER_OPTION (element);
+ xmlNodePtr n, work;
+
+ /* Chain up to parent's xml_create() method. */
+ E_FILTER_ELEMENT_CLASS (parent_class)->xml_create (element, node);
+
+ n = node->children;
+ while (n) {
+ if (!strcmp ((gchar *)n->name, "option")) {
+ gchar *tmp, *value, *title = NULL, *code = NULL;
+
+ value = (gchar *)xmlGetProp (n, (xmlChar *)"value");
+ work = n->children;
+ while (work) {
+ if (!strcmp ((gchar *)work->name, "title") || !strcmp ((gchar *)work->name, "_title")) {
+ if (!title) {
+ if (!(tmp = (gchar *)xmlNodeGetContent (work)))
+ tmp = (gchar *)xmlStrdup ((xmlChar *)"");
+
+ title = g_strdup (tmp);
+ xmlFree (tmp);
+ }
+ } else if (!strcmp ((gchar *)work->name, "code")) {
+ if (!code) {
+ if (!(tmp = (gchar *)xmlNodeGetContent (work)))
+ tmp = (gchar *)xmlStrdup ((xmlChar *)"");
+
+ code = g_strdup (tmp);
+ xmlFree (tmp);
+ }
+ }
+ work = work->next;
+ }
+
+ e_filter_option_add (option, value, title, code, FALSE);
+ xmlFree (value);
+ g_free (title);
+ g_free (code);
+ } else if (g_str_equal ((gchar *)n->name, "dynamic")) {
+ if (option->dynamic_func) {
+ g_warning ("Only one 'dynamic' node is acceptable in the optionlist '%s'", element->name);
+ } else {
+ /* Expecting only one <dynamic func="cb" /> in the option list,
+ The 'cb' should be of this prototype:
+ GSList *cb (void);
+ returning GSList of struct _filter_option, all newly allocated, because it'll
+ be freed with g_free and g_slist_free. 'is_dynamic' member is ignored here.
+ */
+ xmlChar *fn;
+
+ fn = xmlGetProp (n, (xmlChar *)"func");
+ if (fn && *fn) {
+ GSList *items, *i;
+ struct _filter_option *op;
+
+ option->dynamic_func = g_strdup ((const gchar *)fn);
+
+ /* get options now, to have them available when reading saved rules */
+ items = filter_option_get_dynamic_options (option);
+ for (i = items; i; i = i->next) {
+ op = i->data;
+
+ if (op) {
+ e_filter_option_add (option, op->value, op->title, op->code, TRUE);
+ free_option (op);
+ }
+ }
+
+ g_slist_free (items);
+ } else {
+ g_warning ("Missing 'func' attribute within '%s' node in optionlist '%s'", n->name, element->name);
+ }
+
+ xmlFree (fn);
+ }
+ } else if (n->type == XML_ELEMENT_NODE) {
+ g_warning ("Unknown xml node within optionlist: %s\n", n->name);
+ }
+ n = n->next;
+ }
+}
+
+static xmlNodePtr
+filter_option_xml_encode (EFilterElement *element)
+{
+ EFilterOption *option = E_FILTER_OPTION (element);
+ xmlNodePtr value;
+
+ value = xmlNewNode (NULL, (xmlChar *) "value");
+ xmlSetProp (value, (xmlChar *) "name", (xmlChar *) element->name);
+ xmlSetProp (value, (xmlChar *) "type", (xmlChar *) option->type);
+ if (option->current)
+ xmlSetProp (value, (xmlChar *) "value", (xmlChar *)option->current->value);
+
+ return value;
+}
+
+static gint
+filter_option_xml_decode (EFilterElement *element,
+ xmlNodePtr node)
+{
+ EFilterOption *option = E_FILTER_OPTION (element);
+ gchar *value;
+
+ xmlFree (element->name);
+ element->name = (gchar *)xmlGetProp (node, (xmlChar *)"name");
+
+ value = (gchar *)xmlGetProp (node, (xmlChar *)"value");
+ if (value) {
+ option->current = find_option (option, value);
+ xmlFree (value);
+ } else {
+ option->current = NULL;
+ }
+
+ return 0;
+}
+
+static EFilterElement *
+filter_option_clone (EFilterElement *element)
+{
+ EFilterOption *option = E_FILTER_OPTION (element);
+ EFilterOption *clone_option;
+ EFilterElement *clone;
+ GList *link;
+
+ /* Chain up to parent's clone() method. */
+ clone = E_FILTER_ELEMENT_CLASS (parent_class)->clone (element);
+
+ clone_option = E_FILTER_OPTION (clone);
+
+ for (link = option->options; link != NULL; link = g_list_next (link)) {
+ struct _filter_option *op = link->data;
+ struct _filter_option *newop;
+
+ newop = e_filter_option_add (
+ clone_option, op->value,
+ op->title, op->code, op->is_dynamic);
+ if (option->current == op)
+ clone_option->current = newop;
+ }
+
+ clone_option->dynamic_func = g_strdup (option->dynamic_func);
+
+ return clone;
+}
+
+static GtkWidget *
+filter_option_get_widget (EFilterElement *element)
+{
+ EFilterOption *option = E_FILTER_OPTION (element);
+ GtkWidget *combobox;
+ GList *l;
+ struct _filter_option *op;
+ gint index = 0, current = 0;
+
+ if (option->dynamic_func) {
+ /* it is dynamically filled, thus remove all dynamics and put there the fresh ones */
+ GSList *items, *i;
+ GList *old_ops;
+ struct _filter_option *old_cur;
+
+ old_ops = option->options;
+ old_cur = option->current;
+ l = old_ops;
+
+ /* start with an empty list */
+ option->current = NULL;
+ option->options = NULL;
+
+ for (l = option->options; l; l = l->next) {
+ op = l->data;
+
+ if (op->is_dynamic) {
+ break;
+ } else {
+ e_filter_option_add (option, op->value, op->title, op->code, FALSE);
+ }
+ }
+
+ items = filter_option_get_dynamic_options (option);
+ for (i = items; i; i = i->next) {
+ op = i->data;
+
+ if (op) {
+ e_filter_option_add (option, op->value, op->title, op->code, TRUE);
+ free_option (op);
+ }
+ }
+
+ g_slist_free (items);
+
+ /* maybe some static left after those dynamic, add them too */
+ for (; l; l = l->next) {
+ op = l->data;
+
+ if (!op->is_dynamic)
+ e_filter_option_add (option, op->value, op->title, op->code, FALSE);
+ }
+
+ if (old_cur)
+ e_filter_option_set_current (option, old_cur->value);
+
+ /* free old list */
+ g_list_foreach (old_ops, (GFunc)free_option, NULL);
+ g_list_free (old_ops);
+ }
+
+ combobox = gtk_combo_box_new_text ();
+ l = option->options;
+ while (l) {
+ op = l->data;
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), _(op->title));
+
+ if (op == option->current)
+ current = index;
+
+ l = g_list_next (l);
+ index++;
+ }
+
+ g_signal_connect (
+ combobox, "changed",
+ G_CALLBACK (filter_option_combobox_changed), element);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), current);
+
+ return combobox;
+}
+
+static void
+filter_option_build_code (EFilterElement *element,
+ GString *out,
+ EFilterPart *part)
+{
+ EFilterOption *option = E_FILTER_OPTION (element);
+
+ if (option->current && option->current->code)
+ e_filter_part_expand_code (part, option->current->code, out);
+}
+
+static void
+filter_option_format_sexp (EFilterElement *element,
+ GString *out)
+{
+ EFilterOption *option = E_FILTER_OPTION (element);
+
+ if (option->current)
+ e_sexp_encode_string (out, option->current->value);
+}
+
+static void
+filter_option_class_init (EFilterOptionClass *class)
+{
+ GObjectClass *object_class;
+ EFilterElementClass *filter_element_class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = filter_option_finalize;
+
+ filter_element_class = E_FILTER_ELEMENT_CLASS (class);
+ filter_element_class->eq = filter_option_eq;
+ filter_element_class->xml_create = filter_option_xml_create;
+ filter_element_class->xml_encode = filter_option_xml_encode;
+ filter_element_class->xml_decode = filter_option_xml_decode;
+ filter_element_class->clone = filter_option_clone;
+ 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;
+}
+
+static void
+filter_option_init (EFilterOption *option)
+{
+ option->type = "option";
+ option->dynamic_func = NULL;
+}
+
+GType
+e_filter_option_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterOptionClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_option_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterOption),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) filter_option_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ E_TYPE_FILTER_ELEMENT, "EFilterOption", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * filter_option_new:
+ *
+ * Create a new EFilterOption object.
+ *
+ * Return value: A new #EFilterOption object.
+ **/
+EFilterOption *
+e_filter_option_new (void)
+{
+ return g_object_new (E_TYPE_FILTER_OPTION, NULL);
+}
+
+void
+e_filter_option_set_current (EFilterOption *option,
+ const gchar *name)
+{
+ g_return_if_fail (E_IS_FILTER_OPTION (option));
+
+ option->current = find_option (option, name);
+}
+
+/* used by implementers to add additional options */
+struct _filter_option *
+e_filter_option_add (EFilterOption *option,
+ const gchar *value,
+ const gchar *title,
+ const gchar *code,
+ gboolean is_dynamic)
+{
+ struct _filter_option *op;
+
+ g_return_val_if_fail (E_IS_FILTER_OPTION (option), NULL);
+ g_return_val_if_fail (find_option (option, value) == NULL, NULL);
+
+ op = g_malloc (sizeof (*op));
+ op->title = g_strdup (title);
+ op->value = g_strdup (value);
+ op->code = g_strdup (code);
+ op->is_dynamic = is_dynamic;
+
+ option->options = g_list_append (option->options, op);
+
+ if (option->current == NULL)
+ option->current = op;
+
+ return op;
+}
+
+const gchar *
+e_filter_option_get_current (EFilterOption *option)
+{
+ g_return_val_if_fail (E_IS_FILTER_OPTION (option), NULL);
+
+ if (option->current == NULL)
+ return NULL;
+
+ return option->current->value;
+}
+
+void
+e_filter_option_remove_all (EFilterOption *option)
+{
+ g_return_if_fail (E_IS_FILTER_OPTION (option));
+
+ g_list_foreach (option->options, (GFunc) free_option, NULL);
+ g_list_free (option->options);
+
+ option->options = NULL;
+ option->current = NULL;
+}
diff --git a/filter/e-filter-option.h b/filter/e-filter-option.h
new file mode 100644
index 0000000000..c42efe2fe3
--- /dev/null
+++ b/filter/e-filter-option.h
@@ -0,0 +1,92 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_OPTION_H
+#define E_FILTER_OPTION_H
+
+#include "e-filter-element.h"
+
+/* Standard GObject macros */
+#define E_TYPE_FILTER_OPTION \
+ (e_filter_option_get_type ())
+#define E_FILTER_OPTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_OPTION, EFilterOption))
+#define E_FILTER_OPTION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_OPTION, EFilterOptionClass))
+#define E_IS_FILTER_OPTION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_OPTION))
+#define E_IS_FILTER_OPTION_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_OPTION))
+#define E_FILTER_OPTION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_OPTION, EFilterOptionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EFilterOption EFilterOption;
+typedef struct _EFilterOptionClass EFilterOptionClass;
+typedef struct _EFilterOptionPrivate EFilterOptionPrivate;
+
+struct _filter_option {
+ gchar *title; /* button title */
+ gchar *value; /* value, if it has one */
+ gchar *code; /* used to string code segments together */
+
+ gboolean is_dynamic; /* whether is the option dynamic, FALSE if static */
+};
+
+struct _EFilterOption {
+ EFilterElement parent;
+ EFilterOptionPrivate *priv;
+
+ const gchar *type; /* static memory, type name written to xml */
+
+ GList *options;
+ struct _filter_option *current;
+ gchar *dynamic_func; /* name of the dynamic fill func, called in get_widget */
+};
+
+struct _EFilterOptionClass {
+ EFilterElementClass parent_class;
+};
+
+GType e_filter_option_get_type (void);
+EFilterOption * e_filter_option_new (void);
+void e_filter_option_set_current (EFilterOption *option,
+ const gchar *name);
+const gchar * e_filter_option_get_current (EFilterOption *option);
+struct _filter_option *
+ e_filter_option_add (EFilterOption *option,
+ const gchar *name,
+ const gchar *title,
+ const gchar *code,
+ gboolean is_dynamic);
+void e_filter_option_remove_all (EFilterOption *option);
+
+G_END_DECLS
+
+#endif /* E_FILTER_OPTION_H */
diff --git a/filter/e-filter-part.c b/filter/e-filter-part.c
new file mode 100644
index 0000000000..37e2bf61df
--- /dev/null
+++ b/filter/e-filter-part.c
@@ -0,0 +1,532 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jepartrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "e-filter-file.h"
+#include "e-filter-part.h"
+#include "e-rule-context.h"
+
+static gpointer parent_class;
+
+static void
+filter_part_finalize (GObject *object)
+{
+ EFilterPart *part = E_FILTER_PART (object);
+
+ g_list_foreach (part->elements, (GFunc) g_object_unref, NULL);
+ g_list_free (part->elements);
+
+ g_free (part->name);
+ g_free (part->title);
+ g_free (part->code);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+filter_part_class_init (EFilterPartClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = filter_part_finalize;
+}
+
+GType
+e_filter_part_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterPartClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_part_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterPart),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ G_TYPE_OBJECT, "EFilterPart", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * e_filter_part_new:
+ *
+ * Create a new EFilterPart object.
+ *
+ * Return value: A new #EFilterPart object.
+ **/
+EFilterPart *
+e_filter_part_new (void)
+{
+ return g_object_new (E_TYPE_FILTER_PART, NULL);
+}
+
+gboolean
+e_filter_part_validate (EFilterPart *part,
+ GtkWindow *error_parent)
+{
+ GList *link;
+
+ g_return_val_if_fail (E_IS_FILTER_PART (part), FALSE);
+
+ /* The part is valid if all of its elements are valid. */
+ for (link = part->elements; link != NULL; link = g_list_next (link)) {
+ EFilterElement *element = link->data;
+
+ if (!e_filter_element_validate (element, error_parent))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gint
+e_filter_part_eq (EFilterPart *part_a,
+ EFilterPart *part_b)
+{
+ GList *link_a, *link_b;
+
+ g_return_val_if_fail (E_IS_FILTER_PART (part_a), FALSE);
+ g_return_val_if_fail (E_IS_FILTER_PART (part_b), FALSE);
+
+ if (g_strcmp0 (part_a->name, part_b->name) != 0)
+ return FALSE;
+
+ if (g_strcmp0 (part_a->title, part_b->title) != 0)
+ return FALSE;
+
+ if (g_strcmp0 (part_a->code, part_b->code) != 0)
+ return FALSE;
+
+ link_a = part_a->elements;
+ link_b = part_b->elements;
+
+ while (link_a != NULL && link_b != NULL) {
+ EFilterElement *element_a = link_a->data;
+ EFilterElement *element_b = link_b->data;
+
+ if (!e_filter_element_eq (element_a, element_b))
+ return FALSE;
+
+ link_a = g_list_next (link_a);
+ link_b = g_list_next (link_b);
+ }
+
+ if (link_a != NULL || link_b != NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+gint
+e_filter_part_xml_create (EFilterPart *part,
+ xmlNodePtr node,
+ ERuleContext *context)
+{
+ xmlNodePtr n;
+ gchar *type, *str;
+ EFilterElement *el;
+
+ g_return_val_if_fail (E_IS_FILTER_PART (part), FALSE);
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), FALSE);
+
+ str = (gchar *)xmlGetProp (node, (xmlChar *)"name");
+ part->name = g_strdup (str);
+ if (str)
+ xmlFree (str);
+
+ n = node->children;
+ while (n) {
+ if (!strcmp ((gchar *)n->name, "input")) {
+ type = (gchar *)xmlGetProp (n, (xmlChar *)"type");
+ if (type != NULL
+ && (el = e_rule_context_new_element (context, type)) != NULL) {
+ e_filter_element_xml_create (el, n);
+ xmlFree (type);
+ part->elements = g_list_append (part->elements, el);
+ } else {
+ g_warning ("Invalid xml format, missing/unknown input type");
+ }
+ } else if (!strcmp ((gchar *)n->name, "title") || !strcmp ((gchar *)n->name, "_title")) {
+ if (!part->title) {
+ str = (gchar *)xmlNodeGetContent (n);
+ part->title = g_strdup (str);
+ if (str)
+ xmlFree (str);
+ }
+ } else if (!strcmp ((gchar *)n->name, "code")) {
+ if (!part->code) {
+ str = (gchar *)xmlNodeGetContent (n);
+ part->code = g_strdup (str);
+ if (str)
+ xmlFree (str);
+ }
+ } else if (n->type == XML_ELEMENT_NODE) {
+ g_warning ("Unknown part element in xml: %s\n", n->name);
+ }
+ n = n->next;
+ }
+
+ return 0;
+}
+
+xmlNodePtr
+e_filter_part_xml_encode (EFilterPart *part)
+{
+ xmlNodePtr node;
+ GList *link;
+
+ g_return_val_if_fail (E_IS_FILTER_PART (part), NULL);
+
+ node = xmlNewNode (NULL, (xmlChar *)"part");
+ xmlSetProp (node, (xmlChar *)"name", (xmlChar *)part->name);
+
+ for (link = part->elements; link != NULL; link = g_list_next (link)) {
+ EFilterElement *element = link->data;
+ xmlNodePtr value;
+
+ value = e_filter_element_xml_encode (element);
+ xmlAddChild (node, value);
+ }
+
+ return node;
+}
+
+gint
+e_filter_part_xml_decode (EFilterPart *part,
+ xmlNodePtr node)
+{
+ xmlNodePtr child;
+
+ g_return_val_if_fail (E_IS_FILTER_PART (part), -1);
+ g_return_val_if_fail (node != NULL, -1);
+
+ for (child = node->children; child != NULL; child = child->next) {
+ EFilterElement *element;
+ xmlChar *name;
+
+ if (strcmp ((gchar *) child->name, "value") != 0)
+ continue;
+
+ name = xmlGetProp (child, (xmlChar *) "name");
+ element = e_filter_part_find_element (part, (gchar *) name);
+ xmlFree (name);
+
+ if (element != NULL)
+ e_filter_element_xml_decode (element, child);
+ }
+
+ return 0;
+}
+
+EFilterPart *
+e_filter_part_clone (EFilterPart *part)
+{
+ EFilterPart *clone;
+ GList *link;
+
+ g_return_val_if_fail (E_IS_FILTER_PART (part), NULL);
+
+ clone = g_object_new (G_OBJECT_TYPE (part), NULL, NULL);
+ clone->name = g_strdup (part->name);
+ clone->title = g_strdup (part->title);
+ clone->code = g_strdup (part->code);
+
+ for (link = part->elements; link != NULL; link = g_list_next (link)) {
+ EFilterElement *element = link->data;
+ EFilterElement *clone_element;
+
+ clone_element = e_filter_element_clone (element);
+ clone->elements = g_list_append (clone->elements, clone_element);
+ }
+
+ return clone;
+}
+
+/* only copies values of matching parts in the right order */
+void
+e_filter_part_copy_values (EFilterPart *dst_part,
+ EFilterPart *src_part)
+{
+ GList *dst_link, *src_link;
+
+ g_return_if_fail (E_IS_FILTER_PART (dst_part));
+ g_return_if_fail (E_IS_FILTER_PART (src_part));
+
+ /* NOTE: we go backwards, it just works better that way */
+
+ /* for each source type, search the dest type for
+ a matching type in the same order */
+ src_link = g_list_last (src_part->elements);
+ dst_link = g_list_last (dst_part->elements);
+
+ while (src_link != NULL && dst_link != NULL) {
+ EFilterElement *src_element = src_link->data;
+ GList *link = dst_link;
+
+ while (link != NULL) {
+ EFilterElement *dst_element = link->data;
+ GType dst_type = G_OBJECT_TYPE (dst_element);
+ GType src_type = G_OBJECT_TYPE (src_element);
+
+ if (dst_type == src_type) {
+ e_filter_element_copy_value (
+ dst_element, src_element);
+ dst_link = g_list_previous (link);
+ break;
+ }
+
+ link = g_list_previous (link);
+ }
+
+ src_link = g_list_previous (src_link);
+ }
+}
+
+EFilterElement *
+e_filter_part_find_element (EFilterPart *part,
+ const gchar *name)
+{
+ GList *link;
+
+ g_return_val_if_fail (E_IS_FILTER_PART (part), NULL);
+
+ if (name == NULL)
+ return NULL;
+
+ for (link = part->elements; link != NULL; link = g_list_next (link)) {
+ EFilterElement *element = link->data;
+
+ if (g_strcmp0 (element->name, name) == 0)
+ return element;
+ }
+
+ return NULL;
+}
+
+GtkWidget *
+e_filter_part_get_widget (EFilterPart *part)
+{
+ GtkWidget *hbox;
+ GList *link;
+
+ g_return_val_if_fail (E_IS_FILTER_PART (part), NULL);
+
+ hbox = gtk_hbox_new (FALSE, 3);
+
+ for (link = part->elements; link != NULL; link = g_list_next (link)) {
+ EFilterElement *element = link->data;
+ GtkWidget *widget;
+
+ widget = e_filter_element_get_widget (element);
+ if (widget != NULL)
+ gtk_box_pack_start (
+ GTK_BOX (hbox), widget,
+ E_IS_FILTER_FILE (element),
+ E_IS_FILTER_FILE (element), 3);
+ }
+
+ gtk_widget_show_all (hbox);
+
+ return hbox;
+}
+
+/**
+ * e_filter_part_build_code:
+ * @part:
+ * @out:
+ *
+ * Outputs the code of a part.
+ **/
+void
+e_filter_part_build_code (EFilterPart *part,
+ GString *out)
+{
+ GList *link;
+
+ g_return_if_fail (E_IS_FILTER_PART (part));
+ g_return_if_fail (out != NULL);
+
+ if (part->code != NULL)
+ e_filter_part_expand_code (part, part->code, out);
+
+ for (link = part->elements; link != NULL; link = g_list_next (link)) {
+ EFilterElement *element = link->data;
+ e_filter_element_build_code (element, out, part);
+ }
+}
+
+/**
+ * e_filter_part_build_code_list:
+ * @l:
+ * @out:
+ *
+ * Construct a list of the filter parts code into
+ * a single string.
+ **/
+void
+e_filter_part_build_code_list (GList *list,
+ GString *out)
+{
+ GList *link;
+
+ g_return_if_fail (out != NULL);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ EFilterPart *part = link->data;
+
+ e_filter_part_build_code (part, out);
+ g_string_append (out, "\n ");
+ }
+}
+
+/**
+ * e_filter_part_find_list:
+ * @l:
+ * @name:
+ *
+ * Find a filter part stored in a list.
+ *
+ * Return value:
+ **/
+EFilterPart *
+e_filter_part_find_list (GList *list,
+ const gchar *name)
+{
+ GList *link;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ EFilterPart *part = link->data;
+
+ if (!g_strcmp0 (part->name, name) == 0)
+ return part;
+ }
+
+ return NULL;
+}
+
+/**
+ * e_filter_part_next_list:
+ * @l:
+ * @last: The last item retrieved, or NULL to start
+ * from the beginning of the list.
+ *
+ * Iterate through a filter part list.
+ *
+ * Return value: The next value in the list, or NULL if the
+ * list is expired.
+ **/
+EFilterPart *
+e_filter_part_next_list (GList *list,
+ EFilterPart *last)
+{
+ GList *link = list;
+
+ if (last != NULL) {
+ link = g_list_find (list, last);
+ if (link == NULL)
+ link = list;
+ else
+ link = link->next;
+ }
+
+ return (link != NULL) ? link->data : NULL;
+}
+
+/**
+ * e_filter_part_expand_code:
+ * @part:
+ * @str:
+ * @out:
+ *
+ * Expands the variables in string @str based on the values of the part.
+ **/
+void
+e_filter_part_expand_code (EFilterPart *part,
+ const gchar *source,
+ GString *out)
+{
+ const gchar *newstart, *start, *end;
+ gchar *name = alloca (32);
+ gint len, namelen = 32;
+
+ g_return_if_fail (E_IS_FILTER_PART (part));
+ g_return_if_fail (source != NULL);
+ g_return_if_fail (out != NULL);
+
+ start = source;
+
+ while (start && (newstart = strstr (start, "${"))
+ && (end = strstr (newstart+2, "}")) ) {
+ EFilterElement *element;
+
+ len = end - newstart - 2;
+ if (len + 1 > namelen) {
+ namelen = (len + 1) * 2;
+ name = g_alloca (namelen);
+ }
+ memcpy (name, newstart+2, len);
+ name[len] = 0;
+
+ element = e_filter_part_find_element (part, name);
+ if (element != NULL) {
+ g_string_append_printf (out, "%.*s", (gint)(newstart-start), start);
+ e_filter_element_format_sexp (element, out);
+#if 0
+ } else if ((val = g_hash_table_lookup (part->globals, name))) {
+ g_string_append_printf (out, "%.*s", newstart-start, start);
+ e_sexp_encode_string (out, val);
+#endif
+ } else {
+ g_string_append_printf (out, "%.*s", (gint)(end-start+1), start);
+ }
+ start = end + 1;
+ }
+
+ g_string_append (out, start);
+}
diff --git a/filter/e-filter-part.h b/filter/e-filter-part.h
new file mode 100644
index 0000000000..697cf8f787
--- /dev/null
+++ b/filter/e-filter-part.h
@@ -0,0 +1,104 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_PART_H
+#define E_FILTER_PART_H
+
+#include <gtk/gtk.h>
+#include "e-filter-input.h"
+
+/* Standard GObject macros */
+#define E_TYPE_FILTER_PART \
+ (e_filter_part_get_type ())
+#define E_FILTER_PART(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_PART, EFilterPart))
+#define E_FILTER_PART_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_PART, EFilterPartClass))
+#define E_IS_FILTER_PART(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_PART))
+#define E_IS_FILTER_PART_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_PART))
+#define E_FILTER_PART_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_PART, EFilterPartClass))
+
+G_BEGIN_DECLS
+
+struct _ERuleContext;
+
+typedef struct _EFilterPart EFilterPart;
+typedef struct _EFilterPartClass EFilterPartClass;
+typedef struct _EFilterPartPrivate EFilterPartPrivate;
+
+struct _EFilterPart {
+ GObject parent;
+ EFilterPartPrivate *priv;
+
+ gchar *name;
+ gchar *title;
+ gchar *code;
+ GList *elements;
+};
+
+struct _EFilterPartClass {
+ GObjectClass parent_class;
+};
+
+GType e_filter_part_get_type (void);
+EFilterPart * e_filter_part_new (void);
+gboolean e_filter_part_validate (EFilterPart *part,
+ GtkWindow *error_parent);
+gint e_filter_part_eq (EFilterPart *part_a,
+ EFilterPart *part_b);
+gint e_filter_part_xml_create (EFilterPart *part,
+ xmlNodePtr node,
+ struct _ERuleContext *rc);
+xmlNodePtr e_filter_part_xml_encode (EFilterPart *fe);
+gint e_filter_part_xml_decode (EFilterPart *fe,
+ xmlNodePtr node);
+EFilterPart * e_filter_part_clone (EFilterPart *part);
+void e_filter_part_copy_values (EFilterPart *dst_part,
+ EFilterPart *src_part);
+EFilterElement *e_filter_part_find_element (EFilterPart *part,
+ const gchar *name);
+GtkWidget * e_filter_part_get_widget (EFilterPart *part);
+void e_filter_part_build_code (EFilterPart *part,
+ GString *out);
+void e_filter_part_expand_code (EFilterPart *part,
+ const gchar *str,
+ GString *out);
+
+void e_filter_part_build_code_list (GList *list,
+ GString *out);
+EFilterPart * e_filter_part_find_list (GList *list,
+ const gchar *name);
+EFilterPart * e_filter_part_next_list (GList *list,
+ EFilterPart *last);
+
+G_END_DECLS
+
+#endif /* E_FILTER_PART_H */
diff --git a/filter/e-filter-rule.c b/filter/e-filter-rule.c
new file mode 100644
index 0000000000..f60230dc31
--- /dev/null
+++ b/filter/e-filter-rule.c
@@ -0,0 +1,1116 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "e-util/e-error.h"
+
+#include "e-filter-rule.h"
+#include "e-rule-context.h"
+
+#define E_FILTER_RULE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_FILTER_RULE, EFilterRulePrivate))
+
+typedef struct _FilterPartData FilterPartData;
+typedef struct _FilterRuleData FilterRuleData;
+
+struct _EFilterRulePrivate {
+ gint frozen;
+};
+
+struct _FilterPartData {
+ EFilterRule *rule;
+ ERuleContext *context;
+ EFilterPart *part;
+ GtkWidget *partwidget;
+ GtkWidget *container;
+};
+
+struct _FilterRuleData {
+ EFilterRule *rule;
+ ERuleContext *context;
+ GtkWidget *parts;
+};
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+
+static void
+filter_rule_grouping_changed_cb (GtkComboBox *combo_box,
+ EFilterRule *rule)
+{
+ rule->grouping = gtk_combo_box_get_active (combo_box);
+}
+
+static void
+filter_rule_threading_changed_cb (GtkComboBox *combo_box,
+ EFilterRule *rule)
+{
+ rule->threading = gtk_combo_box_get_active (combo_box);
+}
+
+static void
+part_combobox_changed (GtkComboBox *combobox,
+ FilterPartData *data)
+{
+ EFilterPart *part = NULL;
+ EFilterPart *newpart;
+ gint index, i;
+
+ index = gtk_combo_box_get_active (combobox);
+ for (i = 0, part = e_rule_context_next_part (data->context, part); part && i < index; i++, part = e_rule_context_next_part (data->context, part)) {
+ /* traverse until reached index */
+ }
+
+ g_return_if_fail (part != NULL);
+ g_return_if_fail (i == index);
+
+ /* dont update if we haven't changed */
+ if (!strcmp (part->title, data->part->title))
+ return;
+
+ /* here we do a widget shuffle, throw away the old widget/rulepart,
+ and create another */
+ if (data->partwidget)
+ gtk_container_remove (GTK_CONTAINER (data->container), data->partwidget);
+
+ newpart = e_filter_part_clone (part);
+ e_filter_part_copy_values (newpart, data->part);
+ e_filter_rule_replace_part (data->rule, data->part, newpart);
+ g_object_unref (data->part);
+ data->part = newpart;
+ data->partwidget = e_filter_part_get_widget (newpart);
+ if (data->partwidget)
+ gtk_box_pack_start (GTK_BOX (data->container), data->partwidget, TRUE, TRUE, 0);
+}
+
+static GtkWidget *
+get_rule_part_widget (ERuleContext *context,
+ EFilterPart *newpart,
+ EFilterRule *rule)
+{
+ EFilterPart *part = NULL;
+ GtkWidget *combobox;
+ GtkWidget *hbox;
+ GtkWidget *p;
+ gint index = 0, current = 0;
+ FilterPartData *data;
+
+ data = g_malloc0 (sizeof (*data));
+ data->rule = rule;
+ data->context = context;
+ data->part = newpart;
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ /* only set to automatically clean up the memory */
+ g_object_set_data_full ((GObject *) hbox, "data", data, g_free);
+
+ p = e_filter_part_get_widget (newpart);
+
+ data->partwidget = p;
+ data->container = hbox;
+
+ combobox = gtk_combo_box_new_text ();
+
+ /* sigh, this is a little ugly */
+ while ((part = e_rule_context_next_part (context, part))) {
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), _(part->title));
+
+ if (!strcmp (newpart->title, part->title))
+ current = index;
+
+ index++;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), current);
+ g_signal_connect (combobox, "changed", G_CALLBACK (part_combobox_changed), data);
+ gtk_widget_show (combobox);
+
+ gtk_box_pack_start (GTK_BOX (hbox), combobox, FALSE, FALSE, 0);
+ if (p)
+ gtk_box_pack_start (GTK_BOX (hbox), p, TRUE, TRUE, 0);
+
+ gtk_widget_show_all (hbox);
+
+ return hbox;
+}
+
+static void
+less_parts (GtkWidget *button,
+ FilterRuleData *data)
+{
+ EFilterPart *part;
+ GtkWidget *rule;
+ FilterPartData *part_data;
+
+ if (g_list_length (data->rule->parts) < 1)
+ return;
+
+ rule = g_object_get_data ((GObject *) button, "rule");
+ part_data = g_object_get_data ((GObject *) rule, "data");
+
+ g_return_if_fail (part_data != NULL);
+
+ part = part_data->part;
+
+ /* remove the part from the list */
+ e_filter_rule_remove_part (data->rule, part);
+ g_object_unref (part);
+
+ /* and from the display */
+ gtk_container_remove (GTK_CONTAINER (data->parts), rule);
+ gtk_container_remove (GTK_CONTAINER (data->parts), button);
+}
+
+static void
+attach_rule (GtkWidget *rule,
+ FilterRuleData *data,
+ EFilterPart *part, gint row)
+{
+ GtkWidget *remove;
+
+ gtk_table_attach (GTK_TABLE (data->parts), rule, 0, 1, row, row + 1,
+ GTK_EXPAND | GTK_FILL, 0, 0, 0);
+
+ remove = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
+ g_object_set_data ((GObject *) remove, "rule", rule);
+ g_signal_connect (remove, "clicked", G_CALLBACK (less_parts), data);
+ gtk_table_attach (GTK_TABLE (data->parts), remove, 1, 2, row, row + 1,
+ 0, 0, 0, 0);
+
+ gtk_widget_show (remove);
+}
+
+static void
+do_grab_focus_cb (GtkWidget *widget,
+ gpointer data)
+{
+ gboolean *done = (gboolean *) data;
+
+ if (*done)
+ return;
+
+ if (widget && GTK_WIDGET_CAN_FOCUS (widget)) {
+ *done = TRUE;
+ gtk_widget_grab_focus (widget);
+ }
+}
+
+static void
+more_parts (GtkWidget *button,
+ FilterRuleData *data)
+{
+ EFilterPart *new;
+
+ /* first make sure that the last part is ok */
+ if (data->rule->parts) {
+ EFilterPart *part;
+ GList *l;
+
+ l = g_list_last (data->rule->parts);
+ part = l->data;
+ if (!e_filter_part_validate (part, GTK_WINDOW (gtk_widget_get_toplevel (button))))
+ return;
+ }
+
+ /* create a new rule entry, use the first type of rule */
+ new = e_rule_context_next_part (data->context, NULL);
+ if (new) {
+ GtkWidget *w;
+ gint rows;
+
+ new = e_filter_part_clone (new);
+ e_filter_rule_add_part (data->rule, new);
+ w = get_rule_part_widget (data->context, new, data->rule);
+
+ rows = GTK_TABLE (data->parts)->nrows;
+ gtk_table_resize (GTK_TABLE (data->parts), rows + 1, 2);
+ attach_rule (w, data, new, rows);
+
+ if (GTK_IS_CONTAINER (w)) {
+ gboolean done = FALSE;
+
+ gtk_container_foreach (GTK_CONTAINER (w), do_grab_focus_cb, &done);
+ } else
+ gtk_widget_grab_focus (w);
+
+ /* also scroll down to see new part */
+ w = (GtkWidget*) g_object_get_data (G_OBJECT (button), "scrolled-window");
+ if (w) {
+ GtkAdjustment *adjustment;
+
+ adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (w));
+ if (adjustment) {
+ gdouble upper;
+
+ upper = gtk_adjustment_get_upper (adjustment);
+ gtk_adjustment_set_value (adjustment, upper);
+ }
+
+ }
+ }
+}
+
+static void
+name_changed (GtkEntry *entry,
+ EFilterRule *rule)
+{
+ g_free (rule->name);
+ rule->name = g_strdup (gtk_entry_get_text (entry));
+}
+
+GtkWidget *
+e_filter_rule_get_widget (EFilterRule *rule,
+ ERuleContext *context)
+{
+ EFilterRuleClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_RULE (rule), NULL);
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+
+ class = E_FILTER_RULE_GET_CLASS (rule);
+ g_return_val_if_fail (class->get_widget != NULL, NULL);
+
+ return class->get_widget (rule, context);
+}
+
+static void
+filter_rule_load_set (xmlNodePtr node,
+ EFilterRule *rule,
+ ERuleContext *context)
+{
+ xmlNodePtr work;
+ gchar *rulename;
+ EFilterPart *part;
+
+ work = node->children;
+ while (work) {
+ if (!strcmp ((gchar *)work->name, "part")) {
+ rulename = (gchar *)xmlGetProp (work, (xmlChar *)"name");
+ part = e_rule_context_find_part (context, rulename);
+ if (part) {
+ part = e_filter_part_clone (part);
+ e_filter_part_xml_decode (part, work);
+ e_filter_rule_add_part (rule, part);
+ } else {
+ g_warning ("cannot find rule part '%s'\n", rulename);
+ }
+ xmlFree (rulename);
+ } else if (work->type == XML_ELEMENT_NODE) {
+ g_warning ("Unknown xml node in part: %s", work->name);
+ }
+ work = work->next;
+ }
+}
+
+static void
+filter_rule_finalize (GObject *object)
+{
+ EFilterRule *rule = E_FILTER_RULE (object);
+
+ g_free (rule->name);
+ g_free (rule->source);
+
+ g_list_foreach (rule->parts, (GFunc) g_object_unref, NULL);
+ g_list_free (rule->parts);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gint
+filter_rule_validate (EFilterRule *rule,
+ GtkWindow *error_parent)
+{
+ gint valid = TRUE;
+ GList *parts;
+
+ if (!rule->name || !*rule->name) {
+ e_error_run (error_parent, "filter:no-name", NULL);
+
+ return FALSE;
+ }
+
+ /* validate rule parts */
+ parts = rule->parts;
+ valid = parts != NULL;
+ while (parts && valid) {
+ valid = e_filter_part_validate ((EFilterPart *) parts->data, error_parent);
+ parts = parts->next;
+ }
+
+ return valid;
+}
+
+static gint
+filter_rule_eq (EFilterRule *rule_a,
+ EFilterRule *rule_b)
+{
+ GList *link_a;
+ GList *link_b;
+
+ if (rule_a->enabled != rule_b->enabled)
+ return FALSE;
+
+ if (rule_a->grouping != rule_b->grouping)
+ return FALSE;
+
+ if (rule_a->threading != rule_b->threading)
+ return FALSE;
+
+ if (g_strcmp0 (rule_a->name, rule_b->name) != 0)
+ return FALSE;
+
+ if (g_strcmp0 (rule_a->source, rule_b->source) != 0)
+ return FALSE;
+
+ link_a = rule_a->parts;
+ link_b = rule_b->parts;
+
+ while (link_a != NULL && link_b != NULL) {
+ EFilterPart *part_a = link_a->data;
+ EFilterPart *part_b = link_b->data;
+
+ if (!e_filter_part_eq (part_a, part_b))
+ return FALSE;
+
+ link_a = g_list_next (link_a);
+ link_b = g_list_next (link_b);
+ }
+
+ if (link_a != NULL || link_b != NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+static xmlNodePtr
+filter_rule_xml_encode (EFilterRule *rule)
+{
+ xmlNodePtr node, set, work;
+ GList *l;
+
+ node = xmlNewNode (NULL, (xmlChar *)"rule");
+
+ xmlSetProp (node, (xmlChar *)"enabled", (xmlChar *)(rule->enabled ? "true" : "false"));
+
+ switch (rule->grouping) {
+ case E_FILTER_GROUP_ALL:
+ xmlSetProp (node, (xmlChar *)"grouping", (xmlChar *)"all");
+ break;
+ case E_FILTER_GROUP_ANY:
+ xmlSetProp (node, (xmlChar *)"grouping", (xmlChar *)"any");
+ break;
+ }
+
+ switch (rule->threading) {
+ case E_FILTER_THREAD_NONE:
+ break;
+ case E_FILTER_THREAD_ALL:
+ xmlSetProp (node, (xmlChar *)"threading", (xmlChar *)"all");
+ break;
+ case E_FILTER_THREAD_REPLIES:
+ xmlSetProp (node, (xmlChar *)"threading", (xmlChar *)"replies");
+ break;
+ case E_FILTER_THREAD_REPLIES_PARENTS:
+ xmlSetProp (node, (xmlChar *)"threading", (xmlChar *)"replies_parents");
+ break;
+ case E_FILTER_THREAD_SINGLE:
+ xmlSetProp (node, (xmlChar *)"threading", (xmlChar *)"single");
+ break;
+ }
+
+ if (rule->source) {
+ xmlSetProp (node, (xmlChar *)"source", (xmlChar *)rule->source);
+ } else {
+ /* set to the default filter type */
+ xmlSetProp (node, (xmlChar *)"source", (xmlChar *)"incoming");
+ }
+
+ if (rule->name) {
+ gchar *escaped = g_markup_escape_text (rule->name, -1);
+
+ work = xmlNewNode (NULL, (xmlChar *)"title");
+ xmlNodeSetContent (work, (xmlChar *)escaped);
+ xmlAddChild (node, work);
+
+ g_free (escaped);
+ }
+
+ set = xmlNewNode (NULL, (xmlChar *)"partset");
+ xmlAddChild (node, set);
+ l = rule->parts;
+ while (l) {
+ work = e_filter_part_xml_encode ((EFilterPart *) l->data);
+ xmlAddChild (set, work);
+ l = l->next;
+ }
+
+ return node;
+}
+
+static gint
+filter_rule_xml_decode (EFilterRule *rule,
+ xmlNodePtr node,
+ ERuleContext *context)
+{
+ xmlNodePtr work;
+ gchar *grouping;
+ gchar *source;
+
+ g_free (rule->name);
+ rule->name = NULL;
+
+ grouping = (gchar *)xmlGetProp (node, (xmlChar *)"enabled");
+ if (!grouping)
+ rule->enabled = TRUE;
+ else {
+ rule->enabled = strcmp (grouping, "false") != 0;
+ xmlFree (grouping);
+ }
+
+ grouping = (gchar *)xmlGetProp (node, (xmlChar *)"grouping");
+ if (!strcmp (grouping, "any"))
+ rule->grouping = E_FILTER_GROUP_ANY;
+ else
+ rule->grouping = E_FILTER_GROUP_ALL;
+ xmlFree (grouping);
+
+ rule->threading = E_FILTER_THREAD_NONE;
+ if (context->flags & E_RULE_CONTEXT_THREADING
+ && (grouping = (gchar *)xmlGetProp (node, (xmlChar *)"threading"))) {
+ if (!strcmp (grouping, "all"))
+ rule->threading = E_FILTER_THREAD_ALL;
+ else if (!strcmp (grouping, "replies"))
+ rule->threading = E_FILTER_THREAD_REPLIES;
+ else if (!strcmp (grouping, "replies_parents"))
+ rule->threading = E_FILTER_THREAD_REPLIES_PARENTS;
+ else if (!strcmp (grouping, "single"))
+ rule->threading = E_FILTER_THREAD_SINGLE;
+ xmlFree (grouping);
+ }
+
+ g_free (rule->source);
+ source = (gchar *)xmlGetProp (node, (xmlChar *)"source");
+ if (source) {
+ rule->source = g_strdup (source);
+ xmlFree (source);
+ } else {
+ /* default filter type */
+ rule->source = g_strdup ("incoming");
+ }
+
+ work = node->children;
+ while (work) {
+ if (!strcmp ((gchar *)work->name, "partset")) {
+ filter_rule_load_set (work, rule, context);
+ } else if (!strcmp ((gchar *)work->name, "title") || !strcmp ((gchar *)work->name, "_title")) {
+ if (!rule->name) {
+ gchar *str, *decstr = NULL;
+
+ str = (gchar *)xmlNodeGetContent (work);
+ if (str) {
+ decstr = g_strdup (_(str));
+ xmlFree (str);
+ }
+ rule->name = decstr;
+ }
+ }
+ work = work->next;
+ }
+
+ return 0;
+}
+
+static void
+filter_rule_build_code (EFilterRule *rule,
+ GString *out)
+{
+ switch (rule->threading) {
+ case E_FILTER_THREAD_NONE:
+ break;
+ case E_FILTER_THREAD_ALL:
+ g_string_append (out, " (match-threads \"all\" ");
+ break;
+ case E_FILTER_THREAD_REPLIES:
+ g_string_append (out, " (match-threads \"replies\" ");
+ break;
+ case E_FILTER_THREAD_REPLIES_PARENTS:
+ g_string_append (out, " (match-threads \"replies_parents\" ");
+ break;
+ case E_FILTER_THREAD_SINGLE:
+ g_string_append (out, " (match-threads \"single\" ");
+ break;
+ }
+
+ switch (rule->grouping) {
+ case E_FILTER_GROUP_ALL:
+ g_string_append (out, " (and\n ");
+ break;
+ case E_FILTER_GROUP_ANY:
+ g_string_append (out, " (or\n ");
+ break;
+ default:
+ g_warning ("Invalid grouping");
+ }
+
+ e_filter_part_build_code_list (rule->parts, out);
+ g_string_append (out, ")\n");
+
+ if (rule->threading != E_FILTER_THREAD_NONE)
+ g_string_append (out, ")\n");
+}
+
+static void
+filter_rule_copy (EFilterRule *dest, EFilterRule *src)
+{
+ GList *node;
+
+ dest->enabled = src->enabled;
+
+ g_free (dest->name);
+ dest->name = g_strdup (src->name);
+
+ g_free (dest->source);
+ dest->source = g_strdup (src->source);
+
+ dest->grouping = src->grouping;
+ dest->threading = src->threading;
+
+ if (dest->parts) {
+ g_list_foreach (dest->parts, (GFunc) g_object_unref, NULL);
+ g_list_free (dest->parts);
+ dest->parts = NULL;
+ }
+
+ node = src->parts;
+ while (node) {
+ EFilterPart *part;
+
+ part = e_filter_part_clone (node->data);
+ dest->parts = g_list_append (dest->parts, part);
+ node = node->next;
+ }
+}
+
+static GtkWidget *
+filter_rule_get_widget (EFilterRule *rule,
+ ERuleContext *context)
+{
+ GtkWidget *hbox, *vbox, *parts, *inruleame;
+ GtkWidget *add, *label, *name, *w;
+ GtkWidget *combobox;
+ GtkWidget *scrolledwindow;
+ GtkObject *hadj, *vadj;
+ GList *l;
+ gchar *text;
+ EFilterPart *part;
+ FilterRuleData *data;
+ gint rows, i;
+
+ /* this stuff should probably be a table, but the
+ rule parts need to be a vbox */
+ vbox = gtk_vbox_new (FALSE, 6);
+
+ label = gtk_label_new_with_mnemonic (_("R_ule name:"));
+ name = gtk_entry_new ();
+ gtk_label_set_mnemonic_widget ((GtkLabel *)label, name);
+
+ if (!rule->name) {
+ rule->name = g_strdup (_("Untitled"));
+ gtk_entry_set_text (GTK_ENTRY (name), rule->name);
+ /* FIXME: do we want the following code in the future? */
+ /*gtk_editable_select_region (GTK_EDITABLE (name), 0, -1);*/
+ } else {
+ gtk_entry_set_text (GTK_ENTRY (name), rule->name);
+ }
+
+ g_signal_connect (
+ name, "realize",
+ G_CALLBACK (gtk_widget_grab_focus), name);
+
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), name, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ g_signal_connect (name, "changed", G_CALLBACK (name_changed), rule);
+ gtk_widget_show (label);
+ gtk_widget_show (hbox);
+
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ text = g_strdup_printf ("<b>%s</b>", _("Find items that meet the following conditions"));
+ label = gtk_label_new (text);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ g_free (text);
+
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ inruleame = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), inruleame, TRUE, TRUE, 0);
+
+ /* this is the parts table, it should probably be inside a scrolling list */
+ rows = g_list_length (rule->parts);
+ parts = gtk_table_new (rows, 2, FALSE);
+
+ /* data for the parts part of the display */
+ data = g_malloc0 (sizeof (*data));
+ data->context = context;
+ data->rule = rule;
+ data->parts = parts;
+
+ /* only set to automatically clean up the memory */
+ g_object_set_data_full ((GObject *) vbox, "data", data, g_free);
+
+ hbox = gtk_hbox_new (FALSE, 3);
+
+ add = gtk_button_new_with_mnemonic (_("A_dd Condition"));
+ gtk_button_set_image (GTK_BUTTON (add), gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON));
+ g_signal_connect (add, "clicked", G_CALLBACK (more_parts), data);
+ gtk_box_pack_start (GTK_BOX (hbox), add, FALSE, FALSE, 0);
+
+ if (context->flags & E_RULE_CONTEXT_GROUPING) {
+ const gchar *thread_types[] = { N_("If all conditions are met"), N_("If any conditions are met") };
+
+ label = gtk_label_new_with_mnemonic (_("_Find items:"));
+ combobox = gtk_combo_box_new_text ();
+
+ for (i=0;i<2;i++) {
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), _(thread_types[i]));
+ }
+
+ gtk_label_set_mnemonic_widget ((GtkLabel *)label, combobox);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), rule->grouping);
+ gtk_widget_show (combobox);
+
+ gtk_box_pack_end (GTK_BOX (hbox), combobox, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ g_signal_connect (
+ combobox, "changed",
+ G_CALLBACK (filter_rule_grouping_changed_cb), rule);
+ }
+
+ if (context->flags & E_RULE_CONTEXT_THREADING) {
+ const gchar *thread_types[] = { N_("None"), N_("All related"), N_("Replies"), N_("Replies and parents"), N_("No reply or parent") };
+
+ label = gtk_label_new_with_mnemonic (_("I_nclude threads"));
+ combobox = gtk_combo_box_new_text ();
+
+ for (i=0;i<5;i++) {
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), _(thread_types[i]));
+ }
+
+ gtk_label_set_mnemonic_widget ((GtkLabel *)label, combobox);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), rule->threading);
+ gtk_widget_show (combobox);
+
+ gtk_box_pack_end (GTK_BOX (hbox), combobox, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ g_signal_connect (
+ combobox, "changed",
+ G_CALLBACK (filter_rule_threading_changed_cb), rule);
+ }
+
+ gtk_box_pack_start (GTK_BOX (inruleame), hbox, FALSE, FALSE, 3);
+
+ l = rule->parts;
+ i = 0;
+ while (l) {
+ part = l->data;
+ w = get_rule_part_widget (context, part, rule);
+ attach_rule (w, data, part, i++);
+ l = g_list_next (l);
+ }
+
+ hadj = gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0);
+ vadj = gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0);
+ scrolledwindow = gtk_scrolled_window_new (GTK_ADJUSTMENT (hadj), GTK_ADJUSTMENT (vadj));
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledwindow), parts);
+
+ gtk_box_pack_start (GTK_BOX (inruleame), scrolledwindow, TRUE, TRUE, 3);
+
+ gtk_widget_show_all (vbox);
+
+ g_object_set_data (G_OBJECT (add), "scrolled-window", scrolledwindow);
+
+ return vbox;
+}
+
+static void
+filter_rule_class_init (EFilterRuleClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EFilterRulePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = filter_rule_finalize;
+
+ class->validate = filter_rule_validate;
+ class->eq = filter_rule_eq;
+ class->xml_encode = filter_rule_xml_encode;
+ class->xml_decode = filter_rule_xml_decode;
+ class->build_code = filter_rule_build_code;
+ class->copy = filter_rule_copy;
+ class->get_widget = filter_rule_get_widget;
+
+ signals[CHANGED] = g_signal_new (
+ "changed",
+ E_TYPE_FILTER_RULE,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EFilterRuleClass, changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+filter_rule_init (EFilterRule *rule)
+{
+ rule->priv = E_FILTER_RULE_GET_PRIVATE (rule);
+ rule->enabled = TRUE;
+}
+
+GType
+e_filter_rule_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EFilterRuleClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) filter_rule_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EFilterRule),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) filter_rule_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ G_TYPE_OBJECT, "EFilterRule", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * filter_rule_new:
+ *
+ * Create a new EFilterRule object.
+ *
+ * Return value: A new #EFilterRule object.
+ **/
+EFilterRule *
+e_filter_rule_new (void)
+{
+ return g_object_new (E_TYPE_FILTER_RULE, NULL);
+}
+
+EFilterRule *
+e_filter_rule_clone (EFilterRule *rule)
+{
+ EFilterRule *clone;
+
+ g_return_val_if_fail (E_IS_FILTER_RULE (rule), NULL);
+
+ clone = g_object_new (G_OBJECT_TYPE (rule), NULL);
+ e_filter_rule_copy (clone, rule);
+
+ return clone;
+}
+
+void
+e_filter_rule_set_name (EFilterRule *rule,
+ const gchar *name)
+{
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+
+ if (g_strcmp0 (rule->name, name) == 0)
+ return;
+
+ g_free (rule->name);
+ rule->name = g_strdup (name);
+
+ e_filter_rule_emit_changed (rule);
+}
+
+void
+e_filter_rule_set_source (EFilterRule *rule,
+ const gchar *source)
+{
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+
+ if (g_strcmp0 (rule->source, source) == 0)
+ return;
+
+ g_free (rule->source);
+ rule->source = g_strdup (source);
+
+ e_filter_rule_emit_changed (rule);
+}
+
+gint
+e_filter_rule_validate (EFilterRule *rule,
+ GtkWindow *error_parent)
+{
+ EFilterRuleClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_RULE (rule), FALSE);
+
+ class = E_FILTER_RULE_GET_CLASS (rule);
+ g_return_val_if_fail (class->validate != NULL, FALSE);
+
+ return class->validate (rule, error_parent);
+}
+
+gint
+e_filter_rule_eq (EFilterRule *rule_a,
+ EFilterRule *rule_b)
+{
+ EFilterRuleClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_RULE (rule_a), FALSE);
+ g_return_val_if_fail (E_IS_FILTER_RULE (rule_b), FALSE);
+
+ class = E_FILTER_RULE_GET_CLASS (rule_a);
+ g_return_val_if_fail (class->eq != NULL, FALSE);
+
+ if (G_OBJECT_TYPE (rule_a) != G_OBJECT_TYPE (rule_b))
+ return FALSE;
+
+ return class->eq (rule_a, rule_b);
+}
+
+xmlNodePtr
+e_filter_rule_xml_encode (EFilterRule *rule)
+{
+ EFilterRuleClass *class;
+
+ g_return_val_if_fail (E_IS_FILTER_RULE (rule), NULL);
+
+ class = E_FILTER_RULE_GET_CLASS (rule);
+ g_return_val_if_fail (class->xml_encode != NULL, NULL);
+
+ return class->xml_encode (rule);
+}
+
+gint
+e_filter_rule_xml_decode (EFilterRule *rule,
+ xmlNodePtr node,
+ ERuleContext *context)
+{
+ EFilterRuleClass *class;
+ gint result;
+
+ g_return_val_if_fail (E_IS_FILTER_RULE (rule), FALSE);
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), FALSE);
+
+ class = E_FILTER_RULE_GET_CLASS (rule);
+ g_return_val_if_fail (class->xml_decode != NULL, FALSE);
+
+ rule->priv->frozen++;
+ result = class->xml_decode (rule, node, context);
+ rule->priv->frozen--;
+
+ e_filter_rule_emit_changed (rule);
+
+ return result;
+}
+
+void
+e_filter_rule_copy (EFilterRule *dst_rule,
+ EFilterRule *src_rule)
+{
+ EFilterRuleClass *class;
+
+ g_return_if_fail (E_IS_FILTER_RULE (dst_rule));
+ g_return_if_fail (E_IS_FILTER_RULE (src_rule));
+
+ class = E_FILTER_RULE_GET_CLASS (dst_rule);
+ g_return_if_fail (class->copy != NULL);
+
+ class->copy (dst_rule, src_rule);
+
+ e_filter_rule_emit_changed (dst_rule);
+}
+
+void
+e_filter_rule_add_part (EFilterRule *rule,
+ EFilterPart *part)
+{
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+ g_return_if_fail (E_IS_FILTER_PART (part));
+
+ rule->parts = g_list_append (rule->parts, part);
+
+ e_filter_rule_emit_changed (rule);
+}
+
+void
+e_filter_rule_remove_part (EFilterRule *rule,
+ EFilterPart *part)
+{
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+ g_return_if_fail (E_IS_FILTER_PART (part));
+
+ rule->parts = g_list_remove (rule->parts, part);
+
+ e_filter_rule_emit_changed (rule);
+}
+
+void
+e_filter_rule_replace_part (EFilterRule *rule,
+ EFilterPart *old_part,
+ EFilterPart *new_part)
+{
+ GList *link;
+
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+ g_return_if_fail (E_IS_FILTER_PART (old_part));
+ g_return_if_fail (E_IS_FILTER_PART (new_part));
+
+ link = g_list_find (rule->parts, old_part);
+ if (link != NULL)
+ link->data = new_part;
+ else
+ rule->parts = g_list_append (rule->parts, new_part);
+
+ e_filter_rule_emit_changed (rule);
+}
+
+void
+e_filter_rule_build_code (EFilterRule *rule,
+ GString *out)
+{
+ EFilterRuleClass *class;
+
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+ g_return_if_fail (out != NULL);
+
+ class = E_FILTER_RULE_GET_CLASS (rule);
+ g_return_if_fail (class->build_code != NULL);
+
+ class->build_code (rule, out);
+}
+
+void
+e_filter_rule_emit_changed (EFilterRule *rule)
+{
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+
+ if (rule->priv->frozen == 0)
+ g_signal_emit (rule, signals[CHANGED], 0);
+}
+
+EFilterRule *
+e_filter_rule_next_list (GList *list,
+ EFilterRule *last,
+ const gchar *source)
+{
+ GList *link = list;
+
+ if (last != NULL) {
+ link = g_list_find (link, last);
+ if (link == NULL)
+ link = list;
+ else
+ link = g_list_next (link);
+ }
+
+ if (source != NULL) {
+ while (link != NULL) {
+ EFilterRule *rule = link->data;
+
+ if (g_strcmp0 (rule->source, source) == 0)
+ break;
+
+ link = g_list_next (link);
+ }
+ }
+
+ return (link != NULL) ? link->data : NULL;
+}
+
+EFilterRule *
+e_filter_rule_find_list (GList * list,
+ const gchar *name,
+ const gchar *source)
+{
+ GList *link;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ EFilterRule *rule = link->data;
+
+ if (strcmp (rule->name, name) == 0)
+ if (source == NULL || (rule->source != NULL && strcmp (rule->source, source) == 0))
+ return rule;
+ }
+
+ return NULL;
+}
+
+#ifdef FOR_TRANSLATIONS_ONLY
+
+static gchar *list[] = {
+ N_("Incoming"), N_("Outgoing")
+};
+#endif
diff --git a/filter/e-filter-rule.h b/filter/e-filter-rule.h
new file mode 100644
index 0000000000..c84cdefb0e
--- /dev/null
+++ b/filter/e-filter-rule.h
@@ -0,0 +1,161 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FILTER_RULE_H
+#define E_FILTER_RULE_H
+
+#include <glib-object.h>
+
+#include "e-filter-part.h"
+
+/* Standard GObject macros */
+#define E_TYPE_FILTER_RULE \
+ (e_filter_rule_get_type ())
+#define E_FILTER_RULE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_FILTER_RULE, EFilterRule))
+#define E_FILTER_RULE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_FILTER_RULE, EFilterRuleClass))
+#define E_IS_FILTER_RULE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_FILTER_RULE))
+#define E_IS_FILTER_RULE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_FILTER_RULE))
+#define E_FILTER_RULE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_FILTER_RULE, EFilterRuleClass))
+
+G_BEGIN_DECLS
+
+struct _RuleContext;
+
+typedef struct _EFilterRule EFilterRule;
+typedef struct _EFilterRuleClass EFilterRuleClass;
+typedef struct _EFilterRulePrivate EFilterRulePrivate;
+
+enum _filter_grouping_t {
+ E_FILTER_GROUP_ALL, /* all rules must match */
+ E_FILTER_GROUP_ANY /* any rule must match */
+};
+
+/* threading, if the context supports it */
+enum _filter_threading_t {
+ E_FILTER_THREAD_NONE, /* don't add any thread matching */
+ E_FILTER_THREAD_ALL, /* add all possible threads */
+ E_FILTER_THREAD_REPLIES, /* add only replies */
+ E_FILTER_THREAD_REPLIES_PARENTS, /* replies plus parents */
+ E_FILTER_THREAD_SINGLE /* messages with no replies or parents */
+};
+
+#define E_FILTER_SOURCE_INCOMING "incoming" /* performed on incoming email */
+#define E_FILTER_SOURCE_DEMAND "demand" /* performed on the selected folder
+ * when the user asks for it */
+#define E_FILTER_SOURCE_OUTGOING "outgoing"/* performed on outgoing mail */
+#define E_FILTER_SOURCE_JUNKTEST "junktest"/* perform only junktest on incoming mail */
+
+struct _EFilterRule {
+ GObject parent_object;
+ EFilterRulePrivate *priv;
+
+ gchar *name;
+ gchar *source;
+
+ enum _filter_grouping_t grouping;
+ enum _filter_threading_t threading;
+
+ guint system:1; /* this is a system rule, cannot be edited/deleted */
+ GList *parts;
+
+ gboolean enabled;
+};
+
+struct _EFilterRuleClass {
+ GObjectClass parent_class;
+
+ /* virtual methods */
+ gint (*validate) (EFilterRule *rule,
+ GtkWindow *error_parent);
+ gint (*eq) (EFilterRule *rule_a,
+ EFilterRule *rule_b);
+
+ xmlNodePtr (*xml_encode) (EFilterRule *rule);
+ gint (*xml_decode) (EFilterRule *rule,
+ xmlNodePtr node,
+ struct _ERuleContext *context);
+
+ void (*build_code) (EFilterRule *rule,
+ GString *out);
+
+ void (*copy) (EFilterRule *dst_rule,
+ EFilterRule *src_rule);
+
+ GtkWidget * (*get_widget) (EFilterRule *rule,
+ struct _ERuleContext *context);
+
+ /* signals */
+ void (*changed) (EFilterRule *rule);
+};
+
+GType e_filter_rule_get_type (void);
+EFilterRule * e_filter_rule_new (void);
+EFilterRule * e_filter_rule_clone (EFilterRule *rule);
+void e_filter_rule_set_name (EFilterRule *rule,
+ const gchar *name);
+void e_filter_rule_set_source (EFilterRule *rule,
+ const gchar *source);
+gint e_filter_rule_validate (EFilterRule *rule,
+ GtkWindow *error_parent);
+gint e_filter_rule_eq (EFilterRule *rule_a,
+ EFilterRule *rule_b);
+xmlNodePtr e_filter_rule_xml_encode (EFilterRule *rule);
+gint e_filter_rule_xml_decode (EFilterRule *rule,
+ xmlNodePtr node,
+ struct _ERuleContext *context);
+void e_filter_rule_copy (EFilterRule *dst_rule,
+ EFilterRule *src_rule);
+void e_filter_rule_add_part (EFilterRule *rule,
+ EFilterPart *part);
+void e_filter_rule_remove_part (EFilterRule *rule,
+ EFilterPart *part);
+void e_filter_rule_replace_part (EFilterRule *rule,
+ EFilterPart *old_part,
+ EFilterPart *new_part);
+GtkWidget * e_filter_rule_get_widget (EFilterRule *rule,
+ struct _ERuleContext *context);
+void e_filter_rule_build_code (EFilterRule *rule,
+ GString *out);
+void e_filter_rule_emit_changed (EFilterRule *rule);
+
+/* static functions */
+EFilterRule * e_filter_rule_next_list (GList *list,
+ EFilterRule *last,
+ const gchar *source);
+EFilterRule * e_filter_rule_find_list (GList *list,
+ const gchar *name,
+ const gchar *source);
+
+G_END_DECLS
+
+#endif /* E_FILTER_RULE_H */
diff --git a/filter/e-rule-context.c b/filter/e-rule-context.c
new file mode 100644
index 0000000000..648011b8e5
--- /dev/null
+++ b/filter/e-rule-context.c
@@ -0,0 +1,988 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include <glib/gi18n.h>
+
+#include <libedataserver/e-xml-utils.h>
+
+#include "e-util/e-error.h"
+#include "e-util/e-xml-utils.h"
+
+#include "e-filter-code.h"
+#include "e-filter-color.h"
+#include "e-filter-datespec.h"
+#include "e-filter-file.h"
+#include "e-filter-input.h"
+#include "e-filter-int.h"
+#include "e-filter-option.h"
+#include "e-filter-rule.h"
+#include "e-rule-context.h"
+
+#define E_RULE_CONTEXT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_RULE_CONTEXT, ERuleContextPrivate))
+
+struct _ERuleContextPrivate {
+ gint frozen;
+};
+
+enum {
+ RULE_ADDED,
+ RULE_REMOVED,
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+
+struct _revert_data {
+ GHashTable *rules;
+ gint rank;
+};
+
+static void
+rule_context_set_error (ERuleContext *context,
+ gchar *error)
+{
+ g_free (context->error);
+ context->error = error;
+}
+
+static void
+new_rule_response (GtkWidget *dialog,
+ gint button,
+ ERuleContext *context)
+{
+ if (button == GTK_RESPONSE_OK) {
+ EFilterRule *rule = g_object_get_data ((GObject *) dialog, "rule");
+ gchar *user = g_object_get_data ((GObject *) dialog, "path");
+
+ if (!e_filter_rule_validate (rule, GTK_WINDOW (dialog))) {
+ /* no need to popup a dialog because the validate code does that. */
+ return;
+ }
+
+ if (e_rule_context_find_rule (context, rule->name, rule->source)) {
+ e_error_run ((GtkWindow *)dialog, "filter:bad-name-notunique", rule->name, NULL);
+
+ return;
+ }
+
+ g_object_ref (rule);
+ e_rule_context_add_rule (context, rule);
+ if (user)
+ e_rule_context_save (context, user);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+revert_rule_remove (gpointer key,
+ EFilterRule *rule,
+ ERuleContext *context)
+{
+ e_rule_context_remove_rule (context, rule);
+ g_object_unref (rule);
+}
+
+static void
+revert_source_remove (gpointer key,
+ struct _revert_data *rest_data,
+ ERuleContext *context)
+{
+ g_hash_table_foreach (
+ rest_data->rules, (GHFunc) revert_rule_remove, context);
+ g_hash_table_destroy (rest_data->rules);
+ g_free (rest_data);
+}
+
+static guint
+source_hashf (const gchar *a)
+{
+ return (a != NULL) ? g_str_hash (a) : 0;
+}
+
+static gint
+source_eqf (const gchar *a,
+ const gchar *b)
+{
+ return (g_strcmp0 (a, b) == 0);
+}
+
+static void
+free_part_set (struct _part_set_map *map)
+{
+ g_free (map->name);
+ g_free (map);
+}
+
+static void
+free_rule_set (struct _rule_set_map *map)
+{
+ g_free (map->name);
+ g_free (map);
+}
+
+static void
+rule_context_finalize (GObject *obj)
+{
+ ERuleContext *context =(ERuleContext *) obj;
+
+ g_list_foreach (context->rule_set_list, (GFunc)free_rule_set, NULL);
+ g_list_free (context->rule_set_list);
+ g_hash_table_destroy (context->rule_set_map);
+
+ g_list_foreach (context->part_set_list, (GFunc)free_part_set, NULL);
+ g_list_free (context->part_set_list);
+ g_hash_table_destroy (context->part_set_map);
+
+ g_free (context->error);
+
+ g_list_foreach (context->parts, (GFunc)g_object_unref, NULL);
+ g_list_free (context->parts);
+
+ g_list_foreach (context->rules, (GFunc)g_object_unref, NULL);
+ g_list_free (context->rules);
+
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static gint
+rule_context_load (ERuleContext *context,
+ const gchar *system,
+ const gchar *user)
+{
+ xmlNodePtr set, rule, root;
+ xmlDocPtr systemdoc, userdoc;
+ struct _part_set_map *part_map;
+ struct _rule_set_map *rule_map;
+
+ rule_context_set_error (context, NULL);
+
+ systemdoc = e_xml_parse_file (system);
+ if (systemdoc == NULL) {
+ rule_context_set_error (context, g_strdup_printf ("Unable to load system rules '%s': %s",
+ system, g_strerror (errno)));
+ return -1;
+ }
+
+ root = xmlDocGetRootElement (systemdoc);
+ if (root == NULL || strcmp ((gchar *)root->name, "filterdescription")) {
+ rule_context_set_error (context, g_strdup_printf ("Unable to load system rules '%s': Invalid format", system));
+ xmlFreeDoc (systemdoc);
+ return -1;
+ }
+ /* doesn't matter if this doens't exist */
+ userdoc = NULL;
+ if (g_file_test (user, G_FILE_TEST_IS_REGULAR))
+ userdoc = e_xml_parse_file (user);
+
+ /* now parse structure */
+ /* get rule parts */
+ set = root->children;
+ while (set) {
+ part_map = g_hash_table_lookup (context->part_set_map, set->name);
+ if (part_map) {
+ rule = set->children;
+ while (rule) {
+ if (!strcmp ((gchar *)rule->name, "part")) {
+ EFilterPart *part = E_FILTER_PART (g_object_new (part_map->type, NULL, NULL));
+
+ if (e_filter_part_xml_create (part, rule, context) == 0) {
+ part_map->append (context, part);
+ } else {
+ g_object_unref (part);
+ g_warning ("Cannot load filter part");
+ }
+ }
+ rule = rule->next;
+ }
+ } else if ((rule_map = g_hash_table_lookup (context->rule_set_map, set->name))) {
+ rule = set->children;
+ while (rule) {
+ if (!strcmp ((gchar *)rule->name, "rule")) {
+ EFilterRule *part = E_FILTER_RULE (g_object_new (rule_map->type, NULL, NULL));
+
+ if (e_filter_rule_xml_decode (part, rule, context) == 0) {
+ part->system = TRUE;
+ rule_map->append (context, part);
+ } else {
+ g_object_unref (part);
+ g_warning ("Cannot load filter part");
+ }
+ }
+ rule = rule->next;
+ }
+ }
+ set = set->next;
+ }
+
+ /* now load actual rules */
+ if (userdoc) {
+ root = xmlDocGetRootElement (userdoc);
+ set = root?root->children:NULL;
+ while (set) {
+ rule_map = g_hash_table_lookup (context->rule_set_map, set->name);
+ if (rule_map) {
+ rule = set->children;
+ while (rule) {
+ if (!strcmp ((gchar *)rule->name, "rule")) {
+ EFilterRule *part = E_FILTER_RULE (g_object_new (rule_map->type, NULL, NULL));
+
+ if (e_filter_rule_xml_decode (part, rule, context) == 0) {
+ rule_map->append (context, part);
+ } else {
+ g_object_unref (part);
+ g_warning ("Cannot load filter part");
+ }
+ }
+ rule = rule->next;
+ }
+ }
+ set = set->next;
+ }
+ }
+
+ xmlFreeDoc (userdoc);
+ xmlFreeDoc (systemdoc);
+
+ return 0;
+}
+
+static gint
+rule_context_save (ERuleContext *context,
+ const gchar *user)
+{
+ xmlDocPtr doc;
+ xmlNodePtr root, rules, work;
+ GList *l;
+ EFilterRule *rule;
+ struct _rule_set_map *map;
+ gint ret;
+
+ doc = xmlNewDoc ((xmlChar *)"1.0");
+ /* FIXME: set character encoding to UTF-8? */
+ root = xmlNewDocNode (doc, NULL, (xmlChar *)"filteroptions", NULL);
+ xmlDocSetRootElement (doc, root);
+ l = context->rule_set_list;
+ while (l) {
+ map = l->data;
+ rules = xmlNewDocNode (doc, NULL, (xmlChar *)map->name, NULL);
+ xmlAddChild (root, rules);
+ rule = NULL;
+ while ((rule = map->next (context, rule, NULL))) {
+ if (!rule->system) {
+ work = e_filter_rule_xml_encode (rule);
+ xmlAddChild (rules, work);
+ }
+ }
+ l = g_list_next (l);
+ }
+
+ ret = e_xml_save_file (user, doc);
+
+ xmlFreeDoc (doc);
+
+ return ret;
+}
+
+static gint
+rule_context_revert (ERuleContext *context,
+ const gchar *user)
+{
+ xmlNodePtr set, rule;
+ /*struct _part_set_map *part_map;*/
+ struct _rule_set_map *rule_map;
+ struct _revert_data *rest_data;
+ GHashTable *source_hash;
+ xmlDocPtr userdoc;
+ EFilterRule *frule;
+
+ rule_context_set_error (context, NULL);
+
+ userdoc = e_xml_parse_file (user);
+ if (userdoc == NULL)
+ /* clear out anythign we have? */
+ return 0;
+
+ source_hash = g_hash_table_new ((GHashFunc)source_hashf, (GCompareFunc)source_eqf);
+
+ /* setup stuff we have now */
+ /* Note that we assume there is only 1 set of rules in a given rule context,
+ although other parts of the code dont assume this */
+ frule = NULL;
+ while ((frule = e_rule_context_next_rule (context, frule, NULL))) {
+ rest_data = g_hash_table_lookup (source_hash, frule->source);
+ if (rest_data == NULL) {
+ rest_data = g_malloc0 (sizeof (*rest_data));
+ rest_data->rules = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (source_hash, frule->source, rest_data);
+ }
+ g_hash_table_insert (rest_data->rules, frule->name, frule);
+ }
+
+ /* make what we have, match what we load */
+ set = xmlDocGetRootElement (userdoc);
+ set = set?set->children:NULL;
+ while (set) {
+ rule_map = g_hash_table_lookup (context->rule_set_map, set->name);
+ if (rule_map) {
+ rule = set->children;
+ while (rule) {
+ if (!strcmp ((gchar *)rule->name, "rule")) {
+ EFilterRule *part = E_FILTER_RULE (g_object_new (rule_map->type, NULL, NULL));
+
+ if (e_filter_rule_xml_decode (part, rule, context) == 0) {
+ /* use the revert data to keep track of the right rank of this rule part */
+ rest_data = g_hash_table_lookup (source_hash, part->source);
+ if (rest_data == NULL) {
+ rest_data = g_malloc0 (sizeof (*rest_data));
+ rest_data->rules = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (source_hash, part->source, rest_data);
+ }
+ frule = g_hash_table_lookup (rest_data->rules, part->name);
+ if (frule) {
+ if (context->priv->frozen == 0 && !e_filter_rule_eq (frule, part))
+ e_filter_rule_copy (frule, part);
+
+ g_object_unref (part);
+ e_rule_context_rank_rule (context, frule, frule->source, rest_data->rank);
+ g_hash_table_remove (rest_data->rules, frule->name);
+ } else {
+ e_rule_context_add_rule (context, part);
+ e_rule_context_rank_rule (context, part, part->source, rest_data->rank);
+ }
+ rest_data->rank++;
+ } else {
+ g_object_unref (part);
+ g_warning ("Cannot load filter part");
+ }
+ }
+ rule = rule->next;
+ }
+ }
+ set = set->next;
+ }
+
+ xmlFreeDoc (userdoc);
+
+ /* remove any we still have that weren't in the file */
+ g_hash_table_foreach (source_hash, (GHFunc)revert_source_remove, context);
+ g_hash_table_destroy (source_hash);
+
+ return 0;
+}
+
+static EFilterElement *
+rule_context_new_element (ERuleContext *context,
+ const gchar *type)
+{
+ if (!strcmp (type, "string")) {
+ return (EFilterElement *) e_filter_input_new ();
+ } else if (!strcmp (type, "address")) {
+ /* FIXME: temporary ... need real address type */
+ return (EFilterElement *) e_filter_input_new_type_name (type);
+ } else if (!strcmp (type, "code")) {
+ return (EFilterElement *) e_filter_code_new (FALSE);
+ } else if (!strcmp (type, "rawcode")) {
+ return (EFilterElement *) e_filter_code_new (TRUE);
+ } else if (!strcmp (type, "colour")) {
+ return (EFilterElement *) e_filter_color_new ();
+ } else if (!strcmp (type, "optionlist")) {
+ return (EFilterElement *) e_filter_option_new ();
+ } else if (!strcmp (type, "datespec")) {
+ return (EFilterElement *) e_filter_datespec_new ();
+ } else if (!strcmp (type, "command")) {
+ return (EFilterElement *) e_filter_file_new_type_name (type);
+ } else if (!strcmp (type, "file")) {
+ return (EFilterElement *) e_filter_file_new_type_name (type);
+ } else if (!strcmp (type, "integer")) {
+ return (EFilterElement *) e_filter_int_new ();
+ } else if (!strcmp (type, "regex")) {
+ return (EFilterElement *) e_filter_input_new_type_name (type);
+ }else if (!strcmp (type, "completedpercent")) {
+ return (EFilterElement *) e_filter_int_new_type ("completedpercent", 0,100);
+
+ } else {
+ g_warning ("Unknown filter type '%s'", type);
+ return NULL;
+ }
+}
+
+static void
+rule_context_class_init (ERuleContextClass *class)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ERuleContextPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = rule_context_finalize;
+
+ class->load = rule_context_load;
+ class->save = rule_context_save;
+ class->revert = rule_context_revert;
+ class->new_element = rule_context_new_element;
+
+ signals[RULE_ADDED] = g_signal_new (
+ "rule-added",
+ E_TYPE_RULE_CONTEXT,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ERuleContextClass, rule_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+
+ signals[RULE_REMOVED] = g_signal_new (
+ "rule-removed",
+ E_TYPE_RULE_CONTEXT,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ERuleContextClass, rule_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+
+ signals[CHANGED] = g_signal_new (
+ "changed",
+ E_TYPE_RULE_CONTEXT,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ERuleContextClass, changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+rule_context_init (ERuleContext *context)
+{
+ context->priv = E_RULE_CONTEXT_GET_PRIVATE (context);
+
+ context->part_set_map = g_hash_table_new (g_str_hash, g_str_equal);
+ context->rule_set_map = g_hash_table_new (g_str_hash, g_str_equal);
+
+ context->flags = E_RULE_CONTEXT_GROUPING;
+}
+
+GType
+e_rule_context_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (ERuleContextClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) rule_context_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (ERuleContext),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) rule_context_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ G_TYPE_OBJECT, "ERuleContext", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * e_rule_context_new:
+ *
+ * Create a new ERuleContext object.
+ *
+ * Return value: A new #ERuleContext object.
+ **/
+ERuleContext *
+e_rule_context_new (void)
+{
+ return g_object_new (E_TYPE_RULE_CONTEXT, NULL);
+}
+
+void
+e_rule_context_add_part_set (ERuleContext *context,
+ const gchar *setname,
+ GType part_type,
+ ERuleContextPartFunc append,
+ ERuleContextNextPartFunc next)
+{
+ struct _part_set_map *map;
+
+ g_return_if_fail (E_IS_RULE_CONTEXT (context));
+ g_return_if_fail (setname != NULL);
+ g_return_if_fail (append != NULL);
+ g_return_if_fail (next != NULL);
+
+ g_return_if_fail (g_hash_table_lookup (context->part_set_map, setname) == NULL);
+
+ map = g_malloc0 (sizeof (*map));
+ map->type = part_type;
+ map->append = append;
+ map->next = next;
+ map->name = g_strdup (setname);
+ g_hash_table_insert (context->part_set_map, map->name, map);
+ context->part_set_list = g_list_append (context->part_set_list, map);
+}
+
+void
+e_rule_context_add_rule_set (ERuleContext *context,
+ const gchar *setname,
+ GType rule_type,
+ ERuleContextRuleFunc append,
+ ERuleContextNextRuleFunc next)
+{
+ struct _rule_set_map *map;
+
+ g_return_if_fail (E_IS_RULE_CONTEXT (context));
+ g_return_if_fail (setname != NULL);
+ g_return_if_fail (append != NULL);
+ g_return_if_fail (next != NULL);
+
+ g_return_if_fail (g_hash_table_lookup (context->rule_set_map, setname) == NULL);
+
+ map = g_malloc0 (sizeof (*map));
+ map->type = rule_type;
+ map->append = append;
+ map->next = next;
+ map->name = g_strdup (setname);
+ g_hash_table_insert (context->rule_set_map, map->name, map);
+ context->rule_set_list = g_list_append (context->rule_set_list, map);
+}
+
+/**
+ * e_rule_context_load:
+ * @f:
+ * @system:
+ * @user:
+ *
+ * Load a rule context from a system and user description file.
+ *
+ * Return value:
+ **/
+gint
+e_rule_context_load (ERuleContext *context,
+ const gchar *system,
+ const gchar *user)
+{
+ ERuleContextClass *class;
+ gint result;
+
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
+ g_return_val_if_fail (system != NULL, -1);
+ g_return_val_if_fail (user != NULL, -1);
+
+ class = E_RULE_CONTEXT_GET_CLASS (context);
+ g_return_val_if_fail (class->load != NULL, -1);
+
+ context->priv->frozen++;
+ result = class->load (context, system, user);
+ context->priv->frozen--;
+
+ return result;
+}
+
+/**
+ * e_rule_context_save:
+ * @f:
+ * @user:
+ *
+ * Save a rule context to disk.
+ *
+ * Return value:
+ **/
+gint
+e_rule_context_save (ERuleContext *context,
+ const gchar *user)
+{
+ ERuleContextClass *class;
+
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
+ g_return_val_if_fail (user != NULL, -1);
+
+ class = E_RULE_CONTEXT_GET_CLASS (context);
+ g_return_val_if_fail (class->save != NULL, -1);
+
+ return class->save (context, user);
+}
+
+/**
+ * e_rule_context_revert:
+ * @f:
+ * @user:
+ *
+ * Reverts a rule context from a user description file. Assumes the
+ * system description file is unchanged from when it was loaded.
+ *
+ * Return value:
+ **/
+gint
+e_rule_context_revert (ERuleContext *context,
+ const gchar *user)
+{
+ ERuleContextClass *class;
+
+ g_return_val_if_fail (E_RULE_CONTEXT (context), 0);
+ g_return_val_if_fail (user != NULL, 0);
+
+ class = E_RULE_CONTEXT_GET_CLASS (context);
+ g_return_val_if_fail (class->revert != NULL, 0);
+
+ return class->revert (context, user);
+}
+
+EFilterPart *
+e_rule_context_find_part (ERuleContext *context,
+ const gchar *name)
+{
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ return e_filter_part_find_list (context->parts, name);
+}
+
+EFilterPart *
+e_rule_context_create_part (ERuleContext *context,
+ const gchar *name)
+{
+ EFilterPart *part;
+
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ part = e_rule_context_find_part (context, name);
+
+ if (part == NULL)
+ return NULL;
+
+ return e_filter_part_clone (part);
+}
+
+EFilterPart *
+e_rule_context_next_part (ERuleContext *context,
+ EFilterPart *last)
+{
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+
+ return e_filter_part_next_list (context->parts, last);
+}
+
+EFilterRule *
+e_rule_context_next_rule (ERuleContext *context,
+ EFilterRule *last,
+ const gchar *source)
+{
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+
+ return e_filter_rule_next_list (context->rules, last, source);
+}
+
+EFilterRule *
+e_rule_context_find_rule (ERuleContext *context,
+ const gchar *name,
+ const gchar *source)
+{
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ return e_filter_rule_find_list (context->rules, name, source);
+}
+
+void
+e_rule_context_add_part (ERuleContext *context,
+ EFilterPart *part)
+{
+ g_return_if_fail (E_IS_RULE_CONTEXT (context));
+ g_return_if_fail (E_IS_FILTER_PART (part));
+
+ context->parts = g_list_append (context->parts, part);
+}
+
+void
+e_rule_context_add_rule (ERuleContext *context,
+ EFilterRule *rule)
+{
+ g_return_if_fail (E_IS_RULE_CONTEXT (context));
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+
+ context->rules = g_list_append (context->rules, rule);
+
+ if (context->priv->frozen == 0) {
+ g_signal_emit (context, signals[RULE_ADDED], 0, rule);
+ g_signal_emit (context, signals[CHANGED], 0);
+ }
+}
+
+/* add a rule, with a gui, asking for confirmation first ... optionally save to path */
+void
+e_rule_context_add_rule_gui (ERuleContext *context,
+ EFilterRule *rule,
+ const gchar *title,
+ const gchar *path)
+{
+ GtkDialog *dialog;
+ GtkWidget *widget;
+ GtkWidget *content_area;
+
+ g_return_if_fail (E_IS_RULE_CONTEXT (context));
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+
+ widget = e_filter_rule_get_widget (rule, context);
+ gtk_widget_show (widget);
+
+ dialog =(GtkDialog *) gtk_dialog_new ();
+ gtk_dialog_add_buttons (dialog,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_has_separator (dialog, FALSE);
+
+ gtk_window_set_title ((GtkWindow *) dialog, title);
+ gtk_window_set_default_size ((GtkWindow *) dialog, 600, 400);
+ gtk_window_set_resizable ((GtkWindow *) dialog, TRUE);
+
+ content_area = gtk_dialog_get_content_area (dialog);
+ gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
+
+ g_object_set_data_full ((GObject *) dialog, "rule", rule, g_object_unref);
+ if (path)
+ g_object_set_data_full ((GObject *) dialog, "path", g_strdup (path), g_free);
+
+ g_signal_connect (dialog, "response", G_CALLBACK (new_rule_response), context);
+
+ g_object_ref (context);
+
+ g_object_set_data_full ((GObject *) dialog, "context", context, g_object_unref);
+
+ gtk_widget_show ((GtkWidget *) dialog);
+}
+
+void
+e_rule_context_remove_rule (ERuleContext *context,
+ EFilterRule *rule)
+{
+ g_return_if_fail (E_IS_RULE_CONTEXT (context));
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+
+ context->rules = g_list_remove (context->rules, rule);
+
+ if (context->priv->frozen == 0) {
+ g_signal_emit (context, signals[RULE_REMOVED], 0, rule);
+ g_signal_emit (context, signals[CHANGED], 0);
+ }
+}
+
+void
+e_rule_context_rank_rule (ERuleContext *context,
+ EFilterRule *rule,
+ const gchar *source,
+ gint rank)
+{
+ GList *node;
+ gint i = 0, index = 0;
+
+ g_return_if_fail (E_IS_RULE_CONTEXT (context));
+ g_return_if_fail (E_IS_FILTER_RULE (rule));
+
+ if (e_rule_context_get_rank_rule (context, rule, source) == rank)
+ return;
+
+ context->rules = g_list_remove (context->rules, rule);
+ node = context->rules;
+ while (node) {
+ EFilterRule *r = node->data;
+
+ if (i == rank) {
+ context->rules = g_list_insert (context->rules, rule, index);
+ if (context->priv->frozen == 0)
+ g_signal_emit (context, signals[CHANGED], 0);
+
+ return;
+ }
+
+ index++;
+ if (source == NULL || (r->source && strcmp (r->source, source) == 0))
+ i++;
+
+ node = node->next;
+ }
+
+ context->rules = g_list_append (context->rules, rule);
+ if (context->priv->frozen == 0)
+ g_signal_emit (context, signals[CHANGED], 0);
+}
+
+gint
+e_rule_context_get_rank_rule (ERuleContext *context,
+ EFilterRule *rule,
+ const gchar *source)
+{
+ GList *node;
+ gint i = 0;
+
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
+ g_return_val_if_fail (E_IS_FILTER_RULE (rule), -1);
+
+ node = context->rules;
+ while (node) {
+ EFilterRule *r = node->data;
+
+ if (r == rule)
+ return i;
+
+ if (source == NULL || (r->source && strcmp (r->source, source) == 0))
+ i++;
+
+ node = node->next;
+ }
+
+ return -1;
+}
+
+EFilterRule *
+e_rule_context_find_rank_rule (ERuleContext *context,
+ gint rank,
+ const gchar *source)
+{
+ GList *node;
+ gint i = 0;
+
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+
+ node = context->rules;
+ while (node) {
+ EFilterRule *r = node->data;
+
+ if (source == NULL || (r->source && strcmp (r->source, source) == 0)) {
+ if (rank == i)
+ return r;
+ i++;
+ }
+
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+GList *
+e_rule_context_rename_uri (ERuleContext *context,
+ const gchar *old_uri,
+ const gchar *new_uri,
+ GCompareFunc compare)
+{
+ ERuleContextClass *class;
+
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+ g_return_val_if_fail (old_uri != NULL, NULL);
+ g_return_val_if_fail (new_uri != NULL, NULL);
+ g_return_val_if_fail (compare != NULL, NULL);
+
+ class = E_RULE_CONTEXT_GET_CLASS (context);
+
+ /* This method is optional. */
+ if (class->rename_uri == NULL)
+ return NULL;
+
+ return class->rename_uri (context, old_uri, new_uri, compare);
+}
+
+GList *
+e_rule_context_delete_uri (ERuleContext *context,
+ const gchar *uri,
+ GCompareFunc compare)
+{
+ ERuleContextClass *class;
+
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (compare != NULL, NULL);
+
+ class = E_RULE_CONTEXT_GET_CLASS (context);
+
+ /* This method is optional. */
+ if (class->delete_uri == NULL)
+ return NULL;
+
+ return class->delete_uri (context, uri, compare);
+}
+
+void
+e_rule_context_free_uri_list (ERuleContext *context,
+ GList *uris)
+{
+ g_return_if_fail (E_IS_RULE_CONTEXT (context));
+
+ /* TODO: should be virtual */
+
+ g_list_foreach (uris, (GFunc) g_free, NULL);
+ g_list_free (uris);
+}
+
+/**
+ * e_rule_context_new_element:
+ * @context:
+ * @name:
+ *
+ * create a new filter element based on name.
+ *
+ * Return value:
+ **/
+EFilterElement *
+e_rule_context_new_element (ERuleContext *context,
+ const gchar *name)
+{
+ ERuleContextClass *class;
+
+ g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ class = E_RULE_CONTEXT_GET_CLASS (context);
+ g_return_val_if_fail (class->new_element != NULL, NULL);
+
+ return class->new_element (context, name);
+}
diff --git a/filter/e-rule-context.h b/filter/e-rule-context.h
new file mode 100644
index 0000000000..102faef1b6
--- /dev/null
+++ b/filter/e-rule-context.h
@@ -0,0 +1,215 @@
+/*
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_RULE_CONTEXT_H
+#define E_RULE_CONTEXT_H
+
+#include <glib-object.h>
+#include <libxml/parser.h>
+
+#include "e-filter-part.h"
+#include "e-filter-rule.h"
+
+/* Standard GObject macros */
+#define E_TYPE_RULE_CONTEXT \
+ (e_rule_context_get_type ())
+#define E_RULE_CONTEXT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_RULE_CONTEXT, ERuleContext))
+#define E_RULE_CONTEXT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_RULE_CONTEXT, ERuleContextClass))
+#define E_IS_RULE_CONTEXT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_RULE_CONTEXT))
+#define E_IS_RULE_CONTEXT_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_RULE_CONTEXT))
+#define E_RULE_CONTEXT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_RULE_CONTEXT, ERuleContextClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ERuleContext ERuleContext;
+typedef struct _ERuleContextClass ERuleContextClass;
+typedef struct _ERuleContextPrivate ERuleContextPrivate;
+
+/* backend capabilities, this is a hack since we don't support nested rules */
+enum {
+ E_RULE_CONTEXT_GROUPING = 1 << 0,
+ E_RULE_CONTEXT_THREADING = 1 << 1
+};
+
+typedef void (*ERuleContextRegisterFunc) (ERuleContext *context,
+ EFilterRule *rule,
+ gpointer user_data);
+typedef void (*ERuleContextPartFunc) (ERuleContext *context,
+ EFilterPart *part);
+typedef void (*ERuleContextRuleFunc) (ERuleContext *context,
+ EFilterRule *part);
+typedef EFilterPart *
+ (*ERuleContextNextPartFunc) (ERuleContext *context,
+ EFilterPart *part);
+typedef EFilterRule *
+ (*ERuleContextNextRuleFunc) (ERuleContext *context,
+ EFilterRule *rule,
+ const gchar *source);
+
+struct _ERuleContext {
+ GObject parent;
+ ERuleContextPrivate *priv;
+
+ gchar *error; /* string version of error */
+
+ guint32 flags; /* capability flags */
+
+ GList *parts;
+ GList *rules;
+
+ GHashTable *part_set_map; /* map set types to part types */
+ GList *part_set_list;
+ GHashTable *rule_set_map; /* map set types to rule types */
+ GList *rule_set_list;
+};
+
+struct _ERuleContextClass {
+ GObjectClass parent_class;
+
+ /* methods */
+ gint (*load) (ERuleContext *context,
+ const gchar *system,
+ const gchar *user);
+ gint (*save) (ERuleContext *context,
+ const gchar *user);
+ gint (*revert) (ERuleContext *context,
+ const gchar *user);
+
+ GList * (*delete_uri) (ERuleContext *context,
+ const gchar *uri,
+ GCompareFunc compare_func);
+ GList * (*rename_uri) (ERuleContext *context,
+ const gchar *old_uri,
+ const gchar *new_uri,
+ GCompareFunc compare_func);
+
+ EFilterElement *(*new_element) (ERuleContext *context,
+ const gchar *name);
+
+ /* signals */
+ void (*rule_added) (ERuleContext *context,
+ EFilterRule *rule);
+ void (*rule_removed) (ERuleContext *context,
+ EFilterRule *rule);
+ void (*changed) (ERuleContext *context);
+};
+
+struct _part_set_map {
+ gchar *name;
+ GType type;
+ ERuleContextPartFunc append;
+ ERuleContextNextPartFunc next;
+};
+
+struct _rule_set_map {
+ gchar *name;
+ GType type;
+ ERuleContextRuleFunc append;
+ ERuleContextNextRuleFunc next;
+};
+
+GType e_rule_context_get_type (void);
+ERuleContext * e_rule_context_new (void);
+
+gint e_rule_context_load (ERuleContext *context,
+ const gchar *system,
+ const gchar *user);
+gint e_rule_context_save (ERuleContext *context,
+ const gchar *user);
+gint e_rule_context_revert (ERuleContext *context,
+ const gchar *user);
+
+void e_rule_context_add_part (ERuleContext *context,
+ EFilterPart *part);
+EFilterPart * e_rule_context_find_part (ERuleContext *context,
+ const gchar *name);
+EFilterPart * e_rule_context_create_part (ERuleContext *context,
+ const gchar *name);
+EFilterPart * e_rule_context_next_part (ERuleContext *context,
+ EFilterPart *last);
+
+EFilterRule * e_rule_context_next_rule (ERuleContext *context,
+ EFilterRule *last,
+ const gchar *source);
+EFilterRule * e_rule_context_find_rule (ERuleContext *context,
+ const gchar *name,
+ const gchar *source);
+EFilterRule * e_rule_context_find_rank_rule (ERuleContext *context,
+ gint rank,
+ const gchar *source);
+void e_rule_context_add_rule (ERuleContext *context,
+ EFilterRule *rule);
+void e_rule_context_add_rule_gui (ERuleContext *context,
+ EFilterRule *rule,
+ const gchar *title,
+ const gchar *path);
+void e_rule_context_remove_rule (ERuleContext *context,
+ EFilterRule *rule);
+
+void e_rule_context_rank_rule (ERuleContext *context,
+ EFilterRule *rule,
+ const gchar *source,
+ gint rank);
+gint e_rule_context_get_rank_rule (ERuleContext *context,
+ EFilterRule *rule,
+ const gchar *source);
+
+void e_rule_context_add_part_set (ERuleContext *context,
+ const gchar *setname,
+ GType part_type,
+ ERuleContextPartFunc append,
+ ERuleContextNextPartFunc next);
+void e_rule_context_add_rule_set (ERuleContext *context,
+ const gchar *setname,
+ GType rule_type,
+ ERuleContextRuleFunc append,
+ ERuleContextNextRuleFunc next);
+
+EFilterElement *e_rule_context_new_element (ERuleContext *context,
+ const gchar *name);
+
+GList * e_rule_context_delete_uri (ERuleContext *context,
+ const gchar *uri,
+ GCompareFunc compare);
+GList * e_rule_context_rename_uri (ERuleContext *context,
+ const gchar *old_uri,
+ const gchar *new_uri,
+ GCompareFunc compare);
+
+void e_rule_context_free_uri_list (ERuleContext *context,
+ GList *uris);
+
+G_END_DECLS
+
+#endif /* E_RULE_CONTEXT_H */
diff --git a/filter/e-rule-editor.c b/filter/e-rule-editor.c
new file mode 100644
index 0000000000..59b0d5de07
--- /dev/null
+++ b/filter/e-rule-editor.c
@@ -0,0 +1,912 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* for getenv only, remove when getenv need removed */
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include "e-util/e-error.h"
+#include "e-util/e-util-private.h"
+
+#include "e-rule-editor.h"
+
+#define E_RULE_EDITOR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_RULE_EDITOR, ERuleEditorPrivate))
+
+static gint enable_undo = 0;
+
+enum {
+ BUTTON_ADD,
+ BUTTON_EDIT,
+ BUTTON_DELETE,
+ BUTTON_TOP,
+ BUTTON_UP,
+ BUTTON_DOWN,
+ BUTTON_BOTTOM,
+ BUTTON_LAST
+};
+
+struct _ERuleEditorPrivate {
+ GtkButton *buttons[BUTTON_LAST];
+};
+
+static gpointer parent_class;
+
+static void
+rule_editor_add_undo (ERuleEditor *editor,
+ gint type,
+ EFilterRule *rule,
+ gint rank,
+ gint newrank)
+{
+ ERuleEditorUndo *undo;
+
+ if (!editor->undo_active && enable_undo) {
+ undo = g_malloc0 (sizeof (*undo));
+ undo->rule = rule;
+ undo->type = type;
+ undo->rank = rank;
+ undo->newrank = newrank;
+
+ undo->next = editor->undo_log;
+ editor->undo_log = undo;
+ } else {
+ g_object_unref (rule);
+ }
+}
+
+static void
+rule_editor_play_undo (ERuleEditor *editor)
+{
+ ERuleEditorUndo *undo, *next;
+ EFilterRule *rule;
+
+ editor->undo_active = TRUE;
+ undo = editor->undo_log;
+ editor->undo_log = NULL;
+ while (undo) {
+ next = undo->next;
+ switch (undo->type) {
+ case E_RULE_EDITOR_LOG_EDIT:
+ rule = e_rule_context_find_rank_rule (editor->context, undo->rank, undo->rule->source);
+ if (rule) {
+ e_filter_rule_copy (rule, undo->rule);
+ } else {
+ g_warning ("Could not find the right rule to undo against?");
+ }
+ break;
+ case E_RULE_EDITOR_LOG_ADD:
+ rule = e_rule_context_find_rank_rule (editor->context, undo->rank, undo->rule->source);
+ if (rule)
+ e_rule_context_remove_rule (editor->context, rule);
+ break;
+ case E_RULE_EDITOR_LOG_REMOVE:
+ g_object_ref (undo->rule);
+ e_rule_context_add_rule (editor->context, undo->rule);
+ e_rule_context_rank_rule (editor->context, undo->rule, editor->source, undo->rank);
+ break;
+ case E_RULE_EDITOR_LOG_RANK:
+ rule = e_rule_context_find_rank_rule (editor->context, undo->newrank, undo->rule->source);
+ if (rule)
+ e_rule_context_rank_rule (editor->context, rule, editor->source, undo->rank);
+ break;
+ }
+
+ g_object_unref (undo->rule);
+ g_free (undo);
+ undo = next;
+ }
+ editor->undo_active = FALSE;
+}
+
+static void
+dialog_rule_changed (EFilterRule *fr, GtkWidget *dialog)
+{
+ g_return_if_fail (dialog != NULL);
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, fr && fr->parts);
+}
+
+static void
+add_editor_response (GtkWidget *dialog, gint button, ERuleEditor *editor)
+{
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ if (button == GTK_RESPONSE_OK) {
+ if (!e_filter_rule_validate (editor->edit, GTK_WINDOW (dialog))) {
+ /* no need to popup a dialog because the validate code does that. */
+ return;
+ }
+
+ if (e_rule_context_find_rule (editor->context, editor->edit->name, editor->edit->source)) {
+ e_error_run ((GtkWindow *)dialog, "filter:bad-name-notunique", editor->edit->name, NULL);
+ return;
+ }
+
+ g_object_ref (editor->edit);
+
+ gtk_list_store_append (editor->model, &iter);
+ gtk_list_store_set (editor->model, &iter, 0, editor->edit->name, 1, editor->edit, 2, editor->edit->enabled, -1);
+ selection = gtk_tree_view_get_selection (editor->list);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ /* scroll to the newly added row */
+ path = gtk_tree_model_get_path ((GtkTreeModel *) editor->model, &iter);
+ gtk_tree_view_scroll_to_cell (editor->list, path, NULL, TRUE, 1.0, 0.0);
+ gtk_tree_path_free (path);
+
+ editor->current = editor->edit;
+ e_rule_context_add_rule (editor->context, editor->current);
+
+ g_object_ref (editor->current);
+ rule_editor_add_undo (editor, E_RULE_EDITOR_LOG_ADD, editor->current,
+ e_rule_context_get_rank_rule (editor->context, editor->current, editor->current->source), 0);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+editor_destroy (ERuleEditor *editor,
+ GObject *deadbeef)
+{
+ if (editor->edit) {
+ g_object_unref (editor->edit);
+ editor->edit = NULL;
+ }
+
+ editor->dialog = NULL;
+
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
+ e_rule_editor_set_sensitive (editor);
+}
+
+static gboolean
+update_selected_rule (ERuleEditor *editor)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ selection = gtk_tree_view_get_selection (editor->list);
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (editor->model), &iter, 1, &editor->current, -1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+cursor_changed (GtkTreeView *treeview, ERuleEditor *editor)
+{
+ if (update_selected_rule(editor)) {
+ g_return_if_fail (editor->current);
+
+ e_rule_editor_set_sensitive (editor);
+ }
+}
+
+static void
+editor_response (GtkWidget *dialog, gint button, ERuleEditor *editor)
+{
+ if (button == GTK_RESPONSE_CANCEL) {
+ if (enable_undo)
+ rule_editor_play_undo (editor);
+ else {
+ ERuleEditorUndo *undo, *next;
+
+ undo = editor->undo_log;
+ editor->undo_log = NULL;
+ while (undo) {
+ next = undo->next;
+ g_object_unref (undo->rule);
+ g_free (undo);
+ undo = next;
+ }
+ }
+ }
+}
+
+static void
+rule_add (GtkWidget *widget, ERuleEditor *editor)
+{
+ GtkWidget *rules;
+ GtkWidget *content_area;
+
+ if (editor->edit != NULL)
+ return;
+
+ editor->edit = e_rule_editor_create_rule (editor);
+ e_filter_rule_set_source (editor->edit, editor->source);
+ rules = e_filter_rule_get_widget (editor->edit, editor->context);
+
+ editor->dialog = gtk_dialog_new ();
+ gtk_dialog_add_buttons ((GtkDialog *) editor->dialog,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_has_separator ((GtkDialog *) editor->dialog, FALSE);
+
+ gtk_window_set_title ((GtkWindow *) editor->dialog, _("Add Rule"));
+ gtk_window_set_default_size (GTK_WINDOW (editor->dialog), 650, 400);
+ gtk_window_set_resizable (GTK_WINDOW (editor->dialog), TRUE);
+ gtk_window_set_transient_for ((GtkWindow *) editor->dialog, (GtkWindow *) editor);
+ gtk_container_set_border_width ((GtkContainer *) editor->dialog, 6);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (editor->dialog));
+ gtk_box_pack_start (GTK_BOX (content_area), rules, TRUE, TRUE, 3);
+
+ g_signal_connect (editor->dialog, "response", G_CALLBACK (add_editor_response), editor);
+ g_object_weak_ref ((GObject *) editor->dialog, (GWeakNotify) editor_destroy, editor);
+
+ g_signal_connect (editor->edit, "changed", G_CALLBACK (dialog_rule_changed), editor->dialog);
+ dialog_rule_changed (editor->edit, editor->dialog);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
+
+ gtk_widget_show (editor->dialog);
+}
+
+static void
+edit_editor_response (GtkWidget *dialog, gint button, ERuleEditor *editor)
+{
+ EFilterRule *rule;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gint pos;
+
+ if (button == GTK_RESPONSE_OK) {
+ if (!e_filter_rule_validate (editor->edit, GTK_WINDOW (dialog))) {
+ /* no need to popup a dialog because the validate code does that. */
+ return;
+ }
+
+ rule = e_rule_context_find_rule (editor->context, editor->edit->name, editor->edit->source);
+ if (rule != NULL && rule != editor->current) {
+ e_error_run ((GtkWindow *)dialog, "filter:bad-name-notunique", rule->name, NULL);
+
+ return;
+ }
+
+ pos = e_rule_context_get_rank_rule (editor->context, editor->current, editor->source);
+ if (pos != -1) {
+ path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, pos);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->model), &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_list_store_set (editor->model, &iter, 0, editor->edit->name, -1);
+
+ rule_editor_add_undo (editor, E_RULE_EDITOR_LOG_EDIT, e_filter_rule_clone (editor->current),
+ pos, 0);
+
+ /* replace the old rule with the new rule */
+ e_filter_rule_copy (editor->current, editor->edit);
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+rule_edit (GtkWidget *widget, ERuleEditor *editor)
+{
+ GtkWidget *rules;
+ GtkWidget *content_area;
+
+ update_selected_rule(editor);
+
+ if (editor->current == NULL || editor->edit != NULL)
+ return;
+
+ editor->edit = e_filter_rule_clone (editor->current);
+
+ rules = e_filter_rule_get_widget (editor->edit, editor->context);
+
+ editor->dialog = gtk_dialog_new ();
+ gtk_dialog_add_buttons ((GtkDialog *) editor->dialog,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_has_separator ((GtkDialog *) editor->dialog, FALSE);
+
+ gtk_window_set_title ((GtkWindow *) editor->dialog, _("Edit Rule"));
+ gtk_window_set_default_size (GTK_WINDOW (editor->dialog), 650, 400);
+ gtk_window_set_resizable (GTK_WINDOW (editor->dialog), TRUE);
+ gtk_widget_set_parent_window (GTK_WIDGET (editor->dialog), GTK_WIDGET (editor)->window);
+ gtk_container_set_border_width ((GtkContainer *) editor->dialog, 6);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (editor->dialog));
+ gtk_box_pack_start (GTK_BOX (content_area), rules, TRUE, TRUE, 3);
+
+ g_signal_connect (editor->dialog, "response", G_CALLBACK (edit_editor_response), editor);
+ g_object_weak_ref ((GObject *) editor->dialog, (GWeakNotify) editor_destroy, editor);
+
+ g_signal_connect (editor->edit, "changed", G_CALLBACK (dialog_rule_changed), editor->dialog);
+ dialog_rule_changed (editor->edit, editor->dialog);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
+
+ gtk_widget_show (editor->dialog);
+}
+
+static void
+rule_delete (GtkWidget *widget, ERuleEditor *editor)
+{
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gint pos, len;
+
+ update_selected_rule(editor);
+
+ pos = e_rule_context_get_rank_rule (editor->context, editor->current, editor->source);
+ if (pos != -1) {
+ e_rule_context_remove_rule (editor->context, editor->current);
+
+ path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, pos);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->model), &iter, path);
+ gtk_list_store_remove (editor->model, &iter);
+ gtk_tree_path_free (path);
+
+ rule_editor_add_undo (editor, E_RULE_EDITOR_LOG_REMOVE, editor->current,
+ e_rule_context_get_rank_rule (editor->context, editor->current, editor->current->source), 0);
+#if 0
+ g_object_unref (editor->current);
+#endif
+ editor->current = NULL;
+
+ /* now select the next rule */
+ len = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (editor->model), NULL);
+ pos = pos >= len ? len - 1 : pos;
+
+ if (pos >= 0) {
+ path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, pos);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->model), &iter, path);
+ gtk_tree_path_free (path);
+
+ /* select the new row */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (editor->list));
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ /* scroll to the selected row */
+ path = gtk_tree_model_get_path ((GtkTreeModel *) editor->model, &iter);
+ gtk_tree_view_scroll_to_cell (editor->list, path, NULL, FALSE, 0.0, 0.0);
+ gtk_tree_path_free (path);
+
+ /* update our selection state */
+ cursor_changed (editor->list, editor);
+ return;
+ }
+ }
+
+ e_rule_editor_set_sensitive (editor);
+}
+
+static void
+rule_move (ERuleEditor *editor, gint from, gint to)
+{
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ EFilterRule *rule;
+
+ rule_editor_add_undo (
+ editor, E_RULE_EDITOR_LOG_RANK,
+ g_object_ref (editor->current),
+ e_rule_context_get_rank_rule (editor->context,
+ editor->current, editor->source), to);
+
+ e_rule_context_rank_rule (
+ editor->context, editor->current, editor->source, to);
+
+ path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, from);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->model), &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (editor->model), &iter, 1, &rule, -1);
+ g_return_if_fail (rule != NULL);
+
+ /* remove and then re-insert the row at the new location */
+ gtk_list_store_remove (editor->model, &iter);
+ gtk_list_store_insert (editor->model, &iter, to);
+
+ /* set the data on the row */
+ gtk_list_store_set (editor->model, &iter, 0, rule->name, 1, rule, 2, rule->enabled, -1);
+
+ /* select the row */
+ selection = gtk_tree_view_get_selection (editor->list);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ /* scroll to the selected row */
+ path = gtk_tree_model_get_path ((GtkTreeModel *) editor->model, &iter);
+ gtk_tree_view_scroll_to_cell (editor->list, path, NULL, FALSE, 0.0, 0.0);
+ gtk_tree_path_free (path);
+
+ e_rule_editor_set_sensitive (editor);
+}
+
+static void
+rule_top (GtkWidget *widget, ERuleEditor *editor)
+{
+ gint pos;
+
+ update_selected_rule(editor);
+
+ pos = e_rule_context_get_rank_rule (
+ editor->context, editor->current, editor->source);
+ if (pos > 0)
+ rule_move (editor, pos, 0);
+}
+
+static void
+rule_up (GtkWidget *widget, ERuleEditor *editor)
+{
+ gint pos;
+
+ update_selected_rule(editor);
+
+ pos = e_rule_context_get_rank_rule (
+ editor->context, editor->current, editor->source);
+ if (pos > 0)
+ rule_move (editor, pos, pos - 1);
+}
+
+static void
+rule_down (GtkWidget *widget, ERuleEditor *editor)
+{
+ gint pos;
+
+ update_selected_rule(editor);
+
+ pos = e_rule_context_get_rank_rule (
+ editor->context, editor->current, editor->source);
+ if (pos >= 0)
+ rule_move (editor, pos, pos + 1);
+}
+
+static void
+rule_bottom (GtkWidget *widget, ERuleEditor *editor)
+{
+ gint pos;
+ gint index = -1, count = 0;
+ EFilterRule *rule = NULL;
+
+ update_selected_rule(editor);
+
+ pos = e_rule_context_get_rank_rule (
+ editor->context, editor->current, editor->source);
+ /* There's probably a better/faster way to get the count of the list here */
+ while ((rule = e_rule_context_next_rule (editor->context, rule, editor->source))) {
+ if (rule == editor->current)
+ index = count;
+ count++;
+ }
+ count--;
+ if (pos >= 0)
+ rule_move (editor, pos, count);
+}
+
+static struct {
+ const gchar *name;
+ GCallback func;
+} edit_buttons[] = {
+ { "rule_add", G_CALLBACK (rule_add) },
+ { "rule_edit", G_CALLBACK (rule_edit) },
+ { "rule_delete", G_CALLBACK (rule_delete) },
+ { "rule_top", G_CALLBACK (rule_top) },
+ { "rule_up", G_CALLBACK (rule_up) },
+ { "rule_down", G_CALLBACK (rule_down) },
+ { "rule_bottom", G_CALLBACK (rule_bottom) },
+};
+
+static void
+rule_editor_finalize (GObject *object)
+{
+ ERuleEditor *editor = E_RULE_EDITOR (object);
+ ERuleEditorUndo *undo, *next;
+
+ g_object_unref (editor->context);
+
+ undo = editor->undo_log;
+ while (undo) {
+ next = undo->next;
+ g_object_unref (undo->rule);
+ g_free (undo);
+ undo = next;
+ }
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+rule_editor_destroy (GtkObject *gtk_object)
+{
+ ERuleEditor *editor = E_RULE_EDITOR (gtk_object);
+
+ if (editor->dialog != NULL) {
+ gtk_widget_destroy (GTK_WIDGET (editor->dialog));
+ editor->dialog = NULL;
+ }
+
+ /* Chain up to parent's destroy() method. */
+ GTK_OBJECT_CLASS (parent_class)->destroy (gtk_object);
+}
+
+static void
+rule_editor_set_source (ERuleEditor *editor,
+ const gchar *source)
+{
+ EFilterRule *rule = NULL;
+ GtkTreeIter iter;
+
+ gtk_list_store_clear (editor->model);
+
+ while ((rule = e_rule_context_next_rule (editor->context, rule, source)) != NULL) {
+ gtk_list_store_append (editor->model, &iter);
+ gtk_list_store_set (
+ editor->model, &iter,
+ 0, rule->name, 1, rule, 2, rule->enabled, -1);
+ }
+
+ g_free (editor->source);
+ editor->source = g_strdup (source);
+ editor->current = NULL;
+ e_rule_editor_set_sensitive (editor);
+}
+
+static void
+rule_editor_set_sensitive (ERuleEditor *editor)
+{
+ EFilterRule *rule = NULL;
+ gint index = -1, count = 0;
+
+ while ((rule = e_rule_context_next_rule (editor->context, rule, editor->source))) {
+ if (rule == editor->current)
+ index = count;
+ count++;
+ }
+
+ count--;
+
+ gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_EDIT]), index != -1);
+ gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_DELETE]), index != -1);
+ gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_TOP]), index > 0);
+ gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_UP]), index > 0);
+ gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_DOWN]), index >= 0 && index < count);
+ gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_BOTTOM]), index >= 0 && index < count);
+}
+
+static EFilterRule *
+rule_editor_create_rule (ERuleEditor *editor)
+{
+ EFilterRule *rule;
+ EFilterPart *part;
+
+ /* create a rule with 1 part in it */
+ rule = e_filter_rule_new ();
+ part = e_rule_context_next_part (editor->context, NULL);
+ e_filter_rule_add_part (rule, e_filter_part_clone (part));
+
+ return rule;
+}
+
+static void
+rule_editor_class_init (ERuleEditorClass *class)
+{
+ GObjectClass *object_class;
+ GtkObjectClass *gtk_object_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (ERuleEditorPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = rule_editor_finalize;
+
+ gtk_object_class = GTK_OBJECT_CLASS (class);
+ gtk_object_class->destroy = rule_editor_destroy;
+
+ class->set_source = rule_editor_set_source;
+ class->set_sensitive = rule_editor_set_sensitive;
+ class->create_rule = rule_editor_create_rule;
+
+ /* TODO: Remove when it works (or never will) */
+ enable_undo = getenv ("EVOLUTION_RULE_UNDO") != NULL;
+}
+
+static void
+rule_editor_init (ERuleEditor *editor)
+{
+ editor->priv = E_RULE_EDITOR_GET_PRIVATE (editor);
+}
+
+GType
+e_rule_editor_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (ERuleEditorClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) rule_editor_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (ERuleEditor),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) rule_editor_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ GTK_TYPE_DIALOG, "ERuleEditor", &type_info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * rule_editor_new:
+ *
+ * Create a new ERuleEditor object.
+ *
+ * Return value: A new #ERuleEditor object.
+ **/
+ERuleEditor *
+e_rule_editor_new (ERuleContext *context,
+ const gchar *source,
+ const gchar *label)
+{
+ ERuleEditor *editor = (ERuleEditor *) g_object_new (E_TYPE_RULE_EDITOR, NULL);
+ GladeXML *gui;
+ gchar *filter_glade = g_build_filename (EVOLUTION_GLADEDIR,
+ "filter.glade",
+ NULL);
+
+ gui = glade_xml_new (filter_glade, "rule_editor", NULL);
+ g_free (filter_glade);
+ e_rule_editor_construct (editor, context, gui, source, label);
+ gtk_widget_hide (glade_xml_get_widget (gui, "label17"));
+ gtk_widget_hide (glade_xml_get_widget (gui, "filter_source_combobox"));
+ g_object_unref (gui);
+
+ return editor;
+}
+
+void
+e_rule_editor_set_sensitive (ERuleEditor *editor)
+{
+ ERuleEditorClass *class;
+
+ g_return_if_fail (E_IS_RULE_EDITOR (editor));
+
+ class = E_RULE_EDITOR_GET_CLASS (editor);
+ g_return_if_fail (class->set_sensitive != NULL);
+
+ class->set_sensitive (editor);
+}
+
+void
+e_rule_editor_set_source (ERuleEditor *editor,
+ const gchar *source)
+{
+ ERuleEditorClass *class;
+
+ g_return_if_fail (E_IS_RULE_EDITOR (editor));
+
+ class = E_RULE_EDITOR_GET_CLASS (editor);
+ g_return_if_fail (class->set_source != NULL);
+
+ class->set_source (editor, source);
+}
+
+EFilterRule *
+e_rule_editor_create_rule (ERuleEditor *editor)
+{
+ ERuleEditorClass *class;
+
+ g_return_val_if_fail (E_IS_RULE_EDITOR (editor), NULL);
+
+ class = E_RULE_EDITOR_GET_CLASS (editor);
+ g_return_val_if_fail (class->create_rule != NULL, NULL);
+
+ return class->create_rule (editor);
+}
+
+static void
+double_click (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, ERuleEditor *editor)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ selection = gtk_tree_view_get_selection (editor->list);
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ gtk_tree_model_get (GTK_TREE_MODEL (editor->model), &iter, 1, &editor->current, -1);
+
+ if (editor->current)
+ rule_edit ((GtkWidget *) treeview, editor);
+}
+
+static void
+rule_able_toggled (GtkCellRendererToggle *renderer,
+ gchar *arg1,
+ gpointer user_data)
+{
+ GtkWidget *table = user_data;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_path_new_from_string (arg1);
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (table));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (table));
+
+ if (gtk_tree_model_get_iter (model, &iter, path)) {
+ EFilterRule *rule = NULL;
+
+ gtk_tree_model_get (model, &iter, 1, &rule, -1);
+
+ if (rule) {
+ rule->enabled = !rule->enabled;
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, rule->enabled, -1);
+ }
+ }
+
+ gtk_tree_path_free (path);
+}
+
+GtkWidget *
+rule_editor_treeview_new (gchar *widget_name,
+ gchar *string1,
+ gchar *string2,
+ gint int1,
+ gint int2);
+
+GtkWidget *
+rule_editor_treeview_new (gchar *widget_name,
+ gchar *string1,
+ gchar *string2,
+ gint int1,
+ gint int2)
+{
+ GtkWidget *table, *scrolled;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkListStore *model;
+ GtkTreeViewColumn *column;
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
+ table = gtk_tree_view_new_with_model ((GtkTreeModel *) model);
+ gtk_tree_view_set_headers_visible ((GtkTreeView *) table, FALSE);
+
+ renderer = gtk_cell_renderer_toggle_new ();
+ g_object_set (G_OBJECT (renderer), "activatable", TRUE, NULL);
+ gtk_tree_view_insert_column_with_attributes ((GtkTreeView *) table, -1,
+ _("Enabled"), renderer,
+ "active", 2, NULL);
+ g_signal_connect (renderer, "toggled", G_CALLBACK (rule_able_toggled), table);
+
+ /* hide enable column by default */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (table), 0);
+ gtk_tree_view_column_set_visible (column, FALSE);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes ((GtkTreeView *) table, -1,
+ _("Rule name"), renderer,
+ "text", 0, NULL);
+
+ selection = gtk_tree_view_get_selection ((GtkTreeView *) table);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+ gtk_container_add (GTK_CONTAINER (scrolled), table);
+
+ g_object_set_data ((GObject *) scrolled, "table", table);
+ g_object_set_data ((GObject *) scrolled, "model", model);
+
+ gtk_widget_show (scrolled);
+ gtk_widget_show (table);
+
+ g_object_unref (model);
+
+ return scrolled;
+}
+
+void
+e_rule_editor_construct (ERuleEditor *editor,
+ ERuleContext *context,
+ GladeXML *gui,
+ const gchar *source,
+ const gchar *label)
+{
+ GtkWidget *w;
+ GtkWidget *action_area;
+ GtkWidget *content_area;
+ gint i;
+ gchar *tmp;
+
+ g_return_if_fail (E_IS_RULE_EDITOR (editor));
+ g_return_if_fail (E_IS_RULE_CONTEXT (context));
+ g_return_if_fail (GLADE_IS_XML (gui));
+
+ editor->context = g_object_ref (context);
+
+ action_area = gtk_dialog_get_action_area (GTK_DIALOG (editor));
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (editor));
+
+ gtk_window_set_resizable ((GtkWindow *) editor, TRUE);
+ gtk_window_set_default_size ((GtkWindow *) editor, 350, 400);
+ gtk_widget_realize ((GtkWidget *) editor);
+ gtk_container_set_border_width (GTK_CONTAINER (action_area), 12);
+
+ w = glade_xml_get_widget(gui, "rule_editor");
+ gtk_box_pack_start (GTK_BOX (content_area), w, TRUE, TRUE, 3);
+
+ for (i = 0; i < BUTTON_LAST; i++) {
+ editor->priv->buttons[i] = (GtkButton *) (w = glade_xml_get_widget (gui, edit_buttons[i].name));
+ g_signal_connect (w, "clicked", edit_buttons[i].func, editor);
+ }
+
+ w = glade_xml_get_widget (gui, "rule_list");
+ editor->list = (GtkTreeView *) g_object_get_data ((GObject *) w, "table");
+ editor->model = (GtkListStore *) g_object_get_data ((GObject *) w, "model");
+
+ g_signal_connect (editor->list, "cursor-changed", G_CALLBACK (cursor_changed), editor);
+ g_signal_connect (editor->list, "row-activated", G_CALLBACK (double_click), editor);
+
+ w = glade_xml_get_widget (gui, "rule_label");
+ tmp = alloca(strlen(label)+8);
+ sprintf(tmp, "<b>%s</b>", label);
+ gtk_label_set_label((GtkLabel *)w, tmp);
+ gtk_label_set_mnemonic_widget ((GtkLabel *) w, (GtkWidget *) editor->list);
+
+ g_signal_connect (editor, "response", G_CALLBACK (editor_response), editor);
+ rule_editor_set_source (editor, source);
+
+ gtk_dialog_set_has_separator ((GtkDialog *) editor, FALSE);
+ gtk_dialog_add_buttons ((GtkDialog *) editor,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+}
diff --git a/filter/e-rule-editor.h b/filter/e-rule-editor.h
new file mode 100644
index 0000000000..f7f2d2defd
--- /dev/null
+++ b/filter/e-rule-editor.h
@@ -0,0 +1,122 @@
+/*
+ * 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_RULE_EDITOR_H
+#define E_RULE_EDITOR_H
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+
+#include "e-rule-context.h"
+#include "e-filter-rule.h"
+
+/* Standard GObject macros */
+#define E_TYPE_RULE_EDITOR \
+ (e_rule_editor_get_type ())
+#define E_RULE_EDITOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_RULE_EDITOR, ERuleEditor))
+#define E_RULE_EDITOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_RULE_EDITOR, ERuleEditorClass))
+#define E_IS_RULE_EDITOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_RULE_EDITOR))
+#define E_IS_RULE_EDITOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_RULE_EDITOR))
+#define E_RULE_EDITOR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_RULE_EDITOR, ERuleEditorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ERuleEditor ERuleEditor;
+typedef struct _ERuleEditorClass ERuleEditorClass;
+typedef struct _ERuleEditorPrivate ERuleEditorPrivate;
+
+typedef struct _ERuleEditorUndo ERuleEditorUndo;
+
+struct _ERuleEditor {
+ GtkDialog parent;
+
+ GtkListStore *model;
+ GtkTreeView *list;
+
+ ERuleContext *context;
+ EFilterRule *current;
+ EFilterRule *edit; /* for editing/adding rules, so we only do 1 at a time */
+
+ GtkWidget *dialog;
+
+ gchar *source;
+
+ ERuleEditorUndo *undo_log; /* cancel/undo log */
+ guint undo_active:1; /* we're performing undo */
+
+ ERuleEditorPrivate *priv;
+};
+
+struct _ERuleEditorClass {
+ GtkDialogClass parent_class;
+
+ void (*set_sensitive) (ERuleEditor *editor);
+ void (*set_source) (ERuleEditor *editor,
+ const gchar *source);
+
+ EFilterRule * (*create_rule) (ERuleEditor *editor);
+};
+
+enum {
+ E_RULE_EDITOR_LOG_EDIT,
+ E_RULE_EDITOR_LOG_ADD,
+ E_RULE_EDITOR_LOG_REMOVE,
+ E_RULE_EDITOR_LOG_RANK
+};
+
+struct _ERuleEditorUndo {
+ ERuleEditorUndo *next;
+
+ guint type;
+ EFilterRule *rule;
+ gint rank;
+ gint newrank;
+};
+
+GType e_rule_editor_get_type (void);
+ERuleEditor * e_rule_editor_new (ERuleContext *context,
+ const gchar *source,
+ const gchar *label);
+void e_rule_editor_construct (ERuleEditor *editor,
+ ERuleContext *context,
+ GladeXML *gui,
+ const gchar *source,
+ const gchar *label);
+void e_rule_editor_set_source (ERuleEditor *editor,
+ const gchar *source);
+void e_rule_editor_set_sensitive (ERuleEditor *editor);
+EFilterRule * e_rule_editor_create_rule (ERuleEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_RULE_EDITOR_H */
diff --git a/filter/filter-code.c b/filter/filter-code.c
deleted file mode 100644
index 9c7eb152ea..0000000000
--- a/filter/filter-code.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "filter-code.h"
-
-static void build_code (FilterElement *fe, GString *out, struct _FilterPart *ff);
-static void format_sexp (FilterElement *, GString *);
-
-static void filter_code_class_init (FilterCodeClass *class);
-static void filter_code_init (FilterCode *fc);
-static void filter_code_finalise (GObject *obj);
-
-static FilterInputClass *parent_class;
-
-GType
-filter_code_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterCodeClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_code_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterCode),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_code_init,
- };
-
- type = g_type_register_static (FILTER_TYPE_INPUT, "FilterCode", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_code_class_init (FilterCodeClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- FilterElementClass *fe_class = FILTER_ELEMENT_CLASS (klass);
-
- parent_class = g_type_class_ref (FILTER_TYPE_INPUT);
-
- object_class->finalize = filter_code_finalise;
-
- /* override methods */
- fe_class->build_code = build_code;
- fe_class->format_sexp = format_sexp;
-}
-
-static void
-filter_code_init (FilterCode *fc)
-{
- ((FilterInput *) fc)->type = (gchar *)xmlStrdup ((const guchar *)"code");
-}
-
-static void
-filter_code_finalise (GObject *obj)
-{
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_code_new:
- *
- * Create a new FilterCode object.
- *
- * Return value: A new #FilterCode object.
- **/
-FilterCode *
-filter_code_new (gboolean raw_code)
-{
- FilterCode *fc = (FilterCode *) g_object_new (FILTER_TYPE_CODE, NULL, NULL);
-
- if (fc && raw_code) {
- xmlFree (((FilterInput *) fc)->type);
- ((FilterInput *) fc)->type = (gchar *)xmlStrdup ((const guchar *)"rawcode");
- }
-
- return fc;
-}
-
-/* here, the string IS the code */
-static void
-build_code (FilterElement *fe, GString *out, struct _FilterPart *ff)
-{
- GList *l;
- FilterInput *fi = (FilterInput *)fe;
- gboolean is_rawcode = fi && fi->type && g_str_equal (fi->type, "rawcode");
-
- if (!is_rawcode)
- g_string_append(out, "(match-all ");
-
- l = fi->values;
- while (l) {
- g_string_append(out, (gchar *)l->data);
- l = g_list_next(l);
- }
-
- if (!is_rawcode)
- g_string_append (out, ")");
-}
-
-/* and we have no value */
-static void
-format_sexp (FilterElement *fe, GString *out)
-{
- ;
-}
diff --git a/filter/filter-code.h b/filter/filter-code.h
deleted file mode 100644
index 38336fc450..0000000000
--- a/filter/filter-code.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _FILTER_CODE_H
-#define _FILTER_CODE_H
-
-#include "filter-input.h"
-
-#define FILTER_TYPE_CODE (filter_code_get_type ())
-#define FILTER_CODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_CODE, FilterCode))
-#define FILTER_CODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_CODE, FilterCodeClass))
-#define IS_FILTER_CODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_CODE))
-#define IS_FILTER_CODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_CODE))
-#define FILTER_CODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_CODE, FilterCodeClass))
-
-typedef struct _FilterCode FilterCode;
-typedef struct _FilterCodeClass FilterCodeClass;
-
-struct _FilterCode {
- FilterInput parent_object;
-};
-
-struct _FilterCodeClass {
- FilterInputClass parent_class;
-
- /* virtual methods */
-
- /* signals */
-};
-
-GType filter_code_get_type (void);
-FilterCode *filter_code_new (gboolean raw_code);
-
-/* methods */
-
-#endif /* ! _FILTER_CODE_H */
diff --git a/filter/filter-colour.c b/filter/filter-colour.c
deleted file mode 100644
index f466f63390..0000000000
--- a/filter/filter-colour.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <gtk/gtk.h>
-
-#include "libedataserver/e-sexp.h"
-#include "filter-colour.h"
-
-#define d(x)
-
-static gint colour_eq (FilterElement *fe, FilterElement *cm);
-static void xml_create (FilterElement *fe, xmlNodePtr node);
-static xmlNodePtr xml_encode (FilterElement *fe);
-static gint xml_decode (FilterElement *fe, xmlNodePtr node);
-static GtkWidget *get_widget (FilterElement *fe);
-static void build_code (FilterElement *fe, GString *out, struct _FilterPart *ff);
-static void format_sexp (FilterElement *, GString *);
-
-static void filter_colour_class_init (FilterColourClass *klass);
-static void filter_colour_init (FilterColour *fc);
-static void filter_colour_finalise (GObject *obj);
-
-static FilterElementClass *parent_class;
-
-GType
-filter_colour_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterColourClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_colour_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterColour),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_colour_init,
- };
-
- type = g_type_register_static (FILTER_TYPE_ELEMENT, "FilterColour", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_colour_class_init (FilterColourClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- FilterElementClass *fe_class = FILTER_ELEMENT_CLASS (klass);
-
- parent_class = g_type_class_ref (FILTER_TYPE_ELEMENT);
-
- object_class->finalize = filter_colour_finalise;
-
- /* override methods */
- fe_class->eq = colour_eq;
- fe_class->xml_create = xml_create;
- fe_class->xml_encode = xml_encode;
- fe_class->xml_decode = xml_decode;
- fe_class->get_widget = get_widget;
- fe_class->build_code = build_code;
- fe_class->format_sexp = format_sexp;
-}
-
-static void
-filter_colour_init (FilterColour *fc)
-{
- ;
-}
-
-static void
-filter_colour_finalise (GObject *obj)
-{
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_colour_new:
- *
- * Create a new FilterColour object.
- *
- * Return value: A new #FilterColour object.
- **/
-FilterColour *
-filter_colour_new (void)
-{
- return (FilterColour *) g_object_new (FILTER_TYPE_COLOUR, NULL, NULL);
-}
-
-static gint
-colour_eq (FilterElement *fe, FilterElement *cm)
-{
- FilterColour *fc = (FilterColour *) fe;
- FilterColour *cc = (FilterColour *) cm;
-
- return FILTER_ELEMENT_CLASS (parent_class)->eq (fe, cm)
- && gdk_color_equal (&fc->color, &cc->color);
-}
-
-static void
-xml_create (FilterElement *fe, xmlNodePtr node)
-{
- /* parent implementation */
- FILTER_ELEMENT_CLASS (parent_class)->xml_create (fe, node);
-}
-
-static xmlNodePtr
-xml_encode (FilterElement *fe)
-{
- FilterColour *fc = (FilterColour *)fe;
- xmlNodePtr value;
- gchar spec[16];
-
- g_snprintf (spec, sizeof (spec), "#%04x%04x%04x",
- fc->color.red, fc->color.green, fc->color.blue);
-
- value = xmlNewNode(NULL, (const guchar *)"value");
- xmlSetProp(value, (const guchar *)"type", (const guchar *)"colour");
- xmlSetProp(value, (const guchar *)"name", (guchar *)fe->name);
- xmlSetProp(value, (const guchar *)"spec", (guchar *)spec);
-
- return value;
-}
-
-static gint
-xml_decode (FilterElement *fe, xmlNodePtr node)
-{
- FilterColour *fc = (FilterColour *)fe;
- xmlChar *prop;
-
- xmlFree (fe->name);
- fe->name = (gchar *)xmlGetProp(node, (const guchar *)"name");
-
- prop = xmlGetProp(node, (const guchar *)"spec");
- if (prop != NULL) {
- gdk_color_parse((gchar *)prop, &fc->color);
- xmlFree (prop);
- } else {
- /* try reading the old RGB properties */
- prop = xmlGetProp(node, (const guchar *)"red");
- sscanf((gchar *)prop, "%" G_GINT16_MODIFIER "x", &fc->color.red);
- xmlFree (prop);
- prop = xmlGetProp(node, (const guchar *)"green");
- sscanf((gchar *)prop, "%" G_GINT16_MODIFIER "x", &fc->color.green);
- xmlFree (prop);
- prop = xmlGetProp(node, (const guchar *)"blue");
- sscanf((gchar *)prop, "%" G_GINT16_MODIFIER "x", &fc->color.blue);
- xmlFree (prop);
- }
-
- return 0;
-}
-
-static void
-set_color (GtkColorButton *color_button, FilterColour *fc)
-{
- gtk_color_button_get_color (color_button, &fc->color);
-}
-
-static GtkWidget *
-get_widget (FilterElement *fe)
-{
- FilterColour *fc = (FilterColour *) fe;
- GtkWidget *color_button;
-
- color_button = gtk_color_button_new_with_color (&fc->color);
- gtk_widget_show (color_button);
-
- g_signal_connect (
- G_OBJECT (color_button), "color_set",
- G_CALLBACK (set_color), fe);
-
- return color_button;
-}
-
-static void
-build_code (FilterElement *fe, GString *out, struct _FilterPart *ff)
-{
- return;
-}
-
-static void
-format_sexp (FilterElement *fe, GString *out)
-{
- FilterColour *fc = (FilterColour *)fe;
- gchar spec[16];
-
- g_snprintf (spec, sizeof (spec), "#%04x%04x%04x",
- fc->color.red, fc->color.green, fc->color.blue);
- e_sexp_encode_string (out, spec);
-}
diff --git a/filter/filter-colour.h b/filter/filter-colour.h
deleted file mode 100644
index cfd10d12a6..0000000000
--- a/filter/filter-colour.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _FILTER_COLOUR_H
-#define _FILTER_COLOUR_H
-
-#include "filter-element.h"
-
-#define FILTER_TYPE_COLOUR (filter_colour_get_type ())
-#define FILTER_COLOUR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_COLOUR, FilterColour))
-#define FILTER_COLOUR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_COLOUR, FilterColourClass))
-#define IS_FILTER_COLOUR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_COLOUR))
-#define IS_FILTER_COLOUR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_COLOUR))
-#define FILTER_COLOUR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_COLOUR, FilterColourClass))
-
-typedef struct _FilterColour FilterColour;
-typedef struct _FilterColourClass FilterColourClass;
-
-struct _FilterColour {
- FilterElement parent_object;
-
- GdkColor color;
-};
-
-struct _FilterColourClass {
- FilterElementClass parent_class;
-
- /* virtual methods */
-
- /* signals */
-};
-
-GType filter_colour_get_type (void);
-FilterColour *filter_colour_new (void);
-
-/* methods */
-
-#endif /* ! _FILTER_COLOUR_H */
diff --git a/filter/filter-datespec.h b/filter/filter-datespec.h
deleted file mode 100644
index c8eee1bae7..0000000000
--- a/filter/filter-datespec.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _FILTER_DATESPEC_H
-#define _FILTER_DATESPEC_H
-
-#include <time.h>
-#include "filter-element.h"
-
-#define FILTER_TYPE_DATESPEC (filter_datespec_get_type ())
-#define FILTER_DATESPEC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_DATESPEC, FilterDatespec))
-#define FILTER_DATESPEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_DATESPEC, FilterDatespecClass))
-#define IS_FILTER_DATESPEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_DATESPEC))
-#define IS_FILTER_DATESPEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_DATESPEC))
-#define FILTER_DATESPEC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_DATESPEC, FilterDatespecClass))
-
-typedef struct _FilterDatespec FilterDatespec;
-typedef struct _FilterDatespecClass FilterDatespecClass;
-
-typedef enum _FilterDatespec_type {
- FDST_UNKNOWN = -1,
- FDST_NOW,
- FDST_SPECIFIED,
- FDST_X_AGO,
- FDST_X_FUTURE
-} FilterDatespec_type;
-
-struct _FilterDatespec {
- FilterElement parent;
- struct _FilterDatespecPrivate *priv;
-
- FilterDatespec_type type;
-
- /* either a timespan, an absolute time, or 0
- * depending on type -- the above mapping to
- * (X_FUTURE, X_AGO, SPECIFIED, NOW)
- */
-
- time_t value;
-};
-
-struct _FilterDatespecClass {
- FilterElementClass parent_class;
-
- /* virtual methods */
-
- /* signals */
-};
-
-GType filter_datespec_get_type (void);
-FilterDatespec *filter_datespec_new (void);
-
-/* methods */
-
-#endif /* ! _FILTER_DATESPEC_H */
diff --git a/filter/filter-element.c b/filter/filter-element.c
deleted file mode 100644
index 952805e48b..0000000000
--- a/filter/filter-element.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <stdlib.h>
-
-#include "filter-element.h"
-
-struct _element_type {
- gchar *name;
-
- FilterElementFunc create;
- gpointer data;
-};
-
-static gboolean validate (FilterElement *fe, GtkWindow *error_parent);
-static gint element_eq(FilterElement *fe, FilterElement *cm);
-static void xml_create(FilterElement *fe, xmlNodePtr node);
-static FilterElement *clone(FilterElement *fe);
-static void copy_value(FilterElement *de, FilterElement *se);
-
-static void filter_element_class_init (FilterElementClass *klass);
-static void filter_element_init (FilterElement *fe);
-static void filter_element_finalise (GObject *obj);
-
-static GObjectClass *parent_class = NULL;
-
-GType
-filter_element_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterElementClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_element_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterElement),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_element_init,
- };
- type = g_type_register_static (G_TYPE_OBJECT, "FilterElement", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_element_class_init (FilterElementClass *klass)
-{
- parent_class = g_type_class_ref (G_TYPE_OBJECT);
-
- ((GObjectClass *)klass)->finalize = filter_element_finalise;
-
- /* override methods */
- klass->validate = validate;
- klass->eq = element_eq;
- klass->xml_create = xml_create;
- klass->clone = clone;
- klass->copy_value = copy_value;
-}
-
-static void
-filter_element_init (FilterElement *fe)
-{
- ;
-}
-
-static void
-filter_element_finalise (GObject *obj)
-{
- FilterElement *o = (FilterElement *)obj;
-
- xmlFree (o->name);
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_element_new:
- *
- * Create a new FilterElement object.
- *
- * Return value: A new #FilterElement object.
- **/
-FilterElement *
-filter_element_new (void)
-{
- return (FilterElement *) g_object_new (FILTER_TYPE_ELEMENT, NULL, NULL);
-}
-
-gboolean
-filter_element_validate (FilterElement *fe, GtkWindow *error_parent)
-{
- return FILTER_ELEMENT_GET_CLASS (fe)->validate (fe, error_parent);
-}
-
-gint
-filter_element_eq (FilterElement *fe, FilterElement *cm)
-{
- FilterElementClass *klass;
-
- klass = FILTER_ELEMENT_GET_CLASS (fe);
- return (klass == FILTER_ELEMENT_GET_CLASS (cm)) && klass->eq (fe, cm);
-}
-
-/**
- * filter_element_xml_create:
- * @fe: filter element
- * @node: xml node
- *
- * Create a new filter element based on an xml definition of
- * that element.
- **/
-void
-filter_element_xml_create (FilterElement *fe, xmlNodePtr node)
-{
- FILTER_ELEMENT_GET_CLASS (fe)->xml_create (fe, node);
-}
-
-/**
- * filter_element_xml_encode:
- * @fe: filter element
- *
- * Encode the values of a filter element into xml format.
- *
- * Return value:
- **/
-xmlNodePtr
-filter_element_xml_encode (FilterElement *fe)
-{
- return FILTER_ELEMENT_GET_CLASS (fe)->xml_encode (fe);
-}
-
-/**
- * filter_element_xml_decode:
- * @fe: filter element
- * @node: xml node
- *
- * Decode the values of a fitler element from xml format.
- *
- * Return value:
- **/
-gint
-filter_element_xml_decode (FilterElement *fe, xmlNodePtr node)
-{
- return FILTER_ELEMENT_GET_CLASS (fe)->xml_decode (fe, node);
-}
-
-/**
- * filter_element_clone:
- * @fe: filter element
- *
- * Clones the FilterElement @fe.
- *
- * Return value:
- **/
-FilterElement *
-filter_element_clone (FilterElement *fe)
-{
- return FILTER_ELEMENT_GET_CLASS (fe)->clone (fe);
-}
-
-/**
- * filter_element_get_widget:
- * @fe: filter element
- * @node: xml node
- *
- * Create a widget to represent this element.
- *
- * Return value:
- **/
-GtkWidget *
-filter_element_get_widget (FilterElement *fe)
-{
- return FILTER_ELEMENT_GET_CLASS (fe)->get_widget (fe);
-}
-
-/**
- * filter_element_build_code:
- * @fe: filter element
- * @out: output buffer
- * @ff:
- *
- * Add the code representing this element to the output string @out.
- **/
-void
-filter_element_build_code (FilterElement *fe, GString *out, struct _FilterPart *ff)
-{
- FILTER_ELEMENT_GET_CLASS (fe)->build_code (fe, out, ff);
-}
-
-/**
- * filter_element_format_sexp:
- * @fe: filter element
- * @out: output buffer
- *
- * Format the value(s) of this element in a method suitable for the context of
- * sexp where it is used. Usually as space separated, double-quoted strings.
- **/
-void
-filter_element_format_sexp (FilterElement *fe, GString *out)
-{
- FILTER_ELEMENT_GET_CLASS (fe)->format_sexp (fe, out);
-}
-
-void
-filter_element_set_data (FilterElement *fe, gpointer data)
-{
- fe->data = data;
-}
-
-/* default implementations */
-static gboolean
-validate (FilterElement *fe, GtkWindow *error_parent)
-{
- return TRUE;
-}
-
-static gint
-element_eq (FilterElement *fe, FilterElement *cm)
-{
- return ((fe->name && cm->name && strcmp (fe->name, cm->name) == 0)
- || (fe->name == NULL && cm->name == NULL));
-}
-
-static void
-xml_create (FilterElement *fe, xmlNodePtr node)
-{
- fe->name = (gchar *)xmlGetProp (node, (const guchar *)"name");
-}
-
-static FilterElement *
-clone (FilterElement *fe)
-{
- xmlNodePtr node;
- FilterElement *new;
-
- new = (FilterElement *) g_object_new (G_OBJECT_TYPE (fe), NULL, NULL);
- node = filter_element_xml_encode (fe);
- filter_element_xml_decode (new, node);
- xmlFreeNodeList (node);
-
- return new;
-}
-
-/* This is somewhat hackish, implement all the base cases in here */
-#include "filter-input.h"
-#include "filter-option.h"
-#include "filter-code.h"
-#include "filter-colour.h"
-#include "filter-datespec.h"
-#include "filter-int.h"
-#include "filter-file.h"
-
-static void
-copy_value(FilterElement *de, FilterElement *se)
-{
- if (IS_FILTER_INPUT(se)) {
- if (IS_FILTER_INPUT(de)) {
- if (((FilterInput *)se)->values)
- filter_input_set_value((FilterInput*)de, ((FilterInput *)se)->values->data);
- } else if (IS_FILTER_INT(de)) {
- ((FilterInt *)de)->val = atoi((gchar *) ((FilterInput *)se)->values->data);
- }
- } else if (IS_FILTER_COLOUR(se)) {
- if (IS_FILTER_COLOUR(de)) {
- FilterColour *s = (FilterColour *)se;
- FilterColour *d = (FilterColour *)de;
-
- d->color = s->color;
- }
- } else if (IS_FILTER_DATESPEC(se)) {
- if (IS_FILTER_DATESPEC(de)) {
- FilterDatespec *s = (FilterDatespec *)se, *d = (FilterDatespec *)de;
-
- d->type = s->type;
- d->value = s->value;
- }
- } else if (IS_FILTER_INT(se)) {
- if (IS_FILTER_INT(de)) {
- FilterInt *s = (FilterInt *)se, *d = (FilterInt *)de;
-
- d->val = s->val;
- } else if (IS_FILTER_INPUT(de)) {
- FilterInt *s = (FilterInt *)se;
- FilterInput *d = (FilterInput *)de;
- gchar *v;
-
- v = g_strdup_printf("%d", s->val);
- filter_input_set_value(d, v);
- g_free(v);
- }
- } else if (IS_FILTER_OPTION(se)) {
- if (IS_FILTER_OPTION(de)) {
- FilterOption *s = (FilterOption *)se, *d = (FilterOption *)de;
-
- if (s->current)
- filter_option_set_current(d, s->current->value);
- }
- }
-}
-
-/* only copies the value, not the name/type */
-void
-filter_element_copy_value (FilterElement *de, FilterElement *se)
-{
- FILTER_ELEMENT_GET_CLASS (de)->copy_value(de, se);
-}
diff --git a/filter/filter-element.h b/filter/filter-element.h
deleted file mode 100644
index 9bcb3f98df..0000000000
--- a/filter/filter-element.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _FILTER_ELEMENT_H
-#define _FILTER_ELEMENT_H
-
-#include <gtk/gtk.h>
-#include <libxml/parser.h>
-#include <libxml/xmlmemory.h>
-
-#define FILTER_TYPE_ELEMENT (filter_element_get_type ())
-#define FILTER_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_ELEMENT, FilterElement))
-#define FILTER_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_ELEMENT, FilterElementClass))
-#define IS_FILTER_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_ELEMENT))
-#define IS_FILTER_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_ELEMENT))
-#define FILTER_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_ELEMENT, FilterElementClass))
-
-typedef struct _FilterElement FilterElement;
-typedef struct _FilterElementClass FilterElementClass;
-
-typedef FilterElement *(*FilterElementFunc)(gpointer data);
-
-struct _FilterElement {
- GObject parent_object;
-
- gchar *name;
- gpointer data;
-};
-
-struct _FilterPart;
-
-struct _FilterElementClass {
- GObjectClass parent_class;
-
- /* virtual methods */
- gboolean (*validate) (FilterElement *fe, GtkWindow *error_parent);
- gint (*eq) (FilterElement *fe, FilterElement *cm);
-
- void (*xml_create) (FilterElement *, xmlNodePtr);
- xmlNodePtr (*xml_encode) (FilterElement *);
- gint (*xml_decode) (FilterElement *, xmlNodePtr);
-
- FilterElement *(*clone) (FilterElement *fe);
- void (*copy_value)(FilterElement *fe, FilterElement *se);
-
- GtkWidget *(*get_widget) (FilterElement *);
- void (*build_code) (FilterElement *, GString *, struct _FilterPart *ff);
- void (*format_sexp) (FilterElement *, GString *);
-
- /* signals */
-};
-
-GType filter_element_get_type (void);
-FilterElement *filter_element_new (void);
-
-void filter_element_set_data (FilterElement *fe, gpointer data);
-
-/* methods */
-gboolean filter_element_validate (FilterElement *fe, GtkWindow *error_parent);
-gint filter_element_eq (FilterElement *fe, FilterElement *cm);
-
-void filter_element_xml_create (FilterElement *fe, xmlNodePtr node);
-
-xmlNodePtr filter_element_xml_encode (FilterElement *fe);
-gint filter_element_xml_decode (FilterElement *fe, xmlNodePtr node);
-FilterElement *filter_element_clone (FilterElement *fe);
-void filter_element_copy_value (FilterElement *de, FilterElement *se);
-
-GtkWidget *filter_element_get_widget (FilterElement *fe);
-void filter_element_build_code (FilterElement *fe, GString *out, struct _FilterPart *ff);
-void filter_element_format_sexp (FilterElement *fe, GString *out);
-
-#endif /* ! _FILTER_ELEMENT_H */
diff --git a/filter/filter-file.c b/filter/filter-file.c
deleted file mode 100644
index b541022554..0000000000
--- a/filter/filter-file.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#include <config.h>
-
-#include <string.h>
-#include <sys/types.h>
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-
-#include <libedataserver/e-sexp.h>
-
-#include "e-util/e-error.h"
-
-#include "filter-file.h"
-
-#define d(x)
-
-static gboolean validate (FilterElement *fe, GtkWindow *error_parent);
-static gint file_eq (FilterElement *fe, FilterElement *cm);
-static void xml_create (FilterElement *fe, xmlNodePtr node);
-static xmlNodePtr xml_encode (FilterElement *fe);
-static gint xml_decode (FilterElement *fe, xmlNodePtr node);
-static GtkWidget *get_widget (FilterElement *fe);
-static void build_code (FilterElement *fe, GString *out, struct _FilterPart *ff);
-static void format_sexp (FilterElement *, GString *);
-
-static void filter_file_class_init (FilterFileClass *klass);
-static void filter_file_init (FilterFile *ff);
-static void filter_file_finalise (GObject *obj);
-
-static FilterElementClass *parent_class = NULL;
-
-GType
-filter_file_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterFileClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_file_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterFile),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_file_init,
- };
-
- type = g_type_register_static (FILTER_TYPE_ELEMENT, "FilterFile", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_file_class_init (FilterFileClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- FilterElementClass *fe_class = FILTER_ELEMENT_CLASS (klass);
-
- parent_class = g_type_class_ref (FILTER_TYPE_ELEMENT);
-
- object_class->finalize = filter_file_finalise;
-
- /* override methods */
- fe_class->validate = validate;
- fe_class->eq = file_eq;
- fe_class->xml_create = xml_create;
- fe_class->xml_encode = xml_encode;
- fe_class->xml_decode = xml_decode;
- fe_class->get_widget = get_widget;
- fe_class->build_code = build_code;
- fe_class->format_sexp = format_sexp;
-}
-
-static void
-filter_file_init (FilterFile *ff)
-{
- ;
-}
-
-static void
-filter_file_finalise (GObject *obj)
-{
- FilterFile *ff = (FilterFile *) obj;
-
- xmlFree (ff->type);
- g_free (ff->path);
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_file_new:
- *
- * Create a new FilterFile object.
- *
- * Return value: A new #FilterFile object.
- **/
-FilterFile *
-filter_file_new (void)
-{
- return (FilterFile *) g_object_new (FILTER_TYPE_FILE, NULL, NULL);
-}
-
-FilterFile *
-filter_file_new_type_name (const gchar *type)
-{
- FilterFile *file;
-
- file = filter_file_new ();
- file->type = (gchar *)xmlStrdup ((xmlChar *)type);
-
- return file;
-}
-
-void
-filter_file_set_path (FilterFile *file, const gchar *path)
-{
- g_free (file->path);
- file->path = g_strdup (path);
-}
-
-static gboolean
-validate (FilterElement *fe, GtkWindow *error_parent)
-{
- FilterFile *file = (FilterFile *) fe;
-
- if (!file->path) {
- e_error_run (error_parent, "filter:no-file", NULL);
-
- return FALSE;
- }
-
- /* FIXME: do more to validate command-lines? */
-
- if (strcmp (file->type, "file") == 0) {
- if (!g_file_test (file->path, G_FILE_TEST_IS_REGULAR)) {
- e_error_run (error_parent, "filter:bad-file", file->path, NULL);
-
- return FALSE;
- }
- } else if (strcmp (file->type, "command") == 0) {
- /* only requirements so far is that the command can't
- be an empty string */
- return file->path[0] != '\0';
- }
-
- return TRUE;
-}
-
-static gint
-file_eq (FilterElement *fe, FilterElement *cm)
-{
- FilterFile *ff = (FilterFile *)fe, *cf = (FilterFile *)cm;
-
- return FILTER_ELEMENT_CLASS (parent_class)->eq (fe, cm)
- && ((ff->path && cf->path && strcmp (ff->path, cf->path) == 0)
- || (ff->path == NULL && cf->path == NULL))
- && ((ff->type && cf->type && strcmp (ff->type, cf->type) == 0)
- || (ff->type == NULL && cf->type == NULL));
-}
-
-static void
-xml_create (FilterElement *fe, xmlNodePtr node)
-{
- /* parent implementation */
- FILTER_ELEMENT_CLASS (parent_class)->xml_create (fe, node);
-}
-
-static xmlNodePtr
-xml_encode (FilterElement *fe)
-{
- FilterFile *file = (FilterFile *) fe;
- xmlNodePtr cur, value;
- const gchar *type;
-
- type = file->type ? file->type : "file";
-
- d(printf ("Encoding %s as xml\n", type));
-
- value = xmlNewNode (NULL, (const guchar *)"value");
- xmlSetProp (value, (const guchar *)"name", (guchar *)fe->name);
- xmlSetProp (value, (const guchar *)"type", (guchar *)type);
-
- cur = xmlNewChild (value, NULL, (guchar *)type, NULL);
- xmlNodeSetContent (cur, (guchar *)file->path);
-
- return value;
-}
-
-static gint
-xml_decode (FilterElement *fe, xmlNodePtr node)
-{
- FilterFile *file = (FilterFile *)fe;
- gchar *name, *str, *type;
- xmlNodePtr n;
-
- name = (gchar *)xmlGetProp (node, (const guchar *)"name");
- type = (gchar *)xmlGetProp (node, (const guchar *)"type");
-
- d(printf("Decoding %s from xml %p\n", type, fe));
- d(printf ("Name = %s\n", name));
-
- xmlFree (fe->name);
- fe->name = name;
- xmlFree (file->type);
- file->type = type;
-
- g_free (file->path);
- file->path = NULL;
-
- n = node->children;
- while (n != NULL) {
- if (!strcmp ((gchar *)n->name, type)) {
- str = (gchar *)xmlNodeGetContent (n);
- file->path = g_strdup (str ? str : "");
- xmlFree (str);
-
- d(printf (" '%s'\n", file->path));
- break;
- } else if (n->type == XML_ELEMENT_NODE) {
- g_warning ("Unknown node type '%s' encountered decoding a %s\n", n->name, type);
- }
-
- n = n->next;
- }
-
- return 0;
-}
-
-static void
-filename_changed (GtkWidget *widget, FilterElement *fe)
-{
- FilterFile *file = (FilterFile *) fe;
- const gchar *new;
-
- new = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
- g_free (file->path);
- file->path = g_strdup (new);
-}
-
-static GtkWidget *
-get_widget (FilterElement *fe)
-{
- FilterFile *file = (FilterFile *) fe;
- GtkWidget *filewidget;
-
- filewidget = (GtkWidget *) gtk_file_chooser_button_new (_("Choose a file"), GTK_FILE_CHOOSER_ACTION_OPEN);
- gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewidget), file->path);
- g_signal_connect (GTK_FILE_CHOOSER_BUTTON (filewidget), "selection-changed",
- G_CALLBACK (filename_changed), fe);
- return filewidget;
-}
-
-static void
-build_code (FilterElement *fe, GString *out, struct _FilterPart *ff)
-{
- return;
-}
-
-static void
-format_sexp (FilterElement *fe, GString *out)
-{
- FilterFile *file = (FilterFile *) fe;
-
- e_sexp_encode_string (out, file->path);
-}
diff --git a/filter/filter-file.h b/filter/filter-file.h
deleted file mode 100644
index af8d05fe7a..0000000000
--- a/filter/filter-file.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- *
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef __FILTER_FILE_H__
-#define __FILTER_FILE_H__
-
-G_BEGIN_DECLS
-
-#include "filter-element.h"
-
-#define FILTER_TYPE_FILE (filter_file_get_type ())
-#define FILTER_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_FILE, FilterFile))
-#define FILTER_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_FILE, FilterFileClass))
-#define IS_FILTER_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_FILE))
-#define IS_FILTER_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_FILE))
-#define FILTER_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_FILE, FilterFileClass))
-
-typedef struct _FilterFile FilterFile;
-typedef struct _FilterFileClass FilterFileClass;
-
-struct _FilterFile {
- FilterElement parent_object;
-
- gchar *type;
- gchar *path;
-};
-
-struct _FilterFileClass {
- FilterElementClass parent_class;
-
- /* virtual methods */
-
- /* signals */
-};
-
-GType filter_file_get_type (void);
-
-FilterFile *filter_file_new (void);
-
-FilterFile *filter_file_new_type_name (const gchar *type);
-
-/* methods */
-void filter_file_set_path (FilterFile *file, const gchar *path);
-
-G_END_DECLS
-
-#endif /* ! __FILTER_FILE_H__ */
diff --git a/filter/filter-input.c b/filter/filter-input.c
deleted file mode 100644
index 31f74d991f..0000000000
--- a/filter/filter-input.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <sys/types.h>
-#include <regex.h>
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-
-#include "filter-input.h"
-#include "libedataserver/e-sexp.h"
-#include "e-util/e-error.h"
-
-#define d(x)
-
-static gboolean validate (FilterElement *fe, GtkWindow *error_parent);
-static gint input_eq (FilterElement *fe, FilterElement *cm);
-static void xml_create (FilterElement *fe, xmlNodePtr node);
-static xmlNodePtr xml_encode (FilterElement *fe);
-static gint xml_decode (FilterElement *fe, xmlNodePtr node);
-static GtkWidget *get_widget (FilterElement *fe);
-static void build_code (FilterElement *fe, GString *out, struct _FilterPart *ff);
-static void format_sexp (FilterElement *, GString *);
-
-static void filter_input_class_init (FilterInputClass *klass);
-static void filter_input_init (FilterInput *fi);
-static void filter_input_finalise (GObject *obj);
-
-static FilterElementClass *parent_class = NULL;
-
-GType
-filter_input_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterInputClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_input_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterInput),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_input_init,
- };
-
- type = g_type_register_static (FILTER_TYPE_ELEMENT, "FilterInput", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_input_class_init (FilterInputClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- FilterElementClass *fe_class = FILTER_ELEMENT_CLASS (klass);
-
- parent_class = g_type_class_ref (FILTER_TYPE_ELEMENT);
-
- object_class->finalize = filter_input_finalise;
-
- /* override methods */
- fe_class->validate = validate;
- fe_class->eq = input_eq;
- fe_class->xml_create = xml_create;
- fe_class->xml_encode = xml_encode;
- fe_class->xml_decode = xml_decode;
- fe_class->get_widget = get_widget;
- fe_class->build_code = build_code;
- fe_class->format_sexp = format_sexp;
-}
-
-static void
-filter_input_init (FilterInput *fi)
-{
- fi->values = g_list_prepend (NULL, g_strdup (""));
-}
-
-static void
-filter_input_finalise (GObject *obj)
-{
- FilterInput *fi = (FilterInput *) obj;
-
- xmlFree (fi->type);
- g_list_foreach (fi->values, (GFunc)g_free, NULL);
- g_list_free (fi->values);
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_input_new:
- *
- * Create a new FilterInput object.
- *
- * Return value: A new #FilterInput object.
- **/
-FilterInput *
-filter_input_new (void)
-{
- return (FilterInput *) g_object_new (FILTER_TYPE_INPUT, NULL, NULL);
-}
-
-FilterInput *
-filter_input_new_type_name (const gchar *type)
-{
- FilterInput *fi;
-
- fi = filter_input_new ();
- fi->type = (gchar *)xmlStrdup ((xmlChar *)type);
-
- d(printf("new type %s = %p\n", type, fi));
-
- return fi;
-}
-
-void
-filter_input_set_value (FilterInput *fi, const gchar *value)
-{
- GList *l;
-
- d(printf("set_value '%s'\n", value));
-
- l = fi->values;
- while (l) {
- g_free (l->data);
- l = g_list_next (l);
- }
- g_list_free (fi->values);
-
- fi->values = g_list_append (NULL, g_strdup (value));
-}
-
-static gboolean
-validate (FilterElement *fe, GtkWindow *error_parent)
-{
- FilterInput *fi = (FilterInput *)fe;
- gboolean valid = TRUE;
-
- if (fi->values && !strcmp (fi->type, "regex")) {
- const gchar *pattern;
- regex_t regexpat;
- gint regerr;
-
- pattern = fi->values->data;
-
- if ((regerr = regcomp (&regexpat, pattern, REG_EXTENDED | REG_NEWLINE | REG_ICASE))) {
- gsize reglen;
- gchar *regmsg;
-
- /* regerror gets called twice to get the full error string
- length to do proper posix error reporting */
- reglen = regerror (regerr, &regexpat, 0, 0);
- regmsg = g_malloc0 (reglen + 1);
- regerror (regerr, &regexpat, regmsg, reglen);
-
- e_error_run (error_parent, "filter:bad-regexp", pattern, regmsg, NULL);
- g_free (regmsg);
-
- valid = FALSE;
- }
-
- regfree (&regexpat);
- }
-
- return valid;
-}
-
-static gint
-list_eq (GList *al, GList *bl)
-{
- gint truth = TRUE;
-
- while (truth && al && bl) {
- truth = strcmp ((gchar *) al->data, (gchar *) bl->data) == 0;
- al = al->next;
- bl = bl->next;
- }
-
- return truth && al == NULL && bl == NULL;
-}
-
-static gint
-input_eq (FilterElement *fe, FilterElement *cm)
-{
- FilterInput *fi = (FilterInput *)fe, *ci = (FilterInput *)cm;
-
- return FILTER_ELEMENT_CLASS (parent_class)->eq (fe, cm)
- && strcmp (fi->type, ci->type) == 0
- && list_eq (fi->values, ci->values);
-}
-
-static void
-xml_create (FilterElement *fe, xmlNodePtr node)
-{
- /* parent implementation */
- FILTER_ELEMENT_CLASS (parent_class)->xml_create (fe, node);
-}
-
-static xmlNodePtr
-xml_encode (FilterElement *fe)
-{
- xmlNodePtr value;
- GList *l;
- FilterInput *fi = (FilterInput *)fe;
- const gchar *type;
-
- type = fi->type ? fi->type : "string";
-
- d(printf ("Encoding %s as xml\n", type));
-
- value = xmlNewNode (NULL, (const guchar *)"value");
- xmlSetProp (value, (const guchar *)"name", (guchar *)fe->name);
- xmlSetProp (value, (const guchar *)"type", (guchar *)type);
- l = fi->values;
- while (l) {
- xmlNodePtr cur;
- xmlChar *str = l->data;
-
- cur = xmlNewChild (value, NULL, (guchar *)type, NULL);
-
- str = xmlEncodeEntitiesReentrant (NULL, str);
- xmlNodeSetContent (cur, str);
- xmlFree (str);
-
- l = l->next;
- }
-
- return value;
-}
-
-static gint
-xml_decode (FilterElement *fe, xmlNodePtr node)
-{
- FilterInput *fi = (FilterInput *)fe;
- gchar *name, *str, *type;
- xmlNodePtr n;
- GList *l;
-
- l = fi->values;
- while (l) {
- g_free (l->data);
- l = l->next;
- }
- g_list_free (fi->values);
- fi->values = NULL;
-
- name = (gchar *)xmlGetProp (node, (const guchar *)"name");
- type = (gchar *)xmlGetProp (node, (const guchar *)"type");
-
- d(printf("Decoding %s from xml %p\n", type, fe));
- d(printf ("Name = %s\n", name));
- xmlFree (fe->name);
- fe->name = name;
- xmlFree (fi->type);
- fi->type = type;
- n = node->children;
- while (n) {
- if (!strcmp ((gchar *)n->name, type)) {
- if (!(str = (gchar *)xmlNodeGetContent (n)))
- str = (gchar *)xmlStrdup ((const guchar *)"");
-
- d(printf (" '%s'\n", str));
- fi->values = g_list_append (fi->values, g_strdup (str));
- xmlFree (str);
- } else if (n->type == XML_ELEMENT_NODE) {
- g_warning ("Unknown node type '%s' encountered decoding a %s\n", n->name, type);
- }
- n = n->next;
- }
-
- return 0;
-}
-
-static void
-entry_changed (GtkEntry *entry, FilterElement *fe)
-{
- FilterInput *fi = (FilterInput *) fe;
- const gchar *new;
- GList *l;
-
- new = gtk_entry_get_text (entry);
-
- d(printf("entry_changed '%s'\n", new));
-
- /* NOTE: entry only supports a single value ... */
- l = fi->values;
- while (l) {
- g_free (l->data);
- l = l->next;
- }
-
- g_list_free (fi->values);
-
- fi->values = g_list_append (NULL, g_strdup (new));
-}
-
-static GtkWidget *
-get_widget (FilterElement *fe)
-{
- GtkWidget *entry;
- FilterInput *fi = (FilterInput *)fe;
-
- entry = gtk_entry_new ();
- if (fi->values && fi->values->data)
- gtk_entry_set_text (GTK_ENTRY (entry), (const gchar *) fi->values->data);
-
- g_signal_connect (entry, "changed", G_CALLBACK (entry_changed), fe);
-
- return entry;
-}
-
-static void
-build_code (FilterElement *fe, GString *out, struct _FilterPart *ff)
-{
- ;
-}
-
-static void
-format_sexp (FilterElement *fe, GString *out)
-{
- FilterInput *fi = (FilterInput *) fe;
- GList *l;
-
- d(printf("format_sexp, first elem=%p\n", fi->values));
-
- l = fi->values;
- while (l) {
- e_sexp_encode_string (out, l->data);
- l = l->next;
- }
-}
diff --git a/filter/filter-input.h b/filter/filter-input.h
deleted file mode 100644
index 9a5e181eaa..0000000000
--- a/filter/filter-input.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _FILTER_INPUT_H
-#define _FILTER_INPUT_H
-
-#include "filter-element.h"
-
-#define FILTER_TYPE_INPUT (filter_input_get_type ())
-#define FILTER_INPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_INPUT, FilterInput))
-#define FILTER_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_INPUT, FilterInputClass))
-#define IS_FILTER_INPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_INPUT))
-#define IS_FILTER_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_INPUT))
-#define FILTER_INPUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_INPUT, FilterInputClass))
-
-typedef struct _FilterInput FilterInput;
-typedef struct _FilterInputClass FilterInputClass;
-
-struct _FilterInput {
- FilterElement parent_object;
-
- gchar *type; /* name of type */
- GList *values; /* strings */
-};
-
-struct _FilterInputClass {
- FilterElementClass parent_class;
-
- /* virtual methods */
-
- /* signals */
-};
-
-GType filter_input_get_type (void);
-FilterInput *filter_input_new (void);
-
-FilterInput *filter_input_new_type_name (const gchar *type);
-
-/* methods */
-void filter_input_set_value (FilterInput *fi, const gchar *value);
-
-#endif /* ! _FILTER_INPUT_H */
diff --git a/filter/filter-int.c b/filter/filter-int.c
deleted file mode 100644
index ab8208e45e..0000000000
--- a/filter/filter-int.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <gtk/gtk.h>
-
-#include <libedataserver/e-sexp.h>
-#include "filter-int.h"
-
-#define d(x)
-
-static gint int_eq (FilterElement *fe, FilterElement *cm);
-static FilterElement *int_clone(FilterElement *fe);
-static void xml_create (FilterElement *fe, xmlNodePtr node);
-static xmlNodePtr xml_encode (FilterElement *fe);
-static gint xml_decode (FilterElement *fe, xmlNodePtr node);
-static GtkWidget *get_widget (FilterElement *fe);
-static void build_code (FilterElement *fe, GString *out, struct _FilterPart *ff);
-static void format_sexp (FilterElement *fe, GString *out);
-
-static void filter_int_class_init (FilterIntClass *klass);
-static void filter_int_init (FilterInt *fi);
-static void filter_int_finalise (GObject *obj);
-
-static FilterElementClass *parent_class;
-
-GType
-filter_int_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterIntClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_int_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterInt),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_int_init,
- };
-
- type = g_type_register_static (FILTER_TYPE_ELEMENT, "FilterInt", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_int_class_init (FilterIntClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- FilterElementClass *fe_class = FILTER_ELEMENT_CLASS (klass);
-
- parent_class = g_type_class_ref (FILTER_TYPE_ELEMENT);
-
- object_class->finalize = filter_int_finalise;
-
- /* override methods */
- fe_class->eq = int_eq;
- fe_class->clone = int_clone;
- fe_class->xml_create = xml_create;
- fe_class->xml_encode = xml_encode;
- fe_class->xml_decode = xml_decode;
- fe_class->get_widget = get_widget;
- fe_class->build_code = build_code;
- fe_class->format_sexp = format_sexp;
-}
-
-static void
-filter_int_init (FilterInt *fi)
-{
- fi->min = 0;
- fi->max = G_MAXINT;
-}
-
-static void
-filter_int_finalise (GObject *obj)
-{
- FilterInt *fi = (FilterInt *) obj;
-
- g_free (fi->type);
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_int_new:
- *
- * Create a new FilterInt object.
- *
- * Return value: A new #FilterInt object.
- **/
-FilterInt *
-filter_int_new (void)
-{
- return (FilterInt *) g_object_new (FILTER_TYPE_INT, NULL, NULL);
-}
-
-FilterInt *
-filter_int_new_type (const gchar *type, gint min, gint max)
-{
- FilterInt *fi;
-
- fi = filter_int_new ();
-
- fi->type = g_strdup (type);
- fi->min = min;
- fi->max = max;
-
- return fi;
-}
-
-void
-filter_int_set_value (FilterInt *fi, gint val)
-{
- fi->val = val;
-}
-
-static gint
-int_eq (FilterElement *fe, FilterElement *cm)
-{
- return FILTER_ELEMENT_CLASS (parent_class)->eq (fe, cm)
- && ((FilterInt *)fe)->val == ((FilterInt *)cm)->val;
-}
-
-static FilterElement *
-int_clone(FilterElement *fe)
-{
- FilterInt *fi, *fs;
-
- fs = (FilterInt *)fe;
- fi = filter_int_new_type(fs->type, fs->min, fs->max);
- fi->val = fs->val;
- ((FilterElement *)fi)->name = g_strdup(fe->name);
-
- return (FilterElement *)fi;
-}
-
-static void
-xml_create (FilterElement *fe, xmlNodePtr node)
-{
- /* parent implementation */
- FILTER_ELEMENT_CLASS (parent_class)->xml_create (fe, node);
-}
-
-static xmlNodePtr
-xml_encode (FilterElement *fe)
-{
- xmlNodePtr value;
- FilterInt *fs = (FilterInt *)fe;
- gchar intval[32];
- const gchar *type;
-
- type = fs->type?fs->type:"integer";
-
- d(printf("Encoding %s as xml\n", type));
-
- value = xmlNewNode (NULL, (const guchar *)"value");
- xmlSetProp (value, (const guchar *)"name", (guchar *)fe->name);
- xmlSetProp (value, (const guchar *)"type", (guchar *)type);
-
- sprintf(intval, "%d", fs->val);
- xmlSetProp (value, (guchar *)type, (guchar *)intval);
-
- return value;
-}
-
-static gint
-xml_decode (FilterElement *fe, xmlNodePtr node)
-{
- FilterInt *fs = (FilterInt *)fe;
- gchar *name, *type;
- gchar *intval;
-
- d(printf("Decoding integer from xml %p\n", fe));
-
- name = (gchar *)xmlGetProp (node, (const guchar *)"name");
- d(printf ("Name = %s\n", name));
- xmlFree (fe->name);
- fe->name = name;
-
- type = (gchar *)xmlGetProp(node, (const guchar *)"type");
- d(printf ("Type = %s\n", type));
- g_free(fs->type);
- fs->type = g_strdup(type);
- xmlFree(type);
-
- intval = (gchar *)xmlGetProp (node, (guchar *)(fs->type ? fs->type : "integer"));
- if (intval) {
- d(printf ("Value = %s\n", intval));
- fs->val = atoi (intval);
- xmlFree (intval);
- } else {
- d(printf ("Value = ?unknown?\n"));
- fs->val = 0;
- }
-
- return 0;
-}
-
-static void
-spin_changed (GtkWidget *spin, FilterElement *fe)
-{
- FilterInt *fs = (FilterInt *)fe;
-
- fs->val = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin));
-}
-
-static GtkWidget *
-get_widget (FilterElement *fe)
-{
- GtkWidget *spin;
- GtkObject *adjustment;
- FilterInt *fs = (FilterInt *)fe;
-
- adjustment = gtk_adjustment_new (0.0, (gfloat)fs->min, (gfloat)fs->max, 1.0, 1.0, 0);
- spin = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), fs->max>fs->min+1000?5.0:1.0, 0);
- gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE);
-
- if (fs->val)
- gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), (gfloat) fs->val);
-
- g_signal_connect (spin, "value-changed", G_CALLBACK (spin_changed), fe);
-
- return spin;
-}
-
-static void
-build_code (FilterElement *fe, GString *out, struct _FilterPart *ff)
-{
- return;
-}
-
-static void
-format_sexp (FilterElement *fe, GString *out)
-{
- FilterInt *fs = (FilterInt *)fe;
-
- if (fs->val < 0)
- /* See #364731 #457523 C6*/
- g_string_append_printf (out, "(- %d)", (fs->val * -1));
- else
- g_string_append_printf (out, "%d", fs->val);
-}
diff --git a/filter/filter-int.h b/filter/filter-int.h
deleted file mode 100644
index 4167bc8cf3..0000000000
--- a/filter/filter-int.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _FILTER_INT_H
-#define _FILTER_INT_H
-
-#include "filter-element.h"
-
-#define FILTER_TYPE_INT (filter_int_get_type ())
-#define FILTER_INT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_INT, FilterInt))
-#define FILTER_INT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_INT, FilterIntClass))
-#define IS_FILTER_INT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_INT))
-#define IS_FILTER_INT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_INT))
-#define FILTER_INT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_INT, FilterIntClass))
-
-typedef struct _FilterInt FilterInt;
-typedef struct _FilterIntClass FilterIntClass;
-
-struct _FilterInt {
- FilterElement parent_object;
-
- gchar *type;
- gint val;
- gint min;
- gint max;
-};
-
-struct _FilterIntClass {
- FilterElementClass parent_class;
-
- /* virtual methods */
-
- /* signals */
-};
-
-GType filter_int_get_type (void);
-FilterInt *filter_int_new (void);
-FilterInt *filter_int_new_type (const gchar *type, gint min, gint max);
-void filter_int_set_value (FilterInt *fi, gint val);
-
-/* methods */
-
-#endif /* ! _FILTER_INT_H */
diff --git a/filter/filter-option.c b/filter/filter-option.c
deleted file mode 100644
index 14bb796ff6..0000000000
--- a/filter/filter-option.c
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-#include <gmodule.h>
-
-#include "filter-option.h"
-#include "filter-part.h"
-#include <libedataserver/e-sexp.h>
-
-#define d(x)
-
-static gint option_eq (FilterElement *fe, FilterElement *cm);
-static void xml_create (FilterElement *fe, xmlNodePtr node);
-static xmlNodePtr xml_encode (FilterElement *fe);
-static gint xml_decode (FilterElement *fe, xmlNodePtr node);
-static FilterElement *clone (FilterElement *fe);
-static GtkWidget *get_widget (FilterElement *fe);
-static void build_code (FilterElement *fe, GString *out, struct _FilterPart *ff);
-static void format_sexp (FilterElement *, GString *);
-static GSList *get_dynamic_options (FilterOption *fo);
-
-static void filter_option_class_init (FilterOptionClass *klass);
-static void filter_option_init (FilterOption *fo);
-static void filter_option_finalise (GObject *obj);
-
-static FilterElementClass *parent_class;
-
-GType
-filter_option_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterOptionClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_option_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterOption),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_option_init,
- };
-
- type = g_type_register_static (FILTER_TYPE_ELEMENT, "FilterOption", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_option_class_init (FilterOptionClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- FilterElementClass *fe_class = FILTER_ELEMENT_CLASS (klass);
-
- parent_class = g_type_class_ref (FILTER_TYPE_ELEMENT);
-
- object_class->finalize = filter_option_finalise;
-
- /* override methods */
- fe_class->eq = option_eq;
- fe_class->xml_create = xml_create;
- fe_class->xml_encode = xml_encode;
- fe_class->xml_decode = xml_decode;
- fe_class->clone = clone;
- fe_class->get_widget = get_widget;
- fe_class->build_code = build_code;
- fe_class->format_sexp = format_sexp;
-}
-
-static void
-filter_option_init (FilterOption *fo)
-{
- fo->type = "option";
- fo->dynamic_func = NULL;
-}
-
-static void
-free_option (struct _filter_option *o, gpointer data)
-{
- g_free (o->title);
- g_free (o->value);
- g_free (o->code);
- g_free (o);
-}
-
-static void
-filter_option_finalise (GObject *obj)
-{
- FilterOption *fo = (FilterOption *) obj;
-
- g_list_foreach (fo->options, (GFunc)free_option, NULL);
- g_list_free (fo->options);
- g_free (fo->dynamic_func);
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_option_new:
- *
- * Create a new FilterOption object.
- *
- * Return value: A new #FilterOption object.
- **/
-FilterOption *
-filter_option_new (void)
-{
- return (FilterOption *) g_object_new (FILTER_TYPE_OPTION, NULL, NULL);
-}
-
-static struct _filter_option *
-find_option (FilterOption *fo, const gchar *name)
-{
- GList *l = fo->options;
- struct _filter_option *op;
-
- while (l) {
- op = l->data;
- if (!strcmp (name, op->value)) {
- return op;
- }
- l = g_list_next (l);
- }
-
- return NULL;
-}
-
-void
-filter_option_set_current (FilterOption *option, const gchar *name)
-{
- g_return_if_fail (IS_FILTER_OPTION(option));
-
- option->current = find_option (option, name);
-}
-
-/* used by implementers to add additional options */
-struct _filter_option *
-filter_option_add(FilterOption *fo, const gchar *value, const gchar *title, const gchar *code, gboolean is_dynamic)
-{
- struct _filter_option *op;
-
- g_return_val_if_fail (IS_FILTER_OPTION(fo), NULL);
- g_return_val_if_fail(find_option(fo, value) == NULL, NULL);
-
- op = g_malloc(sizeof(*op));
- op->title = g_strdup(title);
- op->value = g_strdup(value);
- op->code = g_strdup(code);
- op->is_dynamic = is_dynamic;
-
- fo->options = g_list_append(fo->options, op);
- if (fo->current == NULL)
- fo->current = op;
-
- return op;
-}
-
-const gchar *
-filter_option_get_current (FilterOption *option)
-{
- g_return_val_if_fail (IS_FILTER_OPTION (option), NULL);
-
- if (!option->current)
- return NULL;
-
- return option->current->value;
-}
-
-void
-filter_option_remove_all (FilterOption *fo)
-{
- g_return_if_fail (IS_FILTER_OPTION (fo));
-
- g_list_foreach (fo->options, (GFunc)free_option, NULL);
- g_list_free (fo->options);
- fo->options = NULL;
-
- fo->current = NULL;
-}
-
-static gint
-option_eq(FilterElement *fe, FilterElement *cm)
-{
- FilterOption *fo = (FilterOption *)fe, *co = (FilterOption *)cm;
-
- return FILTER_ELEMENT_CLASS (parent_class)->eq (fe, cm)
- && ((fo->current && co->current && strcmp(fo->current->value, co->current->value) == 0)
- || (fo->current == NULL && co->current == NULL));
-}
-
-static void
-xml_create (FilterElement *fe, xmlNodePtr node)
-{
- FilterOption *fo = (FilterOption *)fe;
- xmlNodePtr n, work;
-
- /* parent implementation */
- FILTER_ELEMENT_CLASS (parent_class)->xml_create (fe, node);
-
- n = node->children;
- while (n) {
- if (!strcmp ((gchar *)n->name, "option")) {
- gchar *tmp, *value, *title = NULL, *code = NULL;
-
- value = (gchar *)xmlGetProp (n, (const guchar *)"value");
- work = n->children;
- while (work) {
- if (!strcmp ((gchar *)work->name, "title") || !strcmp ((gchar *)work->name, "_title")) {
- if (!title) {
- if (!(tmp = (gchar *)xmlNodeGetContent (work)))
- tmp = (gchar *)xmlStrdup ((const guchar *)"");
-
- title = g_strdup (tmp);
- xmlFree (tmp);
- }
- } else if (!strcmp ((gchar *)work->name, "code")) {
- if (!code) {
- if (!(tmp = (gchar *)xmlNodeGetContent (work)))
- tmp = (gchar *)xmlStrdup ((const guchar *)"");
-
- code = g_strdup (tmp);
- xmlFree (tmp);
- }
- }
- work = work->next;
- }
-
- filter_option_add (fo, value, title, code, FALSE);
- xmlFree (value);
- g_free (title);
- g_free (code);
- } else if (g_str_equal ((gchar *)n->name, "dynamic")) {
- if (fo->dynamic_func) {
- g_warning ("Only one 'dynamic' node is acceptable in the optionlist '%s'", fe->name);
- } else {
- /* Expecting only one <dynamic func="cb" /> in the option list,
- The 'cb' should be of this prototype:
- GSList *cb (void);
- returning GSList of struct _filter_option, all newly allocated, because it'll
- be freed with g_free and g_slist_free. 'is_dynamic' member is ignored here.
- */
- xmlChar *fn;
-
- fn = xmlGetProp (n, (const guchar *)"func");
- if (fn && *fn) {
- GSList *items, *i;
- struct _filter_option *op;
-
- fo->dynamic_func = g_strdup ((const gchar *)fn);
-
- /* get options now, to have them available when reading saved rules */
- items = get_dynamic_options (fo);
- for (i = items; i; i = i->next) {
- op = i->data;
-
- if (op) {
- filter_option_add (fo, op->value, op->title, op->code, TRUE);
- free_option (op, NULL);
- }
- }
-
- g_slist_free (items);
- } else {
- g_warning ("Missing 'func' attribute within '%s' node in optionlist '%s'", n->name, fe->name);
- }
-
- xmlFree (fn);
- }
- } else if (n->type == XML_ELEMENT_NODE) {
- g_warning ("Unknown xml node within optionlist: %s\n", n->name);
- }
- n = n->next;
- }
-}
-
-static xmlNodePtr
-xml_encode (FilterElement *fe)
-{
- xmlNodePtr value;
- FilterOption *fo = (FilterOption *)fe;
-
- d(printf ("Encoding option as xml\n"));
- value = xmlNewNode (NULL, (const guchar *)"value");
- xmlSetProp (value, (const guchar *)"name", (guchar *)fe->name);
- xmlSetProp (value, (const guchar *)"type", (guchar *)fo->type);
- if (fo->current)
- xmlSetProp (value, (const guchar *)"value", (guchar *)fo->current->value);
-
- return value;
-}
-
-static gint
-xml_decode (FilterElement *fe, xmlNodePtr node)
-{
- FilterOption *fo = (FilterOption *)fe;
- gchar *value;
-
- d(printf ("Decoding option from xml\n"));
- xmlFree (fe->name);
- fe->name = (gchar *)xmlGetProp (node, (const guchar *)"name");
- value = (gchar *)xmlGetProp (node, (const guchar *)"value");
- if (value) {
- fo->current = find_option (fo, value);
- xmlFree (value);
- } else {
- fo->current = NULL;
- }
- return 0;
-}
-
-static void
-combobox_changed (GtkWidget *widget, FilterElement *fe)
-{
- FilterOption *fo = (FilterOption *)fe;
-
- fo->current = (struct _filter_option *) g_list_nth_data (fo->options, gtk_combo_box_get_active (GTK_COMBO_BOX (widget)));
-}
-
-static GSList *
-get_dynamic_options (FilterOption *fo)
-{
- GModule *module;
- GSList *(*get_func)(void);
- GSList *res = NULL;
-
- if (!fo || !fo->dynamic_func)
- return res;
-
- module = g_module_open (NULL, G_MODULE_BIND_LAZY);
-
- if (g_module_symbol (module, fo->dynamic_func, (gpointer) &get_func)) {
- res = get_func ();
- } else {
- g_warning ("optionlist dynamic fill function '%s' not found", fo->dynamic_func);
- }
-
- g_module_close (module);
-
- return res;
-}
-
-static GtkWidget *
-get_widget (FilterElement *fe)
-{
- FilterOption *fo = (FilterOption *)fe;
- GtkWidget *combobox;
- GList *l;
- struct _filter_option *op;
- gint index = 0, current = 0;
-
- if (fo->dynamic_func) {
- /* it is dynamically filled, thus remove all dynamics and put there the fresh ones */
- GSList *items, *i;
- GList *old_ops;
- struct _filter_option *old_cur;
-
- old_ops = fo->options;
- old_cur = fo->current;
- l = old_ops;
-
- /* start with an empty list */
- fo->current = NULL;
- fo->options = NULL;
-
- for (l = fo->options; l; l = l->next) {
- op = l->data;
-
- if (op->is_dynamic) {
- break;
- } else {
- filter_option_add (fo, op->value, op->title, op->code, FALSE);
- }
- }
-
- items = get_dynamic_options (fo);
- for (i = items; i; i = i->next) {
- op = i->data;
-
- if (op) {
- filter_option_add (fo, op->value, op->title, op->code, TRUE);
- free_option (op, NULL);
- }
- }
-
- g_slist_free (items);
-
- /* maybe some static left after those dynamic, add them too */
- for (; l; l = l->next) {
- op = l->data;
-
- if (!op->is_dynamic)
- filter_option_add (fo, op->value, op->title, op->code, FALSE);
- }
-
- if (old_cur)
- filter_option_set_current (fo, old_cur->value);
-
- /* free old list */
- g_list_foreach (old_ops, (GFunc)free_option, NULL);
- g_list_free (old_ops);
- }
-
- combobox = gtk_combo_box_new_text ();
- l = fo->options;
- while (l) {
- op = l->data;
- gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), _(op->title));
-
- if (op == fo->current)
- current = index;
-
- l = g_list_next (l);
- index++;
- }
-
- g_signal_connect (combobox, "changed", G_CALLBACK (combobox_changed), fe);
- gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), current);
-
- return combobox;
-}
-
-static void
-build_code (FilterElement *fe, GString *out, struct _FilterPart *ff)
-{
- FilterOption *fo = (FilterOption *)fe;
-
- d(printf ("building option code %p, current = %p\n", fo, fo->current));
-
- if (fo->current && fo->current->code)
- filter_part_expand_code (ff, fo->current->code, out);
-}
-
-static void
-format_sexp (FilterElement *fe, GString *out)
-{
- FilterOption *fo = (FilterOption *)fe;
-
- if (fo->current)
- e_sexp_encode_string (out, fo->current->value);
-}
-
-static FilterElement *
-clone (FilterElement *fe)
-{
- FilterOption *fo = (FilterOption *)fe, *new;
- GList *l;
- struct _filter_option *op, *newop;
-
- d(printf ("cloning option\n"));
-
- new = FILTER_OPTION (FILTER_ELEMENT_CLASS (parent_class)->clone (fe));
- l = fo->options;
- while (l) {
- op = l->data;
- newop = filter_option_add (new, op->value, op->title, op->code, op->is_dynamic);
- if (fo->current == op)
- new->current = newop;
- l = l->next;
- }
-
- new->dynamic_func = g_strdup (fo->dynamic_func);
-
- d(printf ("cloning option code %p, current = %p\n", new, new->current));
-
- return (FilterElement *) new;
-}
diff --git a/filter/filter-option.h b/filter/filter-option.h
deleted file mode 100644
index 9fa1670951..0000000000
--- a/filter/filter-option.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _FILTER_OPTION_H
-#define _FILTER_OPTION_H
-
-#include "filter-element.h"
-
-#define FILTER_TYPE_OPTION (filter_option_get_type ())
-#define FILTER_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_OPTION, FilterOption))
-#define FILTER_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_OPTION, FilterOptionClass))
-#define IS_FILTER_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_OPTION))
-#define IS_FILTER_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_OPTION))
-#define FILTER_OPTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_OPTION, FilterOptionClass))
-
-typedef struct _FilterOption FilterOption;
-typedef struct _FilterOptionClass FilterOptionClass;
-
-struct _filter_option {
- gchar *title; /* button title */
- gchar *value; /* value, if it has one */
- gchar *code; /* used to string code segments together */
-
- gboolean is_dynamic; /* whether is the option dynamic, FALSE if static */
-};
-
-struct _FilterOption {
- FilterElement parent_object;
-
- const gchar *type; /* static memory, type name written to xml */
-
- GList *options;
- struct _filter_option *current;
- gchar *dynamic_func; /* name of the dynamic fill func, called in get_widget */
-};
-
-struct _FilterOptionClass {
- FilterElementClass parent_class;
-
- /* virtual methods */
-
- /* signals */
-};
-
-GType filter_option_get_type (void);
-FilterOption *filter_option_new (void);
-
-/* methods */
-void filter_option_set_current (FilterOption *option, const gchar *name);
-const gchar *filter_option_get_current (FilterOption *option);
-
-struct _filter_option *filter_option_add (FilterOption *fo, const gchar *name, const gchar *title, const gchar *code, gboolean is_dynamic);
-void filter_option_remove_all (FilterOption *fo);
-
-#endif /* ! _FILTER_OPTION_H */
diff --git a/filter/filter-part.c b/filter/filter-part.c
deleted file mode 100644
index d59796a2cc..0000000000
--- a/filter/filter-part.c
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-
-#include "filter-part.h"
-#include "rule-context.h"
-#include "filter-file.h"
-
-#define d(x)
-
-static void filter_part_class_init (FilterPartClass *klass);
-static void filter_part_init (FilterPart *fp);
-static void filter_part_finalise (GObject *obj);
-
-static GObjectClass *parent_class = NULL;
-
-GType
-filter_part_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterPartClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_part_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterPart),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_part_init,
- };
-
- type = g_type_register_static (G_TYPE_OBJECT, "FilterPart", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_part_class_init (FilterPartClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- parent_class = g_type_class_ref (G_TYPE_OBJECT);
-
- object_class->finalize = filter_part_finalise;
-}
-
-static void
-filter_part_init (FilterPart *fp)
-{
- ;
-}
-
-static void
-filter_part_finalise (GObject *obj)
-{
- FilterPart *fp = (FilterPart *) obj;
- GList *l;
-
- l = fp->elements;
- while (l) {
- g_object_unref (l->data);
- l = l->next;
- }
-
- g_list_free (fp->elements);
- g_free (fp->name);
- g_free (fp->title);
- g_free (fp->code);
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_part_new:
- *
- * Create a new FilterPart object.
- *
- * Return value: A new #FilterPart object.
- **/
-FilterPart *
-filter_part_new (void)
-{
- return (FilterPart *) g_object_new (FILTER_TYPE_PART, NULL, NULL);
-}
-
-gboolean
-filter_part_validate (FilterPart *fp, GtkWindow *error_parent)
-{
- gboolean correct = TRUE;
- GList *l;
-
- l = fp->elements;
- while (l && correct) {
- FilterElement *fe = l->data;
-
- correct = filter_element_validate (fe, error_parent);
-
- l = l->next;
- }
-
- return correct;
-}
-
-gint
-filter_part_eq (FilterPart *fp, FilterPart *fc)
-{
- gint truth;
- GList *al, *bl;
-
- truth = ((fp->name && fc->name && strcmp(fp->name, fc->name) == 0)
- || (fp->name == NULL && fc->name == NULL))
- && ((fp->title && fc->title && strcmp(fp->title, fc->title) == 0)
- || (fp->title == NULL && fc->title == NULL))
- && ((fp->code && fc->code && strcmp(fp->code, fc->code) == 0)
- || (fp->code == NULL && fc->code == NULL));
-
- al = fp->elements;
- bl = fc->elements;
- while (truth && al && bl) {
- FilterElement *a = al->data, *b = bl->data;
-
- truth = filter_element_eq(a, b);
-
- al = al->next;
- bl = bl->next;
- }
-
- return truth && al == NULL && bl == NULL;
-}
-
-gint
-filter_part_xml_create (FilterPart *ff, xmlNodePtr node, RuleContext *rc)
-{
- xmlNodePtr n;
- gchar *type, *str;
- FilterElement *el;
-
- str = (gchar *)xmlGetProp (node, (const guchar *)"name");
- ff->name = g_strdup (str);
- if (str)
- xmlFree (str);
-
- n = node->children;
- while (n) {
- if (!strcmp ((gchar *)n->name, "input")) {
- type = (gchar *)xmlGetProp (n, (const guchar *)"type");
- d(printf ("creating new element type input '%s'\n", type));
- if (type != NULL
- && (el = rule_context_new_element(rc, type)) != NULL) {
- filter_element_xml_create (el, n);
- xmlFree (type);
- d(printf ("adding element part %p %s\n", ff, el, el->name));
- ff->elements = g_list_append (ff->elements, el);
- } else {
- g_warning ("Invalid xml format, missing/unknown input type");
- }
- } else if (!strcmp ((gchar *)n->name, "title") || !strcmp ((gchar *)n->name, "_title")) {
- if (!ff->title) {
- str = (gchar *)xmlNodeGetContent (n);
- ff->title = g_strdup (str);
- if (str)
- xmlFree (str);
- }
- } else if (!strcmp ((gchar *)n->name, "code")) {
- if (!ff->code) {
- str = (gchar *)xmlNodeGetContent (n);
- ff->code = g_strdup (str);
- if (str)
- xmlFree (str);
- }
- } else if (n->type == XML_ELEMENT_NODE) {
- g_warning ("Unknown part element in xml: %s\n", n->name);
- }
- n = n->next;
- }
-
- return 0;
-}
-
-xmlNodePtr
-filter_part_xml_encode (FilterPart *fp)
-{
- GList *l;
- FilterElement *fe;
- xmlNodePtr part, value;
-
- g_return_val_if_fail (fp != NULL, NULL);
-
- part = xmlNewNode (NULL, (const guchar *)"part");
- xmlSetProp (part, (const guchar *)"name", (guchar *)fp->name);
- l = fp->elements;
- while (l) {
- fe = l->data;
- value = filter_element_xml_encode (fe);
- xmlAddChild (part, value);
- l = g_list_next (l);
- }
-
- return part;
-}
-
-gint
-filter_part_xml_decode (FilterPart *fp, xmlNodePtr node)
-{
- FilterElement *fe;
- xmlNodePtr n;
- gchar *name;
-
- g_return_val_if_fail (fp != NULL, -1);
- g_return_val_if_fail (node != NULL, -1);
-
- n = node->children;
- while (n) {
- if (!strcmp ((gchar *)n->name, "value")) {
- name = (gchar *)xmlGetProp (n, (const guchar *)"name");
- d(printf ("finding element part %p %s = %p\n", name, name, fe));
- fe = filter_part_find_element (fp, name);
- d(printf ("finding element part %p %s = %p\n", name, name, fe));
- xmlFree (name);
- if (fe)
- filter_element_xml_decode (fe, n);
- }
- n = n->next;
- }
-
- return 0;
-}
-
-FilterPart *
-filter_part_clone (FilterPart *fp)
-{
- FilterPart *new;
- GList *l;
- FilterElement *fe, *ne;
-
- new = (FilterPart *) g_object_new (G_OBJECT_TYPE (fp), NULL, NULL);
- new->name = g_strdup (fp->name);
- new->title = g_strdup (fp->title);
- new->code = g_strdup (fp->code);
- l = fp->elements;
- while (l) {
- fe = l->data;
- ne = filter_element_clone (fe);
- new->elements = g_list_append (new->elements, ne);
- l = g_list_next (l);
- }
-
- return new;
-}
-
-/* only copies values of matching parts in the right order */
-void
-filter_part_copy_values (FilterPart *dst, FilterPart *src)
-{
- GList *dstl, *srcl, *dstt;
- FilterElement *de, *se;
-
- /* NOTE: we go backwards, it just works better that way */
-
- /* for each source type, search the dest type for
- a matching type in the same order */
- srcl = g_list_last (src->elements);
- dstl = g_list_last (dst->elements);
- while (srcl && dstl) {
- se = srcl->data;
- dstt = dstl;
- while (dstt) {
- de = dstt->data;
- if (FILTER_PART_GET_CLASS (de) == FILTER_PART_GET_CLASS (se)) {
- filter_element_copy_value (de, se);
- dstl = dstt->prev;
- break;
- }
- dstt = dstt->prev;
- }
-
- srcl = srcl->prev;
- }
-}
-
-FilterElement *
-filter_part_find_element (FilterPart *ff, const gchar *name)
-{
- GList *l = ff->elements;
- FilterElement *fe;
-
- if (name == NULL)
- return NULL;
-
- while (l) {
- fe = l->data;
- if (fe->name && !strcmp (fe->name, name))
- return fe;
- l = g_list_next (l);
- }
-
- return NULL;
-}
-
-GtkWidget *
-filter_part_get_widget (FilterPart *ff)
-{
- GtkWidget *hbox;
- GList *l = ff->elements;
- FilterElement *fe;
- GtkWidget *w;
-
- hbox = gtk_hbox_new (FALSE, 3);
-
- while (l) {
- fe = l->data;
- w = filter_element_get_widget (fe);
- if (w)
- gtk_box_pack_start (GTK_BOX (hbox), w, IS_FILTER_FILE (fe), IS_FILTER_FILE (fe), 3);
-
- l = g_list_next (l);
- }
-
- gtk_widget_show_all (hbox);
-
- return hbox;
-}
-
-/**
- * filter_part_build_code:
- * @ff:
- * @out:
- *
- * Outputs the code of a part.
- **/
-void
-filter_part_build_code (FilterPart *ff, GString *out)
-{
- GList *l = ff->elements;
- FilterElement *fe;
-
- if (ff->code)
- filter_part_expand_code (ff, ff->code, out);
-
- while (l) {
- fe = l->data;
- filter_element_build_code (fe, out, ff);
- l = l->next;
- }
-}
-
-/**
- * filter_part_build_code_list:
- * @l:
- * @out:
- *
- * Construct a list of the filter parts code into
- * a single string.
- **/
-void
-filter_part_build_code_list (GList *l, GString *out)
-{
- FilterPart *fp;
-
- while (l) {
- fp = l->data;
- filter_part_build_code (fp, out);
- g_string_append (out, "\n ");
- l = l->next;
- }
-}
-
-/**
- * filter_part_find_list:
- * @l:
- * @name:
- *
- * Find a filter part stored in a list.
- *
- * Return value:
- **/
-FilterPart *
-filter_part_find_list (GList *l, const gchar *name)
-{
- FilterPart *part;
-
- d(printf ("Find part named %s\n", name));
-
- while (l) {
- part = l->data;
- if (!strcmp (part->name, name)) {
- d(printf ("Found!\n"));
- return part;
- }
- l = l->next;
- }
-
- return NULL;
-}
-
-/**
- * filter_part_next_list:
- * @l:
- * @last: The last item retrieved, or NULL to start
- * from the beginning of the list.
- *
- * Iterate through a filter part list.
- *
- * Return value: The next value in the list, or NULL if the
- * list is expired.
- **/
-FilterPart *
-filter_part_next_list (GList *l, FilterPart *last)
-{
- GList *node = l;
-
- if (last != NULL) {
- node = g_list_find (node, last);
- if (node == NULL)
- node = l;
- else
- node = node->next;
- }
-
- if (node)
- return node->data;
-
- return NULL;
-}
-
-/**
- * filter_part_expand_code:
- * @ff:
- * @str:
- * @out:
- *
- * Expands the variables in string @str based on the values of the part.
- **/
-void
-filter_part_expand_code (FilterPart *ff, const gchar *source, GString *out)
-{
- const gchar *newstart, *start, *end;
- gchar *name = alloca (32);
- gint len, namelen = 32;
- FilterElement *fe;
-
- start = source;
- while (start && (newstart = strstr (start, "${"))
- && (end = strstr (newstart+2, "}")) ) {
- len = end - newstart - 2;
- if (len + 1 > namelen) {
- namelen = (len + 1) * 2;
- name = g_alloca (namelen);
- }
- memcpy (name, newstart+2, len);
- name[len] = 0;
- fe = filter_part_find_element (ff, name);
- d(printf("expand code: looking up variab le '%s' = %p\n", ff, name, fe));
- if (fe) {
- g_string_append_printf (out, "%.*s", (gint)(newstart-start), start);
- filter_element_format_sexp (fe, out);
-#if 0
- } else if ((val = g_hash_table_lookup (ff->globals, name))) {
- g_string_append_printf (out, "%.*s", newstart-start, start);
- e_sexp_encode_string (out, val);
-#endif
- } else {
- g_string_append_printf (out, "%.*s", (gint)(end-start+1), start);
- }
- start = end + 1;
- }
- g_string_append (out, start);
-}
-
-#if 0
-gint main(gint argc, gchar **argv)
-{
- GtkWidget *dialog, *w;
- xmlDocPtr system;
- xmlNodePtr node;
- FilterPart *ff;
- GString *code;
-
- gnome_init ("test", "0.0", argc, argv);
-
- system = xmlParseFile ("form.xml");
- if (system == NULL) {
- printf("i/o error\n");
- return 1;
- }
-
- ff = filter_part_new ();
- filter_part_xml_create (ff, system->root);
-
- w = filter_part_get_widget (ff);
-
- dialog = gtk_dialog_new ();
- gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_BUTTONS_OK, NULL);
- gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
- gtk_window_set_title (GTK_WINDOW (dialog), _("Test"));
- gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
- gtk_box_pack_start (GTK_BOX (dialog->vbox), w, TRUE, TRUE, 0);
-
- gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_widget_destroy (dialog);
-
- code = g_string_new ("");
- filter_part_build_code (ff, code);
- printf ("code is:\n%s\n", code->str);
-
- return 0;
-}
-#endif
diff --git a/filter/filter-part.h b/filter/filter-part.h
deleted file mode 100644
index 10e58979e7..0000000000
--- a/filter/filter-part.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _FILTER_PART_H
-#define _FILTER_PART_H
-
-#include <glib.h>
-#include <glib-object.h>
-#include <gtk/gtk.h>
-
-#include "filter-input.h"
-
-struct _RuleContext;
-
-#define FILTER_TYPE_PART (filter_part_get_type ())
-#define FILTER_PART(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_PART, FilterPart))
-#define FILTER_PART_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_PART, FilterPartClass))
-#define IS_FILTER_PART(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_PART))
-#define IS_FILTER_PART_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_PART))
-#define FILTER_PART_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_PART, FilterElementClass))
-
-typedef struct _FilterPart FilterPart;
-typedef struct _FilterPartClass FilterPartClass;
-
-struct _FilterPart {
- GObject parent_object;
- struct _FilterPartPrivate *priv;
-
- gchar *name;
- gchar *title;
- gchar *code;
- GList *elements;
-};
-
-struct _FilterPartClass {
- GObjectClass parent_class;
-
- /* virtual methods */
-
- /* signals */
-};
-
-GType filter_part_get_type (void);
-FilterPart *filter_part_new (void);
-
-/* methods */
-gboolean filter_part_validate (FilterPart *fp, GtkWindow *error_parent);
-gint filter_part_eq (FilterPart *fp, FilterPart *fc);
-
-gint filter_part_xml_create (FilterPart *ff, xmlNodePtr node, struct _RuleContext *rc);
-
-xmlNodePtr filter_part_xml_encode (FilterPart *fe);
-gint filter_part_xml_decode (FilterPart *fe, xmlNodePtr node);
-
-FilterPart *filter_part_clone (FilterPart *fp);
-void filter_part_copy_values (FilterPart *dfp, FilterPart *sfp);
-
-FilterElement *filter_part_find_element (FilterPart *ff, const gchar *name);
-
-GtkWidget *filter_part_get_widget (FilterPart *ff);
-void filter_part_build_code (FilterPart *ff, GString *out);
-void filter_part_expand_code (FilterPart *ff, const gchar *str, GString *out);
-
-/* static functions */
-void filter_part_build_code_list (GList *l, GString *out);
-FilterPart *filter_part_find_list (GList *l, const gchar *name);
-FilterPart *filter_part_next_list (GList *l, FilterPart *last);
-
-#endif /* ! _FILTER_PART_H */
diff --git a/filter/filter-rule.c b/filter/filter-rule.c
deleted file mode 100644
index 28cf83cb8a..0000000000
--- a/filter/filter-rule.c
+++ /dev/null
@@ -1,1044 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-
-#include "e-util/e-error.h"
-#include "filter-rule.h"
-#include "rule-context.h"
-
-#define d(x)
-
-static gint validate(FilterRule *, GtkWindow *error_parent);
-static gint rule_eq(FilterRule *fr, FilterRule *cm);
-static xmlNodePtr xml_encode (FilterRule *);
-static gint xml_decode (FilterRule *, xmlNodePtr, RuleContext *);
-static void build_code (FilterRule *, GString * out);
-static void rule_copy (FilterRule *dest, FilterRule *src);
-static GtkWidget *get_widget (FilterRule * fr, struct _RuleContext *f);
-
-static void filter_rule_class_init (FilterRuleClass *klass);
-static void filter_rule_init (FilterRule *fr);
-static void filter_rule_finalise (GObject *obj);
-
-#define _PRIVATE(x) (((FilterRule *)(x))->priv)
-
-struct _FilterRulePrivate {
- gint frozen;
-};
-
-static GObjectClass *parent_class = NULL;
-
-enum {
- CHANGED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-GType
-filter_rule_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (FilterRuleClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) filter_rule_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (FilterRule),
- 0, /* n_preallocs */
- (GInstanceInitFunc) filter_rule_init,
- };
-
- type = g_type_register_static (G_TYPE_OBJECT, "FilterRule", &info, 0);
- }
-
- return type;
-}
-
-static void
-filter_rule_class_init (FilterRuleClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- parent_class = g_type_class_ref (G_TYPE_OBJECT);
-
- object_class->finalize = filter_rule_finalise;
-
- /* override methods */
- klass->validate = validate;
- klass->eq = rule_eq;
- klass->xml_encode = xml_encode;
- klass->xml_decode = xml_decode;
- klass->build_code = build_code;
- klass->copy = rule_copy;
- klass->get_widget = get_widget;
-
- /* signals */
- signals[CHANGED] =
- g_signal_new ("changed",
- FILTER_TYPE_RULE,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (FilterRuleClass, changed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-}
-
-static void
-filter_rule_init (FilterRule *fr)
-{
- fr->priv = g_malloc0 (sizeof (*fr->priv));
- fr->enabled = TRUE;
-}
-
-static void
-filter_rule_finalise (GObject *obj)
-{
- FilterRule *fr = (FilterRule *) obj;
-
- g_free (fr->name);
- g_free (fr->source);
- g_list_foreach (fr->parts, (GFunc)g_object_unref, NULL);
- g_list_free (fr->parts);
-
- g_free (fr->priv);
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-/**
- * filter_rule_new:
- *
- * Create a new FilterRule object.
- *
- * Return value: A new #FilterRule object.
- **/
-FilterRule *
-filter_rule_new (void)
-{
- return (FilterRule *) g_object_new (FILTER_TYPE_RULE, NULL, NULL);
-}
-
-FilterRule *
-filter_rule_clone (FilterRule *base)
-{
- FilterRule *rule;
-
- g_return_val_if_fail (IS_FILTER_RULE (base), NULL);
-
- rule = g_object_new (G_OBJECT_TYPE (base), NULL, NULL);
- filter_rule_copy (rule, base);
-
- return rule;
-}
-
-void
-filter_rule_set_name (FilterRule *fr, const gchar *name)
-{
- g_return_if_fail (IS_FILTER_RULE (fr));
-
- if ((fr->name && name && strcmp (fr->name, name) == 0)
- || (fr->name == NULL && name == NULL))
- return;
-
- g_free (fr->name);
- fr->name = g_strdup (name);
-
- filter_rule_emit_changed (fr);
-}
-
-void
-filter_rule_set_source (FilterRule *fr, const gchar *source)
-{
- g_return_if_fail (IS_FILTER_RULE (fr));
-
- if ((fr->source && source && strcmp (fr->source, source) == 0)
- || (fr->source == NULL && source == NULL))
- return;
-
- g_free (fr->source);
- fr->source = g_strdup (source);
-
- filter_rule_emit_changed (fr);
-}
-
-gint
-filter_rule_validate (FilterRule *fr, GtkWindow *error_parent)
-{
- g_return_val_if_fail (IS_FILTER_RULE (fr), 0);
-
- return FILTER_RULE_GET_CLASS (fr)->validate (fr, error_parent);
-}
-
-static gint
-validate (FilterRule *fr, GtkWindow *error_parent)
-{
- gint valid = TRUE;
- GList *parts;
-
- if (!fr->name || !*fr->name) {
- e_error_run (error_parent, "filter:no-name", NULL);
-
- return FALSE;
- }
-
- /* validate rule parts */
- parts = fr->parts;
- valid = parts != NULL;
- while (parts && valid) {
- valid = filter_part_validate ((FilterPart *) parts->data, error_parent);
- parts = parts->next;
- }
-
- return valid;
-}
-
-gint
-filter_rule_eq (FilterRule *fr, FilterRule *cm)
-{
- g_return_val_if_fail (IS_FILTER_RULE (fr), 0);
- g_return_val_if_fail (IS_FILTER_RULE (cm), 0);
-
- return (FILTER_RULE_GET_CLASS (fr) == FILTER_RULE_GET_CLASS (cm))
- && FILTER_RULE_GET_CLASS (fr)->eq (fr, cm);
-}
-
-static gint
-list_eq(GList *al, GList *bl)
-{
- gint truth = TRUE;
-
- while (truth && al && bl) {
- FilterPart *a = al->data, *b = bl->data;
-
- truth = filter_part_eq (a, b);
- al = al->next;
- bl = bl->next;
- }
-
- return truth && al == NULL && bl == NULL;
-}
-
-static gint
-rule_eq (FilterRule *fr, FilterRule *cm)
-{
- return fr->enabled == cm->enabled
- && fr->grouping == cm->grouping
- && fr->threading == fr->threading
- && ((fr->name && cm->name && strcmp (fr->name, cm->name) == 0)
- || (fr->name == NULL && cm->name == NULL))
- && ((fr->source && cm->source && strcmp (fr->source, cm->source) == 0)
- || (fr->source == NULL && cm->source == NULL) )
- && list_eq (fr->parts, cm->parts);
-}
-
-xmlNodePtr
-filter_rule_xml_encode (FilterRule *fr)
-{
- g_return_val_if_fail (IS_FILTER_RULE (fr), NULL);
-
- return FILTER_RULE_GET_CLASS (fr)->xml_encode (fr);
-}
-
-static xmlNodePtr
-xml_encode (FilterRule *fr)
-{
- xmlNodePtr node, set, work;
- GList *l;
-
- node = xmlNewNode (NULL, (const guchar *)"rule");
-
- xmlSetProp (node, (const guchar *)"enabled", (const guchar *)(fr->enabled ? "true" : "false"));
-
- switch (fr->grouping) {
- case FILTER_GROUP_ALL:
- xmlSetProp (node, (const guchar *)"grouping", (const guchar *)"all");
- break;
- case FILTER_GROUP_ANY:
- xmlSetProp (node, (const guchar *)"grouping", (const guchar *)"any");
- break;
- }
-
- switch (fr->threading) {
- case FILTER_THREAD_NONE:
- break;
- case FILTER_THREAD_ALL:
- xmlSetProp(node, (const guchar *)"threading", (const guchar *)"all");
- break;
- case FILTER_THREAD_REPLIES:
- xmlSetProp(node, (const guchar *)"threading", (const guchar *)"replies");
- break;
- case FILTER_THREAD_REPLIES_PARENTS:
- xmlSetProp(node, (const guchar *)"threading", (const guchar *)"replies_parents");
- break;
- case FILTER_THREAD_SINGLE:
- xmlSetProp(node, (const guchar *)"threading", (const guchar *)"single");
- break;
- }
-
- if (fr->source) {
- xmlSetProp (node, (const guchar *)"source", (guchar *)fr->source);
- } else {
- /* set to the default filter type */
- xmlSetProp (node, (const guchar *)"source", (const guchar *)"incoming");
- }
-
- if (fr->name) {
- gchar *escaped = g_markup_escape_text (fr->name, -1);
-
- work = xmlNewNode (NULL, (const guchar *)"title");
- xmlNodeSetContent (work, (guchar *)escaped);
- xmlAddChild (node, work);
-
- g_free (escaped);
- }
-
- set = xmlNewNode (NULL, (const guchar *)"partset");
- xmlAddChild (node, set);
- l = fr->parts;
- while (l) {
- work = filter_part_xml_encode ((FilterPart *) l->data);
- xmlAddChild (set, work);
- l = l->next;
- }
-
- return node;
-}
-
-static void
-load_set (xmlNodePtr node, FilterRule *fr, RuleContext *f)
-{
- xmlNodePtr work;
- gchar *rulename;
- FilterPart *part;
-
- work = node->children;
- while (work) {
- if (!strcmp ((gchar *)work->name, "part")) {
- rulename = (gchar *)xmlGetProp (work, (const guchar *)"name");
- part = rule_context_find_part (f, rulename);
- if (part) {
- part = filter_part_clone (part);
- filter_part_xml_decode (part, work);
- filter_rule_add_part (fr, part);
- } else {
- g_warning ("cannot find rule part '%s'\n", rulename);
- }
- xmlFree (rulename);
- } else if (work->type == XML_ELEMENT_NODE) {
- g_warning ("Unknown xml node in part: %s", work->name);
- }
- work = work->next;
- }
-}
-
-gint
-filter_rule_xml_decode (FilterRule *fr, xmlNodePtr node, RuleContext *f)
-{
- gint res;
-
- g_return_val_if_fail (IS_FILTER_RULE (fr), 0);
- g_return_val_if_fail (IS_RULE_CONTEXT (f), 0);
- g_return_val_if_fail (node != NULL, 0);
-
- fr->priv->frozen++;
- res = FILTER_RULE_GET_CLASS (fr)->xml_decode (fr, node, f);
- fr->priv->frozen--;
-
- filter_rule_emit_changed (fr);
-
- return res;
-}
-
-static gint
-xml_decode (FilterRule *fr, xmlNodePtr node, RuleContext *f)
-{
- xmlNodePtr work;
- gchar *grouping;
- gchar *source;
-
- if (fr->name) {
- g_free (fr->name);
- fr->name = NULL;
- }
-
- grouping = (gchar *)xmlGetProp (node, (const guchar *)"enabled");
- if (!grouping)
- fr->enabled = TRUE;
- else {
- fr->enabled = strcmp (grouping, "false") != 0;
- xmlFree (grouping);
- }
-
- grouping = (gchar *)xmlGetProp (node, (const guchar *)"grouping");
- if (!strcmp (grouping, "any"))
- fr->grouping = FILTER_GROUP_ANY;
- else
- fr->grouping = FILTER_GROUP_ALL;
- xmlFree (grouping);
-
- fr->threading = FILTER_THREAD_NONE;
- if (f->flags & RULE_CONTEXT_THREADING
- && (grouping = (gchar *)xmlGetProp (node, (const guchar *)"threading"))) {
- if (!strcmp(grouping, "all"))
- fr->threading = FILTER_THREAD_ALL;
- else if (!strcmp(grouping, "replies"))
- fr->threading = FILTER_THREAD_REPLIES;
- else if (!strcmp(grouping, "replies_parents"))
- fr->threading = FILTER_THREAD_REPLIES_PARENTS;
- else if (!strcmp(grouping, "single"))
- fr->threading = FILTER_THREAD_SINGLE;
- xmlFree (grouping);
- }
-
- g_free (fr->source);
- source = (gchar *)xmlGetProp (node, (const guchar *)"source");
- if (source) {
- fr->source = g_strdup (source);
- xmlFree (source);
- } else {
- /* default filter type */
- fr->source = g_strdup ("incoming");
- }
-
- work = node->children;
- while (work) {
- if (!strcmp ((gchar *)work->name, "partset")) {
- load_set (work, fr, f);
- } else if (!strcmp ((gchar *)work->name, "title") || !strcmp ((gchar *)work->name, "_title")) {
- if (!fr->name) {
- gchar *str, *decstr = NULL;
-
- str = (gchar *)xmlNodeGetContent (work);
- if (str) {
- decstr = g_strdup (_(str));
- xmlFree (str);
- }
- fr->name = decstr;
- }
- }
- work = work->next;
- }
-
- return 0;
-}
-
-static void
-rule_copy (FilterRule *dest, FilterRule *src)
-{
- GList *node;
-
- dest->enabled = src->enabled;
-
- g_free (dest->name);
- dest->name = g_strdup (src->name);
-
- g_free (dest->source);
- dest->source = g_strdup (src->source);
-
- dest->grouping = src->grouping;
- dest->threading = src->threading;
-
- if (dest->parts) {
- g_list_foreach (dest->parts, (GFunc) g_object_unref, NULL);
- g_list_free (dest->parts);
- dest->parts = NULL;
- }
-
- node = src->parts;
- while (node) {
- FilterPart *part;
-
- part = filter_part_clone (node->data);
- dest->parts = g_list_append (dest->parts, part);
- node = node->next;
- }
-}
-
-void
-filter_rule_copy (FilterRule *dest, FilterRule *src)
-{
- g_return_if_fail (IS_FILTER_RULE (dest));
- g_return_if_fail (IS_FILTER_RULE (src));
-
- FILTER_RULE_GET_CLASS (dest)->copy (dest, src);
-
- filter_rule_emit_changed (dest);
-}
-
-void
-filter_rule_add_part (FilterRule *fr, FilterPart *fp)
-{
- g_return_if_fail (IS_FILTER_RULE (fr));
- g_return_if_fail (IS_FILTER_PART (fp));
-
- fr->parts = g_list_append (fr->parts, fp);
-
- filter_rule_emit_changed (fr);
-}
-
-void
-filter_rule_remove_part (FilterRule *fr, FilterPart *fp)
-{
- g_return_if_fail (IS_FILTER_RULE (fr));
- g_return_if_fail (IS_FILTER_PART (fp));
-
- fr->parts = g_list_remove (fr->parts, fp);
-
- filter_rule_emit_changed (fr);
-}
-
-void
-filter_rule_replace_part (FilterRule *fr, FilterPart *fp, FilterPart *new)
-{
- GList *l;
-
- g_return_if_fail (IS_FILTER_RULE (fr));
- g_return_if_fail (IS_FILTER_PART (fp));
- g_return_if_fail (IS_FILTER_PART (new));
-
- l = g_list_find (fr->parts, fp);
- if (l) {
- l->data = new;
- } else {
- fr->parts = g_list_append (fr->parts, new);
- }
-
- filter_rule_emit_changed (fr);
-}
-
-void
-filter_rule_build_code (FilterRule *fr, GString *out)
-{
- g_return_if_fail (IS_FILTER_RULE (fr));
- g_return_if_fail (out != NULL);
-
- FILTER_RULE_GET_CLASS (fr)->build_code (fr, out);
-
- d(printf ("build_code: [%s](%d)", out->str, out->len));
-}
-
-void
-filter_rule_emit_changed(FilterRule *fr)
-{
- g_return_if_fail (IS_FILTER_RULE (fr));
-
- if (fr->priv->frozen == 0)
- g_signal_emit (fr, signals[CHANGED], 0);
-}
-
-static void
-build_code (FilterRule *fr, GString *out)
-{
- switch (fr->threading) {
- case FILTER_THREAD_NONE:
- break;
- case FILTER_THREAD_ALL:
- g_string_append(out, " (match-threads \"all\" ");
- break;
- case FILTER_THREAD_REPLIES:
- g_string_append(out, " (match-threads \"replies\" ");
- break;
- case FILTER_THREAD_REPLIES_PARENTS:
- g_string_append(out, " (match-threads \"replies_parents\" ");
- break;
- case FILTER_THREAD_SINGLE:
- g_string_append(out, " (match-threads \"single\" ");
- break;
- }
-
- switch (fr->grouping) {
- case FILTER_GROUP_ALL:
- g_string_append (out, " (and\n ");
- break;
- case FILTER_GROUP_ANY:
- g_string_append (out, " (or\n ");
- break;
- default:
- g_warning ("Invalid grouping");
- }
-
- filter_part_build_code_list (fr->parts, out);
- g_string_append (out, ")\n");
-
- if (fr->threading != FILTER_THREAD_NONE)
- g_string_append (out, ")\n");
-}
-
-static void
-fr_grouping_changed(GtkWidget *w, FilterRule *fr)
-{
- fr->grouping = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
-}
-
-static void
-fr_threading_changed(GtkWidget *w, FilterRule *fr)
-{
- fr->threading = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
-}
-
-struct _part_data {
- FilterRule *fr;
- RuleContext *f;
- FilterPart *part;
- GtkWidget *partwidget, *container;
-};
-
-static void
-part_combobox_changed (GtkComboBox *combobox, struct _part_data *data)
-{
- FilterPart *part = NULL;
- FilterPart *newpart;
- gint index, i;
-
- index = gtk_combo_box_get_active (combobox);
- for (i = 0, part = rule_context_next_part (data->f, part); part && i < index; i++, part = rule_context_next_part (data->f, part)) {
- /* traverse until reached index */
- }
-
- g_return_if_fail (part != NULL);
- g_return_if_fail (i == index);
-
- /* dont update if we haven't changed */
- if (!strcmp (part->title, data->part->title))
- return;
-
- /* here we do a widget shuffle, throw away the old widget/rulepart,
- and create another */
- if (data->partwidget)
- gtk_container_remove (GTK_CONTAINER (data->container), data->partwidget);
-
- newpart = filter_part_clone (part);
- filter_part_copy_values (newpart, data->part);
- filter_rule_replace_part (data->fr, data->part, newpart);
- g_object_unref (data->part);
- data->part = newpart;
- data->partwidget = filter_part_get_widget (newpart);
- if (data->partwidget)
- gtk_box_pack_start (GTK_BOX (data->container), data->partwidget, TRUE, TRUE, 0);
-}
-
-static GtkWidget *
-get_rule_part_widget (RuleContext *f, FilterPart *newpart, FilterRule *fr)
-{
- FilterPart *part = NULL;
- GtkWidget *combobox;
- GtkWidget *hbox;
- GtkWidget *p;
- gint index = 0, current = 0;
- struct _part_data *data;
-
- data = g_malloc0 (sizeof (*data));
- data->fr = fr;
- data->f = f;
- data->part = newpart;
-
- hbox = gtk_hbox_new (FALSE, 0);
- /* only set to automatically clean up the memory */
- g_object_set_data_full ((GObject *) hbox, "data", data, g_free);
-
- p = filter_part_get_widget (newpart);
-
- data->partwidget = p;
- data->container = hbox;
-
- combobox = gtk_combo_box_new_text ();
-
- /* sigh, this is a little ugly */
- while ((part = rule_context_next_part (f, part))) {
- gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), _(part->title));
-
- if (!strcmp (newpart->title, part->title))
- current = index;
-
- index++;
- }
-
- gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), current);
- g_signal_connect (combobox, "changed", G_CALLBACK (part_combobox_changed), data);
- gtk_widget_show (combobox);
-
- gtk_box_pack_start (GTK_BOX (hbox), combobox, FALSE, FALSE, 0);
- if (p)
- gtk_box_pack_start (GTK_BOX (hbox), p, TRUE, TRUE, 0);
-
- gtk_widget_show_all (hbox);
-
- return hbox;
-}
-
-struct _rule_data {
- FilterRule *fr;
- RuleContext *f;
- GtkWidget *parts;
-};
-
-static void
-less_parts (GtkWidget *button, struct _rule_data *data)
-{
- FilterPart *part;
- GtkWidget *rule;
- struct _part_data *part_data;
-
- if (g_list_length (data->fr->parts) < 1)
- return;
-
- rule = g_object_get_data ((GObject *) button, "rule");
- part_data = g_object_get_data ((GObject *) rule, "data");
-
- g_return_if_fail (part_data != NULL);
-
- part = part_data->part;
-
- /* remove the part from the list */
- filter_rule_remove_part (data->fr, part);
- g_object_unref (part);
-
- /* and from the display */
- gtk_container_remove (GTK_CONTAINER (data->parts), rule);
- gtk_container_remove (GTK_CONTAINER (data->parts), button);
-}
-
-static void
-attach_rule (GtkWidget *rule, struct _rule_data *data, FilterPart *part, gint row)
-{
- GtkWidget *remove;
-
- gtk_table_attach (GTK_TABLE (data->parts), rule, 0, 1, row, row + 1,
- GTK_EXPAND | GTK_FILL, 0, 0, 0);
-
- remove = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
- g_object_set_data ((GObject *) remove, "rule", rule);
- /*gtk_button_set_relief (GTK_BUTTON (remove), GTK_RELIEF_NONE);*/
- g_signal_connect (remove, "clicked", G_CALLBACK (less_parts), data);
- gtk_table_attach (GTK_TABLE (data->parts), remove, 1, 2, row, row + 1,
- 0, 0, 0, 0);
-
- gtk_widget_show (remove);
-}
-
-static void
-do_grab_focus_cb (GtkWidget *widget, gpointer data)
-{
- gboolean *done = (gboolean *) data;
-
- if (*done)
- return;
-
- if (widget && GTK_WIDGET_CAN_FOCUS (widget)) {
- *done = TRUE;
- gtk_widget_grab_focus (widget);
- }
-}
-
-static void
-more_parts (GtkWidget *button, struct _rule_data *data)
-{
- FilterPart *new;
-
- /* first make sure that the last part is ok */
- if (data->fr->parts) {
- FilterPart *part;
- GList *l;
-
- l = g_list_last (data->fr->parts);
- part = l->data;
- if (!filter_part_validate (part, GTK_WINDOW (gtk_widget_get_toplevel (button))))
- return;
- }
-
- /* create a new rule entry, use the first type of rule */
- new = rule_context_next_part (data->f, NULL);
- if (new) {
- GtkWidget *w;
- gint rows;
-
- new = filter_part_clone (new);
- filter_rule_add_part (data->fr, new);
- w = get_rule_part_widget (data->f, new, data->fr);
-
- rows = GTK_TABLE (data->parts)->nrows;
- gtk_table_resize (GTK_TABLE (data->parts), rows + 1, 2);
- attach_rule (w, data, new, rows);
-
- if (GTK_IS_CONTAINER (w)) {
- gboolean done = FALSE;
-
- gtk_container_foreach (GTK_CONTAINER (w), do_grab_focus_cb, &done);
- } else
- gtk_widget_grab_focus (w);
-
- /* also scroll down to see new part */
- w = (GtkWidget*) g_object_get_data (G_OBJECT (button), "scrolled-window");
- if (w) {
- GtkAdjustment *adjustment;
-
- adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (w));
- if (adjustment) {
- gdouble upper;
-
- upper = gtk_adjustment_get_upper (adjustment);
- gtk_adjustment_set_value (adjustment, upper);
- }
-
- }
- }
-}
-
-static void
-name_changed (GtkEntry *entry, FilterRule *fr)
-{
- g_free (fr->name);
- fr->name = g_strdup (gtk_entry_get_text (entry));
-}
-
-GtkWidget *
-filter_rule_get_widget (FilterRule *fr, RuleContext *rc)
-{
- return FILTER_RULE_GET_CLASS (fr)->get_widget (fr, rc);
-}
-
-static void
-grab_focus (GtkWidget *entry, gpointer data)
-{
- gtk_widget_grab_focus (entry);
-}
-
-static GtkWidget *
-get_widget (FilterRule *fr, struct _RuleContext *f)
-{
- GtkWidget *hbox, *vbox, *parts, *inframe;
- GtkWidget *add, *label, *name, *w;
- GtkWidget *combobox;
- GtkWidget *scrolledwindow;
- GtkObject *hadj, *vadj;
- GList *l;
- gchar *text;
- FilterPart *part;
- struct _rule_data *data;
- gint rows, i;
-
- /* this stuff should probably be a table, but the
- rule parts need to be a vbox */
- vbox = gtk_vbox_new (FALSE, 6);
-
- label = gtk_label_new_with_mnemonic (_("R_ule name:"));
- name = gtk_entry_new ();
- gtk_label_set_mnemonic_widget ((GtkLabel *)label, name);
-
- if (!fr->name) {
- fr->name = g_strdup (_("Untitled"));
- gtk_entry_set_text (GTK_ENTRY (name), fr->name);
- /* FIXME: do we want the following code in the future? */
- /*gtk_editable_select_region (GTK_EDITABLE (name), 0, -1);*/
- } else {
- gtk_entry_set_text (GTK_ENTRY (name), fr->name);
- }
-
- /* evil kludgy hack because gtk sucks */
- g_signal_connect (name, "realize", G_CALLBACK (grab_focus), name);
-
- hbox = gtk_hbox_new (FALSE, 12);
- gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start (GTK_BOX (hbox), name, TRUE, TRUE, 0);
- gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
- g_signal_connect (name, "changed", G_CALLBACK (name_changed), fr);
- gtk_widget_show (label);
- gtk_widget_show (hbox);
-
- hbox = gtk_hbox_new (FALSE, 12);
- gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
- gtk_widget_show (hbox);
-
- text = g_strdup_printf("<b>%s</b>", _("Find items that meet the following conditions"));
- label = gtk_label_new (text);
- gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
- gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
- gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
- gtk_widget_show (label);
- g_free(text);
-
- hbox = gtk_hbox_new (FALSE, 12);
- gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
- gtk_widget_show (hbox);
-
- label = gtk_label_new ("");
- gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
- gtk_widget_show (label);
-
- inframe = gtk_vbox_new (FALSE, 6);
- gtk_box_pack_start (GTK_BOX (hbox), inframe, TRUE, TRUE, 0);
-
- /* this is the parts table, it should probably be inside a scrolling list */
- rows = g_list_length (fr->parts);
- parts = gtk_table_new (rows, 2, FALSE);
-
- /* data for the parts part of the display */
- data = g_malloc0 (sizeof (*data));
- data->f = f;
- data->fr = fr;
- data->parts = parts;
-
- /* only set to automatically clean up the memory */
- g_object_set_data_full ((GObject *) vbox, "data", data, g_free);
-
- hbox = gtk_hbox_new (FALSE, 3);
-
- add = gtk_button_new_with_mnemonic (_("A_dd Condition"));
- gtk_button_set_image (GTK_BUTTON (add), gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON));
- g_signal_connect (add, "clicked", G_CALLBACK (more_parts), data);
- gtk_box_pack_start (GTK_BOX (hbox), add, FALSE, FALSE, 0);
-
- if (f->flags & RULE_CONTEXT_GROUPING) {
- const gchar *thread_types[] = { N_("If all conditions are met"), N_("If any conditions are met") };
-
- label = gtk_label_new_with_mnemonic (_("_Find items:"));
- combobox = gtk_combo_box_new_text ();
-
- for (i=0;i<2;i++) {
- gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), _(thread_types[i]));
- }
-
- gtk_label_set_mnemonic_widget ((GtkLabel *)label, combobox);
- gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), fr->grouping);
- gtk_widget_show (combobox);
-
- gtk_box_pack_end (GTK_BOX (hbox), combobox, FALSE, FALSE, 0);
- gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-
- g_signal_connect (combobox, "changed", G_CALLBACK (fr_grouping_changed), fr);
- }
-
- if (f->flags & RULE_CONTEXT_THREADING) {
- const gchar *thread_types[] = { N_("None"), N_("All related"), N_("Replies"), N_("Replies and parents"), N_("No reply or parent") };
-
- label = gtk_label_new_with_mnemonic (_("I_nclude threads"));
- combobox = gtk_combo_box_new_text ();
-
- for (i=0;i<5;i++) {
- gtk_combo_box_append_text (GTK_COMBO_BOX (combobox), _(thread_types[i]));
- }
-
- gtk_label_set_mnemonic_widget ((GtkLabel *)label, combobox);
- gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), fr->threading);
- gtk_widget_show (combobox);
-
- gtk_box_pack_end (GTK_BOX (hbox), combobox, FALSE, FALSE, 0);
- gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-
- g_signal_connect (combobox, "changed", G_CALLBACK (fr_threading_changed), fr);
- }
-
- gtk_box_pack_start (GTK_BOX (inframe), hbox, FALSE, FALSE, 3);
-
- l = fr->parts;
- i = 0;
- while (l) {
- part = l->data;
- d(printf ("adding rule %s\n", part->title));
- w = get_rule_part_widget (f, part, fr);
- attach_rule (w, data, part, i++);
- l = g_list_next (l);
- }
-
- hadj = gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0);
- vadj = gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0);
- scrolledwindow = gtk_scrolled_window_new (GTK_ADJUSTMENT (hadj), GTK_ADJUSTMENT (vadj));
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
- GTK_POLICY_AUTOMATIC,
- GTK_POLICY_AUTOMATIC);
-
- gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledwindow), parts);
-
- gtk_box_pack_start (GTK_BOX (inframe), scrolledwindow, TRUE, TRUE, 3);
-
- gtk_widget_show_all (vbox);
-
- g_object_set_data (G_OBJECT (add), "scrolled-window", scrolledwindow);
-
- return vbox;
-}
-
-FilterRule *
-filter_rule_next_list (GList *l, FilterRule *last, const gchar *source)
-{
- GList *node = l;
-
- if (last != NULL) {
- node = g_list_find (node, last);
- if (node == NULL)
- node = l;
- else
- node = node->next;
- }
-
- if (source) {
- while (node) {
- FilterRule *rule = node->data;
-
- if (rule->source && strcmp (rule->source, source) == 0)
- break;
- node = node->next;
- }
- }
-
- if (node)
- return node->data;
-
- return NULL;
-}
-
-FilterRule *
-filter_rule_find_list (GList * l, const gchar *name, const gchar *source)
-{
- while (l) {
- FilterRule *rule = l->data;
-
- if (strcmp (rule->name, name) == 0)
- if (source == NULL || (rule->source != NULL && strcmp (rule->source, source) == 0))
- return rule;
- l = l->next;
- }
-
- return NULL;
-}
-
-#ifdef FOR_TRANSLATIONS_ONLY
-
-static gchar *list[] = {
- N_("Incoming"), N_("Outgoing")
-};
-#endif
diff --git a/filter/filter-rule.h b/filter/filter-rule.h
deleted file mode 100644
index 113026fce9..0000000000
--- a/filter/filter-rule.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _FILTER_RULE_H
-#define _FILTER_RULE_H
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include "filter-part.h"
-
-#define FILTER_TYPE_RULE (filter_rule_get_type ())
-#define FILTER_RULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_RULE, FilterRule))
-#define FILTER_RULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_RULE, FilterRuleClass))
-#define IS_FILTER_RULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_RULE))
-#define IS_FILTER_RULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_RULE))
-#define FILTER_RULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_RULE, FilterRuleClass))
-
-struct _RuleContext;
-
-typedef struct _FilterRule FilterRule;
-typedef struct _FilterRuleClass FilterRuleClass;
-
-enum _filter_grouping_t {
- FILTER_GROUP_ALL, /* all rules must match */
- FILTER_GROUP_ANY /* any rule must match */
-};
-
-/* threading, if the context supports it */
-enum _filter_threading_t {
- FILTER_THREAD_NONE, /* don't add any thread matching */
- FILTER_THREAD_ALL, /* add all possible threads */
- FILTER_THREAD_REPLIES, /* add only replies */
- FILTER_THREAD_REPLIES_PARENTS, /* replies plus parents */
- FILTER_THREAD_SINGLE /* messages with no replies or parents */
-};
-
-#define FILTER_SOURCE_INCOMING "incoming" /* performed on incoming email */
-#define FILTER_SOURCE_DEMAND "demand" /* performed on the selected folder
- * when the user asks for it */
-#define FILTER_SOURCE_OUTGOING "outgoing"/* performed on outgoing mail */
-#define FILTER_SOURCE_JUNKTEST "junktest"/* perform only junktest on incoming mail */
-
-struct _FilterRule {
- GObject parent_object;
- struct _FilterRulePrivate *priv;
-
- gchar *name;
- gchar *source;
-
- enum _filter_grouping_t grouping;
- enum _filter_threading_t threading;
-
- guint system:1; /* this is a system rule, cannot be edited/deleted */
- GList *parts;
-
- gboolean enabled;
-};
-
-struct _FilterRuleClass {
- GObjectClass parent_class;
-
- /* virtual methods */
- gint (*validate) (FilterRule *, GtkWindow *error_parent);
- gint (*eq) (FilterRule *fr, FilterRule *cm);
-
- xmlNodePtr (*xml_encode) (FilterRule *);
- gint (*xml_decode) (FilterRule *, xmlNodePtr, struct _RuleContext *);
-
- void (*build_code) (FilterRule *, GString *out);
-
- void (*copy) (FilterRule *dest, FilterRule *src);
-
- GtkWidget *(*get_widget) (FilterRule *fr, struct _RuleContext *f);
-
- /* signals */
- void (*changed) (FilterRule *fr);
-};
-
-GType filter_rule_get_type (void);
-FilterRule *filter_rule_new (void);
-
-FilterRule *filter_rule_clone (FilterRule *base);
-
-/* methods */
-void filter_rule_set_name (FilterRule *fr, const gchar *name);
-void filter_rule_set_source (FilterRule *fr, const gchar *source);
-
-gint filter_rule_validate (FilterRule *fr, GtkWindow *error_parent);
-gint filter_rule_eq (FilterRule *fr, FilterRule *cm);
-
-xmlNodePtr filter_rule_xml_encode (FilterRule *fr);
-gint filter_rule_xml_decode (FilterRule *fr, xmlNodePtr node, struct _RuleContext *f);
-
-void filter_rule_copy (FilterRule *dest, FilterRule *src);
-
-void filter_rule_add_part (FilterRule *fr, FilterPart *fp);
-void filter_rule_remove_part (FilterRule *fr, FilterPart *fp);
-void filter_rule_replace_part (FilterRule *fr, FilterPart *fp, FilterPart *new);
-
-GtkWidget *filter_rule_get_widget (FilterRule *fr, struct _RuleContext *f);
-
-void filter_rule_build_code (FilterRule *fr, GString *out);
-/*
-void filter_rule_build_action(FilterRule *fr, GString *out);
-*/
-
-void filter_rule_emit_changed (FilterRule *fr);
-
-/* static functions */
-FilterRule *filter_rule_next_list (GList *l, FilterRule *last, const gchar *source);
-FilterRule *filter_rule_find_list (GList *l, const gchar *name, const gchar *source);
-
-#endif /* ! _FILTER_RULE_H */
diff --git a/filter/rule-context.c b/filter/rule-context.c
deleted file mode 100644
index 45f495e37e..0000000000
--- a/filter/rule-context.c
+++ /dev/null
@@ -1,952 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#include <config.h>
-
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <glib.h>
-#include <glib/gstdio.h>
-
-#include <gtk/gtk.h>
-
-#include <glib/gi18n.h>
-
-#include <libedataserver/e-xml-utils.h>
-
-#include "e-util/e-error.h"
-#include "e-util/e-xml-utils.h"
-
-#include "filter-code.h"
-#include "filter-colour.h"
-#include "filter-datespec.h"
-#include "filter-file.h"
-#include "filter-input.h"
-#include "filter-int.h"
-#include "filter-option.h"
-#include "filter-rule.h"
-#include "rule-context.h"
-
-#define d(x)
-
-static gint load(RuleContext *rc, const gchar *system, const gchar *user);
-static gint save(RuleContext *rc, const gchar *user);
-static gint revert(RuleContext *rc, const gchar *user);
-static GList *rename_uri(RuleContext *rc, const gchar *olduri, const gchar *newuri, GCompareFunc cmp);
-static GList *delete_uri(RuleContext *rc, const gchar *uri, GCompareFunc cmp);
-static FilterElement *new_element(RuleContext *rc, const gchar *name);
-
-static void rule_context_class_init(RuleContextClass *klass);
-static void rule_context_init(RuleContext *rc);
-static void rule_context_finalise(GObject *obj);
-
-#define _PRIVATE(x)(((RuleContext *)(x))->priv)
-
-struct _RuleContextPrivate {
- gint frozen;
-};
-
-static GObjectClass *parent_class = NULL;
-
-enum {
- RULE_ADDED,
- RULE_REMOVED,
- CHANGED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-GType
-rule_context_get_type(void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof(RuleContextClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) rule_context_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof(RuleContext),
- 0, /* n_preallocs */
- (GInstanceInitFunc) rule_context_init,
- };
-
- type = g_type_register_static(G_TYPE_OBJECT, "RuleContext", &info, 0);
- }
-
- return type;
-}
-
-static void
-rule_context_class_init(RuleContextClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS(klass);
-
- parent_class = g_type_class_ref(G_TYPE_OBJECT);
-
- object_class->finalize = rule_context_finalise;
-
- /* override methods */
- klass->load = load;
- klass->save = save;
- klass->revert = revert;
- klass->rename_uri = rename_uri;
- klass->delete_uri = delete_uri;
- klass->new_element = new_element;
-
- /* signals */
- signals[RULE_ADDED] =
- g_signal_new("rule_added",
- RULE_TYPE_CONTEXT,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET(RuleContextClass, rule_added),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE, 1, G_TYPE_POINTER);
-
- signals[RULE_REMOVED] =
- g_signal_new("rule_removed",
- RULE_TYPE_CONTEXT,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET(RuleContextClass, rule_removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE, 1, G_TYPE_POINTER);
-
- signals[CHANGED] =
- g_signal_new("changed",
- RULE_TYPE_CONTEXT,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET(RuleContextClass, changed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-}
-
-static void
-rule_context_init(RuleContext *rc)
-{
- rc->priv = g_malloc0(sizeof(*rc->priv));
-
- rc->part_set_map = g_hash_table_new(g_str_hash, g_str_equal);
- rc->rule_set_map = g_hash_table_new(g_str_hash, g_str_equal);
-
- rc->flags = RULE_CONTEXT_GROUPING;
-}
-
-static void
-free_part_set(struct _part_set_map *map, gpointer data)
-{
- g_free(map->name);
- g_free(map);
-}
-
-static void
-free_rule_set(struct _rule_set_map *map, gpointer data)
-{
- g_free(map->name);
- g_free(map);
-}
-
-static void
-rule_context_finalise(GObject *obj)
-{
- RuleContext *rc =(RuleContext *) obj;
-
- g_list_foreach(rc->rule_set_list, (GFunc)free_rule_set, NULL);
- g_list_free(rc->rule_set_list);
- g_hash_table_destroy(rc->rule_set_map);
-
- g_list_foreach(rc->part_set_list, (GFunc)free_part_set, NULL);
- g_list_free(rc->part_set_list);
- g_hash_table_destroy(rc->part_set_map);
-
- g_free(rc->error);
-
- g_list_foreach(rc->parts, (GFunc)g_object_unref, NULL);
- g_list_free(rc->parts);
- g_list_foreach(rc->rules, (GFunc)g_object_unref, NULL);
- g_list_free(rc->rules);
-
- g_free(rc->priv);
-
- G_OBJECT_CLASS(parent_class)->finalize(obj);
-}
-
-/**
- * rule_context_new:
- *
- * Create a new RuleContext object.
- *
- * Return value: A new #RuleContext object.
- **/
-RuleContext *
-rule_context_new(void)
-{
- return(RuleContext *) g_object_new(RULE_TYPE_CONTEXT, NULL, NULL);
-}
-
-void
-rule_context_add_part_set(RuleContext *rc, const gchar *setname, GType part_type, RCPartFunc append, RCNextPartFunc next)
-{
- struct _part_set_map *map;
-
- g_return_if_fail (g_hash_table_lookup(rc->part_set_map, setname) == NULL);
-
- map = g_malloc0(sizeof(*map));
- map->type = part_type;
- map->append = append;
- map->next = next;
- map->name = g_strdup(setname);
- g_hash_table_insert(rc->part_set_map, map->name, map);
- rc->part_set_list = g_list_append(rc->part_set_list, map);
- d(printf("adding part set '%s'\n", setname));
-}
-
-void
-rule_context_add_rule_set(RuleContext *rc, const gchar *setname, GType rule_type, RCRuleFunc append, RCNextRuleFunc next)
-{
- struct _rule_set_map *map;
-
- g_return_if_fail (g_hash_table_lookup(rc->rule_set_map, setname) == NULL);
-
- map = g_malloc0(sizeof(*map));
- map->type = rule_type;
- map->append = append;
- map->next = next;
- map->name = g_strdup(setname);
- g_hash_table_insert(rc->rule_set_map, map->name, map);
- rc->rule_set_list = g_list_append(rc->rule_set_list, map);
- d(printf("adding rule set '%s'\n", setname));
-}
-
-/**
- * rule_context_set_error:
- * @f:
- * @error:
- *
- * Set the text error for the context, or NULL to clear it.
- **/
-static void
-rule_context_set_error(RuleContext *rc, gchar *error)
-{
- g_return_if_fail (rc);
-
- g_free(rc->error);
- rc->error = error;
-}
-
-/**
- * rule_context_load:
- * @f:
- * @system:
- * @user:
- *
- * Load a rule context from a system and user description file.
- *
- * Return value:
- **/
-gint
-rule_context_load(RuleContext *rc, const gchar *system, const gchar *user)
-{
- gint res;
-
- g_return_val_if_fail (rc, -1);
-
- d(printf("rule_context: loading %s %s\n", system, user));
-
- rc->priv->frozen++;
- res = RULE_CONTEXT_GET_CLASS(rc)->load(rc, system, user);
- rc->priv->frozen--;
-
- return res;
-}
-
-static gint
-load(RuleContext *rc, const gchar *system, const gchar *user)
-{
- xmlNodePtr set, rule, root;
- xmlDocPtr systemdoc, userdoc;
- struct _part_set_map *part_map;
- struct _rule_set_map *rule_map;
-
- rule_context_set_error(rc, NULL);
-
- d(printf("loading rules %s %s\n", system, user));
-
- systemdoc = e_xml_parse_file (system);
- if (systemdoc == NULL) {
- rule_context_set_error(rc, g_strdup_printf("Unable to load system rules '%s': %s",
- system, g_strerror(errno)));
- return -1;
- }
-
- root = xmlDocGetRootElement(systemdoc);
- if (root == NULL || strcmp((gchar *)root->name, "filterdescription")) {
- rule_context_set_error(rc, g_strdup_printf("Unable to load system rules '%s': Invalid format", system));
- xmlFreeDoc(systemdoc);
- return -1;
- }
- /* doesn't matter if this doens't exist */
- userdoc = NULL;
- if (g_file_test(user, G_FILE_TEST_IS_REGULAR))
- userdoc = e_xml_parse_file (user);
-
- /* now parse structure */
- /* get rule parts */
- set = root->children;
- while (set) {
- d(printf("set name = %s\n", set->name));
- part_map = g_hash_table_lookup(rc->part_set_map, set->name);
- if (part_map) {
- d(printf("loading parts ...\n"));
- rule = set->children;
- while (rule) {
- if (!strcmp((gchar *)rule->name, "part")) {
- FilterPart *part = FILTER_PART(g_object_new(part_map->type, NULL, NULL));
-
- if (filter_part_xml_create(part, rule, rc) == 0) {
- part_map->append(rc, part);
- } else {
- g_object_unref(part);
- g_warning("Cannot load filter part");
- }
- }
- rule = rule->next;
- }
- } else if ((rule_map = g_hash_table_lookup(rc->rule_set_map, set->name))) {
- d(printf("loading system rules ...\n"));
- rule = set->children;
- while (rule) {
- d(printf("checking node: %s\n", rule->name));
- if (!strcmp((gchar *)rule->name, "rule")) {
- FilterRule *part = FILTER_RULE(g_object_new(rule_map->type, NULL, NULL));
-
- if (filter_rule_xml_decode(part, rule, rc) == 0) {
- part->system = TRUE;
- rule_map->append(rc, part);
- } else {
- g_object_unref(part);
- g_warning("Cannot load filter part");
- }
- }
- rule = rule->next;
- }
- }
- set = set->next;
- }
-
- /* now load actual rules */
- if (userdoc) {
- root = xmlDocGetRootElement(userdoc);
- set = root?root->children:NULL;
- while (set) {
- d(printf("set name = %s\n", set->name));
- rule_map = g_hash_table_lookup(rc->rule_set_map, set->name);
- if (rule_map) {
- d(printf("loading rules ...\n"));
- rule = set->children;
- while (rule) {
- d(printf("checking node: %s\n", rule->name));
- if (!strcmp((gchar *)rule->name, "rule")) {
- FilterRule *part = FILTER_RULE(g_object_new(rule_map->type, NULL, NULL));
-
- if (filter_rule_xml_decode(part, rule, rc) == 0) {
- rule_map->append(rc, part);
- } else {
- g_object_unref(part);
- g_warning("Cannot load filter part");
- }
- }
- rule = rule->next;
- }
- }
- set = set->next;
- }
- }
-
- xmlFreeDoc(userdoc);
- xmlFreeDoc(systemdoc);
-
- return 0;
-}
-
-/**
- * rule_context_save:
- * @f:
- * @user:
- *
- * Save a rule context to disk.
- *
- * Return value:
- **/
-gint
-rule_context_save(RuleContext *rc, const gchar *user)
-{
- g_return_val_if_fail (rc, -1);
- g_return_val_if_fail (user, -1);
-
- return RULE_CONTEXT_GET_CLASS(rc)->save(rc, user);
-}
-
-static gint
-save(RuleContext *rc, const gchar *user)
-{
- xmlDocPtr doc;
- xmlNodePtr root, rules, work;
- GList *l;
- FilterRule *rule;
- struct _rule_set_map *map;
- gint ret;
-
- doc = xmlNewDoc((const guchar *)"1.0");
- /* FIXME: set character encoding to UTF-8? */
- root = xmlNewDocNode(doc, NULL, (const guchar *)"filteroptions", NULL);
- xmlDocSetRootElement(doc, root);
- l = rc->rule_set_list;
- while (l) {
- map = l->data;
- rules = xmlNewDocNode(doc, NULL, (guchar *)map->name, NULL);
- xmlAddChild(root, rules);
- rule = NULL;
- while ((rule = map->next(rc, rule, NULL))) {
- if (!rule->system) {
- d(printf("processing rule %s\n", rule->name));
- work = filter_rule_xml_encode(rule);
- xmlAddChild(rules, work);
- }
- }
- l = g_list_next(l);
- }
-
- ret = e_xml_save_file(user, doc);
-
- xmlFreeDoc(doc);
-
- return ret;
-}
-
-/**
- * rule_context_revert:
- * @f:
- * @user:
- *
- * Reverts a rule context from a user description file. Assumes the
- * system description file is unchanged from when it was loaded.
- *
- * Return value:
- **/
-gint
-rule_context_revert(RuleContext *rc, const gchar *user)
-{
- g_return_val_if_fail (rc, 0);
-
- d(printf("rule_context: restoring %s\n", user));
-
- return RULE_CONTEXT_GET_CLASS(rc)->revert(rc, user);
-}
-
-struct _revert_data {
- GHashTable *rules;
- gint rank;
-};
-
-static void
-revert_rule_remove(gpointer key, FilterRule *frule, RuleContext *rc)
-{
- rule_context_remove_rule(rc, frule);
- g_object_unref(frule);
-}
-
-static void
-revert_source_remove(gpointer key, struct _revert_data *rest_data, RuleContext *rc)
-{
- g_hash_table_foreach(rest_data->rules, (GHFunc)revert_rule_remove, rc);
- g_hash_table_destroy(rest_data->rules);
- g_free(rest_data);
-}
-
-static guint
-source_hashf(const gchar *a)
-{
- if (a)
- return g_str_hash(a);
- return 0;
-}
-
-static gint
-source_eqf(const gchar *a, const gchar *b)
-{
- return((a && b && strcmp(a, b) == 0))
- || (a == NULL && b == NULL);
-}
-
-static gint
-revert(RuleContext *rc, const gchar *user)
-{
- xmlNodePtr set, rule;
- /*struct _part_set_map *part_map;*/
- struct _rule_set_map *rule_map;
- struct _revert_data *rest_data;
- GHashTable *source_hash;
- xmlDocPtr userdoc;
- FilterRule *frule;
-
- rule_context_set_error(rc, NULL);
-
- d(printf("restoring rules %s\n", user));
-
- userdoc = e_xml_parse_file (user);
- if (userdoc == NULL)
- /* clear out anythign we have? */
- return 0;
-
- source_hash = g_hash_table_new((GHashFunc)source_hashf, (GCompareFunc)source_eqf);
-
- /* setup stuff we have now */
- /* Note that we assume there is only 1 set of rules in a given rule context,
- although other parts of the code dont assume this */
- frule = NULL;
- while ((frule = rule_context_next_rule(rc, frule, NULL))) {
- rest_data = g_hash_table_lookup(source_hash, frule->source);
- if (rest_data == NULL) {
- rest_data = g_malloc0(sizeof(*rest_data));
- rest_data->rules = g_hash_table_new(g_str_hash, g_str_equal);
- g_hash_table_insert(source_hash, frule->source, rest_data);
- }
- g_hash_table_insert(rest_data->rules, frule->name, frule);
- }
-
- /* make what we have, match what we load */
- set = xmlDocGetRootElement(userdoc);
- set = set?set->children:NULL;
- while (set) {
- d(printf("set name = %s\n", set->name));
- rule_map = g_hash_table_lookup(rc->rule_set_map, set->name);
- if (rule_map) {
- d(printf("loading rules ...\n"));
- rule = set->children;
- while (rule) {
- d(printf("checking node: %s\n", rule->name));
- if (!strcmp((gchar *)rule->name, "rule")) {
- FilterRule *part = FILTER_RULE(g_object_new(rule_map->type, NULL, NULL));
-
- if (filter_rule_xml_decode(part, rule, rc) == 0) {
- /* use the revert data to keep track of the right rank of this rule part */
- rest_data = g_hash_table_lookup(source_hash, part->source);
- if (rest_data == NULL) {
- rest_data = g_malloc0(sizeof(*rest_data));
- rest_data->rules = g_hash_table_new(g_str_hash, g_str_equal);
- g_hash_table_insert(source_hash, part->source, rest_data);
- }
- frule = g_hash_table_lookup(rest_data->rules, part->name);
- if (frule) {
- if (rc->priv->frozen == 0 && !filter_rule_eq(frule, part))
- filter_rule_copy(frule, part);
-
- g_object_unref(part);
- rule_context_rank_rule(rc, frule, frule->source, rest_data->rank);
- g_hash_table_remove(rest_data->rules, frule->name);
- } else {
- rule_context_add_rule(rc, part);
- rule_context_rank_rule(rc, part, part->source, rest_data->rank);
- }
- rest_data->rank++;
- } else {
- g_object_unref(part);
- g_warning("Cannot load filter part");
- }
- }
- rule = rule->next;
- }
- }
- set = set->next;
- }
-
- xmlFreeDoc(userdoc);
-
- /* remove any we still have that weren't in the file */
- g_hash_table_foreach(source_hash, (GHFunc)revert_source_remove, rc);
- g_hash_table_destroy(source_hash);
-
- return 0;
-}
-
-FilterPart *
-rule_context_find_part(RuleContext *rc, const gchar *name)
-{
- g_return_val_if_fail (rc, NULL);
- g_return_val_if_fail (name, NULL);
-
- d(printf("find part : "));
- return filter_part_find_list(rc->parts, name);
-}
-
-FilterPart *
-rule_context_create_part(RuleContext *rc, const gchar *name)
-{
- FilterPart *part;
-
- g_return_val_if_fail (rc, NULL);
- g_return_val_if_fail (name, NULL);
-
- if ((part = rule_context_find_part(rc, name)))
- return filter_part_clone(part);
-
- return NULL;
-}
-
-FilterPart *
-rule_context_next_part(RuleContext *rc, FilterPart *last)
-{
- g_return_val_if_fail (rc, NULL);
-
- return filter_part_next_list(rc->parts, last);
-}
-
-FilterRule *
-rule_context_next_rule(RuleContext *rc, FilterRule *last, const gchar *source)
-{
- g_return_val_if_fail (rc, NULL);
-
- return filter_rule_next_list(rc->rules, last, source);
-}
-
-FilterRule *
-rule_context_find_rule(RuleContext *rc, const gchar *name, const gchar *source)
-{
- g_return_val_if_fail (name, NULL);
- g_return_val_if_fail (rc, NULL);
-
- return filter_rule_find_list(rc->rules, name, source);
-}
-
-void
-rule_context_add_part(RuleContext *rc, FilterPart *part)
-{
- g_return_if_fail (rc);
- g_return_if_fail (part);
-
- rc->parts = g_list_append(rc->parts, part);
-}
-
-void
-rule_context_add_rule(RuleContext *rc, FilterRule *new)
-{
- g_return_if_fail (rc);
- g_return_if_fail (new);
-
- d(printf("add rule '%s'\n", new->name));
-
- rc->rules = g_list_append(rc->rules, new);
-
- if (rc->priv->frozen == 0) {
- g_signal_emit(rc, signals[RULE_ADDED], 0, new);
- g_signal_emit(rc, signals[CHANGED], 0);
- }
-}
-
-static void
-new_rule_response(GtkWidget *dialog, gint button, RuleContext *context)
-{
- if (button == GTK_RESPONSE_OK) {
- FilterRule *rule = g_object_get_data((GObject *) dialog, "rule");
- gchar *user = g_object_get_data((GObject *) dialog, "path");
-
- if (!filter_rule_validate (rule, GTK_WINDOW (dialog))) {
- /* no need to popup a dialog because the validate code does that. */
- return;
- }
-
- if (rule_context_find_rule (context, rule->name, rule->source)) {
- e_error_run ((GtkWindow *)dialog, "filter:bad-name-notunique", rule->name, NULL);
-
- return;
- }
-
- g_object_ref(rule);
- rule_context_add_rule(context, rule);
- if (user)
- rule_context_save(context, user);
- }
-
- gtk_widget_destroy(dialog);
-}
-
-/* add a rule, with a gui, asking for confirmation first ... optionally save to path */
-void
-rule_context_add_rule_gui(RuleContext *rc, FilterRule *rule, const gchar *title, const gchar *path)
-{
- GtkDialog *dialog;
- GtkWidget *widget;
- GtkWidget *content_area;
-
- d(printf("add rule gui '%s'\n", rule->name));
-
- g_return_if_fail (rc);
- g_return_if_fail (rule);
-
- widget = filter_rule_get_widget(rule, rc);
- gtk_widget_show(widget);
-
- dialog =(GtkDialog *) gtk_dialog_new();
- gtk_dialog_add_buttons(dialog,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_OK, GTK_RESPONSE_OK,
- NULL);
- gtk_dialog_set_has_separator (dialog, FALSE);
-
- gtk_window_set_title((GtkWindow *) dialog, title);
- gtk_window_set_default_size((GtkWindow *) dialog, 600, 400);
- gtk_window_set_resizable((GtkWindow *) dialog, TRUE);
-
- content_area = gtk_dialog_get_content_area (dialog);
- gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
-
- g_object_set_data_full((GObject *) dialog, "rule", rule, g_object_unref);
- if (path)
- g_object_set_data_full((GObject *) dialog, "path", g_strdup(path), g_free);
-
- g_signal_connect(dialog, "response", G_CALLBACK(new_rule_response), rc);
-
- g_object_ref(rc);
-
- g_object_set_data_full((GObject *) dialog, "context", rc, g_object_unref);
-
- gtk_widget_show((GtkWidget *) dialog);
-}
-
-void
-rule_context_remove_rule(RuleContext *rc, FilterRule *rule)
-{
- g_return_if_fail (rc);
- g_return_if_fail (rule);
-
- d(printf("remove rule '%s'\n", rule->name));
-
- rc->rules = g_list_remove(rc->rules, rule);
-
- if (rc->priv->frozen == 0) {
- g_signal_emit(rc, signals[RULE_REMOVED], 0, rule);
- g_signal_emit(rc, signals[CHANGED], 0);
- }
-}
-
-void
-rule_context_rank_rule(RuleContext *rc, FilterRule *rule, const gchar *source, gint rank)
-{
- GList *node;
- gint i = 0, index = 0;
-
- g_return_if_fail (rc);
- g_return_if_fail (rule);
-
- if (rule_context_get_rank_rule (rc, rule, source) == rank)
- return;
-
- rc->rules = g_list_remove(rc->rules, rule);
- node = rc->rules;
- while (node) {
- FilterRule *r = node->data;
-
- if (i == rank) {
- rc->rules = g_list_insert(rc->rules, rule, index);
- if (rc->priv->frozen == 0)
- g_signal_emit(rc, signals[CHANGED], 0);
-
- return;
- }
-
- index++;
- if (source == NULL || (r->source && strcmp(r->source, source) == 0))
- i++;
-
- node = node->next;
- }
-
- rc->rules = g_list_append(rc->rules, rule);
- if (rc->priv->frozen == 0)
- g_signal_emit(rc, signals[CHANGED], 0);
-}
-
-gint
-rule_context_get_rank_rule(RuleContext *rc, FilterRule *rule, const gchar *source)
-{
- GList *node;
- gint i = 0;
-
- g_return_val_if_fail (rc, -1);
- g_return_val_if_fail (rule, -1);
-
- d(printf("getting rank of rule '%s'\n", rule->name));
-
- node = rc->rules;
- while (node) {
- FilterRule *r = node->data;
-
- d(printf(" checking against rule '%s' rank '%d'\n", r->name, i));
-
- if (r == rule)
- return i;
-
- if (source == NULL || (r->source && strcmp(r->source, source) == 0))
- i++;
-
- node = node->next;
- }
-
- return -1;
-}
-
-FilterRule *
-rule_context_find_rank_rule(RuleContext *rc, gint rank, const gchar *source)
-{
- GList *node;
- gint i = 0;
-
- g_return_val_if_fail (rc, NULL);
-
- d(printf("getting rule at rank %d source '%s'\n", rank, source?source:"<any>"));
-
- node = rc->rules;
- while (node) {
- FilterRule *r = node->data;
-
- d(printf(" checking against rule '%s' rank '%d'\n", r->name, i));
-
- if (source == NULL || (r->source && strcmp(r->source, source) == 0)) {
- if (rank == i)
- return r;
- i++;
- }
-
- node = node->next;
- }
-
- return NULL;
-}
-
-static GList *
-delete_uri(RuleContext *rc, const gchar *uri, GCompareFunc cmp)
-{
- return NULL;
-}
-
-GList *
-rule_context_delete_uri(RuleContext *rc, const gchar *uri, GCompareFunc cmp)
-{
- return RULE_CONTEXT_GET_CLASS(rc)->delete_uri(rc, uri, cmp);
-}
-
-static GList *
-rename_uri(RuleContext *rc, const gchar *olduri, const gchar *newuri, GCompareFunc cmp)
-{
- return NULL;
-}
-
-GList *
-rule_context_rename_uri(RuleContext *rc, const gchar *olduri, const gchar *newuri, GCompareFunc cmp)
-{
- return RULE_CONTEXT_GET_CLASS(rc)->rename_uri(rc, olduri, newuri, cmp);
-}
-
-void
-rule_context_free_uri_list(RuleContext *rc, GList *uris)
-{
- GList *l = uris, *n;
-
- /* TODO: should be virtual */
-
- while (l) {
- n = l->next;
- g_free(l->data);
- g_list_free_1(l);
- l = n;
- }
-}
-
-static FilterElement *
-new_element(RuleContext *rc, const gchar *type)
-{
- if (!strcmp (type, "string")) {
- return (FilterElement *) filter_input_new ();
- } else if (!strcmp (type, "address")) {
- /* FIXME: temporary ... need real address type */
- return (FilterElement *) filter_input_new_type_name (type);
- } else if (!strcmp (type, "code")) {
- return (FilterElement *) filter_code_new (FALSE);
- } else if (!strcmp (type, "rawcode")) {
- return (FilterElement *) filter_code_new (TRUE);
- } else if (!strcmp (type, "colour")) {
- return (FilterElement *) filter_colour_new ();
- } else if (!strcmp (type, "optionlist")) {
- return (FilterElement *) filter_option_new ();
- } else if (!strcmp (type, "datespec")) {
- return (FilterElement *) filter_datespec_new ();
- } else if (!strcmp (type, "command")) {
- return (FilterElement *) filter_file_new_type_name (type);
- } else if (!strcmp (type, "file")) {
- return (FilterElement *) filter_file_new_type_name (type);
- } else if (!strcmp (type, "integer")) {
- return (FilterElement *) filter_int_new ();
- } else if (!strcmp (type, "regex")) {
- return (FilterElement *) filter_input_new_type_name (type);
- }else if (!strcmp(type, "completedpercent")) {
- return (FilterElement *) filter_int_new_type("completedpercent", 0,100);
-
- } else {
- g_warning("Unknown filter type '%s'", type);
- return NULL;
- }
-}
-
-/**
- * rule_context_new_element:
- * @rc:
- * @name:
- *
- * create a new filter element based on name.
- *
- * Return value:
- **/
-FilterElement *
-rule_context_new_element(RuleContext *rc, const gchar *name)
-{
- if (name == NULL)
- return NULL;
-
- return RULE_CONTEXT_GET_CLASS(rc)->new_element(rc, name);
-}
-
diff --git a/filter/rule-context.h b/filter/rule-context.h
deleted file mode 100644
index 63c9abaa67..0000000000
--- a/filter/rule-context.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- *
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _RULE_CONTEXT_H
-#define _RULE_CONTEXT_H
-
-#include <glib.h>
-#include <glib-object.h>
-#include <libxml/parser.h>
-
-#include "filter-part.h"
-#include "filter-rule.h"
-
-#define RULE_TYPE_CONTEXT (rule_context_get_type ())
-#define RULE_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RULE_TYPE_CONTEXT, RuleContext))
-#define RULE_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RULE_TYPE_CONTEXT, RuleContextClass))
-#define IS_RULE_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RULE_TYPE_CONTEXT))
-#define IS_RULE_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), RULE_TYPE_CONTEXT))
-#define RULE_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RULE_TYPE_CONTEXT, RuleContextClass))
-
-typedef struct _RuleContext RuleContext;
-typedef struct _RuleContextClass RuleContextClass;
-
-/* backend capabilities, this is a hack since we don't support nested rules */
-enum {
- RULE_CONTEXT_GROUPING = 1 << 0,
- RULE_CONTEXT_THREADING = 1 << 1
-};
-
-struct _RuleContext {
- GObject parent_object;
- struct _RuleContextPrivate *priv;
-
- gchar *error; /* string version of error */
-
- guint32 flags; /* capability flags */
-
- GList *parts;
- GList *rules;
-
- GHashTable *part_set_map; /* map set types to part types */
- GList *part_set_list;
- GHashTable *rule_set_map; /* map set types to rule types */
- GList *rule_set_list;
-};
-
-typedef void (*RCRegisterFunc) (RuleContext *rc, FilterRule *rule, gpointer user_data);
-
-struct _RuleContextClass {
- GObjectClass parent_class;
-
- /* virtual methods */
- gint (*load) (RuleContext *rc, const gchar *system, const gchar *user);
- gint (*save) (RuleContext *rc, const gchar *user);
- gint (*revert) (RuleContext *rc, const gchar *user);
-
- GList *(*delete_uri) (RuleContext *rc, const gchar *uri, GCompareFunc cmp);
- GList *(*rename_uri) (RuleContext *rc, const gchar *olduri, const gchar *newuri, GCompareFunc cmp);
-
- FilterElement *(*new_element)(RuleContext *rc, const gchar *name);
-
- /* signals */
- void (*rule_added) (RuleContext *rc, FilterRule *rule);
- void (*rule_removed) (RuleContext *rc, FilterRule *rule);
- void (*changed) (RuleContext *rc);
-};
-
-typedef void (*RCPartFunc) (RuleContext *rc, FilterPart *part);
-typedef void (*RCRuleFunc) (RuleContext *rc, FilterRule *part);
-typedef FilterPart * (*RCNextPartFunc) (RuleContext *rc, FilterPart *part);
-typedef FilterRule * (*RCNextRuleFunc) (RuleContext *rc, FilterRule *rule, const gchar *source);
-
-struct _part_set_map {
- gchar *name;
- GType type;
- RCPartFunc append;
- RCNextPartFunc next;
-};
-
-struct _rule_set_map {
- gchar *name;
- GType type;
- RCRuleFunc append;
- RCNextRuleFunc next;
-};
-
-GType rule_context_get_type (void);
-
-/* methods */
-RuleContext *rule_context_new (void);
-
-/* io */
-gint rule_context_load (RuleContext *rc, const gchar *system, const gchar *user);
-gint rule_context_save (RuleContext *rc, const gchar *user);
-gint rule_context_revert (RuleContext *rc, const gchar *user);
-
-void rule_context_add_part (RuleContext *rc, FilterPart *new);
-FilterPart *rule_context_find_part (RuleContext *rc, const gchar *name);
-FilterPart *rule_context_create_part (RuleContext *rc, const gchar *name);
-FilterPart *rule_context_next_part (RuleContext *rc, FilterPart *last);
-
-FilterRule *rule_context_next_rule (RuleContext *rc, FilterRule *last, const gchar *source);
-FilterRule *rule_context_find_rule (RuleContext *rc, const gchar *name, const gchar *source);
-FilterRule *rule_context_find_rank_rule (RuleContext *rc, gint rank, const gchar *source);
-void rule_context_add_rule (RuleContext *rc, FilterRule *new);
-void rule_context_add_rule_gui (RuleContext *rc, FilterRule *rule, const gchar *title, const gchar *path);
-void rule_context_remove_rule (RuleContext *rc, FilterRule *rule);
-
-/* get/set the rank (position) of a rule */
-void rule_context_rank_rule (RuleContext *rc, FilterRule *rule, const gchar *source, gint rank);
-gint rule_context_get_rank_rule (RuleContext *rc, FilterRule *rule, const gchar *source);
-
-/* setup type for set parts */
-void rule_context_add_part_set (RuleContext *rc, const gchar *setname, GType part_type,
- RCPartFunc append, RCNextPartFunc next);
-void rule_context_add_rule_set (RuleContext *rc, const gchar *setname, GType rule_type,
- RCRuleFunc append, RCNextRuleFunc next);
-
-/* dynamic element types */
-FilterElement *rule_context_new_element(RuleContext *rc, const gchar *name);
-
-/* uri's disappear/renamed externally */
-GList *rule_context_delete_uri (RuleContext *rc, const gchar *uri, GCompareFunc cmp);
-GList *rule_context_rename_uri (RuleContext *rc, const gchar *olduri, const gchar *newuri, GCompareFunc cmp);
-
-void rule_context_free_uri_list (RuleContext *rc, GList *uris);
-
-#endif /* ! _RULE_CONTEXT_H */
diff --git a/filter/rule-editor.c b/filter/rule-editor.c
deleted file mode 100644
index edda65e116..0000000000
--- a/filter/rule-editor.c
+++ /dev/null
@@ -1,880 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-/* for getenv only, remove when getenv need removed */
-#include <stdlib.h>
-#include <string.h>
-
-#include <glib/gi18n.h>
-
-#include "e-util/e-error.h"
-#include "e-util/e-util-private.h"
-#include "rule-editor.h"
-
-static gint enable_undo = 0;
-
-#define d(x)
-
-static void set_source (RuleEditor *re, const gchar *source);
-static void set_sensitive (RuleEditor *re);
-static FilterRule *create_rule (RuleEditor *re);
-
-static gboolean update_selected_rule (RuleEditor *re);
-static void cursor_changed (GtkTreeView *treeview, RuleEditor *re);
-
-static void rule_editor_class_init (RuleEditorClass *klass);
-static void rule_editor_init (RuleEditor *re);
-static void rule_editor_finalise (GObject *obj);
-static void rule_editor_destroy (GtkObject *obj);
-
-static void dialog_rule_changed (FilterRule *fr, GtkWidget *dialog);
-
-#define _PRIVATE(x)(((RuleEditor *)(x))->priv)
-
-enum {
- BUTTON_ADD,
- BUTTON_EDIT,
- BUTTON_DELETE,
- BUTTON_TOP,
- BUTTON_UP,
- BUTTON_DOWN,
- BUTTON_BOTTOM,
- BUTTON_LAST
-};
-
-struct _RuleEditorPrivate {
- GtkButton *buttons[BUTTON_LAST];
-};
-
-static GtkDialogClass *parent_class = NULL;
-
-GType
-rule_editor_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (RuleEditorClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) rule_editor_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (RuleEditor),
- 0, /* n_preallocs */
- (GInstanceInitFunc) rule_editor_init,
- };
-
- /* TODO: Remove when it works (or never will) */
- enable_undo = getenv ("EVOLUTION_RULE_UNDO") != NULL;
-
- type = g_type_register_static (gtk_dialog_get_type (), "RuleEditor", &info, 0);
- }
-
- return type;
-}
-
-static void
-rule_editor_class_init (RuleEditorClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GtkObjectClass *object_class = (GtkObjectClass *) klass;
-
- parent_class = g_type_class_ref(gtk_dialog_get_type ());
-
- gobject_class->finalize = rule_editor_finalise;
- object_class->destroy = rule_editor_destroy;
-
- /* override methods */
- klass->set_source = set_source;
- klass->set_sensitive = set_sensitive;
- klass->create_rule = create_rule;
-}
-
-static void
-rule_editor_init (RuleEditor *re)
-{
- re->priv = g_malloc0 (sizeof (*re->priv));
-}
-
-static void
-rule_editor_finalise (GObject *obj)
-{
- RuleEditor *re = (RuleEditor *)obj;
- RuleEditorUndo *undo, *next;
-
- g_object_unref (re->context);
- g_free (re->priv);
-
- undo = re->undo_log;
- while (undo) {
- next = undo->next;
- g_object_unref (undo->rule);
- g_free (undo);
- undo = next;
- }
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-static void
-rule_editor_destroy (GtkObject *obj)
-{
- RuleEditor *re = (RuleEditor *) obj;
-
- if (re->dialog) {
- gtk_widget_destroy (GTK_WIDGET (re->dialog));
- re->dialog = NULL;
- }
-
- ((GtkObjectClass *)(parent_class))->destroy (obj);
-}
-
-/**
- * rule_editor_new:
- *
- * Create a new RuleEditor object.
- *
- * Return value: A new #RuleEditor object.
- **/
-RuleEditor *
-rule_editor_new (RuleContext *rc, const gchar *source, const gchar *label)
-{
- RuleEditor *re = (RuleEditor *) g_object_new (RULE_TYPE_EDITOR, NULL);
- GladeXML *gui;
- gchar *filter_glade = g_build_filename (EVOLUTION_GLADEDIR,
- "filter.glade",
- NULL);
-
- gui = glade_xml_new (filter_glade, "rule_editor", NULL);
- g_free (filter_glade);
- rule_editor_construct (re, rc, gui, source, label);
- gtk_widget_hide (glade_xml_get_widget (gui, "label17"));
- gtk_widget_hide (glade_xml_get_widget (gui, "filter_source_combobox"));
- g_object_unref (gui);
-
- return re;
-}
-
-/* used internally by implementations if required */
-void
-rule_editor_set_sensitive (RuleEditor *re)
-{
- RULE_EDITOR_GET_CLASS (re)->set_sensitive (re);
-}
-
-/* used internally by implementations */
-void
-rule_editor_set_source (RuleEditor *re, const gchar *source)
-{
- RULE_EDITOR_GET_CLASS (re)->set_source (re, source);
-}
-
-/* factory method for "add" button */
-FilterRule *
-rule_editor_create_rule (RuleEditor *re)
-{
- return RULE_EDITOR_GET_CLASS (re)->create_rule (re);
-}
-
-static FilterRule *
-create_rule (RuleEditor *re)
-{
- FilterRule *rule = filter_rule_new ();
- FilterPart *part;
-
- /* create a rule with 1 part in it */
- part = rule_context_next_part (re->context, NULL);
- filter_rule_add_part (rule, filter_part_clone (part));
-
- return rule;
-}
-
-static void
-editor_destroy (RuleEditor *re, GObject *deadbeef)
-{
- if (re->edit) {
- g_object_unref (re->edit);
- re->edit = NULL;
- }
-
- re->dialog = NULL;
-
- gtk_widget_set_sensitive (GTK_WIDGET (re), TRUE);
- rule_editor_set_sensitive (re);
-}
-
-static void
-rule_editor_add_undo (RuleEditor *re, gint type, FilterRule *rule, gint rank, gint newrank)
-{
- RuleEditorUndo *undo;
-
- if (!re->undo_active && enable_undo) {
- undo = g_malloc0 (sizeof (*undo));
- undo->rule = rule;
- undo->type = type;
- undo->rank = rank;
- undo->newrank = newrank;
-
- undo->next = re->undo_log;
- re->undo_log = undo;
- } else {
- g_object_unref (rule);
- }
-}
-
-static void
-rule_editor_play_undo (RuleEditor *re)
-{
- RuleEditorUndo *undo, *next;
- FilterRule *rule;
-
- re->undo_active = TRUE;
- undo = re->undo_log;
- re->undo_log = NULL;
- while (undo) {
- next = undo->next;
- switch (undo->type) {
- case RULE_EDITOR_LOG_EDIT:
- d(printf ("Undoing edit on rule '%s'\n", undo->rule->name));
- rule = rule_context_find_rank_rule (re->context, undo->rank, undo->rule->source);
- if (rule) {
- d(printf (" name was '%s'\n", rule->name));
- filter_rule_copy (rule, undo->rule);
- d(printf (" name is '%s'\n", rule->name));
- } else {
- g_warning ("Could not find the right rule to undo against?");
- }
- break;
- case RULE_EDITOR_LOG_ADD:
- d(printf ("Undoing add on rule '%s'\n", undo->rule->name));
- rule = rule_context_find_rank_rule (re->context, undo->rank, undo->rule->source);
- if (rule)
- rule_context_remove_rule (re->context, rule);
- break;
- case RULE_EDITOR_LOG_REMOVE:
- d(printf ("Undoing remove on rule '%s'\n", undo->rule->name));
- g_object_ref (undo->rule);
- rule_context_add_rule (re->context, undo->rule);
- rule_context_rank_rule (re->context, undo->rule, re->source, undo->rank);
- break;
- case RULE_EDITOR_LOG_RANK:
- rule = rule_context_find_rank_rule (re->context, undo->newrank, undo->rule->source);
- if (rule)
- rule_context_rank_rule (re->context, rule, re->source, undo->rank);
- break;
- }
-
- g_object_unref (undo->rule);
- g_free (undo);
- undo = next;
- }
- re->undo_active = FALSE;
-}
-
-static void
-editor_response (GtkWidget *dialog, gint button, RuleEditor *re)
-{
- if (button == GTK_RESPONSE_CANCEL) {
- if (enable_undo)
- rule_editor_play_undo (re);
- else {
- RuleEditorUndo *undo, *next;
-
- undo = re->undo_log;
- re->undo_log = NULL;
- while (undo) {
- next = undo->next;
- g_object_unref (undo->rule);
- g_free (undo);
- undo = next;
- }
- }
- }
-}
-
-static void
-add_editor_response (GtkWidget *dialog, gint button, RuleEditor *re)
-{
- GtkTreeSelection *selection;
- GtkTreePath *path;
- GtkTreeIter iter;
-
- if (button == GTK_RESPONSE_OK) {
- if (!filter_rule_validate (re->edit, GTK_WINDOW (dialog))) {
- /* no need to popup a dialog because the validate code does that. */
- return;
- }
-
- if (rule_context_find_rule (re->context, re->edit->name, re->edit->source)) {
- e_error_run ((GtkWindow *)dialog, "filter:bad-name-notunique", re->edit->name, NULL);
- return;
- }
-
- g_object_ref (re->edit);
-
- gtk_list_store_append (re->model, &iter);
- gtk_list_store_set (re->model, &iter, 0, re->edit->name, 1, re->edit, 2, re->edit->enabled, -1);
- selection = gtk_tree_view_get_selection (re->list);
- gtk_tree_selection_select_iter (selection, &iter);
-
- /* scroll to the newly added row */
- path = gtk_tree_model_get_path ((GtkTreeModel *) re->model, &iter);
- gtk_tree_view_scroll_to_cell (re->list, path, NULL, TRUE, 1.0, 0.0);
- gtk_tree_path_free (path);
-
- re->current = re->edit;
- rule_context_add_rule (re->context, re->current);
-
- g_object_ref (re->current);
- rule_editor_add_undo (re, RULE_EDITOR_LOG_ADD, re->current,
- rule_context_get_rank_rule (re->context, re->current, re->current->source), 0);
- }
-
- gtk_widget_destroy (dialog);
-}
-
-static void
-rule_add (GtkWidget *widget, RuleEditor *re)
-{
- GtkWidget *rules;
- GtkWidget *content_area;
-
- if (re->edit != NULL)
- return;
-
- re->edit = rule_editor_create_rule (re);
- filter_rule_set_source (re->edit, re->source);
- rules = filter_rule_get_widget (re->edit, re->context);
-
- re->dialog = gtk_dialog_new ();
- gtk_dialog_add_buttons ((GtkDialog *) re->dialog,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_OK, GTK_RESPONSE_OK,
- NULL);
- gtk_dialog_set_has_separator ((GtkDialog *) re->dialog, FALSE);
-
- gtk_window_set_title ((GtkWindow *) re->dialog, _("Add Rule"));
- gtk_window_set_default_size (GTK_WINDOW (re->dialog), 650, 400);
- gtk_window_set_resizable (GTK_WINDOW (re->dialog), TRUE);
- gtk_window_set_transient_for ((GtkWindow *) re->dialog, (GtkWindow *) re);
- gtk_container_set_border_width ((GtkContainer *) re->dialog, 6);
-
- content_area = gtk_dialog_get_content_area (GTK_DIALOG (re->dialog));
- gtk_box_pack_start (GTK_BOX (content_area), rules, TRUE, TRUE, 3);
-
- g_signal_connect (re->dialog, "response", G_CALLBACK (add_editor_response), re);
- g_object_weak_ref ((GObject *) re->dialog, (GWeakNotify) editor_destroy, re);
-
- g_signal_connect (re->edit, "changed", G_CALLBACK (dialog_rule_changed), re->dialog);
- dialog_rule_changed (re->edit, re->dialog);
-
- gtk_widget_set_sensitive (GTK_WIDGET (re), FALSE);
-
- gtk_widget_show (re->dialog);
-}
-
-static void
-edit_editor_response (GtkWidget *dialog, gint button, RuleEditor *re)
-{
- FilterRule *rule;
- GtkTreePath *path;
- GtkTreeIter iter;
- gint pos;
-
- if (button == GTK_RESPONSE_OK) {
- if (!filter_rule_validate (re->edit, GTK_WINDOW (dialog))) {
- /* no need to popup a dialog because the validate code does that. */
- return;
- }
-
- rule = rule_context_find_rule (re->context, re->edit->name, re->edit->source);
- if (rule != NULL && rule != re->current) {
- e_error_run ((GtkWindow *)dialog, "filter:bad-name-notunique", rule->name, NULL);
-
- return;
- }
-
- pos = rule_context_get_rank_rule (re->context, re->current, re->source);
- if (pos != -1) {
- path = gtk_tree_path_new ();
- gtk_tree_path_append_index (path, pos);
- gtk_tree_model_get_iter (GTK_TREE_MODEL (re->model), &iter, path);
- gtk_tree_path_free (path);
-
- gtk_list_store_set (re->model, &iter, 0, re->edit->name, -1);
-
- rule_editor_add_undo (re, RULE_EDITOR_LOG_EDIT, filter_rule_clone (re->current),
- pos, 0);
-
- /* replace the old rule with the new rule */
- filter_rule_copy (re->current, re->edit);
- }
- }
-
- gtk_widget_destroy (dialog);
-}
-
-static void
-rule_edit (GtkWidget *widget, RuleEditor *re)
-{
- GtkWidget *rules;
- GtkWidget *content_area;
-
- update_selected_rule(re);
-
- if (re->current == NULL || re->edit != NULL)
- return;
-
- re->edit = filter_rule_clone (re->current);
-
- rules = filter_rule_get_widget (re->edit, re->context);
-
- re->dialog = gtk_dialog_new ();
- gtk_dialog_add_buttons ((GtkDialog *) re->dialog,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_OK, GTK_RESPONSE_OK,
- NULL);
- gtk_dialog_set_has_separator ((GtkDialog *) re->dialog, FALSE);
-
- gtk_window_set_title ((GtkWindow *) re->dialog, _("Edit Rule"));
- gtk_window_set_default_size (GTK_WINDOW (re->dialog), 650, 400);
- gtk_window_set_resizable (GTK_WINDOW (re->dialog), TRUE);
- gtk_widget_set_parent_window (GTK_WIDGET (re->dialog), GTK_WIDGET (re)->window);
- gtk_container_set_border_width ((GtkContainer *) re->dialog, 6);
-
- content_area = gtk_dialog_get_content_area (GTK_DIALOG (re->dialog));
- gtk_box_pack_start (GTK_BOX (content_area), rules, TRUE, TRUE, 3);
-
- g_signal_connect (re->dialog, "response", G_CALLBACK (edit_editor_response), re);
- g_object_weak_ref ((GObject *) re->dialog, (GWeakNotify) editor_destroy, re);
-
- g_signal_connect (re->edit, "changed", G_CALLBACK (dialog_rule_changed), re->dialog);
- dialog_rule_changed (re->edit, re->dialog);
-
- gtk_widget_set_sensitive (GTK_WIDGET (re), FALSE);
-
- gtk_widget_show (re->dialog);
-}
-
-static void
-rule_delete (GtkWidget *widget, RuleEditor *re)
-{
- GtkTreeSelection *selection;
- GtkTreePath *path;
- GtkTreeIter iter;
- gint pos, len;
-
- update_selected_rule(re);
-
- d(printf ("delete rule\n"));
- pos = rule_context_get_rank_rule (re->context, re->current, re->source);
- if (pos != -1) {
- rule_context_remove_rule (re->context, re->current);
-
- path = gtk_tree_path_new ();
- gtk_tree_path_append_index (path, pos);
- gtk_tree_model_get_iter (GTK_TREE_MODEL (re->model), &iter, path);
- gtk_list_store_remove (re->model, &iter);
- gtk_tree_path_free (path);
-
- rule_editor_add_undo (re, RULE_EDITOR_LOG_REMOVE, re->current,
- rule_context_get_rank_rule (re->context, re->current, re->current->source), 0);
-#if 0
- g_object_unref (re->current);
-#endif
- re->current = NULL;
-
- /* now select the next rule */
- len = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (re->model), NULL);
- pos = pos >= len ? len - 1 : pos;
-
- if (pos >= 0) {
- path = gtk_tree_path_new ();
- gtk_tree_path_append_index (path, pos);
- gtk_tree_model_get_iter (GTK_TREE_MODEL (re->model), &iter, path);
- gtk_tree_path_free (path);
-
- /* select the new row */
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (re->list));
- gtk_tree_selection_select_iter (selection, &iter);
-
- /* scroll to the selected row */
- path = gtk_tree_model_get_path ((GtkTreeModel *) re->model, &iter);
- gtk_tree_view_scroll_to_cell (re->list, path, NULL, FALSE, 0.0, 0.0);
- gtk_tree_path_free (path);
-
- /* update our selection state */
- cursor_changed (re->list, re);
- return;
- }
- }
-
- rule_editor_set_sensitive (re);
-}
-
-static void
-rule_move (RuleEditor *re, gint from, gint to)
-{
- GtkTreeSelection *selection;
- GtkTreePath *path;
- GtkTreeIter iter;
- FilterRule *rule;
-
- g_object_ref (re->current);
- rule_editor_add_undo (re, RULE_EDITOR_LOG_RANK, re->current,
- rule_context_get_rank_rule (re->context, re->current, re->source), to);
-
- d(printf ("moving %d to %d\n", from, to));
- rule_context_rank_rule (re->context, re->current, re->source, to);
-
- path = gtk_tree_path_new ();
- gtk_tree_path_append_index (path, from);
- gtk_tree_model_get_iter (GTK_TREE_MODEL (re->model), &iter, path);
- gtk_tree_path_free (path);
-
- gtk_tree_model_get (GTK_TREE_MODEL (re->model), &iter, 1, &rule, -1);
- g_return_if_fail (rule != NULL);
-
- /* remove and then re-insert the row at the new location */
- gtk_list_store_remove (re->model, &iter);
- gtk_list_store_insert (re->model, &iter, to);
-
- /* set the data on the row */
- gtk_list_store_set (re->model, &iter, 0, rule->name, 1, rule, 2, rule->enabled, -1);
-
- /* select the row */
- selection = gtk_tree_view_get_selection (re->list);
- gtk_tree_selection_select_iter (selection, &iter);
-
- /* scroll to the selected row */
- path = gtk_tree_model_get_path ((GtkTreeModel *) re->model, &iter);
- gtk_tree_view_scroll_to_cell (re->list, path, NULL, FALSE, 0.0, 0.0);
- gtk_tree_path_free (path);
-
- rule_editor_set_sensitive (re);
-}
-
-static void
-rule_top (GtkWidget *widget, RuleEditor *re)
-{
- gint pos;
-
- update_selected_rule(re);
-
- d(printf ("top rule\n"));
- pos = rule_context_get_rank_rule (re->context, re->current, re->source);
- if (pos > 0)
- rule_move (re, pos, 0);
-}
-
-static void
-rule_up (GtkWidget *widget, RuleEditor *re)
-{
- gint pos;
-
- update_selected_rule(re);
-
- d(printf ("up rule\n"));
- pos = rule_context_get_rank_rule (re->context, re->current, re->source);
- if (pos > 0)
- rule_move (re, pos, pos - 1);
-}
-
-static void
-rule_down (GtkWidget *widget, RuleEditor *re)
-{
- gint pos;
-
- update_selected_rule(re);
-
- d(printf ("down rule\n"));
- pos = rule_context_get_rank_rule (re->context, re->current, re->source);
- if (pos >= 0)
- rule_move (re, pos, pos + 1);
-}
-
-static void
-rule_bottom (GtkWidget *widget, RuleEditor *re)
-{
- gint pos;
- gint index = -1, count = 0;
- FilterRule *rule = NULL;
-
- update_selected_rule(re);
-
- d(printf ("bottom rule\n"));
- pos = rule_context_get_rank_rule (re->context, re->current, re->source);
- /* There's probably a better/faster way to get the count of the list here */
- while ((rule = rule_context_next_rule (re->context, rule, re->source))) {
- if (rule == re->current)
- index = count;
- count++;
- }
- count--;
- if (pos >= 0)
- rule_move (re, pos, count);
-}
-
-static struct {
- const gchar *name;
- GCallback func;
-} edit_buttons[] = {
- { "rule_add", G_CALLBACK (rule_add) },
- { "rule_edit", G_CALLBACK (rule_edit) },
- { "rule_delete", G_CALLBACK (rule_delete) },
- { "rule_top", G_CALLBACK (rule_top) },
- { "rule_up", G_CALLBACK (rule_up) },
- { "rule_down", G_CALLBACK (rule_down) },
- { "rule_bottom", G_CALLBACK (rule_bottom) },
-};
-
-static void
-set_sensitive (RuleEditor *re)
-{
- FilterRule *rule = NULL;
- gint index = -1, count = 0;
-
- while ((rule = rule_context_next_rule (re->context, rule, re->source))) {
- if (rule == re->current)
- index = count;
- count++;
- }
-
- d(printf("index = %d count=%d\n", index, count));
-
- count--;
-
- gtk_widget_set_sensitive (GTK_WIDGET (re->priv->buttons[BUTTON_EDIT]), index != -1);
- gtk_widget_set_sensitive (GTK_WIDGET (re->priv->buttons[BUTTON_DELETE]), index != -1);
- gtk_widget_set_sensitive (GTK_WIDGET (re->priv->buttons[BUTTON_TOP]), index > 0);
- gtk_widget_set_sensitive (GTK_WIDGET (re->priv->buttons[BUTTON_UP]), index > 0);
- gtk_widget_set_sensitive (GTK_WIDGET (re->priv->buttons[BUTTON_DOWN]), index >= 0 && index < count);
- gtk_widget_set_sensitive (GTK_WIDGET (re->priv->buttons[BUTTON_BOTTOM]), index >= 0 && index < count);
-}
-
-static void
-dialog_rule_changed (FilterRule *fr, GtkWidget *dialog)
-{
- g_return_if_fail (dialog != NULL);
-
- gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, fr && fr->parts);
-}
-
-static gboolean
-update_selected_rule (RuleEditor *re)
-{
- GtkTreeSelection *selection;
- GtkTreeModel *model;
- GtkTreeIter iter;
-
- selection = gtk_tree_view_get_selection (re->list);
- if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
- gtk_tree_model_get (GTK_TREE_MODEL (re->model), &iter, 1, &re->current, -1);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-cursor_changed (GtkTreeView *treeview, RuleEditor *re)
-{
- if (update_selected_rule(re)) {
- g_return_if_fail (re->current);
-
- rule_editor_set_sensitive (re);
- }
-}
-
-static void
-double_click (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, RuleEditor *re)
-{
- GtkTreeSelection *selection;
- GtkTreeModel *model;
- GtkTreeIter iter;
-
- selection = gtk_tree_view_get_selection (re->list);
- if (gtk_tree_selection_get_selected (selection, &model, &iter))
- gtk_tree_model_get (GTK_TREE_MODEL (re->model), &iter, 1, &re->current, -1);
-
- if (re->current)
- rule_edit ((GtkWidget *) treeview, re);
-}
-
-static void
-set_source (RuleEditor *re, const gchar *source)
-{
- FilterRule *rule = NULL;
- GtkTreeIter iter;
-
- gtk_list_store_clear (re->model);
-
- d(printf("Checking for rules that are of type %s\n", source ? source : "<nil>"));
- while ((rule = rule_context_next_rule (re->context, rule, source)) != NULL) {
- d(printf("Adding row '%s'\n", rule->name));
- gtk_list_store_append (re->model, &iter);
- gtk_list_store_set (re->model, &iter, 0, rule->name, 1, rule, 2, rule->enabled, -1);
- }
-
- g_free (re->source);
- re->source = g_strdup (source);
- re->current = NULL;
- rule_editor_set_sensitive (re);
-}
-
-static void
-rule_able_toggled (GtkCellRendererToggle *renderer, gchar *arg1, gpointer user_data)
-{
- GtkWidget *table = user_data;
- GtkTreeSelection *selection;
- GtkTreeModel *model;
- GtkTreePath *path;
- GtkTreeIter iter;
-
- path = gtk_tree_path_new_from_string (arg1);
- model = gtk_tree_view_get_model (GTK_TREE_VIEW (table));
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (table));
-
- if (gtk_tree_model_get_iter (model, &iter, path)) {
- FilterRule *rule = NULL;
-
- gtk_tree_model_get (model, &iter, 1, &rule, -1);
-
- if (rule) {
- rule->enabled = !rule->enabled;
- gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, rule->enabled, -1);
- }
- }
-
- gtk_tree_path_free (path);
-}
-
-GtkWidget *rule_editor_treeview_new (gchar *widget_name, gchar *string1, gchar *string2,
- gint int1, gint int2);
-
-GtkWidget *
-rule_editor_treeview_new (gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2)
-{
- GtkWidget *table, *scrolled;
- GtkTreeSelection *selection;
- GtkCellRenderer *renderer;
- GtkListStore *model;
- GtkTreeViewColumn *column;
-
- scrolled = gtk_scrolled_window_new (NULL, NULL);
- gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
- GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-
- model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
- table = gtk_tree_view_new_with_model ((GtkTreeModel *) model);
- gtk_tree_view_set_headers_visible ((GtkTreeView *) table, FALSE);
-
- renderer = gtk_cell_renderer_toggle_new ();
- g_object_set (G_OBJECT (renderer), "activatable", TRUE, NULL);
- gtk_tree_view_insert_column_with_attributes ((GtkTreeView *) table, -1,
- _("Enabled"), renderer,
- "active", 2, NULL);
- g_signal_connect (renderer, "toggled", G_CALLBACK (rule_able_toggled), table);
-
- /* hide enable column by default */
- column = gtk_tree_view_get_column (GTK_TREE_VIEW (table), 0);
- gtk_tree_view_column_set_visible (column, FALSE);
-
- renderer = gtk_cell_renderer_text_new ();
- gtk_tree_view_insert_column_with_attributes ((GtkTreeView *) table, -1,
- _("Rule name"), renderer,
- "text", 0, NULL);
-
- selection = gtk_tree_view_get_selection ((GtkTreeView *) table);
- gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
-
- gtk_container_add (GTK_CONTAINER (scrolled), table);
-
- g_object_set_data ((GObject *) scrolled, "table", table);
- g_object_set_data ((GObject *) scrolled, "model", model);
-
- gtk_widget_show (scrolled);
- gtk_widget_show (table);
-
- g_object_unref (model);
-
- return scrolled;
-}
-
-void
-rule_editor_construct (RuleEditor *re, RuleContext *context, GladeXML *gui, const gchar *source, const gchar *label)
-{
- GtkWidget *w;
- GtkWidget *action_area;
- GtkWidget *content_area;
- gint i;
- gchar *tmp;
-
- re->context = context;
- g_object_ref (context);
-
- action_area = gtk_dialog_get_action_area (GTK_DIALOG (re));
- content_area = gtk_dialog_get_content_area (GTK_DIALOG (re));
-
- gtk_window_set_resizable ((GtkWindow *) re, TRUE);
- gtk_window_set_default_size ((GtkWindow *) re, 350, 400);
- gtk_widget_realize ((GtkWidget *) re);
- gtk_container_set_border_width (GTK_CONTAINER (action_area), 12);
-
- w = glade_xml_get_widget(gui, "rule_editor");
- gtk_box_pack_start (GTK_BOX (content_area), w, TRUE, TRUE, 3);
-
- for (i = 0; i < BUTTON_LAST; i++) {
- re->priv->buttons[i] = (GtkButton *) (w = glade_xml_get_widget (gui, edit_buttons[i].name));
- g_signal_connect (w, "clicked", edit_buttons[i].func, re);
- }
-
- w = glade_xml_get_widget (gui, "rule_list");
- re->list = (GtkTreeView *) g_object_get_data ((GObject *) w, "table");
- re->model = (GtkListStore *) g_object_get_data ((GObject *) w, "model");
-
- g_signal_connect (re->list, "cursor-changed", G_CALLBACK (cursor_changed), re);
- g_signal_connect (re->list, "row-activated", G_CALLBACK (double_click), re);
-
- w = glade_xml_get_widget (gui, "rule_label");
- tmp = alloca(strlen(label)+8);
- sprintf(tmp, "<b>%s</b>", label);
- gtk_label_set_label((GtkLabel *)w, tmp);
- gtk_label_set_mnemonic_widget ((GtkLabel *) w, (GtkWidget *) re->list);
-
- g_signal_connect (re, "response", G_CALLBACK (editor_response), re);
- rule_editor_set_source (re, source);
-
- gtk_dialog_set_has_separator ((GtkDialog *) re, FALSE);
- gtk_dialog_add_buttons ((GtkDialog *) re,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_OK, GTK_RESPONSE_OK,
- NULL);
-}
diff --git a/filter/rule-editor.h b/filter/rule-editor.h
deleted file mode 100644
index e5017eac25..0000000000
--- a/filter/rule-editor.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Not Zed <notzed@lostzed.mmc.com.au>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef _RULE_EDITOR_H
-#define _RULE_EDITOR_H
-
-#include <gtk/gtk.h>
-#include <glade/glade.h>
-
-#include "rule-context.h"
-#include "filter-rule.h"
-
-#define RULE_TYPE_EDITOR (rule_editor_get_type ())
-#define RULE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RULE_TYPE_EDITOR, RuleEditor))
-#define RULE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RULE_TYPE_EDITOR, RuleEditorClass))
-#define IS_RULE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RULE_TYPE_EDITOR))
-#define IS_RULE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), RULE_TYPE_EDITOR))
-#define RULE_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RULE_TYPE_EDITOR, RuleEditorClass))
-
-typedef struct _RuleEditor RuleEditor;
-typedef struct _RuleEditorClass RuleEditorClass;
-typedef struct _RuleEditorUndo RuleEditorUndo;
-
-struct _RuleEditor {
- GtkDialog parent_object;
-
- GtkListStore *model;
- GtkTreeView *list;
-
- RuleContext *context;
- FilterRule *current;
- FilterRule *edit; /* for editing/adding rules, so we only do 1 at a time */
-
- GtkWidget *dialog;
-
- gchar *source;
-
- struct _RuleEditorUndo *undo_log; /* cancel/undo log */
- guint undo_active:1; /* we're performing undo */
-
- struct _RuleEditorPrivate *priv;
-};
-
-struct _RuleEditorClass {
- GtkDialogClass parent_class;
-
- /* virtual methods */
- void (*set_sensitive) (RuleEditor *);
- void (*set_source) (RuleEditor *, const gchar *source);
-
- FilterRule *(*create_rule) (RuleEditor *);
-
- /* signals */
-};
-
-enum {
- RULE_EDITOR_LOG_EDIT,
- RULE_EDITOR_LOG_ADD,
- RULE_EDITOR_LOG_REMOVE,
- RULE_EDITOR_LOG_RANK
-};
-
-struct _RuleEditorUndo {
- struct _RuleEditorUndo *next;
-
- guint type;
- FilterRule *rule;
- gint rank;
- gint newrank;
-};
-
-GType rule_editor_get_type(void);
-RuleEditor *rule_editor_new(RuleContext *rc, const gchar *source, const gchar *label);
-
-void rule_editor_construct(RuleEditor *re, RuleContext *context, GladeXML *gui, const gchar *source, const gchar *label);
-
-/* methods */
-void rule_editor_set_source(RuleEditor *re, const gchar *source);
-/* calculates the sensitivity of the editor */
-void rule_editor_set_sensitive(RuleEditor *re);
-/* used internally to create a new rule appropriate for the editor */
-struct _FilterRule *rule_editor_create_rule(RuleEditor *re);
-
-#endif /* ! _RULE_EDITOR_H */