From 2b16aef84141800099f859e72d05e1e6bf8e02dd Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Sun, 25 Oct 2009 09:17:23 -0400 Subject: Cleanup and rename filter classes. --- filter/Makefile.am | 72 +-- filter/e-filter-code.c | 126 +++++ filter/e-filter-code.h | 68 +++ filter/e-filter-color.c | 180 +++++++ filter/e-filter-color.h | 70 +++ filter/e-filter-datespec.c | 492 +++++++++++++++++++ filter/e-filter-datespec.h | 87 ++++ filter/e-filter-element.c | 472 +++++++++++++++++++ filter/e-filter-element.h | 118 +++++ filter/e-filter-file.c | 278 +++++++++++ filter/e-filter-file.h | 74 +++ filter/e-filter-input.c | 322 +++++++++++++ filter/e-filter-input.h | 74 +++ filter/e-filter-int.c | 261 +++++++++++ filter/e-filter-int.h | 77 +++ filter/e-filter-option.c | 514 ++++++++++++++++++++ filter/e-filter-option.h | 92 ++++ filter/e-filter-part.c | 532 +++++++++++++++++++++ filter/e-filter-part.h | 104 +++++ filter/e-filter-rule.c | 1116 ++++++++++++++++++++++++++++++++++++++++++++ filter/e-filter-rule.h | 161 +++++++ filter/e-rule-context.c | 988 +++++++++++++++++++++++++++++++++++++++ filter/e-rule-context.h | 215 +++++++++ filter/e-rule-editor.c | 912 ++++++++++++++++++++++++++++++++++++ filter/e-rule-editor.h | 122 +++++ filter/filter-code.c | 136 ------ filter/filter-code.h | 56 --- filter/filter-colour.c | 219 --------- filter/filter-colour.h | 58 --- filter/filter-datespec.c | 516 -------------------- filter/filter-datespec.h | 75 --- filter/filter-element.c | 336 ------------- filter/filter-element.h | 93 ---- filter/filter-file.c | 292 ------------ filter/filter-file.h | 67 --- filter/filter-input.c | 360 -------------- filter/filter-input.h | 62 --- filter/filter-int.c | 270 ----------- filter/filter-int.h | 62 --- filter/filter-option.c | 497 -------------------- filter/filter-option.h | 75 --- filter/filter-part.c | 541 --------------------- filter/filter-part.h | 89 ---- filter/filter-rule.c | 1044 ----------------------------------------- filter/filter-rule.h | 134 ------ filter/rule-context.c | 952 ------------------------------------- filter/rule-context.h | 149 ------ filter/rule-editor.c | 880 ---------------------------------- filter/rule-editor.h | 104 ----- 49 files changed, 7491 insertions(+), 7103 deletions(-) create mode 100644 filter/e-filter-code.c create mode 100644 filter/e-filter-code.h create mode 100644 filter/e-filter-color.c create mode 100644 filter/e-filter-color.h create mode 100644 filter/e-filter-datespec.c create mode 100644 filter/e-filter-datespec.h create mode 100644 filter/e-filter-element.c create mode 100644 filter/e-filter-element.h create mode 100644 filter/e-filter-file.c create mode 100644 filter/e-filter-file.h create mode 100644 filter/e-filter-input.c create mode 100644 filter/e-filter-input.h create mode 100644 filter/e-filter-int.c create mode 100644 filter/e-filter-int.h create mode 100644 filter/e-filter-option.c create mode 100644 filter/e-filter-option.h create mode 100644 filter/e-filter-part.c create mode 100644 filter/e-filter-part.h create mode 100644 filter/e-filter-rule.c create mode 100644 filter/e-filter-rule.h create mode 100644 filter/e-rule-context.c create mode 100644 filter/e-rule-context.h create mode 100644 filter/e-rule-editor.c create mode 100644 filter/e-rule-editor.h delete mode 100644 filter/filter-code.c delete mode 100644 filter/filter-code.h delete mode 100644 filter/filter-colour.c delete mode 100644 filter/filter-colour.h delete mode 100644 filter/filter-datespec.c delete mode 100644 filter/filter-datespec.h delete mode 100644 filter/filter-element.c delete mode 100644 filter/filter-element.h delete mode 100644 filter/filter-file.c delete mode 100644 filter/filter-file.h delete mode 100644 filter/filter-input.c delete mode 100644 filter/filter-input.h delete mode 100644 filter/filter-int.c delete mode 100644 filter/filter-int.h delete mode 100644 filter/filter-option.c delete mode 100644 filter/filter-option.h delete mode 100644 filter/filter-part.c delete mode 100644 filter/filter-part.h delete mode 100644 filter/filter-rule.c delete mode 100644 filter/filter-rule.h delete mode 100644 filter/rule-context.c delete mode 100644 filter/rule-context.h delete mode 100644 filter/rule-editor.c delete mode 100644 filter/rule-editor.h (limited to 'filter') 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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * 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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * 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/e-filter-datespec.c b/filter/e-filter-datespec.c new file mode 100644 index 0000000000..1a34cce525 --- /dev/null +++ b/filter/e-filter-datespec.c @@ -0,0 +1,492 @@ +/* + * 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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 E_FILTER_DATESPEC_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_FILTER_DATESPEC, EFilterDatespecPrivate)) + +#define d(x) + +typedef struct { + guint32 seconds; + const gchar *past_singular; + const gchar *past_plural; + const gchar *future_singular; + const gchar *future_plural; + gfloat max; +} timespan; + +#ifdef ngettext +#undef ngettext +#endif + +/* This is a nasty hack trying to keep both ngettext function and xgettext tool happy */ +/* It *will* cause problems if ngettext is a macro */ +#define ngettext(a, b) a, b + +static const timespan timespans[] = { + { 1, ngettext("1 second ago", "%d seconds ago"), ngettext("1 second in the future", "%d seconds in the future"), 59.0 }, + { 60, ngettext("1 minute ago", "%d minutes ago"), ngettext("1 minute in the future", "%d minutes in the future"), 59.0 }, + { 3600, ngettext("1 hour ago", "%d hours ago"), ngettext("1 hour in the future", "%d hours in the future"), 23.0 }, + { 86400, ngettext("1 day ago", "%d days ago"), ngettext("1 day in the future", "%d days in the future"), 31.0 }, + { 604800, ngettext("1 week ago", "%d weeks ago"), ngettext("1 week in the future", "%d weeks in the future"), 52.0 }, + { 2419200, ngettext("1 month ago", "%d months ago"), ngettext("1 month in the future", "%d months in the future"), 12.0 }, + { 31557600, ngettext("1 year ago", "%d years ago"), ngettext("1 year in the future", "%d years in the future"), 1000.0 }, +}; + +/* now we let the compiler see the real function call */ +#undef ngettext + +#define DAY_INDEX 3 + +struct _EFilterDatespecPrivate { + GtkWidget *label_button; + GtkWidget *notebook_type, *combobox_type, *calendar_specify, *spin_relative, *combobox_relative, *combobox_past_future; + EFilterDatespecType type; + gint span; +}; + +static gpointer parent_class; + +static gint +get_best_span (time_t val) +{ + gint i; + + for (i=G_N_ELEMENTS (timespans)-1;i>=0;i--) { + if (val % timespans[i].seconds == 0) + return i; + } + + return 0; +} + +/* sets button label */ +static void +set_button (EFilterDatespec *fds) +{ + gchar buf[128]; + gchar *label = buf; + + switch (fds->type) { + case FDST_UNKNOWN: + label = _(""); + break; + case FDST_NOW: + label = _("now"); + break; + case FDST_SPECIFIED: { + struct tm tm; + + localtime_r(&fds->value, &tm); + /* strftime for date filter display, only needs to show a day date (i.e. no time) */ + strftime(buf, sizeof(buf), _("%d-%b-%Y"), &tm); + break; } + case FDST_X_AGO: + if (fds->value == 0) + label = _("now"); + else { + gint span, count; + + span = get_best_span(fds->value); + count = fds->value / timespans[span].seconds; + sprintf(buf, ngettext(timespans[span].past_singular, timespans[span].past_plural, count), count); + } + break; + case FDST_X_FUTURE: + if (fds->value == 0) + label = _("now"); + else { + gint span, count; + + span = get_best_span(fds->value); + count = fds->value / timespans[span].seconds; + sprintf(buf, ngettext(timespans[span].future_singular, timespans[span].future_plural, count), count); + } + break; + } + + gtk_label_set_text((GtkLabel *)fds->priv->label_button, label); +} + +static void +get_values (EFilterDatespec *fds) +{ + EFilterDatespecPrivate *p = E_FILTER_DATESPEC_GET_PRIVATE (fds); + + switch (fds->priv->type) { + case FDST_SPECIFIED: { + guint year, month, day; + struct tm tm; + + gtk_calendar_get_date((GtkCalendar *)p->calendar_specify, &year, &month, &day); + memset(&tm, 0, sizeof(tm)); + tm.tm_mday = day; + tm.tm_year = year - 1900; + tm.tm_mon = month; + fds->value = mktime(&tm); + /* what about timezone? */ + break; } + case FDST_X_FUTURE: + case FDST_X_AGO: { + gint val; + + val = gtk_spin_button_get_value_as_int((GtkSpinButton *)p->spin_relative); + fds->value = timespans[p->span].seconds * val; + break; } + case FDST_NOW: + default: + break; + } + + fds->type = p->type; +} + +static void +set_values (EFilterDatespec *fds) +{ + gint note_type; + + EFilterDatespecPrivate *p = E_FILTER_DATESPEC_GET_PRIVATE (fds); + + p->type = fds->type==FDST_UNKNOWN ? FDST_NOW : fds->type; + + note_type = p->type==FDST_X_FUTURE ? FDST_X_AGO : p->type; /* FUTURE and AGO use the same notebook pages/etc. */ + + switch (p->type) { + case FDST_NOW: + case FDST_UNKNOWN: + /* noop */ + break; + case FDST_SPECIFIED: + { + struct tm tm; + + localtime_r(&fds->value, &tm); + gtk_calendar_select_month((GtkCalendar*)p->calendar_specify, tm.tm_mon, tm.tm_year + 1900); + gtk_calendar_select_day((GtkCalendar*)p->calendar_specify, tm.tm_mday); + break; + } + case FDST_X_AGO: + p->span = get_best_span(fds->value); + gtk_spin_button_set_value((GtkSpinButton*)p->spin_relative, fds->value/timespans[p->span].seconds); + gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_relative), p->span); + gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_past_future), 0); + break; + case FDST_X_FUTURE: + p->span = get_best_span(fds->value); + gtk_spin_button_set_value((GtkSpinButton*)p->spin_relative, fds->value/timespans[p->span].seconds); + gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_relative), p->span); + gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_past_future), 1); + break; + } + + gtk_notebook_set_current_page ((GtkNotebook*) p->notebook_type, note_type); + gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_type), note_type); +} + +static void +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, EFilterDatespec *fds) +{ + fds->priv->span = gtk_combo_box_get_active (combobox); +} + +static void +set_combobox_past_future (GtkComboBox *combobox, EFilterDatespec *fds) +{ + if (gtk_combo_box_get_active (combobox) == 0) + fds->type = fds->priv->type = FDST_X_AGO; + else + fds->type = fds->priv->type = FDST_X_FUTURE; +} + +static void +button_clicked (GtkButton *button, EFilterDatespec *fds) +{ + EFilterDatespecPrivate *p = E_FILTER_DATESPEC_GET_PRIVATE (fds); + GtkWidget *content_area; + GtkWidget *toplevel; + GtkDialog *dialog; + GladeXML *gui; + gchar *filter_glade = g_build_filename (EVOLUTION_GLADEDIR, + "filter.glade", + NULL); + + gui = glade_xml_new (filter_glade, "filter_datespec", NULL); + g_free (filter_glade); + toplevel = glade_xml_get_widget (gui, "filter_datespec"); + + dialog = (GtkDialog *) gtk_dialog_new (); + gtk_window_set_title ((GtkWindow *) dialog, _("Select a time to compare against")); + 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); + + p->notebook_type = glade_xml_get_widget (gui, "notebook_type"); + p->combobox_type = glade_xml_get_widget (gui, "combobox_type"); + p->calendar_specify = glade_xml_get_widget (gui, "calendar_specify"); + p->spin_relative = glade_xml_get_widget (gui, "spin_relative"); + p->combobox_relative = glade_xml_get_widget (gui, "combobox_relative"); + p->combobox_past_future = glade_xml_get_widget (gui, "combobox_past_future"); + + set_values (fds); + + g_signal_connect (p->combobox_type, "changed", G_CALLBACK (set_combobox_type), fds); + g_signal_connect (p->combobox_relative, "changed", G_CALLBACK (set_combobox_relative), fds); + g_signal_connect (p->combobox_past_future, "changed", G_CALLBACK (set_combobox_past_future), fds); + + content_area = gtk_dialog_get_content_area (dialog); + gtk_box_pack_start (GTK_BOX (content_area), toplevel, TRUE, TRUE, 3); + + if (gtk_dialog_run (dialog) == GTK_RESPONSE_OK) { + get_values (fds); + set_button (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 * +filter_datespec_get_widget (EFilterElement *element) +{ + EFilterDatespec *fds = E_FILTER_DATESPEC (element); + GtkWidget *button; + + fds->priv->label_button = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (fds->priv->label_button), 0.5, 0.5); + set_button(fds); + + button = gtk_button_new(); + gtk_container_add (GTK_CONTAINER (button), fds->priv->label_button); + g_signal_connect (button, "clicked", G_CALLBACK (button_clicked), fds); + + gtk_widget_show (button); + gtk_widget_show (fds->priv->label_button); + + return button; +} + +static void +filter_datespec_format_sexp (EFilterElement *element, + GString *out) +{ + EFilterDatespec *fds = E_FILTER_DATESPEC (element); + + switch (fds->type) { + case FDST_UNKNOWN: + g_warning ("user hasn't selected a datespec yet!"); + /* fall through */ + case FDST_NOW: + g_string_append (out, "(get-current-date)"); + break; + case FDST_SPECIFIED: + 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); + break; + case FDST_X_FUTURE: + 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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_FILTER_DATESPEC_H +#define E_FILTER_DATESPEC_H + +#include +#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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_FILTER_ELEMENT_H +#define E_FILTER_ELEMENT_H + +#include +#include +#include + +#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 + * + * + * Authors: + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include + +#include +#include + +#include +#include +#include + +#include + +#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 + * + * + * Authors: + * Jeffrey Stedfast + * + * 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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include + +#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 (®expat, 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, ®expat, 0, 0); + regmsg = g_malloc0 (reglen + 1); + regerror (regerr, ®expat, regmsg, reglen); + + e_error_run (error_parent, "filter:bad-regexp", pattern, regmsg, NULL); + g_free (regmsg); + + valid = FALSE; + } + + regfree (®expat); + } + + 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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * 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 + * + * + * Authors: + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#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 + * + * + * Authors: + * Jeffrey Stedfast + * + * 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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include + +#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 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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * 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 + * + * + * Authors: + * Not Zed + * Jepartrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_FILTER_PART_H +#define E_FILTER_PART_H + +#include +#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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#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 ("%s", _("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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_FILTER_RULE_H +#define E_FILTER_RULE_H + +#include + +#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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include + +#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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_RULE_CONTEXT_H +#define E_RULE_CONTEXT_H + +#include +#include + +#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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* for getenv only, remove when getenv need removed */ +#include +#include + +#include + +#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, "%s", 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 + * + * + * Authors: + * Not Zed + * Jeffrey Stedfast + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_RULE_EDITOR_H +#define E_RULE_EDITOR_H + +#include +#include + +#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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * 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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * 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.c b/filter/filter-datespec.c deleted file mode 100644 index a0e981d111..0000000000 --- a/filter/filter-datespec.c +++ /dev/null @@ -1,516 +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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include - -#include "filter-datespec.h" -#include "libedataserver/e-sexp.h" -#include "e-util/e-error.h" -#include "e-util/e-util-private.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 *); - -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) - -typedef struct _timespan { - guint32 seconds; - const gchar *past_singular; - const gchar *past_plural; - const gchar *future_singular; - const gchar *future_plural; - gfloat max; -} timespan; - -#ifdef ngettext -#undef ngettext -#endif - -/* This is a nasty hack trying to keep both ngettext function and xgettext tool happy */ -/* It *will* cause problems if ngettext is a macro */ -#define ngettext(a, b) a, b - -static const timespan timespans[] = { - { 1, ngettext("1 second ago", "%d seconds ago"), ngettext("1 second in the future", "%d seconds in the future"), 59.0 }, - { 60, ngettext("1 minute ago", "%d minutes ago"), ngettext("1 minute in the future", "%d minutes in the future"), 59.0 }, - { 3600, ngettext("1 hour ago", "%d hours ago"), ngettext("1 hour in the future", "%d hours in the future"), 23.0 }, - { 86400, ngettext("1 day ago", "%d days ago"), ngettext("1 day in the future", "%d days in the future"), 31.0 }, - { 604800, ngettext("1 week ago", "%d weeks ago"), ngettext("1 week in the future", "%d weeks in the future"), 52.0 }, - { 2419200, ngettext("1 month ago", "%d months ago"), ngettext("1 month in the future", "%d months in the future"), 12.0 }, - { 31557600, ngettext("1 year ago", "%d years ago"), ngettext("1 year in the future", "%d years in the future"), 1000.0 }, -}; - -/* now we let the compiler see the real function call */ -#undef ngettext - -#define DAY_INDEX 3 - -struct _FilterDatespecPrivate { - GtkWidget *label_button; - GtkWidget *notebook_type, *combobox_type, *calendar_specify, *spin_relative, *combobox_relative, *combobox_past_future; - FilterDatespec_type 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 gint -get_best_span (time_t val) -{ - gint i; - - for (i=G_N_ELEMENTS (timespans)-1;i>=0;i--) { - if (val % timespans[i].seconds == 0) - return i; - } - - return 0; -} - -/* sets button label */ -static void -set_button (FilterDatespec *fds) -{ - gchar buf[128]; - gchar *label = buf; - - switch (fds->type) { - case FDST_UNKNOWN: - label = _(""); - break; - case FDST_NOW: - label = _("now"); - break; - case FDST_SPECIFIED: { - struct tm tm; - - localtime_r(&fds->value, &tm); - /* strftime for date filter display, only needs to show a day date (i.e. no time) */ - strftime(buf, sizeof(buf), _("%d-%b-%Y"), &tm); - break; } - case FDST_X_AGO: - if (fds->value == 0) - label = _("now"); - else { - gint span, count; - - span = get_best_span(fds->value); - count = fds->value / timespans[span].seconds; - sprintf(buf, ngettext(timespans[span].past_singular, timespans[span].past_plural, count), count); - } - break; - case FDST_X_FUTURE: - if (fds->value == 0) - label = _("now"); - else { - gint span, count; - - span = get_best_span(fds->value); - count = fds->value / timespans[span].seconds; - sprintf(buf, ngettext(timespans[span].future_singular, timespans[span].future_plural, count), count); - } - break; - } - - gtk_label_set_text((GtkLabel *)fds->priv->label_button, label); -} - -static void -get_values (FilterDatespec *fds) -{ - struct _FilterDatespecPrivate *p = PRIV(fds); - - switch (fds->priv->type) { - case FDST_SPECIFIED: { - guint year, month, day; - struct tm tm; - - gtk_calendar_get_date((GtkCalendar *)p->calendar_specify, &year, &month, &day); - memset(&tm, 0, sizeof(tm)); - tm.tm_mday = day; - tm.tm_year = year - 1900; - tm.tm_mon = month; - fds->value = mktime(&tm); - /* what about timezone? */ - break; } - case FDST_X_FUTURE: - case FDST_X_AGO: { - gint val; - - val = gtk_spin_button_get_value_as_int((GtkSpinButton *)p->spin_relative); - fds->value = timespans[p->span].seconds * val; - break; } - case FDST_NOW: - default: - break; - } - - fds->type = p->type; -} - -static void -set_values (FilterDatespec *fds) -{ - gint note_type; - - struct _FilterDatespecPrivate *p = PRIV(fds); - - p->type = fds->type==FDST_UNKNOWN ? FDST_NOW : fds->type; - - note_type = p->type==FDST_X_FUTURE ? FDST_X_AGO : p->type; /* FUTURE and AGO use the same notebook pages/etc. */ - - switch (p->type) { - case FDST_NOW: - case FDST_UNKNOWN: - /* noop */ - break; - case FDST_SPECIFIED: - { - struct tm tm; - - localtime_r(&fds->value, &tm); - gtk_calendar_select_month((GtkCalendar*)p->calendar_specify, tm.tm_mon, tm.tm_year + 1900); - gtk_calendar_select_day((GtkCalendar*)p->calendar_specify, tm.tm_mday); - break; - } - case FDST_X_AGO: - p->span = get_best_span(fds->value); - gtk_spin_button_set_value((GtkSpinButton*)p->spin_relative, fds->value/timespans[p->span].seconds); - gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_relative), p->span); - gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_past_future), 0); - break; - case FDST_X_FUTURE: - p->span = get_best_span(fds->value); - gtk_spin_button_set_value((GtkSpinButton*)p->spin_relative, fds->value/timespans[p->span].seconds); - gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_relative), p->span); - gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_past_future), 1); - break; - } - - gtk_notebook_set_current_page ((GtkNotebook*) p->notebook_type, note_type); - gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox_type), note_type); -} - -static void -set_combobox_type (GtkComboBox *combobox, FilterDatespec *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) -{ - fds->priv->span = gtk_combo_box_get_active (combobox); -} - -static void -set_combobox_past_future (GtkComboBox *combobox, FilterDatespec *fds) -{ - if (gtk_combo_box_get_active (combobox) == 0) - fds->type = fds->priv->type = FDST_X_AGO; - else - fds->type = fds->priv->type = FDST_X_FUTURE; -} - -static void -button_clicked (GtkButton *button, FilterDatespec *fds) -{ - struct _FilterDatespecPrivate *p = PRIV(fds); - GtkWidget *content_area; - GtkWidget *toplevel; - GtkDialog *dialog; - GladeXML *gui; - gchar *filter_glade = g_build_filename (EVOLUTION_GLADEDIR, - "filter.glade", - NULL); - - gui = glade_xml_new (filter_glade, "filter_datespec", NULL); - g_free (filter_glade); - toplevel = glade_xml_get_widget (gui, "filter_datespec"); - - dialog = (GtkDialog *) gtk_dialog_new (); - gtk_window_set_title ((GtkWindow *) dialog, _("Select a time to compare against")); - 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); - - p->notebook_type = glade_xml_get_widget (gui, "notebook_type"); - p->combobox_type = glade_xml_get_widget (gui, "combobox_type"); - p->calendar_specify = glade_xml_get_widget (gui, "calendar_specify"); - p->spin_relative = glade_xml_get_widget (gui, "spin_relative"); - p->combobox_relative = glade_xml_get_widget (gui, "combobox_relative"); - p->combobox_past_future = glade_xml_get_widget (gui, "combobox_past_future"); - - set_values (fds); - - g_signal_connect (p->combobox_type, "changed", G_CALLBACK (set_combobox_type), fds); - g_signal_connect (p->combobox_relative, "changed", G_CALLBACK (set_combobox_relative), fds); - g_signal_connect (p->combobox_past_future, "changed", G_CALLBACK (set_combobox_past_future), fds); - - content_area = gtk_dialog_get_content_area (dialog); - gtk_box_pack_start (GTK_BOX (content_area), toplevel, TRUE, TRUE, 3); - - if (gtk_dialog_run (dialog) == GTK_RESPONSE_OK) { - get_values (fds); - set_button (fds); - } - - gtk_widget_destroy ((GtkWidget *)dialog); -} - -static GtkWidget * -get_widget (FilterElement *fe) -{ - FilterDatespec *fds = (FilterDatespec *)fe; - GtkWidget *button; - - fds->priv->label_button = gtk_label_new (""); - gtk_misc_set_alignment (GTK_MISC (fds->priv->label_button), 0.5, 0.5); - set_button(fds); - - button = gtk_button_new(); - gtk_container_add (GTK_CONTAINER (button), fds->priv->label_button); - g_signal_connect (button, "clicked", G_CALLBACK (button_clicked), fds); - - gtk_widget_show (button); - gtk_widget_show (fds->priv->label_button); - - return button; -} - -static void -build_code (FilterElement *fe, GString *out, struct _FilterPart *fp) -{ - return; -} - -static void -format_sexp (FilterElement *fe, GString *out) -{ - FilterDatespec *fds = (FilterDatespec *)fe; - - switch (fds->type) { - case FDST_UNKNOWN: - g_warning ("user hasn't selected a datespec yet!"); - /* fall through */ - case FDST_NOW: - g_string_append (out, "(get-current-date)"); - break; - case FDST_SPECIFIED: - 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); - break; - case FDST_X_FUTURE: - g_string_append_printf (out, "(+ (get-current-date) %d)", (gint) fds->value); - break; - } -} 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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef _FILTER_DATESPEC_H -#define _FILTER_DATESPEC_H - -#include -#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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef _FILTER_ELEMENT_H -#define _FILTER_ELEMENT_H - -#include -#include -#include - -#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 - * - * - * Authors: - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#include - -#include -#include - -#include -#include -#include - -#include - -#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 - * - * - * Authors: - * Jeffrey Stedfast - * - * 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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include - -#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 (®expat, 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, ®expat, 0, 0); - regmsg = g_malloc0 (reglen + 1); - regerror (regerr, ®expat, regmsg, reglen); - - e_error_run (error_parent, "filter:bad-regexp", pattern, regmsg, NULL); - g_free (regmsg); - - valid = FALSE; - } - - regfree (®expat); - } - - 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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * 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 - * - * - * Authors: - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include -#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 - * - * - * Authors: - * Jeffrey Stedfast - * - * 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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include -#include -#include - -#include "filter-option.h" -#include "filter-part.h" -#include - -#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 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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * 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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include -#include - -#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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef _FILTER_PART_H -#define _FILTER_PART_H - -#include -#include -#include - -#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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include -#include - -#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("%s", _("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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef _FILTER_RULE_H -#define _FILTER_RULE_H - -#include -#include - -#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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include - -#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:"")); - - 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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef _RULE_CONTEXT_H -#define _RULE_CONTEXT_H - -#include -#include -#include - -#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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -/* for getenv only, remove when getenv need removed */ -#include -#include - -#include - -#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 : "")); - 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, "%s", 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 - * - * - * Authors: - * Not Zed - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef _RULE_EDITOR_H -#define _RULE_EDITOR_H - -#include -#include - -#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 */ -- cgit