aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mail/ChangeLog26
-rw-r--r--mail/Makefile.am35
-rw-r--r--mail/em-filter-context.c297
-rw-r--r--mail/em-filter-context.h57
-rw-r--r--mail/em-filter-editor.c164
-rw-r--r--mail/em-filter-editor.h56
-rw-r--r--mail/em-filter-folder-element.c268
-rw-r--r--mail/em-filter-folder-element.h53
-rw-r--r--mail/em-filter-i18n.h65
-rw-r--r--mail/em-filter-rule.c545
-rw-r--r--mail/em-filter-rule.h57
-rw-r--r--mail/em-filter-source-element.c377
-rw-r--r--mail/em-filter-source-element.h52
-rw-r--r--mail/em-folder-browser.c6
-rw-r--r--mail/em-folder-tree.c6
-rw-r--r--mail/em-search-context.c112
-rw-r--r--mail/em-search-context.h51
-rw-r--r--mail/em-utils.c16
-rw-r--r--mail/em-vfolder-context.c113
-rw-r--r--mail/em-vfolder-context.h50
-rw-r--r--mail/em-vfolder-editor.c123
-rw-r--r--mail/em-vfolder-editor.h51
-rw-r--r--mail/em-vfolder-rule.c643
-rw-r--r--mail/em-vfolder-rule.h67
-rw-r--r--mail/filtertypes.xml746
-rw-r--r--mail/importers/netscape-importer.c50
-rw-r--r--mail/mail-autofilter.c42
-rw-r--r--mail/mail-autofilter.h8
-rw-r--r--mail/mail-component.c14
-rw-r--r--mail/mail-config.glade278
-rw-r--r--mail/mail-errors.xml17
-rw-r--r--mail/mail-errors.xml.h14
-rw-r--r--mail/mail-ops.c28
-rw-r--r--mail/mail-send-recv.c4
-rw-r--r--mail/mail-session.c8
-rw-r--r--mail/mail-tools.c4
-rw-r--r--mail/mail-vfolder.c46
-rw-r--r--mail/mail-vfolder.h4
-rw-r--r--mail/searchtypes.xml529
-rw-r--r--mail/vfoldertypes.xml424
40 files changed, 5381 insertions, 125 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog
index f0631393ba..5284eb2d7e 100644
--- a/mail/ChangeLog
+++ b/mail/ChangeLog
@@ -2,6 +2,32 @@
** See #59885.
+ ** Moved all of the mail specific filtering stuff from filter/* to
+ here. Renamed appropriately into em* space, etc.
+
+ * em-filter-folder-element.c (emff_copy_value): implement for folders.
+
+ * em-vfolder-rule.c (get_widget): read the vfolder glade from
+ mail-config.glade.
+
+ * mail-config.glade: moved the vfolder source selector here.
+
+ * em-search-context.c: new mail search specific rule context.
+
+ * mail-component.c (setup_search_context): use the new
+ em_search_context.
+
+ * vfolder-rule.c (validate): change error to mail context.
+
+ * filter-folder.c (validate): change error to mail context.
+
+ * Makefile.am (em-filter-i18n.h): added rule for i18n of mail
+ filter type stuff.
+ (libevolution_mail_la_SOURCES): added in the filter and vfolder
+ rule stuff specific to mail.
+
+ ** See #59885.
+
* em-format-html-quote.[ch]: remove and remove from build, not
used.
diff --git a/mail/Makefile.am b/mail/Makefile.am
index 8e31f7a068..29e4f303e9 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -66,6 +66,16 @@ libevolution_mail_la_SOURCES = \
em-mailer-prefs.h \
em-inline-filter.c \
em-inline-filter.h \
+ em-filter-context.c \
+ em-filter-context.h \
+ em-filter-editor.c \
+ em-filter-editor.h \
+ em-filter-rule.c \
+ em-filter-rule.h \
+ em-filter-folder-element.c \
+ em-filter-folder-element.h \
+ em-filter-source-element.h \
+ em-filter-source-element.c \
em-folder-properties.c \
em-folder-properties.h \
em-folder-selection.c \
@@ -104,6 +114,8 @@ libevolution_mail_la_SOURCES = \
em-popup.h \
em-utils.c \
em-utils.h \
+ em-search-context.c \
+ em-search-context.h \
em-subscribe-editor.c \
em-subscribe-editor.h \
em-sync-stream.c \
@@ -116,6 +128,12 @@ libevolution_mail_la_SOURCES = \
em-junk-plugin.h \
em-html-stream.c \
em-html-stream.h \
+ em-vfolder-context.c \
+ em-vfolder-context.h \
+ em-vfolder-editor.c \
+ em-vfolder-editor.h \
+ em-vfolder-rule.c \
+ em-vfolder-rule.h \
mail-account-editor.c \
mail-account-editor.h \
mail-account-gui.c \
@@ -181,6 +199,8 @@ libevolution_mail_la_LIBADD = \
libevolution_mail_la_LDFLAGS = \
-avoid-version -module
+libevolution_mail_la_DEPENDENCIES = em-filter-i18n.h
+
# .server files
server_in_files = GNOME_Evolution_Mail.server.in.in
@@ -189,6 +209,8 @@ server_DATA = $(server_in_files:.server.in.in=_$(BASE_VERSION).server)
@INTLTOOL_SERVER_RULE@
# Misc data to install
+filterdir = $(privdatadir)
+filter_DATA = filtertypes.xml vfoldertypes.xml searchtypes.xml
error_DATA = mail-errors.xml
error_i18n = $(error_DATA:.xml=.xml.h)
@@ -196,6 +218,12 @@ errordir = $(privdatadir)/errors
%.xml.h: %.xml
$(top_builddir)/e-util/e-error-tool $^
+em-filter-i18n.h: filtertypes.xml vfoldertypes.xml searchtypes.xml
+ echo "/* Automatically generated. Do not edit. */" > $@; \
+ cat $(srcdir)/filtertypes.xml $(srcdir)/vfoldertypes.xml $(srcdir)/searchtypes.xml | \
+ sed -n -e 's:.*<title>\(.*\)</title>:char *s = N_("\1");:p' | \
+ sort -u >> $@
+
glade_DATA = mail-config.glade subscribe-dialog.glade message-tags.glade mail-search.glade mail-security.glade
MARSHAL_GENERATED = em-marshal.c em-marshal.h
@EVO_MARSHAL_RULE@
@@ -211,8 +239,11 @@ EXTRA_DIST = \
$(glade_DATA) \
$(schema_DATA) \
$(server_in_files) \
- $(etspec_DATA)
-
+ $(etspec_DATA) \
+ filtertypes.xml \
+ vfoldertypes.xml \
+ searchtypes.xml \
+ em-filter-i18n.h
# Purify support
diff --git a/mail/em-filter-context.c b/mail/em-filter-context.c
new file mode 100644
index 0000000000..84ffc00a55
--- /dev/null
+++ b/mail/em-filter-context.c
@@ -0,0 +1,297 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "em-filter-context.h"
+#include "em-filter-rule.h"
+#include "filter/filter-option.h"
+#include "filter/filter-int.h"
+#include "em-filter-source-element.h"
+
+/* For poking into filter-folder guts */
+#include "em-filter-folder-element.h"
+
+#define d(x)
+
+static void em_filter_context_class_init(EMFilterContextClass *klass);
+static void em_filter_context_init(EMFilterContext *fc);
+static void em_filter_context_finalise(GObject *obj);
+
+static GList *filter_rename_uri(RuleContext *rc, const char *olduri, const char *newuri, GCompareFunc cmp);
+static GList *filter_delete_uri(RuleContext *rc, const char *uri, GCompareFunc cmp);
+static FilterElement *filter_new_element(RuleContext *rc, const char *name);
+
+static RuleContextClass *parent_class = NULL;
+
+GType
+em_filter_context_get_type(void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof(EMFilterContextClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) em_filter_context_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(EMFilterContext),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) em_filter_context_init,
+ };
+
+ type = g_type_register_static(RULE_TYPE_CONTEXT, "EMFilterContext", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+em_filter_context_class_init(EMFilterContextClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ RuleContextClass *rc_class = RULE_CONTEXT_CLASS(klass);
+
+ parent_class = g_type_class_ref(RULE_TYPE_CONTEXT);
+
+ object_class->finalize = em_filter_context_finalise;
+
+ /* override methods */
+ rc_class->rename_uri = filter_rename_uri;
+ rc_class->delete_uri = filter_delete_uri;
+ rc_class->new_element = filter_new_element;
+}
+
+static void
+em_filter_context_init(EMFilterContext *fc)
+{
+ rule_context_add_part_set((RuleContext *) fc, "partset", filter_part_get_type(),
+ rule_context_add_part, rule_context_next_part);
+ rule_context_add_part_set((RuleContext *) fc, "actionset", filter_part_get_type(),
+ (RCPartFunc) em_filter_context_add_action,
+ (RCNextPartFunc) em_filter_context_next_action);
+
+ rule_context_add_rule_set((RuleContext *) fc, "ruleset", em_filter_rule_get_type(),
+ (RCRuleFunc) rule_context_add_rule, rule_context_next_rule);
+}
+
+static void
+em_filter_context_finalise(GObject *obj)
+{
+ EMFilterContext *fc = (EMFilterContext *)obj;
+
+ g_list_foreach(fc->actions, (GFunc)g_object_unref, NULL);
+ g_list_free(fc->actions);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+/**
+ * em_filter_context_new:
+ *
+ * Create a new EMFilterContext object.
+ *
+ * Return value: A new #EMFilterContext object.
+ **/
+EMFilterContext *
+em_filter_context_new(void)
+{
+ return (EMFilterContext *) g_object_new(em_filter_context_get_type(), NULL, NULL);
+}
+
+void
+em_filter_context_add_action(EMFilterContext *fc, FilterPart *action)
+{
+ d(printf("find action : "));
+ fc->actions = g_list_append(fc->actions, action);
+}
+
+FilterPart *
+em_filter_context_find_action(EMFilterContext *fc, const char *name)
+{
+ d(printf("find action : "));
+ return filter_part_find_list(fc->actions, name);
+}
+
+FilterPart *
+em_filter_context_create_action(EMFilterContext *fc, const char *name)
+{
+ FilterPart *part;
+
+ if ((part = em_filter_context_find_action(fc, name)))
+ return filter_part_clone(part);
+
+ return NULL;
+}
+
+FilterPart *
+em_filter_context_next_action(EMFilterContext *fc, FilterPart *last)
+{
+ return filter_part_next_list(fc->actions, last);
+}
+
+/* We search for any folders in our actions list that need updating, update them */
+static GList *
+filter_rename_uri(RuleContext *rc, const char *olduri, const char *newuri, GCompareFunc cmp)
+{
+ FilterRule *rule;
+ GList *l, *el;
+ FilterPart *action;
+ FilterElement *element;
+ int count = 0;
+ GList *changed = NULL;
+
+ d(printf("uri '%s' renamed to '%s'\n", olduri, newuri));
+
+ /* For all rules, for all actions, for all elements, rename any folder elements */
+ /* Yes we could do this inside each part itself, but not today */
+ rule = NULL;
+ while ((rule = rule_context_next_rule(rc, rule, NULL))) {
+ int rulecount = 0;
+
+ d(printf("checking rule '%s'\n", rule->name));
+
+ l = EM_FILTER_RULE(rule)->actions;
+ while (l) {
+ action = l->data;
+
+ d(printf("checking action '%s'\n", action->name));
+
+ el = action->elements;
+ while (el) {
+ element = el->data;
+
+ d(printf("checking element '%s'\n", element->name));
+ if (EM_IS_FILTER_FOLDER_ELEMENT(element)) {
+ d(printf(" is folder, existing uri = '%s'\n",
+ FILTER_FOLDER(element)->uri));
+ }
+
+ if (EM_IS_FILTER_FOLDER_ELEMENT(element)
+ && cmp(((EMFilterFolderElement *)element)->uri, olduri)) {
+ d(printf(" Changed!\n"));
+ em_filter_folder_element_set_value((EMFilterFolderElement *)element, newuri);
+ rulecount++;
+ }
+ el = el->next;
+ }
+ l = l->next;
+ }
+
+ if (rulecount) {
+ changed = g_list_append(changed, g_strdup(rule->name));
+ filter_rule_emit_changed(rule);
+ }
+
+ count += rulecount;
+ }
+
+ /* might need to call parent class, if it did anything ... parent_class->rename_uri(f, olduri, newuri, cmp); */
+
+ return changed;
+}
+
+static GList *
+filter_delete_uri(RuleContext *rc, const char *uri, GCompareFunc cmp)
+{
+ /* We basically do similar to above, but when we find it,
+ Remove the action, and if thats the last action, this might create an empty rule? remove the rule? */
+
+ FilterRule *rule;
+ GList *l, *el;
+ FilterPart *action;
+ FilterElement *element;
+ int count = 0;
+ GList *deleted = NULL;
+
+ d(printf("uri '%s' deleted\n", uri));
+
+ /* For all rules, for all actions, for all elements, check deleted folder elements */
+ /* Yes we could do this inside each part itself, but not today */
+ rule = NULL;
+ while ((rule = rule_context_next_rule(rc, rule, NULL))) {
+ int recorded = 0;
+
+ d(printf("checking rule '%s'\n", rule->name));
+
+ l = EM_FILTER_RULE(rule)->actions;
+ while (l) {
+ action = l->data;
+
+ d(printf("checking action '%s'\n", action->name));
+
+ el = action->elements;
+ while (el) {
+ element = el->data;
+
+ d(printf("checking element '%s'\n", element->name));
+ if (EM_IS_FILTER_FOLDER_ELEMENT(element)) {
+ d(printf(" is folder, existing uri = '%s'\n",
+ FILTER_FOLDER(element)->uri));
+ }
+
+ if (EM_IS_FILTER_FOLDER_ELEMENT(element)
+ && cmp(((EMFilterFolderElement *)element)->uri, uri)) {
+ d(printf(" Deleted!\n"));
+ /* check if last action, if so, remove rule instead? */
+ l = l->next;
+ em_filter_rule_remove_action((EMFilterRule *)rule, action);
+ g_object_unref(action);
+ count++;
+ if (!recorded)
+ deleted = g_list_append(deleted, g_strdup(rule->name));
+ goto next_action;
+ }
+ el = el->next;
+ }
+ l = l->next;
+ next_action:
+ ;
+ }
+ }
+
+ /* TODO: could call parent and merge lists */
+
+ return deleted;
+}
+
+static FilterElement *
+filter_new_element(RuleContext *rc, const char *type)
+{
+ if (!strcmp(type, "folder")) {
+ return (FilterElement *) em_filter_folder_element_new();
+ } else if (!strcmp(type, "system-flag")) {
+ return (FilterElement *) filter_option_new();
+ } else if (!strcmp(type, "score")) {
+ return (FilterElement *) filter_int_new_type("score", -3, 3);
+ } else if (!strcmp(type, "source")) {
+ return (FilterElement *) em_filter_source_element_new();
+ } else {
+ return parent_class->new_element(rc, type);
+ }
+}
diff --git a/mail/em-filter-context.h b/mail/em-filter-context.h
new file mode 100644
index 0000000000..9b29645a0c
--- /dev/null
+++ b/mail/em-filter-context.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _EM_FILTER_CONTEXT_H
+#define _EM_FILTER_CONTEXT_H
+
+#include "filter/rule-context.h"
+
+#define EM_TYPE_FILTER_CONTEXT (em_filter_context_get_type ())
+#define EM_FILTER_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILTER_TYPE_CONTEXT, EMFilterContext))
+#define EM_FILTER_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FILTER_TYPE_CONTEXT, EMFilterContextClass))
+#define EM_IS_FILTER_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FILTER_TYPE_CONTEXT))
+#define EM_IS_FILTER_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FILTER_TYPE_CONTEXT))
+#define EM_FILTER_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FILTER_TYPE_CONTEXT, EMFilterContextClass))
+
+typedef struct _EMFilterContext EMFilterContext;
+typedef struct _EMFilterContextClass EMFilterContextClass;
+
+struct _EMFilterContext {
+ RuleContext parent_object;
+
+ GList *actions;
+};
+
+struct _EMFilterContextClass {
+ RuleContextClass parent_class;
+};
+
+GType em_filter_context_get_type (void);
+EMFilterContext *em_filter_context_new (void);
+
+/* methods */
+void em_filter_context_add_action (EMFilterContext *fc, FilterPart *action);
+FilterPart *em_filter_context_find_action (EMFilterContext *fc, const char *name);
+FilterPart *em_filter_context_create_action (EMFilterContext *fc, const char *name);
+FilterPart *em_filter_context_next_action (EMFilterContext *fc, FilterPart *last);
+
+#endif /* ! _EM_FILTER_CONTEXT_H */
diff --git a/mail/em-filter-editor.c b/mail/em-filter-editor.c
new file mode 100644
index 0000000000..f736cc72f6
--- /dev/null
+++ b/mail/em-filter-editor.c
@@ -0,0 +1,164 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <libgnome/gnome-i18n.h>
+
+#include "em-filter-editor.h"
+#include "em-filter-rule.h"
+
+#define d(x)
+
+static FilterRule *create_rule (RuleEditor *re);
+
+static void em_filter_editor_class_init (EMFilterEditorClass *klass);
+static void em_filter_editor_init (EMFilterEditor *fe);
+static void em_filter_editor_finalise (GObject *obj);
+
+
+static RuleEditorClass *parent_class = NULL;
+
+
+GtkType
+em_filter_editor_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (EMFilterEditorClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) em_filter_editor_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EMFilterEditor),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) em_filter_editor_init,
+ };
+
+ type = g_type_register_static (RULE_TYPE_EDITOR, "EMFilterEditor", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+em_filter_editor_class_init (EMFilterEditorClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ RuleEditorClass *re_class = (RuleEditorClass *) klass;
+
+ parent_class = g_type_class_ref (rule_editor_get_type ());
+
+ gobject_class->finalize = em_filter_editor_finalise;
+
+ /* override methods */
+ re_class->create_rule = create_rule;
+}
+
+static void
+em_filter_editor_init (EMFilterEditor *fe)
+{
+ ;
+}
+
+static void
+em_filter_editor_finalise (GObject *obj)
+{
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+/**
+ * em_filter_editor_new:
+ *
+ * Create a new EMFilterEditor object.
+ *
+ * Return value: A new #EMFilterEditor object.
+ **/
+EMFilterEditor *
+em_filter_editor_new (EMFilterContext *fc, const char **source_names)
+{
+ EMFilterEditor *fe = (EMFilterEditor *) g_object_new (em_filter_editor_get_type(), NULL);
+ GladeXML *gui;
+
+ gui = glade_xml_new (EVOLUTION_GLADEDIR "/filter.glade", "rule_editor", NULL);
+ em_filter_editor_construct (fe, fc, gui, source_names);
+ g_object_unref (gui);
+
+ return fe;
+}
+
+static void
+select_source (GtkMenuItem *mi, EMFilterEditor *fe)
+{
+ char *source;
+
+ source = g_object_get_data(G_OBJECT(mi), "source");
+ g_assert (source);
+
+ rule_editor_set_source ((RuleEditor *)fe, source);
+}
+
+void
+em_filter_editor_construct (EMFilterEditor *fe, EMFilterContext *fc, GladeXML *gui, const char **source_names)
+{
+ GtkWidget *menu, *item, *omenu;
+ int i;
+
+ omenu = glade_xml_get_widget (gui, "filter_source");
+ gtk_option_menu_remove_menu (GTK_OPTION_MENU (omenu));
+ menu = gtk_menu_new ();
+
+ for (i = 0; source_names[i]; i++) {
+ item = gtk_menu_item_new_with_label (_(source_names[i]));
+ g_object_set_data_full (G_OBJECT (item), "source", g_strdup (source_names[i]), g_free);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ gtk_widget_show (item);
+ g_signal_connect (item, "activate", G_CALLBACK (select_source), fe);
+ }
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu);
+ gtk_widget_show (omenu);
+
+ rule_editor_construct ((RuleEditor *) fe, (RuleContext *) fc, gui, source_names[0], _("_Filter Rules"));
+}
+
+static FilterRule *
+create_rule (RuleEditor *re)
+{
+ FilterRule *rule = filter_rule_new ();
+ FilterPart *part;
+
+ /* create a rule with 1 part & 1 action in it */
+ rule = (FilterRule *)em_filter_rule_new ();
+ part = rule_context_next_part (re->context, NULL);
+ filter_rule_add_part (rule, filter_part_clone (part));
+ part = em_filter_context_next_action ((EMFilterContext *)re->context, NULL);
+ em_filter_rule_add_action ((EMFilterRule *)rule, filter_part_clone (part));
+
+ return rule;
+}
diff --git a/mail/em-filter-editor.h b/mail/em-filter-editor.h
new file mode 100644
index 0000000000..ff65be5222
--- /dev/null
+++ b/mail/em-filter-editor.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _EM_FILTER_EDITOR_H
+#define _EM_FILTER_EDITOR_H
+
+#include "filter/rule-editor.h"
+#include "em-filter-context.h"
+
+#define EM_FILTER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), em_filter_editor_get_type(), EMFilterEditor))
+#define EM_FILTER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), em_filter_editor_get_type(), EMFilterEditorClass))
+#define EM_IS_FILTER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), em_filter_editor_get_type()))
+#define EM_IS_FILTER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), em_filter_editor_get_type()))
+#define EM_FILTER_EDITOR_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), em_filter_editor_get_type(), EMFilterEditorClass))
+
+typedef struct _EMFilterEditor EMFilterEditor;
+typedef struct _EMFilterEditorClass EMFilterEditorClass;
+
+struct _EMFilterEditor {
+ RuleEditor parent_object;
+
+};
+
+struct _EMFilterEditorClass {
+ RuleEditorClass parent_class;
+
+ /* virtual methods */
+
+ /* signals */
+};
+
+GtkType em_filter_editor_get_type (void);
+
+EMFilterEditor *em_filter_editor_new (EMFilterContext *f, const char **source_names);
+void em_filter_editor_construct (EMFilterEditor *fe, EMFilterContext *fc, GladeXML *gui, const char **source_names);
+
+#endif /* ! _EM_FILTER_EDITOR_H */
diff --git a/mail/em-filter-folder-element.c b/mail/em-filter-folder-element.c
new file mode 100644
index 0000000000..c335be630d
--- /dev/null
+++ b/mail/em-filter-folder-element.c
@@ -0,0 +1,268 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright(C)2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <libgnome/gnome-i18n.h>
+
+#include "em-filter-folder-element.h"
+#include "mail/em-folder-selection-button.h"
+#include "mail/mail-component.h"
+#include "mail/em-utils.h"
+#include "e-util/e-sexp.h"
+#include "widgets/misc/e-error.h"
+
+#define d(x)
+
+static gboolean validate(FilterElement *fe);
+static int folder_eq(FilterElement *fe, FilterElement *cm);
+static void xml_create(FilterElement *fe, xmlNodePtr node);
+static xmlNodePtr xml_encode(FilterElement *fe);
+static int 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 emff_copy_value(FilterElement *de, FilterElement *se);
+
+static void em_filter_folder_element_class_init(EMFilterFolderElementClass *class);
+static void em_filter_folder_element_init(EMFilterFolderElement *ff);
+static void em_filter_folder_element_finalise(GObject *obj);
+
+static FilterElementClass *parent_class = NULL;
+
+GType
+em_filter_folder_element_get_type(void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof(EMFilterFolderElementClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc)em_filter_folder_element_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(EMFilterFolderElement),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)em_filter_folder_element_init,
+ };
+
+ type = g_type_register_static(FILTER_TYPE_ELEMENT, "EMFilterFolderElement", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+em_filter_folder_element_class_init(EMFilterFolderElementClass *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 = em_filter_folder_element_finalise;
+
+ /* override methods */
+ fe_class->validate = validate;
+ fe_class->eq = folder_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;
+ fe_class->copy_value = emff_copy_value;
+}
+
+static void
+em_filter_folder_element_init(EMFilterFolderElement *ff)
+{
+ ;
+}
+
+static void
+em_filter_folder_element_finalise(GObject *obj)
+{
+ EMFilterFolderElement *ff = (EMFilterFolderElement *)obj;
+
+ g_free(ff->uri);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+/**
+ * em_filter_folder_element_new:
+ *
+ * Create a new EMFilterFolderElement object.
+ *
+ * Return value: A new #EMFilterFolderElement object.
+ **/
+EMFilterFolderElement *
+em_filter_folder_element_new(void)
+{
+ return(EMFilterFolderElement *)g_object_new(em_filter_folder_element_get_type(), NULL, NULL);
+}
+
+void
+em_filter_folder_element_set_value(EMFilterFolderElement *ff, const char *uri)
+{
+ g_free(ff->uri);
+ ff->uri = g_strdup(uri);
+}
+
+static gboolean
+validate(FilterElement *fe)
+{
+ EMFilterFolderElement *ff = (EMFilterFolderElement *)fe;
+
+ if (ff->uri && *ff->uri) {
+ return TRUE;
+ } else {
+ /* FIXME: FilterElement should probably have a
+ GtkWidget member pointing to the value gotten with
+ ::get_widget()so that we can get the parent window
+ here. */
+ e_error_run(NULL, "mail:no-folder", NULL);
+
+ return FALSE;
+ }
+}
+
+static int
+folder_eq(FilterElement *fe, FilterElement *cm)
+{
+ return FILTER_ELEMENT_CLASS(parent_class)->eq(fe, cm)
+ && strcmp(((EMFilterFolderElement *)fe)->uri, ((EMFilterFolderElement *)cm)->uri)== 0;
+}
+
+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;
+ EMFilterFolderElement *ff = (EMFilterFolderElement *)fe;
+
+ d(printf("Encoding folder as xml\n"));
+
+ value = xmlNewNode(NULL, "value");
+ xmlSetProp(value, "name", fe->name);
+ xmlSetProp(value, "type", "folder");
+
+ work = xmlNewChild(value, NULL, "folder", NULL);
+ xmlSetProp(work, "uri", ff->uri);
+
+ return value;
+}
+
+static int
+xml_decode(FilterElement *fe, xmlNodePtr node)
+{
+ EMFilterFolderElement *ff = (EMFilterFolderElement *)fe;
+ xmlNodePtr n;
+
+ d(printf("Decoding folder from xml %p\n", fe));
+
+ xmlFree(fe->name);
+ fe->name = xmlGetProp(node, "name");
+
+ n = node->children;
+ while(n) {
+ if (!strcmp(n->name, "folder")) {
+ char *uri;
+
+ uri = xmlGetProp(n, "uri");
+ g_free(ff->uri);
+ ff->uri = g_strdup(uri);
+ xmlFree(uri);
+ break;
+ }
+ n = n->next;
+ }
+
+ return 0;
+}
+
+static void
+folder_selected(EMFolderSelectionButton *button, EMFilterFolderElement *ff)
+{
+ const char *uri;
+
+ uri = em_folder_selection_button_get_selection(button);
+ g_free(ff->uri);
+ ff->uri = uri!=NULL?em_uri_from_camel(uri):NULL;
+
+ gdk_window_raise(GTK_WIDGET(gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_WINDOW))->window);
+}
+
+static GtkWidget *
+get_widget(FilterElement *fe)
+{
+ EMFilterFolderElement *ff = (EMFilterFolderElement *)fe;
+ GtkWidget *button;
+ char *uri;
+
+ uri = em_uri_to_camel(ff->uri);
+ button = em_folder_selection_button_new(_("Select Folder"), NULL);
+ em_folder_selection_button_set_selection(EM_FOLDER_SELECTION_BUTTON(button), uri);
+ g_free(uri);
+
+ gtk_widget_show(button);
+ g_signal_connect(button, "selected", G_CALLBACK(folder_selected), ff);
+
+ return button;
+}
+
+static void
+build_code(FilterElement *fe, GString *out, struct _FilterPart *ff)
+{
+ return;
+}
+
+static void
+format_sexp(FilterElement *fe, GString *out)
+{
+ EMFilterFolderElement *ff = (EMFilterFolderElement *)fe;
+
+ e_sexp_encode_string(out, ff->uri);
+}
+
+static void
+emff_copy_value(FilterElement *de, FilterElement *se)
+{
+ if (EM_IS_FILTER_FOLDER_ELEMENT(se))
+ em_filter_folder_element_set_value((EMFilterFolderElement *)de, ((EMFilterFolderElement *)se)->uri);
+ else
+ parent_class->copy_value(de, se);
+}
diff --git a/mail/em-filter-folder-element.h b/mail/em-filter-folder-element.h
new file mode 100644
index 0000000000..f2f5e2d2e7
--- /dev/null
+++ b/mail/em-filter-folder-element.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _EM_FILTER_FOLDER_ELEMENT_H
+#define _EM_FILTER_FOLDER_ELEMENT_H
+
+#include "filter/filter-element.h"
+
+#define EM_FILTER_FOLDER_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), em_filter_folder_element_get_type(), EMFilterFolderElement))
+#define EM_FILTER_FOLDER_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), em_filter_folder_element_get_type(), EMFilterFolderElementClass))
+#define EM_IS_FILTER_FOLDER_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), em_filter_folder_element_get_type()))
+#define EM_IS_FILTER_FOLDER_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), em_filter_folder_element_get_type()))
+#define EM_FILTER_FOLDER_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), em_filter_folder_element_get_type(), EMFilterFolderElementClass))
+
+typedef struct _EMFilterFolderElement EMFilterFolderElement;
+typedef struct _EMFilterFolderElementClass EMFilterFolderElementClass;
+
+struct _EMFilterFolderElement {
+ FilterElement parent_object;
+
+ char *uri;
+};
+
+struct _EMFilterFolderElementClass {
+ FilterElementClass parent_class;
+};
+
+GType em_filter_folder_element_get_type (void);
+EMFilterFolderElement *em_filter_folder_element_new (void);
+
+/* methods */
+void em_filter_folder_element_set_value (EMFilterFolderElement *ff, const char *uri);
+
+#endif /* ! _EM_FILTER_FOLDER_ELEMENT_H */
diff --git a/mail/em-filter-i18n.h b/mail/em-filter-i18n.h
new file mode 100644
index 0000000000..2f5a65ebe7
--- /dev/null
+++ b/mail/em-filter-i18n.h
@@ -0,0 +1,65 @@
+/* Automatically generated. Do not edit. */
+char *s = N_("Adjust Score");
+char *s = N_("Assign Color");
+char *s = N_("Assign Score");
+char *s = N_("Attachments");
+char *s = N_("Beep");
+char *s = N_("contains");
+char *s = N_("Copy to Folder");
+char *s = N_("Date received");
+char *s = N_("Date sent");
+char *s = N_("Delete");
+char *s = N_("Deleted");
+char *s = N_("does not contain");
+char *s = N_("does not end with");
+char *s = N_("does not exist");
+char *s = N_("does not return");
+char *s = N_("does not sound like");
+char *s = N_("does not start with");
+char *s = N_("Do Not Exist");
+char *s = N_("Draft");
+char *s = N_("ends with");
+char *s = N_("Exist");
+char *s = N_("exists");
+char *s = N_("Expression");
+char *s = N_("Follow Up");
+char *s = N_("Important");
+char *s = N_("is");
+char *s = N_("is after");
+char *s = N_("is before");
+char *s = N_("is Flagged");
+char *s = N_("is greater than");
+char *s = N_("is less than");
+char *s = N_("is not");
+char *s = N_("is not Flagged");
+char *s = N_("Junk");
+char *s = N_("Junk Test");
+char *s = N_("Label");
+char *s = N_("Mailing list");
+char *s = N_("Message Body");
+char *s = N_("Message Header");
+char *s = N_("Message is Junk");
+char *s = N_("Message is not Junk");
+char *s = N_("Move to Folder");
+char *s = N_("Pipe to Program");
+char *s = N_("Play Sound");
+char *s = N_("Read");
+char *s = N_("Recipients");
+char *s = N_("Regex Match");
+char *s = N_("Replied to");
+char *s = N_("returns");
+char *s = N_("returns greater than");
+char *s = N_("returns less than");
+char *s = N_("Run Program");
+char *s = N_("Score");
+char *s = N_("Sender");
+char *s = N_("Set Status");
+char *s = N_("Size (kB)");
+char *s = N_("sounds like");
+char *s = N_("Source Account");
+char *s = N_("Specific header");
+char *s = N_("starts with");
+char *s = N_("Status");
+char *s = N_("Stop Processing");
+char *s = N_("Subject");
+char *s = N_("Unset Status");
diff --git a/mail/em-filter-rule.c b/mail/em-filter-rule.c
new file mode 100644
index 0000000000..f782f8827b
--- /dev/null
+++ b/mail/em-filter-rule.c
@@ -0,0 +1,545 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright(C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@ximian.com>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <libgnome/gnome-i18n.h>
+
+#include "em-filter-rule.h"
+#include "em-filter-context.h"
+
+#define d(x)
+
+static int validate(FilterRule *fr);
+static int filter_eq(FilterRule *fr, FilterRule *cm);
+static xmlNodePtr xml_encode(FilterRule *fr);
+static int xml_decode(FilterRule *fr, xmlNodePtr, RuleContext *rc);
+static void rule_copy(FilterRule *dest, FilterRule *src);
+/*static void build_code(FilterRule *, GString *out);*/
+static GtkWidget *get_widget(FilterRule *fr, RuleContext *rc);
+
+static void em_filter_rule_class_init(EMFilterRuleClass *klass);
+static void em_filter_rule_init(EMFilterRule *ff);
+static void em_filter_rule_finalise(GObject *obj);
+
+static FilterRuleClass *parent_class = NULL;
+
+GType
+em_filter_rule_get_type(void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof(EMFilterRuleClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) em_filter_rule_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(EMFilterRule),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)em_filter_rule_init,
+ };
+
+ type = g_type_register_static(FILTER_TYPE_RULE, "EMFilterRule", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+em_filter_rule_class_init(EMFilterRuleClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ FilterRuleClass *fr_class =(FilterRuleClass *)klass;
+
+ parent_class = g_type_class_ref(FILTER_TYPE_RULE);
+
+ object_class->finalize = em_filter_rule_finalise;
+
+ /* override methods */
+ fr_class->validate = validate;
+ fr_class->eq = filter_eq;
+ fr_class->xml_encode = xml_encode;
+ fr_class->xml_decode = xml_decode;
+ /*fr_class->build_code = build_code;*/
+ fr_class->copy = rule_copy;
+ fr_class->get_widget = get_widget;
+}
+
+static void
+em_filter_rule_init(EMFilterRule *ff)
+{
+ ;
+}
+
+static void
+unref_list(GList *l)
+{
+ while (l) {
+ g_object_unref(l->data);
+ l = l->next;
+ }
+}
+
+static void
+em_filter_rule_finalise(GObject *obj)
+{
+ EMFilterRule *ff =(EMFilterRule *) obj;
+
+ unref_list(ff->actions);
+ g_list_free(ff->actions);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+/**
+ * em_filter_rule_new:
+ *
+ * Create a new EMFilterRule object.
+ *
+ * Return value: A new #EMFilterRule object.
+ **/
+EMFilterRule *
+em_filter_rule_new(void)
+{
+ return (EMFilterRule *)g_object_new(em_filter_rule_get_type(), NULL, NULL);
+}
+
+void
+em_filter_rule_add_action(EMFilterRule *fr, FilterPart *fp)
+{
+ fr->actions = g_list_append(fr->actions, fp);
+
+ filter_rule_emit_changed((FilterRule *)fr);
+}
+
+void
+em_filter_rule_remove_action(EMFilterRule *fr, FilterPart *fp)
+{
+ fr->actions = g_list_remove(fr->actions, fp);
+
+ filter_rule_emit_changed((FilterRule *)fr);
+}
+
+void
+em_filter_rule_replace_action(EMFilterRule *fr, FilterPart *fp, FilterPart *new)
+{
+ GList *l;
+
+ l = g_list_find(fr->actions, fp);
+ if (l) {
+ l->data = new;
+ } else {
+ fr->actions = g_list_append(fr->actions, new);
+ }
+
+ filter_rule_emit_changed((FilterRule *)fr);
+}
+
+void
+em_filter_rule_build_action(EMFilterRule *fr, GString *out)
+{
+ g_string_append(out, "(begin\n");
+ filter_part_build_code_list(fr->actions, out);
+ g_string_append(out, ")\n");
+}
+
+static int
+validate(FilterRule *fr)
+{
+ EMFilterRule *ff =(EMFilterRule *)fr;
+ GList *parts;
+ int valid;
+
+ valid = FILTER_RULE_CLASS(parent_class)->validate(fr);
+
+ /* validate rule actions */
+ parts = ff->actions;
+ while (parts && valid) {
+ valid = filter_part_validate((FilterPart *)parts->data);
+ parts = parts->next;
+ }
+
+ return valid;
+}
+
+static int
+list_eq(GList *al, GList *bl)
+{
+ int 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 int
+filter_eq(FilterRule *fr, FilterRule *cm)
+{
+ return FILTER_RULE_CLASS(parent_class)->eq(fr, cm)
+ && list_eq(((EMFilterRule *)fr)->actions,((EMFilterRule *)cm)->actions);
+}
+
+static xmlNodePtr
+xml_encode(FilterRule *fr)
+{
+ EMFilterRule *ff =(EMFilterRule *)fr;
+ xmlNodePtr node, set, work;
+ GList *l;
+
+ node = FILTER_RULE_CLASS(parent_class)->xml_encode(fr);
+ g_assert(node != NULL);
+ set = xmlNewNode(NULL, "actionset");
+ xmlAddChild(node, set);
+ l = ff->actions;
+ while (l) {
+ work = filter_part_xml_encode((FilterPart *)l->data);
+ xmlAddChild(set, work);
+ l = l->next;
+ }
+
+ return node;
+
+}
+
+static void
+load_set(xmlNodePtr node, EMFilterRule *ff, RuleContext *rc)
+{
+ xmlNodePtr work;
+ char *rulename;
+ FilterPart *part;
+
+ work = node->children;
+ while (work) {
+ if (!strcmp(work->name, "part")) {
+ rulename = xmlGetProp(work, "name");
+ part = em_filter_context_find_action((EMFilterContext *)rc, rulename);
+ if (part) {
+ part = filter_part_clone(part);
+ filter_part_xml_decode(part, work);
+ em_filter_rule_add_action(ff, 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 int
+xml_decode(FilterRule *fr, xmlNodePtr node, RuleContext *rc)
+{
+ EMFilterRule *ff =(EMFilterRule *)fr;
+ xmlNodePtr work;
+ int result;
+
+ result = FILTER_RULE_CLASS(parent_class)->xml_decode(fr, node, rc);
+ if (result != 0)
+ return result;
+
+ work = node->children;
+ while (work) {
+ if (!strcmp(work->name, "actionset")) {
+ load_set(work, ff, rc);
+ }
+ work = work->next;
+ }
+
+ return 0;
+}
+
+static void
+rule_copy(FilterRule *dest, FilterRule *src)
+{
+ EMFilterRule *fdest, *fsrc;
+ GList *node;
+
+ fdest =(EMFilterRule *)dest;
+ fsrc =(EMFilterRule *)src;
+
+ if (fdest->actions) {
+ g_list_foreach(fdest->actions, (GFunc)g_object_unref, NULL);
+ g_list_free(fdest->actions);
+ fdest->actions = NULL;
+ }
+
+ node = fsrc->actions;
+ while (node) {
+ FilterPart *part = node->data;
+
+ g_object_ref(part);
+ fdest->actions = g_list_append(fdest->actions, part);
+ node = node->next;
+ }
+
+ FILTER_RULE_CLASS(parent_class)->copy(dest, src);
+}
+
+/*static void build_code(FilterRule *fr, GString *out)
+{
+ return FILTER_RULE_CLASS(parent_class)->build_code(fr, out);
+}*/
+
+struct _part_data {
+ FilterRule *fr;
+ EMFilterContext *f;
+ FilterPart *part;
+ GtkWidget *partwidget, *container;
+};
+
+static void
+option_activate(GtkMenuItem *item, struct _part_data *data)
+{
+ FilterPart *part = g_object_get_data((GObject *)item, "part");
+ FilterPart *newpart;
+
+ /* 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);
+ em_filter_rule_replace_action((EMFilterRule *)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, FALSE, FALSE, 0);
+
+ g_object_set_data((GObject *)data->container, "part", newpart);
+}
+
+static GtkWidget *
+get_rule_part_widget(EMFilterContext *f, FilterPart *newpart, FilterRule *fr)
+{
+ FilterPart *part = NULL;
+ GtkWidget *menu;
+ GtkWidget *item;
+ GtkWidget *omenu;
+ GtkWidget *hbox;
+ GtkWidget *p;
+ int 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);
+ p = filter_part_get_widget(newpart);
+
+ data->partwidget = p;
+ data->container = hbox;
+
+ menu = gtk_menu_new();
+ while ((part = em_filter_context_next_action(f, part))) {
+ item = gtk_menu_item_new_with_label(_(part->title));
+
+ g_object_set_data((GObject *)item, "part", part);
+ g_signal_connect(item, "activate", G_CALLBACK(option_activate), data);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ gtk_widget_show(item);
+
+ if (!strcmp(newpart->title, part->title))
+ current = index;
+
+ index++;
+ }
+
+ omenu = gtk_option_menu_new();
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(omenu), menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(omenu), current);
+ gtk_widget_show(omenu);
+
+ gtk_box_pack_start(GTK_BOX(hbox), omenu, FALSE, FALSE, 0);
+ if (p)
+ gtk_box_pack_start(GTK_BOX(hbox), p, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(hbox);
+
+ return hbox;
+}
+
+struct _rule_data {
+ FilterRule *fr;
+ EMFilterContext *f;
+ GtkWidget *parts;
+};
+
+static void
+less_parts(GtkWidget *button, struct _rule_data *data)
+{
+ FilterPart *part;
+ GtkWidget *rule;
+ GList *l;
+
+ l =((EMFilterRule *)data->fr)->actions;
+ if (g_list_length(l) < 2)
+ return;
+
+ rule = g_object_get_data((GObject *)button, "rule");
+ part = g_object_get_data((GObject *)rule, "part");
+
+ /* remove the part from the list */
+ em_filter_rule_remove_action((EMFilterRule *)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, int 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_object_set_data((GObject *)rule, "part", part);
+ /*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
+more_parts(GtkWidget *button, struct _rule_data *data)
+{
+ FilterPart *new;
+
+ /* create a new rule entry, use the first type of rule */
+ new = em_filter_context_next_action((EMFilterContext *)data->f, NULL);
+ if (new) {
+ GtkWidget *w;
+ guint16 rows;
+
+ new = filter_part_clone(new);
+ em_filter_rule_add_action((EMFilterRule *)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);
+ }
+}
+
+static GtkWidget *
+get_widget(FilterRule *fr, RuleContext *rc)
+{
+ GtkWidget *widget, *hbox, *add, *label;
+ GtkWidget *parts, *inframe, *w;
+ GtkWidget *scrolledwindow;
+ GtkObject *hadj, *vadj;
+ GList *l;
+ FilterPart *part;
+ struct _rule_data *data;
+ EMFilterRule *ff =(EMFilterRule *)fr;
+ int rows, i = 0;
+
+ widget = FILTER_RULE_CLASS(parent_class)->get_widget(fr, rc);
+
+ /* and now for the action area */
+ label = gtk_label_new(_("<b>Then</b>"));
+ 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(widget), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ hbox = gtk_hbox_new(FALSE, 12);
+ gtk_box_pack_start(GTK_BOX(widget), 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);
+
+ rows = g_list_length(ff->actions);
+ parts = gtk_table_new(rows, 2, FALSE);
+ data = g_malloc0(sizeof(*data));
+ data->f =(EMFilterContext *)rc;
+ data->fr = fr;
+ data->parts = parts;
+
+ hbox = gtk_hbox_new(FALSE, 3);
+
+ add = gtk_button_new_from_stock(GTK_STOCK_ADD);
+ /* gtk_button_set_relief(GTK_BUTTON(add), GTK_RELIEF_NONE); */
+ g_signal_connect(add, "clicked", G_CALLBACK(more_parts), data);
+ gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, FALSE, 0);
+
+ gtk_box_pack_start(GTK_BOX(inframe), hbox, FALSE, FALSE, 3);
+
+ l = ff->actions;
+ while (l) {
+ part = l->data;
+ d(printf("adding action %s\n", part->title));
+ w = get_rule_part_widget((EMFilterContext *)rc, part, fr);
+ attach_rule(w, data, part, i++);
+ l = l->next;
+ }
+
+ 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, 0);
+
+ /*gtk_box_pack_start(GTK_BOX(inframe), parts, FALSE, FALSE, 3);*/
+
+ gtk_widget_show_all(widget);
+
+ return widget;
+}
diff --git a/mail/em-filter-rule.h b/mail/em-filter-rule.h
new file mode 100644
index 0000000000..12ab9a0ab4
--- /dev/null
+++ b/mail/em-filter-rule.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _EM_FILTER_RULE_H
+#define _EM_FILTER_RULE_H
+
+#include "filter/filter-rule.h"
+
+#define EM_FILTER_RULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), em_filter_rule_get_type(), EMFilterRule))
+#define EM_FILTER_RULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), em_filter_rule_get_type(), EMFilterRuleClass))
+#define EM_IS_FILTER_RULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), em_filter_rule_get_type()))
+#define EM_IS_FILTER_RULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), em_filter_rule_get_type()))
+#define EM_FILTER_RULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), em_filter_rule_get_type(), EMFilterRuleClass))
+
+typedef struct _EMFilterRule EMFilterRule;
+typedef struct _EMFilterRuleClass EMFilterRuleClass;
+
+struct _EMFilterRule {
+ FilterRule parent_object;
+
+ GList *actions;
+};
+
+struct _EMFilterRuleClass {
+ FilterRuleClass parent_class;
+};
+
+GType em_filter_rule_get_type (void);
+EMFilterRule *em_filter_rule_new (void);
+
+/* methods */
+void em_filter_rule_add_action (EMFilterRule *fr, FilterPart *fp);
+void em_filter_rule_remove_action (EMFilterRule *fr, FilterPart *fp);
+void em_filter_rule_replace_action (EMFilterRule *fr, FilterPart *fp, FilterPart *new);
+
+void em_filter_rule_build_action (EMFilterRule *fr, GString *out);
+
+#endif /* ! _EM_FILTER_RULE_H */
diff --git a/mail/em-filter-source-element.c b/mail/em-filter-source-element.c
new file mode 100644
index 0000000000..5c7c7cb10a
--- /dev/null
+++ b/mail/em-filter-source-element.c
@@ -0,0 +1,377 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jon Trowbridge <trow@ximian.com>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2001-2002 Ximian, Inc.(www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option)any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "em-filter-source-element.h"
+
+#include <gtk/gtk.h>
+#include <e-util/e-url.h>
+#include <e-util/e-sexp.h>
+#include <e-util/e-account-list.h>
+#include <camel/camel-url.h>
+
+
+static void em_filter_source_element_class_init(EMFilterSourceElementClass *klass);
+static void em_filter_source_element_init(EMFilterSourceElement *fs);
+static void em_filter_source_element_finalize(GObject *obj);
+
+static int source_eq(FilterElement *fe, FilterElement *cm);
+static void xml_create(FilterElement *fe, xmlNodePtr node);
+static xmlNodePtr xml_encode(FilterElement *fe);
+static int 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 void em_filter_source_element_add_source (EMFilterSourceElement *fs, const char *account_name, const char *name,
+ const char *addr, const char *url);
+static void em_filter_source_element_get_sources(EMFilterSourceElement *fs);
+
+typedef struct _SourceInfo {
+ char *account_name;
+ char *name;
+ char *address;
+ char *url;
+} SourceInfo;
+
+struct _EMFilterSourceElementPrivate {
+ GList *sources;
+ char *current_url;
+};
+
+
+static FilterElementClass *parent_class = NULL;
+
+
+GType
+em_filter_source_element_get_type(void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof(EMFilterSourceElementClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc)em_filter_source_element_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(EMFilterSourceElement),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)em_filter_source_element_init,
+ };
+
+ type = g_type_register_static(FILTER_TYPE_ELEMENT, "EMFilterSourceElement", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+em_filter_source_element_class_init(EMFilterSourceElementClass *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 = em_filter_source_element_finalize;
+
+ /* override methods */
+ fe_class->eq = source_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
+em_filter_source_element_init(EMFilterSourceElement *fs)
+{
+ fs->priv = g_new(struct _EMFilterSourceElementPrivate, 1);
+ fs->priv->sources = NULL;
+ fs->priv->current_url = NULL;
+}
+
+static void
+em_filter_source_element_finalize(GObject *obj)
+{
+ EMFilterSourceElement *fs = (EMFilterSourceElement *)obj;
+ GList *i = fs->priv->sources;
+
+ while (i) {
+ SourceInfo *info = i->data;
+ g_free(info->account_name);
+ g_free(info->name);
+ g_free(info->address);
+ g_free(info->url);
+ g_free(info);
+ i = g_list_next(i);
+ }
+
+ g_list_free(fs->priv->sources);
+ g_free(fs->priv->current_url);
+
+ g_free(fs->priv);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+EMFilterSourceElement *
+em_filter_source_element_new(void)
+{
+ return (EMFilterSourceElement *)g_object_new(em_filter_source_element_get_type(), NULL, NULL);
+}
+
+static int
+source_eq(FilterElement *fe, FilterElement *cm)
+{
+ EMFilterSourceElement *fs = (EMFilterSourceElement *)fe, *cs = (EMFilterSourceElement *)cm;
+
+ return FILTER_ELEMENT_CLASS(parent_class)->eq(fe, cm)
+ &&((fs->priv->current_url && cs->priv->current_url
+ && strcmp(fs->priv->current_url, cs->priv->current_url)== 0)
+ ||(fs->priv->current_url == NULL && cs->priv->current_url == NULL));
+}
+
+static void
+xml_create(FilterElement *fe, xmlNodePtr node)
+{
+ /* Call parent implementation */
+ FILTER_ELEMENT_CLASS(parent_class)->xml_create(fe, node);
+}
+
+static xmlNodePtr
+xml_encode(FilterElement *fe)
+{
+ xmlNodePtr value;
+
+ EMFilterSourceElement *fs = (EMFilterSourceElement *)fe;
+
+ value = xmlNewNode(NULL, "value");
+ xmlSetProp(value, "name", fe->name);
+ xmlSetProp(value, "type", "uri");
+
+ if (fs->priv->current_url)
+ xmlNewTextChild(value, NULL, "uri", fs->priv->current_url);
+
+ return value;
+}
+
+static gint
+xml_decode(FilterElement *fe, xmlNodePtr node)
+{
+ EMFilterSourceElement *fs = (EMFilterSourceElement *)fe;
+ CamelURL *url;
+ char *uri;
+
+ node = node->children;
+ while (node != NULL) {
+ if (!strcmp(node->name, "uri")) {
+ uri = xmlNodeGetContent(node);
+ url = camel_url_new(uri, NULL);
+ xmlFree(uri);
+
+ g_free(fs->priv->current_url);
+ fs->priv->current_url = camel_url_to_string(url, CAMEL_URL_HIDE_ALL);
+ camel_url_free(url);
+ break;
+ }
+
+ node = node->next;
+ }
+
+ return 0;
+}
+
+static FilterElement *
+clone(FilterElement *fe)
+{
+ EMFilterSourceElement *fs = (EMFilterSourceElement *)fe;
+ EMFilterSourceElement *cpy = em_filter_source_element_new();
+ GList *i;
+
+ ((FilterElement *)cpy)->name = xmlStrdup(fe->name);
+
+ cpy->priv->current_url = g_strdup(fs->priv->current_url);
+
+ for (i = fs->priv->sources; i != NULL; i = g_list_next(i)) {
+ SourceInfo *info = (SourceInfo *)i->data;
+ em_filter_source_element_add_source(cpy, info->account_name, info->name, info->address, info->url);
+ }
+
+ return (FilterElement *)cpy;
+}
+
+static void
+source_changed(GtkWidget *item, EMFilterSourceElement *fs)
+{
+ SourceInfo *info = (SourceInfo *)g_object_get_data((GObject *)item, "source");
+
+ g_free(fs->priv->current_url);
+ fs->priv->current_url = g_strdup(info->url);
+}
+
+static GtkWidget *
+get_widget(FilterElement *fe)
+{
+ EMFilterSourceElement *fs = (EMFilterSourceElement *)fe;
+ GtkWidget *menu;
+ GtkWidget *omenu;
+ GtkWidget *item;
+ GList *i;
+ SourceInfo *first = NULL;
+ int index, current_index;
+
+ if (fs->priv->sources == NULL)
+ em_filter_source_element_get_sources(fs);
+
+ menu = gtk_menu_new();
+
+ index = 0;
+ current_index = -1;
+
+ for (i = fs->priv->sources; i != NULL; i = g_list_next(i)) {
+ SourceInfo *info = (SourceInfo *)i->data;
+ char *label;
+
+ if (info->url != NULL) {
+ if (first == NULL)
+ first = info;
+
+ if (info->account_name && strcmp(info->account_name, info->address))
+ label = g_strdup_printf("%s <%s>(%s)", info->name,
+ info->address, info->account_name);
+ else
+ label = g_strdup_printf("%s <%s>", info->name, info->address);
+
+ item = gtk_menu_item_new_with_label(label);
+ g_free(label);
+
+ g_object_set_data((GObject *)item, "source", info);
+ g_signal_connect(item, "activate", G_CALLBACK(source_changed), fs);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ gtk_widget_show(item);
+
+ if (fs->priv->current_url && !strcmp(info->url, fs->priv->current_url))
+ current_index = index;
+
+ index++;
+ }
+ }
+
+ omenu = gtk_option_menu_new();
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(omenu), menu);
+
+ if (current_index >= 0) {
+ gtk_option_menu_set_history(GTK_OPTION_MENU(omenu), current_index);
+ } else {
+ gtk_option_menu_set_history(GTK_OPTION_MENU(omenu), 0);
+ g_free(fs->priv->current_url);
+
+ if (first)
+ fs->priv->current_url = g_strdup(first->url);
+ else
+ fs->priv->current_url = NULL;
+ }
+
+ return omenu;
+}
+
+static void
+build_code(FilterElement *fe, GString *out, struct _FilterPart *ff)
+{
+ /* We are doing nothing on purpose. */
+}
+
+static void
+format_sexp(FilterElement *fe, GString *out)
+{
+ EMFilterSourceElement *fs = (EMFilterSourceElement *)fe;
+
+ e_sexp_encode_string(out, fs->priv->current_url);
+}
+
+
+static void
+em_filter_source_element_add_source(EMFilterSourceElement *fs, const char *account_name, const char *name,
+ const char *addr, const char *url)
+{
+ SourceInfo *info;
+
+ g_return_if_fail(EM_IS_FILTER_SOURCE_ELEMENT(fs));
+
+ info = g_new0(SourceInfo, 1);
+ info->account_name = g_strdup(account_name);
+ info->name = g_strdup(name);
+ info->address = g_strdup(addr);
+ info->url = g_strdup(url);
+
+ fs->priv->sources = g_list_append(fs->priv->sources, info);
+}
+
+static void
+em_filter_source_element_get_sources(EMFilterSourceElement *fs)
+{
+ EAccountList *accounts;
+ const EAccount *account;
+ GConfClient *gconf;
+ EIterator *it;
+ char *uri;
+ CamelURL *url;
+
+ /* should this get the global object from mail? */
+ gconf = gconf_client_get_default();
+ accounts = e_account_list_new(gconf);
+ g_object_unref(gconf);
+
+ for (it = e_list_get_iterator((EList *)accounts);
+ e_iterator_is_valid(it);
+ e_iterator_next(it)) {
+ account = (const EAccount *)e_iterator_get(it);
+
+ if (account->source == NULL || account->source->url == NULL)
+ continue;
+
+ /* hide secret stuff */
+ url = camel_url_new(account->source->url, NULL);
+ uri = camel_url_to_string(url, CAMEL_URL_HIDE_ALL);
+ camel_url_free(url);
+
+ em_filter_source_element_add_source(fs, account->name, account->id->name, account->id->address, uri);
+ g_free(uri);
+ }
+ g_object_unref(it);
+ g_object_unref(accounts);
+}
diff --git a/mail/em-filter-source-element.h b/mail/em-filter-source-element.h
new file mode 100644
index 0000000000..63cc867776
--- /dev/null
+++ b/mail/em-filter-source-element.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jon Trowbridge <trow@ximian.com>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2001-2002 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _EM_FILTER_SOURCE_ELEMENT_H
+#define _EM_FILTER_SOURCE_ELEMENT_H
+
+#include "filter/filter-element.h"
+
+#define EM_FILTER_SOURCE_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), em_filter_source_element_get_type(), EMFilterSourceElement))
+#define EM_FILTER_SOURCE_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), em_filter_source_element_get_type(), EMFilterSourceElementClass))
+#define EM_IS_FILTER_SOURCE_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), em_filter_source_element_get_type()))
+#define EM_IS_FILTER_SOURCE_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), em_filter_source_element_get_type()))
+#define EM_FILTER_SOURCE_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), em_filter_source_element_get_type(), EMFilterSourceElementClass))
+
+typedef struct _EMFilterSourceElement EMFilterSourceElement;
+typedef struct _EMFilterSourceElementClass EMFilterSourceElementClass;
+
+struct _EMFilterSourceElement {
+ FilterElement parent_object;
+ struct _EMFilterSourceElementPrivate *priv;
+};
+
+struct _EMFilterSourceElementClass {
+ FilterElementClass parent_class;
+};
+
+GType em_filter_source_element_get_type (void);
+EMFilterSourceElement *em_filter_source_element_new (void);
+
+void em_filter_source_element_set_current (EMFilterSourceElement *src, const char *url);
+
+#endif /* _EM_FILTER_SOURCE_ELEMENT_H */
diff --git a/mail/em-folder-browser.c b/mail/em-folder-browser.c
index cb4d25b565..e61453e8ce 100644
--- a/mail/em-folder-browser.c
+++ b/mail/em-folder-browser.c
@@ -62,7 +62,7 @@
/* for efilterbar stuff */
#include <e-util/e-sexp.h>
#include "mail-vfolder.h"
-#include "filter/vfolder-rule.h"
+#include "em-vfolder-rule.h"
#include <widgets/misc/e-filter-bar.h>
#include <camel/camel-search-private.h>
@@ -356,8 +356,8 @@ emfb_search_menu_activated(ESearchBar *esb, int id, EMFolderBrowser *emfb)
g_free (name);
filter_rule_set_source(rule, FILTER_SOURCE_INCOMING);
- vfolder_rule_add_source((VfolderRule *)rule, emfb->view.folder_uri);
- vfolder_gui_add_rule((VfolderRule *)rule);
+ em_vfolder_rule_add_source((EMVFolderRule *)rule, emfb->view.folder_uri);
+ vfolder_gui_add_rule((EMVFolderRule *)rule);
}
break;
}
diff --git a/mail/em-folder-tree.c b/mail/em-folder-tree.c
index 17e4fa5f8c..e524b17df8 100644
--- a/mail/em-folder-tree.c
+++ b/mail/em-folder-tree.c
@@ -53,7 +53,7 @@
#include "widgets/misc/e-error.h"
-#include "filter/vfolder-rule.h"
+#include "em-vfolder-rule.h"
#include "mail-mt.h"
#include "mail-ops.h"
@@ -2285,9 +2285,9 @@ emft_popup_new_folder_response (EMFolderSelector *emfs, int response, EMFolderTr
/* HACK: we need to create vfolders using the vfolder editor */
if (CAMEL_IS_VEE_STORE(store)) {
- VfolderRule *rule;
+ EMVFolderRule *rule;
- rule = vfolder_rule_new();
+ rule = em_vfolder_rule_new();
filter_rule_set_name((FilterRule *)rule, path);
vfolder_gui_add_rule(rule);
gtk_widget_destroy((GtkWidget *)emfs);
diff --git a/mail/em-search-context.c b/mail/em-search-context.c
new file mode 100644
index 0000000000..c3d9f052ed
--- /dev/null
+++ b/mail/em-search-context.c
@@ -0,0 +1,112 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "em-search-context.h"
+#include "filter/filter-rule.h"
+#include "filter/filter-option.h"
+#include "filter/filter-int.h"
+
+static FilterElement *em_search_new_element(RuleContext *rc, const char *type);
+
+static RuleContextClass *parent_class = NULL;
+
+static void
+em_search_context_finalise (GObject *obj)
+{
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+em_search_context_class_init (EMSearchContextClass *klass)
+{
+ parent_class = g_type_class_ref (RULE_TYPE_CONTEXT);
+
+ ((GObjectClass *)klass)->finalize = em_search_context_finalise;
+ ((RuleContextClass *)klass)->new_element = em_search_new_element;
+}
+
+static void
+em_search_context_init (EMSearchContext *vc)
+{
+ rule_context_add_part_set ((RuleContext *)vc, "partset", filter_part_get_type (),
+ rule_context_add_part, rule_context_next_part);
+
+ rule_context_add_rule_set ((RuleContext *)vc, "ruleset", filter_rule_get_type (),
+ rule_context_add_rule, rule_context_next_rule);
+
+ ((RuleContext *)vc)->flags = RULE_CONTEXT_THREADING | RULE_CONTEXT_GROUPING;
+}
+
+GType
+em_search_context_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (EMSearchContextClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) em_search_context_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EMSearchContext),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) em_search_context_init,
+ };
+
+ type = g_type_register_static (RULE_TYPE_CONTEXT, "EMSearchContext", &info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * em_search_context_new:
+ *
+ * Create a new EMSearchContext object.
+ *
+ * Return value: A new #EMSearchContext object.
+ **/
+EMSearchContext *
+em_search_context_new (void)
+{
+ return (EMSearchContext *) g_object_new (EM_SEARCH_TYPE_CONTEXT, NULL, NULL);
+}
+
+static FilterElement *
+em_search_new_element(RuleContext *rc, const char *type)
+{
+ if (!strcmp (type, "system-flag")) {
+ return (FilterElement *) filter_option_new ();
+ } else if (!strcmp (type, "score")) {
+ return (FilterElement *) filter_int_new_type("score", -3, 3);
+ } else {
+ return parent_class->new_element(rc, type);
+ }
+}
diff --git a/mail/em-search-context.h b/mail/em-search-context.h
new file mode 100644
index 0000000000..3d618f99dc
--- /dev/null
+++ b/mail/em-search-context.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _EM_SEARCH_CONTEXT_H
+#define _EM_SEARCH_CONTEXT_H
+
+#include "filter/rule-context.h"
+
+#define EM_SEARCH_TYPE_CONTEXT (em_search_context_get_type ())
+#define EM_SEARCH_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EM_SEARCH_TYPE_CONTEXT, EMSearchContext))
+#define EM_SEARCH_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EM_SEARCH_TYPE_CONTEXT, EMSearchContextClass))
+#define IS_EM_SEARCH_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EM_SEARCH_TYPE_CONTEXT))
+#define IS_EM_SEARCH_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EM_SEARCH_TYPE_CONTEXT))
+#define EM_SEARCH_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EM_SEARCH_TYPE_CONTEXT, EMSearchContextClass))
+
+typedef struct _EMSearchContext EMSearchContext;
+typedef struct _EMSearchContextClass EMSearchContextClass;
+
+struct _EMSearchContext {
+ RuleContext parent_object;
+};
+
+struct _EMSearchContextClass {
+ RuleContextClass parent_class;
+};
+
+GType em_search_context_get_type (void);
+
+EMSearchContext *em_search_context_new (void);
+
+#endif /* ! _EM_SEARCH_CONTEXT_H */
diff --git a/mail/em-utils.c b/mail/em-utils.c
index fe7b30e7c5..0e625110a6 100644
--- a/mail/em-utils.c
+++ b/mail/em-utils.c
@@ -35,7 +35,7 @@
#include <camel/camel-url-scanner.h>
#include <camel/camel-file-utils.h>
-#include <filter/filter-editor.h>
+#include "em-filter-editor.h"
#include <bonobo/bonobo-listener.h>
#include <bonobo/bonobo-widget.h>
@@ -229,9 +229,9 @@ em_utils_check_user_can_send_mail (GtkWidget *parent)
static GtkWidget *filter_editor = NULL;
static void
-filter_editor_response (GtkWidget *dialog, int button, gpointer user_data)
+em_filter_editor_response (GtkWidget *dialog, int button, gpointer user_data)
{
- FilterContext *fc;
+ EMFilterContext *fc;
if (button == GTK_RESPONSE_OK) {
char *user;
@@ -248,7 +248,7 @@ filter_editor_response (GtkWidget *dialog, int button, gpointer user_data)
filter_editor = NULL;
}
-static const char *filter_source_names[] = {
+static const char *em_filter_source_element_names[] = {
"incoming",
"outgoing",
NULL,
@@ -267,14 +267,14 @@ em_utils_edit_filters (GtkWidget *parent)
{
const char *base_directory = mail_component_peek_base_directory (mail_component_peek ());
char *user, *system;
- FilterContext *fc;
+ EMFilterContext *fc;
if (filter_editor) {
gdk_window_raise (GTK_WIDGET (filter_editor)->window);
return;
}
- fc = filter_context_new ();
+ fc = em_filter_context_new ();
user = g_strdup_printf ("%s/mail/filters.xml", base_directory);
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
rule_context_load ((RuleContext *) fc, system, user);
@@ -285,13 +285,13 @@ em_utils_edit_filters (GtkWidget *parent)
return;
}
- filter_editor = (GtkWidget *) filter_editor_new (fc, filter_source_names);
+ filter_editor = (GtkWidget *) em_filter_editor_new (fc, em_filter_source_element_names);
if (parent != NULL)
e_dialog_set_transient_for ((GtkWindow *) filter_editor, parent);
gtk_window_set_title (GTK_WINDOW (filter_editor), _("Filters"));
g_object_set_data_full ((GObject *) filter_editor, "context", fc, (GtkDestroyNotify) g_object_unref);
- g_signal_connect (filter_editor, "response", G_CALLBACK (filter_editor_response), NULL);
+ g_signal_connect (filter_editor, "response", G_CALLBACK (em_filter_editor_response), NULL);
gtk_widget_show (GTK_WIDGET (filter_editor));
}
diff --git a/mail/em-vfolder-context.c b/mail/em-vfolder-context.c
new file mode 100644
index 0000000000..b06e411cf5
--- /dev/null
+++ b/mail/em-vfolder-context.c
@@ -0,0 +1,113 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright(C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "em-vfolder-context.h"
+#include "em-vfolder-rule.h"
+#include "filter/filter-option.h"
+#include "filter/filter-int.h"
+
+static FilterElement *vfolder_new_element(RuleContext *rc, const char *type);
+
+static RuleContextClass *parent_class = NULL;
+
+static void
+em_vfolder_context_finalise(GObject *obj)
+{
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+em_vfolder_context_class_init(EMVFolderContextClass *klass)
+{
+ parent_class = g_type_class_ref(RULE_TYPE_CONTEXT);
+
+ ((GObjectClass *)klass)->finalize = em_vfolder_context_finalise;
+ ((RuleContextClass *)klass)->new_element = vfolder_new_element;
+}
+
+static void
+em_vfolder_context_init(EMVFolderContext *vc)
+{
+ rule_context_add_part_set((RuleContext *) vc, "partset", filter_part_get_type(),
+ rule_context_add_part, rule_context_next_part);
+
+ rule_context_add_rule_set((RuleContext *) vc, "ruleset", em_vfolder_rule_get_type(),
+ rule_context_add_rule, rule_context_next_rule);
+
+ ((RuleContext *)vc)->flags = RULE_CONTEXT_THREADING | RULE_CONTEXT_GROUPING;
+}
+
+GType
+em_vfolder_context_get_type(void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof(EMVFolderContextClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) em_vfolder_context_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(EMVFolderContext),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) em_vfolder_context_init,
+ };
+
+ type = g_type_register_static(RULE_TYPE_CONTEXT, "EMVFolderContext", &info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * em_vfolder_context_new:
+ *
+ * Create a new EMVFolderContext object.
+ *
+ * Return value: A new #EMVFolderContext object.
+ **/
+EMVFolderContext *
+em_vfolder_context_new(void)
+{
+ return (EMVFolderContext *)g_object_new(em_vfolder_context_get_type(), NULL, NULL);
+}
+
+static FilterElement *
+vfolder_new_element(RuleContext *rc, const char *type)
+{
+ if (!strcmp(type, "system-flag")) {
+ return (FilterElement *) filter_option_new();
+ } else if (!strcmp(type, "score")) {
+ return (FilterElement *) filter_int_new_type("score", -3, 3);
+ } else {
+ return parent_class->new_element(rc, type);
+ }
+}
+
diff --git a/mail/em-vfolder-context.h b/mail/em-vfolder-context.h
new file mode 100644
index 0000000000..48fa94b279
--- /dev/null
+++ b/mail/em-vfolder-context.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _EM_VFOLDER_CONTEXT_H
+#define _EM_VFOLDER_CONTEXT_H
+
+#include "filter/rule-context.h"
+
+#define EM_VFOLDER_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), em_vfolder_context_get_type(), EMVFolderContext))
+#define EM_VFOLDER_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), em_vfolder_context_get_type(), EMVFolderContextClass))
+#define EM_IS_VFOLDER_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), em_vfolder_context_get_type()))
+#define EM_IS_VFOLDER_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), em_vfolder_context_get_type()))
+#define EM_VFOLDER_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), em_vfolder_context_get_type(), EMVFolderContextClass))
+
+typedef struct _EMVFolderContext EMVFolderContext;
+typedef struct _EMVFolderContextClass EMVFolderContextClass;
+
+struct _EMVFolderContext {
+ RuleContext parent_object;
+
+};
+
+struct _EMVFolderContextClass {
+ RuleContextClass parent_class;
+};
+
+GType em_vfolder_context_get_type (void);
+
+EMVFolderContext *em_vfolder_context_new (void);
+
+#endif /* ! _EM_VFOLDER_CONTEXT_H */
diff --git a/mail/em-vfolder-editor.c b/mail/em-vfolder-editor.c
new file mode 100644
index 0000000000..3974790223
--- /dev/null
+++ b/mail/em-vfolder-editor.c
@@ -0,0 +1,123 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <libgnome/gnome-i18n.h>
+
+#include "em-vfolder-editor.h"
+#include "em-vfolder-rule.h"
+
+#define d(x)
+
+static FilterRule *create_rule (RuleEditor *re);
+
+static RuleEditorClass *parent_class = NULL;
+
+
+static void
+em_vfolder_editor_finalise (GObject *obj)
+{
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+em_vfolder_editor_class_init (EMVFolderEditorClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ RuleEditorClass *re_class = (RuleEditorClass *) klass;
+
+ parent_class = g_type_class_ref (rule_editor_get_type ());
+
+ gobject_class->finalize = em_vfolder_editor_finalise;
+
+ /* override methods */
+ re_class->create_rule = create_rule;
+}
+
+static void
+em_vfolder_editor_init (EMVFolderEditor *ve)
+{
+ ;
+}
+
+GtkType
+em_vfolder_editor_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (EMVFolderEditorClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) em_vfolder_editor_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EMVFolderEditor),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) em_vfolder_editor_init,
+ };
+
+ type = g_type_register_static (RULE_TYPE_EDITOR, "EMVFolderEditor", &info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * em_vfolder_editor_new:
+ *
+ * Create a new EMVFolderEditor object.
+ *
+ * Return value: A new #EMVFolderEditor object.
+ **/
+EMVFolderEditor *
+em_vfolder_editor_new (EMVFolderContext *vc)
+{
+ EMVFolderEditor *ve = (EMVFolderEditor *) g_object_new (em_vfolder_editor_get_type(), NULL);
+ GladeXML *gui;
+
+ gui = glade_xml_new (EVOLUTION_GLADEDIR "/filter.glade", "rule_editor", NULL);
+ rule_editor_construct ((RuleEditor *) ve, (RuleContext *) vc, gui, NULL, _("Virtual _Folders"));
+ gtk_widget_hide(glade_xml_get_widget (gui, "filter_source"));
+ g_object_unref (gui);
+
+ return ve;
+}
+
+static FilterRule *
+create_rule (RuleEditor *re)
+{
+ FilterRule *rule = filter_rule_new ();
+ FilterPart *part;
+
+ /* create a rule with 1 part in it */
+ rule = (FilterRule *) em_vfolder_rule_new ();
+ part = rule_context_next_part (re->context, NULL);
+ filter_rule_add_part (rule, filter_part_clone (part));
+
+ return rule;
+}
diff --git a/mail/em-vfolder-editor.h b/mail/em-vfolder-editor.h
new file mode 100644
index 0000000000..5771e6b7a6
--- /dev/null
+++ b/mail/em-vfolder-editor.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _EM_VFOLDER_EDITOR_H
+#define _EM_VFOLDER_EDITOR_H
+
+#include "filter/rule-editor.h"
+#include "em-vfolder-context.h"
+
+#define EM_VFOLDER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), em_vfolder_editor_get_type(), EMVFolderEditor))
+#define EM_VFOLDER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), em_vfolder_editor_get_type(), EMVFolderEditorClass))
+#define EM_IS_VFOLDER_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), em_vfolder_editor_get_type()))
+#define EM_IS_VFOLDER_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), em_vfolder_editor_get_type()))
+#define EM_VFOLDER_EDITOR_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), em_vfolder_editor_get_type(), EMVFolderEditorClass))
+
+typedef struct _EMVFolderEditor EMVFolderEditor;
+typedef struct _EMVFolderEditorClass EMVFolderEditorClass;
+
+struct _EMVFolderEditor {
+ RuleEditor parent_object;
+
+};
+
+struct _EMVFolderEditorClass {
+ RuleEditorClass parent_class;
+};
+
+GtkType em_vfolder_editor_get_type (void);
+
+EMVFolderEditor *em_vfolder_editor_new (EMVFolderContext *vc);
+
+#endif /* ! _EM_VFOLDER_EDITOR_H */
diff --git a/mail/em-vfolder-rule.c b/mail/em-vfolder-rule.c
new file mode 100644
index 0000000000..ec4f9bbb2a
--- /dev/null
+++ b/mail/em-vfolder-rule.c
@@ -0,0 +1,643 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright(C)2000-2002 Ximian Inc.
+ *
+ * Author: Not Zed <notzed@lostzed.mmc.com.au>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <libgnome/gnome-i18n.h>
+
+#include "camel/camel-url.h"
+#include "em-vfolder-context.h"
+#include "em-vfolder-rule.h"
+#include "mail/em-utils.h"
+#include "mail/em-folder-tree.h"
+#include "mail/em-folder-selector.h"
+#include "mail/mail-component.h"
+#include "widgets/misc/e-error.h"
+
+#define d(x)
+
+static int validate(FilterRule *);
+static int vfolder_eq(FilterRule *fr, FilterRule *cm);
+static xmlNodePtr xml_encode(FilterRule *);
+static int xml_decode(FilterRule *, xmlNodePtr, RuleContext *f);
+static void rule_copy(FilterRule *dest, FilterRule *src);
+/*static void build_code(FilterRule *, GString *out);*/
+static GtkWidget *get_widget(FilterRule *fr, RuleContext *f);
+
+static void em_vfolder_rule_class_init(EMVFolderRuleClass *klass);
+static void em_vfolder_rule_init(EMVFolderRule *vr);
+static void em_vfolder_rule_finalise(GObject *obj);
+
+/* DO NOT internationalise these strings */
+const char *with_names[] = {
+ "specific",
+ "local",
+ "remote_active",
+ "local_remote_active"
+};
+
+static FilterRuleClass *parent_class = NULL;
+
+GType
+em_vfolder_rule_get_type(void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof(EMVFolderRuleClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc)em_vfolder_rule_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(EMVFolderRule),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)em_vfolder_rule_init,
+ };
+
+ type = g_type_register_static(FILTER_TYPE_RULE, "EMVFolderRule", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+em_vfolder_rule_class_init(EMVFolderRuleClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ FilterRuleClass *fr_class =(FilterRuleClass *)klass;
+
+ parent_class = g_type_class_ref(FILTER_TYPE_RULE);
+
+ object_class->finalize = em_vfolder_rule_finalise;
+
+ /* override methods */
+ fr_class->validate = validate;
+ fr_class->eq = vfolder_eq;
+ fr_class->xml_encode = xml_encode;
+ fr_class->xml_decode = xml_decode;
+ fr_class->copy = rule_copy;
+ /*fr_class->build_code = build_code;*/
+ fr_class->get_widget = get_widget;
+}
+
+static void
+em_vfolder_rule_init(EMVFolderRule *vr)
+{
+ vr->with = EM_VFOLDER_RULE_WITH_SPECIFIC;
+}
+
+static void
+em_vfolder_rule_finalise(GObject *obj)
+{
+ EMVFolderRule *vr =(EMVFolderRule *)obj;
+
+ g_list_foreach(vr->sources, (GFunc)g_free, NULL);
+ g_list_free(vr->sources);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+/**
+ * em_vfolder_rule_new:
+ *
+ * Create a new EMVFolderRule object.
+ *
+ * Return value: A new #EMVFolderRule object.
+ **/
+EMVFolderRule *
+em_vfolder_rule_new(void)
+{
+ return (EMVFolderRule *)g_object_new(em_vfolder_rule_get_type(), NULL, NULL);
+}
+
+void
+em_vfolder_rule_add_source(EMVFolderRule *vr, const char *uri)
+{
+ g_assert(EM_IS_VFOLDER_RULE(vr));
+
+ vr->sources = g_list_append(vr->sources, g_strdup(uri));
+
+ filter_rule_emit_changed((FilterRule *)vr);
+}
+
+const char *
+em_vfolder_rule_find_source(EMVFolderRule *vr, const char *uri)
+{
+ GList *l;
+
+ g_assert(EM_IS_VFOLDER_RULE(vr));
+
+ /* only does a simple string or address comparison, should
+ probably do a decoded url comparison */
+ l = vr->sources;
+ while (l) {
+ if (l->data == uri || !strcmp(l->data, uri))
+ return l->data;
+ l = l->next;
+ }
+
+ return NULL;
+}
+
+void
+em_vfolder_rule_remove_source(EMVFolderRule *vr, const char *uri)
+{
+ char *found;
+
+ g_assert(EM_IS_VFOLDER_RULE(vr));
+
+ found =(char *)em_vfolder_rule_find_source(vr, uri);
+ if (found) {
+ vr->sources = g_list_remove(vr->sources, found);
+ g_free(found);
+ filter_rule_emit_changed((FilterRule *)vr);
+ }
+}
+
+const char *
+em_vfolder_rule_next_source(EMVFolderRule *vr, const char *last)
+{
+ GList *node;
+
+ if (last == NULL) {
+ node = vr->sources;
+ } else {
+ node = g_list_find(vr->sources, (char *)last);
+ if (node == NULL)
+ node = vr->sources;
+ else
+ node = g_list_next(node);
+ }
+
+ if (node)
+ return (const char *)node->data;
+
+ return NULL;
+}
+
+static int
+validate(FilterRule *fr)
+{
+ g_return_val_if_fail(fr != NULL, FALSE);
+
+ if (!fr->name || !*fr->name) {
+ /* FIXME: set a aprent window? */
+ e_error_run(NULL, "mail:no-name-vfolder", NULL);
+ return 0;
+ }
+
+ /* We have to have at least one source set in the "specific" case.
+ Do not translate this string! */
+ if (((EMVFolderRule *)fr)->with == EM_VFOLDER_RULE_WITH_SPECIFIC && ((EMVFolderRule *)fr)->sources == NULL) {
+ /* FIXME: set a parent window? */
+ e_error_run(NULL, "mail:vfolder-no-source", NULL);
+ return 0;
+ }
+
+ return FILTER_RULE_CLASS(parent_class)->validate(fr);
+}
+
+static int
+list_eq(GList *al, GList *bl)
+{
+ int truth = TRUE;
+
+ while (truth && al && bl) {
+ char *a = al->data, *b = bl->data;
+
+ truth = strcmp(a, b)== 0;
+ al = al->next;
+ bl = bl->next;
+ }
+
+ return truth && al == NULL && bl == NULL;
+}
+
+static int
+vfolder_eq(FilterRule *fr, FilterRule *cm)
+{
+ return FILTER_RULE_CLASS(parent_class)->eq(fr, cm)
+ && list_eq(((EMVFolderRule *)fr)->sources, ((EMVFolderRule *)cm)->sources);
+}
+
+static xmlNodePtr
+xml_encode(FilterRule *fr)
+{
+ EMVFolderRule *vr =(EMVFolderRule *)fr;
+ xmlNodePtr node, set, work;
+ GList *l;
+
+ node = FILTER_RULE_CLASS(parent_class)->xml_encode(fr);
+ g_assert(node != NULL);
+ g_assert(vr->with >= 0 && vr->with < sizeof(with_names)/sizeof(with_names[0]));
+ set = xmlNewNode(NULL, "sources");
+ xmlAddChild(node, set);
+ xmlSetProp(set, "with", with_names[vr->with]);
+ l = vr->sources;
+ while (l) {
+ work = xmlNewNode(NULL, "folder");
+ xmlSetProp(work, "uri", l->data);
+ xmlAddChild(set, work);
+ l = l->next;
+ }
+
+ return node;
+}
+
+static void
+set_with(EMVFolderRule *vr, const char *name)
+{
+ int i;
+
+ for(i=0;i<sizeof(with_names)/sizeof(with_names[0]);i++) {
+ if (!strcmp(name, with_names[i])) {
+ vr->with = i;
+ return;
+ }
+ }
+
+ vr->with = 0;
+}
+
+static int
+xml_decode(FilterRule *fr, xmlNodePtr node, struct _RuleContext *f)
+{
+ xmlNodePtr set, work;
+ int result;
+ EMVFolderRule *vr =(EMVFolderRule *)fr;
+ char *tmp;
+
+ result = FILTER_RULE_CLASS(parent_class)->xml_decode(fr, node, f);
+ if (result != 0)
+ return result;
+
+ /* handle old format file, vfolder source is in filterrule */
+ if (strcmp(fr->source, "incoming")!= 0) {
+ set_with(vr, fr->source);
+ g_free(fr->source);
+ fr->source = g_strdup("incoming");
+ }
+
+ set = node->children;
+ while (set) {
+ if (!strcmp(set->name, "sources")) {
+ tmp = xmlGetProp(set, "with");
+ if (tmp) {
+ set_with(vr, tmp);
+ xmlFree(tmp);
+ }
+ work = set->children;
+ while (work) {
+ if (!strcmp(work->name, "folder")) {
+ tmp = xmlGetProp(work, "uri");
+ if (tmp) {
+ vr->sources = g_list_append(vr->sources, g_strdup(tmp));
+ xmlFree(tmp);
+ }
+ }
+ work = work->next;
+ }
+ }
+ set = set->next;
+ }
+ return 0;
+}
+
+static void
+rule_copy(FilterRule *dest, FilterRule *src)
+{
+ EMVFolderRule *vdest, *vsrc;
+ GList *node;
+
+ vdest =(EMVFolderRule *)dest;
+ vsrc =(EMVFolderRule *)src;
+
+ if (vdest->sources) {
+ g_list_foreach(vdest->sources, (GFunc)g_free, NULL);
+ g_list_free(vdest->sources);
+ vdest->sources = NULL;
+ }
+
+ node = vsrc->sources;
+ while (node) {
+ char *uri = node->data;
+
+ vdest->sources = g_list_append(vdest->sources, g_strdup(uri));
+ node = node->next;
+ }
+
+ vdest->with = vsrc->with;
+
+ FILTER_RULE_CLASS(parent_class)->copy(dest, src);
+}
+
+enum {
+ BUTTON_ADD,
+ BUTTON_REMOVE,
+ BUTTON_LAST,
+};
+
+struct _source_data {
+ RuleContext *rc;
+ EMVFolderRule *vr;
+ const char *current;
+ GtkListStore *model;
+ GtkTreeView *list;
+ GtkButton *buttons[BUTTON_LAST];
+};
+
+static void source_add(GtkWidget *widget, struct _source_data *data);
+static void source_remove(GtkWidget *widget, struct _source_data *data);
+
+static struct {
+ char *name;
+ GtkSignalFunc func;
+} edit_buttons[] = {
+ { "source_add", G_CALLBACK(source_add) },
+ { "source_remove", G_CALLBACK(source_remove)},
+};
+
+static void
+set_sensitive(struct _source_data *data)
+{
+ gtk_widget_set_sensitive((GtkWidget *)data->buttons[BUTTON_ADD], TRUE);
+ gtk_widget_set_sensitive((GtkWidget *)data->buttons[BUTTON_REMOVE], data->current != NULL);
+}
+
+static void
+select_source(GtkWidget *list, struct _source_data *data)
+{
+ GtkTreeViewColumn *column;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ gtk_tree_view_get_cursor(data->list, &path, &column);
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path);
+ gtk_tree_path_free(path);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter, 0, &data->current, -1);
+
+ set_sensitive(data);
+}
+
+static void
+select_source_with_changed(GtkWidget *widget, struct _source_data *data)
+{
+ em_vfolder_rule_with_t with;
+
+ with = gtk_option_menu_get_history((GtkOptionMenu *)widget);
+ if (with < EM_VFOLDER_RULE_WITH_SPECIFIC || with > EM_VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE)
+ with = 0;
+ data->vr->with = with;
+}
+
+/* attempt to make a 'nice' folder name out of the raw uri */
+static char *format_source(const char *euri)
+{
+ CamelURL *url;
+ GString *out;
+ char *res, *uri;
+
+ /* This should really probably base it on the account name? */
+ uri = em_uri_to_camel(euri);
+ url = camel_url_new(uri, NULL);
+
+ /* bad uri */
+ if (url == NULL)
+ return uri;
+
+ g_free(uri);
+
+ out = g_string_new(url->protocol);
+ g_string_append_c(out, ':');
+ if (url->user && url->host) {
+ g_string_append_printf(out, "%s@%s", url->user, url->host);
+ if (url->port)
+ g_string_append_printf(out, ":%d", url->port);
+ }
+ if (url->fragment)
+ g_string_append(out, url->fragment);
+ else if (url->path)
+ g_string_append(out, url->path);
+
+ res = out->str;
+ g_string_free(out, FALSE);
+
+ return res;
+}
+
+static void
+vfr_folder_response(GtkWidget *dialog, gint button, struct _source_data *data)
+{
+ const char *uri = em_folder_selector_get_selected_uri((EMFolderSelector *)dialog);
+
+ if (button == GTK_RESPONSE_OK && uri != NULL) {
+ char *urinice, *euri;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ euri = em_uri_from_camel(uri);
+
+ data->vr->sources = g_list_append(data->vr->sources, euri);
+
+ gtk_list_store_append(data->model, &iter);
+ urinice = format_source(euri);
+ gtk_list_store_set(data->model, &iter, 0, urinice, 1, euri, -1);
+ g_free(urinice);
+ selection = gtk_tree_view_get_selection(data->list);
+ gtk_tree_selection_select_iter(selection, &iter);
+ data->current = euri;
+
+ set_sensitive(data);
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static void
+source_add(GtkWidget *widget, struct _source_data *data)
+{
+ EMFolderTree *emft;
+ GtkWidget *dialog;
+
+ emft =(EMFolderTree *)em_folder_tree_new_with_model(mail_component_peek_tree_model(mail_component_peek()));
+
+ dialog = em_folder_selector_new(emft, EM_FOLDER_SELECTOR_CAN_CREATE, _("Select Folder"), NULL, _("_Add"));
+ gtk_window_set_transient_for((GtkWindow *)dialog, (GtkWindow *)gtk_widget_get_toplevel(widget));
+ gtk_window_set_modal((GtkWindow *)dialog, TRUE);
+ g_signal_connect(dialog, "response", G_CALLBACK(vfr_folder_response), data);
+ gtk_widget_show(dialog);
+}
+
+static void
+source_remove(GtkWidget *widget, struct _source_data *data)
+{
+ GtkTreeSelection *selection;
+ const char *source;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ int index = 0;
+ int n;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->list));
+
+ source = NULL;
+ while ((source = em_vfolder_rule_next_source(data->vr, source))) {
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, index);
+
+ if (gtk_tree_selection_path_is_selected(selection, path)) {
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path);
+
+ em_vfolder_rule_remove_source(data->vr, source);
+ gtk_list_store_remove(data->model, &iter);
+ gtk_tree_path_free(path);
+
+ /* now select the next rule */
+ n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(data->model), NULL);
+ index = index >= n ? n - 1 : index;
+
+ if (index >= 0) {
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, index);
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path);
+ gtk_tree_path_free(path);
+
+ gtk_tree_selection_select_iter(selection, &iter);
+ gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter, 0, &data->current, -1);
+ } else {
+ data->current = NULL;
+ }
+
+ break;
+ }
+
+ index++;
+ gtk_tree_path_free(path);
+ }
+
+ set_sensitive(data);
+}
+
+
+GtkWidget *em_vfolder_editor_sourcelist_new(char *widget_name, char *string1, char *string2,
+ int int1, int int2);
+
+GtkWidget *
+em_vfolder_editor_sourcelist_new(char *widget_name, char *string1, char *string2, int int1, int int2)
+{
+ GtkWidget *table, *scrolled;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkListStore *model;
+
+ 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(2, G_TYPE_STRING, G_TYPE_POINTER);
+ table = gtk_tree_view_new_with_model((GtkTreeModel *)model);
+ gtk_tree_view_set_headers_visible((GtkTreeView *)table, FALSE);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes((GtkTreeView *)table, -1,
+ _("VFolder source"), 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);
+
+ return scrolled;
+}
+
+static GtkWidget *
+get_widget(FilterRule *fr, RuleContext *rc)
+{
+ EMVFolderRule *vr =(EMVFolderRule *)fr;
+ GtkWidget *widget, *frame, *list;
+ struct _source_data *data;
+ GtkOptionMenu *omenu;
+ const char *source;
+ GtkTreeIter iter;
+ GladeXML *gui;
+ int i;
+
+ widget = FILTER_RULE_CLASS(parent_class)->get_widget(fr, rc);
+
+ data = g_malloc0(sizeof(*data));
+ data->rc = rc;
+ data->vr = vr;
+
+ gui = glade_xml_new(EVOLUTION_GLADEDIR "/mail-config.glade", "vfolder_source_frame", NULL);
+ frame = glade_xml_get_widget(gui, "vfolder_source_frame");
+
+ g_object_set_data_full((GObject *)frame, "data", data, g_free);
+
+ for(i = 0; i < BUTTON_LAST; i++) {
+ data->buttons[i] =(GtkButton *)glade_xml_get_widget(gui, edit_buttons[i].name);
+ g_signal_connect(data->buttons[i], "clicked", edit_buttons[i].func, data);
+ }
+
+ list = glade_xml_get_widget(gui, "source_list");
+ data->list =(GtkTreeView *)g_object_get_data((GObject *)list, "table");
+ data->model =(GtkListStore *)g_object_get_data((GObject *)list, "model");
+
+ source = NULL;
+ while ((source = em_vfolder_rule_next_source(vr, source))) {
+ char *nice = format_source(source);
+
+ gtk_list_store_append(data->model, &iter);
+ gtk_list_store_set(data->model, &iter, 0, nice, 1, source, -1);
+ g_free(nice);
+ }
+
+ g_signal_connect(data->list, "cursor-changed", G_CALLBACK(select_source), data);
+
+ omenu =(GtkOptionMenu *)glade_xml_get_widget(gui, "source_option");
+ gtk_option_menu_set_history(omenu, vr->with);
+ g_signal_connect(omenu, "changed", G_CALLBACK(select_source_with_changed), data);
+
+ set_sensitive(data);
+
+ g_object_unref(gui);
+
+ gtk_box_pack_start(GTK_BOX(widget), frame, TRUE, TRUE, 3);
+
+ return widget;
+}
diff --git a/mail/em-vfolder-rule.h b/mail/em-vfolder-rule.h
new file mode 100644
index 0000000000..717df74709
--- /dev/null
+++ b/mail/em-vfolder-rule.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2002 Ximian Inc.
+ *
+ * Author: NotZed <notzed@ximian.com>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _EM_VFOLDER_RULE_H
+#define _EM_VFOLDER_RULE_H
+
+#include "filter/filter-rule.h"
+
+#define EM_VFOLDER_RULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), em_vfolder_rule_get_type(), EMVFolderRule))
+#define EM_VFOLDER_RULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), em_vfolder_rule_get_type(), EMVFolderRuleClass))
+#define EM_IS_VFOLDER_RULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), em_vfolder_rule_get_type()))
+#define EM_IS_VFOLDER_RULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), em_vfolder_rule_get_type()))
+#define EM_VFOLDER_RULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), em_vfolder_rule_get_type(), EMVFolderRuleClass))
+
+/* perhaps should be bits? */
+enum _em_vfolder_rule_with_t {
+ EM_VFOLDER_RULE_WITH_SPECIFIC,
+ EM_VFOLDER_RULE_WITH_LOCAL,
+ EM_VFOLDER_RULE_WITH_REMOTE_ACTIVE,
+ EM_VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE,
+};
+
+typedef struct _EMVFolderRule EMVFolderRule;
+typedef struct _EMVFolderRuleClass EMVFolderRuleClass;
+
+typedef enum _em_vfolder_rule_with_t em_vfolder_rule_with_t;
+
+struct _EMVFolderRule {
+ FilterRule rule;
+
+ em_vfolder_rule_with_t with;
+ GList *sources; /* uri's of the source folders */
+};
+
+struct _EMVFolderRuleClass {
+ FilterRuleClass parent_class;
+};
+
+GType em_vfolder_rule_get_type (void);
+EMVFolderRule *em_vfolder_rule_new (void);
+
+/* methods */
+void em_vfolder_rule_add_source (EMVFolderRule *vr, const char *uri);
+void em_vfolder_rule_remove_source (EMVFolderRule *vr, const char *uri);
+const char *em_vfolder_rule_find_source (EMVFolderRule *vr, const char *uri);
+const char *em_vfolder_rule_next_source (EMVFolderRule *vr, const char *last);
+
+#endif /* ! _EM_VFOLDER_RULE_H */
diff --git a/mail/filtertypes.xml b/mail/filtertypes.xml
new file mode 100644
index 0000000000..dffa4e965f
--- /dev/null
+++ b/mail/filtertypes.xml
@@ -0,0 +1,746 @@
+<?xml version="1.0"?>
+<filterdescription>
+<partset>
+ <part name="sender">
+ <title>Sender</title>
+ <input type="optionlist" name="sender-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (match-all (header-contains "From" ${sender}))
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (match-all (not (header-contains "From" ${sender})))
+ </code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (header-matches "From" ${sender}))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (header-matches "From" ${sender})))
+ </code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with "From" ${sender}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with "From" ${sender})))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with "From" ${sender}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with "From" ${sender})))
+ </code>
+ </option>
+ <option value="matches soundex">
+ <title>sounds like</title>
+ <code>
+ (match-all (header-soundex "From" ${sender}))
+ </code>
+ </option>
+ <option value="not match soundex">
+ <title>does not sound like</title>
+ <code>
+ (match-all (not (header-soundex "From" ${sender})))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="sender"/>
+ </part>
+
+ <part name="to">
+ <title>Recipients</title>
+ <input type="optionlist" name="recipient-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (match-all (or (header-contains "To" ${recipient})
+ (header-contains "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (match-all (not (or
+ (header-contains "To" ${recipient})
+ (header-contains "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (or (header-matches "To" ${recipient})
+ (header-matches "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (or
+ (header-matches "To" ${recipient})
+ (header-matches "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (or (header-starts-with "To" ${recipient})
+ (header-starts-with "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (or
+ (header-starts-with "To" ${recipient})
+ (header-starts-with "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (or (header-ends-with "To" ${recipient})
+ (header-ends-with "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (or
+ (header-ends-with "To" ${recipient})
+ (header-ends-with "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="matches soundex">
+ <title>sounds like</title>
+ <code>
+ (match-all (or (header-soundex "To" ${recipient})
+ (header-soundex "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not match soundex">
+ <title>does not sound like</title>
+ <code>
+ (match-all (not (or
+ (header-soundex "To" ${recipient})
+ (header-soundex "Cc" ${recipient}))))
+ </code>
+ </option>
+ </input>
+ <input type="address" name="recipient"/>
+ </part>
+
+ <part name="subject">
+ <title>Subject</title>
+ <input type="optionlist" name="subject-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (match-all (header-contains "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (match-all (not (header-contains "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (header-matches "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (header-matches "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="matches soundex">
+ <title>sounds like</title>
+ <code>
+ (match-all (header-soundex "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not match soundex">
+ <title>does not sound like</title>
+ <code>
+ (match-all (not (header-soundex "Subject" ${subject})))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="subject"/>
+ </part>
+
+ <part name="header">
+ <title>Specific header</title>
+ <input type="string" name="header-field"/>
+ <input type="optionlist" name="header-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (match-all (header-contains ${header-field} ${word}))
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (match-all (not (header-contains ${header-field} ${word})))
+ </code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (header-matches ${header-field} ${word}))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (header-matches ${header-field} ${word})))
+ </code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with ${header-field} ${word}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with ${header-field} ${word})))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with ${header-field} ${word}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with ${header-field} ${word})))
+ </code>
+ </option>
+ <option value="exists">
+ <title>exists</title>
+ <code>
+ (match-all (header-exists ${header-field}))
+ </code>
+ </option>
+ <option value="not exists">
+ <title>does not exist</title>
+ <code>
+ (match-all (not (header-exists ${header-field})))
+ </code>
+ </option>
+ <option value="matches soundex">
+ <title>sounds like</title>
+ <code>
+ (match-all (header-soundex ${header-field} ${word}))
+ </code>
+ </option>
+ <option value="not match soundex">
+ <title>does not sound like</title>
+ <code>
+ (match-all (not (header-soundex ${header-field} ${word})))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="word"/>
+ </part>
+
+ <part name="body">
+ <title>Message Body</title>
+ <input type="optionlist" name="body-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (body-contains ${word})
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (match-all (not (body-contains ${word})))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="word"/>
+ </part>
+
+ <part name="sexp">
+ <title>Expression</title>
+ <input type="code" name="code"/>
+ </part>
+
+ <part name="sent-date">
+ <title>Date sent</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (get-sent-date) ${versus})))
+ </code>
+ </option>
+ <option value="before">
+ <title>is before</title>
+ <code>
+ (match-all (&lt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="after">
+ <title>is after</title>
+ <code>
+ (match-all (&gt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus"/>
+ </part>
+
+ <part name="recv-date">
+ <title>Date received</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (get-received-date) ${versus})))
+ </code>
+ </option>
+ <option value="before">
+ <title>is before</title>
+ <code>
+ (match-all (&lt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="after">
+ <title>is after</title>
+ <code>
+ (match-all (&gt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus"/>
+ </part>
+
+ <part name="label">
+ <title>Label</title>
+ <input type="optionlist" name="label-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (user-tag "label") ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (user-tag "label") ${versus})))
+ </code>
+ </option>
+ </input>
+ <input type="label" name="versus"/>
+ </part>
+
+ <part name="score">
+ <title>Score</title>
+ <input type="optionlist" name="score-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (cast-int (user-tag "score")) ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (cast-int (user-tag "score")) ${versus})))
+ </code>
+ </option>
+ <option value="greater-than">
+ <title>is greater than</title>
+ <code>
+ (match-all (&gt; (cast-int (user-tag "score")) ${versus}))
+ </code>
+ </option>
+ <option value="less-than">
+ <title>is less than</title>
+ <code>
+ (match-all (&lt; (cast-int (user-tag "score")) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="score" name="versus"/>
+ </part>
+
+ <part name="size">
+ <title>Size (kB)</title>
+ <input type="optionlist" name="size-type">
+ <option value="greater-than">
+ <title>is greater than</title>
+ <code>
+ (match-all (&gt; (get-size) ${versus}))
+ </code>
+ </option>
+ <option value="less-than">
+ <title>is less than</title>
+ <code>
+ (match-all (&lt; (get-size) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="integer" name="versus"/>
+ </part>
+
+ <part name="status">
+ <title>Status</title>
+ <input type="optionlist" name="match-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (system-flag ${flag}))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (system-flag ${flag})))
+ </code>
+ </option>
+ </input>
+ <input type="optionlist" name="flag">
+ <option value="Answered">
+ <title>Replied to</title>
+ </option>
+ <option value="Draft">
+ <title>Draft</title>
+ </option>
+ <option value="Flagged">
+ <title>Important</title>
+ </option>
+ <option value="Seen">
+ <title>Read</title>
+ </option>
+ <option value="Junk">
+ <title>Junk</title>
+ </option>
+ </input>
+ </part>
+
+ <part name="follow-up">
+ <title>Follow Up</title>
+ <input type="optionlist" name="match-type">
+ <option value="is">
+ <title>is Flagged</title>
+ <code>
+ (match-all (not (= (user-tag "follow-up") "")))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not Flagged</title>
+ <code>
+ (match-all (= (user-tag "follow-up") ""))
+ </code>
+ </option>
+ </input>
+ </part>
+
+ <part name="attachments">
+ <title>Attachments</title>
+ <input type="optionlist" name="match-type">
+ <option value="exist">
+ <title>Exist</title>
+ <code>
+ (match-all (system-flag "Attachments"))
+ </code>
+ </option>
+ <option value="not exist">
+ <title>Do Not Exist</title>
+ <code>
+ (match-all (not (system-flag "Attachments")))
+ </code>
+ </option>
+ </input>
+ </part>
+
+ <part name="mlist">
+ <title>Mailing list</title>
+ <input type="optionlist" name="mlist-type">
+ <option value="is">
+ <title>is</title>
+ <code>(match-all (header-matches "x-camel-mlist" ${mlist}))</code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>(match-all (not (header-matches "x-camel-mlist" ${mlist})))</code>
+ </option>
+ <option value="contains">
+ <title>contains</title>
+ <code>(match-all (header-contains "x-camel-mlist" ${mlist}))</code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>(match-all (not (header-contains "x-camel-mlist" ${mlist})))</code>
+ </option>
+ </input>
+ <input type="string" name="mlist"/>
+ </part>
+
+ <part name="regex">
+ <title>Regex Match</title>
+ <input type="optionlist" name="match-type">
+ <option value="header">
+ <title>Message Header</title>
+ <code>
+ (match-all (header-full-regex ${expression}))
+ </code>
+ </option>
+ <option value="body">
+ <title>Message Body</title>
+ <code>
+ (match-all (body-regex ${expression}))
+ </code>
+ </option>
+ </input>
+ <input type="regex" name="expression"/>
+ </part>
+
+ <part name="source">
+ <title>Source Account</title>
+ <input type="optionlist" name="srcmatch-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (header-source ${source}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (header-source ${source})))
+ </code>
+ </option>
+ </input>
+ <input type="source" name="source"/>
+ </part>
+
+ <part name="pipe">
+ <title>Pipe to Program</title>
+ <input type="command" name="command"/>
+ <input type="optionlist" name="retval-type">
+ <option value="is">
+ <title>returns</title>
+ <code>
+ (match-all (= (pipe-message "/bin/sh" "-c" ${command}) ${retval}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>does not return</title>
+ <code>
+ (match-all (not (= (pipe-message "/bin/sh" "-c" ${command}) ${retval})))
+ </code>
+ </option>
+ <option value="greater-than">
+ <title>returns greater than</title>
+ <code>
+ (match-all (&gt; (pipe-message "/bin/sh" "-c" ${command}) ${retval}))
+ </code>
+ </option>
+ <option value="less-than">
+ <title>returns less than</title>
+ <code>
+ (match-all (&lt; (pipe-message "/bin/sh" "-c" ${command}) ${retval}))
+ </code>
+ </option>
+ </input>
+ <input type="integer" name="retval"/>
+ </part>
+
+ <part name="junk">
+ <title>Junk Test</title>
+ <input type="optionlist" name="retval-type">
+ <option value="is-junk">
+ <title>Message is Junk</title>
+ <code>
+ (match-all (junk-test))
+ </code>
+ </option>
+ <option value="is-not-junk">
+ <title>Message is not Junk</title>
+ <code>
+ (match-all (not (junk-test)))
+ </code>
+ </option>
+ </input>
+ </part>
+
+</partset>
+
+
+<actionset>
+ <part name="move-to-folder">
+ <title>Move to Folder</title>
+ <code>(move-to ${folder})</code>
+ <input type="folder" name="folder"/>
+ </part>
+ <part name="copy-to-folder">
+ <title>Copy to Folder</title>
+ <code>(copy-to ${folder})</code>
+ <input type="folder" name="folder"/>
+ </part>
+ <part name="delete">
+ <title>Delete</title>
+ <code>(delete)</code>
+ </part>
+ <part name="stop">
+ <title>Stop Processing</title>
+ <code>(stop)</code>
+ </part>
+ <part name="colour">
+ <title>Assign Color</title>
+ <code>(set-colour ${colour})</code>
+ <input type="colour" name="colour"/>
+ </part>
+ <part name="score">
+ <title>Assign Score</title>
+ <code>(set-score ${score})</code>
+ <input type="score" name="score"/>
+ </part>
+ <part name="adj-score">
+ <title>Adjust Score</title>
+ <code>(adjust-score ${score})</code>
+ <input type="score" name="score"/>
+ </part>
+ <part name="set-status">
+ <title>Set Status</title>
+ <code>
+ (set-system-flag ${flag})
+ </code>
+ <input type="optionlist" name="flag">
+ <option value="Answered">
+ <title>Replied to</title>
+ </option>
+ <option value="Deleted">
+ <title>Deleted</title>
+ </option>
+ <option value="Draft">
+ <title>Draft</title>
+ </option>
+ <option value="Flagged">
+ <title>Important</title>
+ </option>
+ <option value="Seen">
+ <title>Read</title>
+ </option>
+ <option value="Junk">
+ <title>Junk</title>
+ </option>
+ </input>
+ </part>
+ <part name="unset-status">
+ <title>Unset Status</title>
+ <code>
+ (unset-system-flag ${flag})
+ </code>
+ <input type="optionlist" name="flag">
+ <option value="Answered">
+ <title>Replied to</title>
+ </option>
+ <option value="Deleted">
+ <title>Deleted</title>
+ </option>
+ <option value="Draft">
+ <title>Draft</title>
+ </option>
+ <option value="Flagged">
+ <title>Important</title>
+ </option>
+ <option value="Seen">
+ <title>Read</title>
+ </option>
+ <option value="Junk">
+ <title>Junk</title>
+ </option>
+ </input>
+ </part>
+ <part name="beep">
+ <title>Beep</title>
+ <code>(beep)</code>
+ </part>
+ <part name="play-sound">
+ <title>Play Sound</title>
+ <code>(play-sound ${sound})</code>
+ <input type="file" name="sound"/>
+ </part>
+ <part name="shell">
+ <title>Run Program</title>
+ <code>(shell "/bin/sh" "-c" ${command})</code>
+ <input type="command" name="command"/>
+ </part>
+ <part name="pipe">
+ <title>Pipe to Program</title>
+ <code>(pipe-message "/bin/sh" "-c" ${command})</code>
+ <input type="command" name="command"/>
+ </part>
+</actionset>
+</filterdescription>
diff --git a/mail/importers/netscape-importer.c b/mail/importers/netscape-importer.c
index 8389e5c5e0..658bc2317f 100644
--- a/mail/importers/netscape-importer.c
+++ b/mail/importers/netscape-importer.c
@@ -49,11 +49,11 @@
#include <importer/GNOME_Evolution_Importer.h>
#include <importer/evolution-importer-client.h>
-#include <filter/filter-context.h>
-#include <filter/filter-filter.h>
+#include "mail/em-filter-context.h"
+#include "mail/em-filter-rule.h"
#include <filter/filter-rule.h>
#include <filter/filter-option.h>
-#include <filter/filter-folder.h>
+#include "mail/em-filter-folder-element.h"
#include <filter/filter-int.h>
#include "e-util/e-account-list.h"
@@ -711,16 +711,16 @@ netscape_filter_body_is_not_supported (void)
static FilterRule*
-netscape_create_priority_converter (FilterContext *fc, NsFilterActionValueType priority)
+netscape_create_priority_converter (EMFilterContext *fc, NsFilterActionValueType priority)
{
- FilterFilter *ff;
+ EMFilterRule *ff;
FilterPart *fp;
FilterRule *fr;
FilterElement *el;
char s[MAXLEN];
int v;
- ff = filter_filter_new ();
+ ff = em_filter_rule_new ();
fr = FILTER_RULE(ff);
g_snprintf (s, MAXLEN, filter_name, ns_filter_action_value_types[priority]);
@@ -737,7 +737,7 @@ netscape_create_priority_converter (FilterContext *fc, NsFilterActionValueType p
filter_input_set_value ((FilterInput*)el,
ns_filter_action_value_types[priority]);
- fp = filter_context_create_action (fc, "score");
+ fp = em_filter_context_create_action (fc, "score");
el = filter_part_find_element (fp, "score");
switch (priority) {
@@ -762,14 +762,14 @@ netscape_create_priority_converter (FilterContext *fc, NsFilterActionValueType p
}
filter_int_set_value((FilterInt *)el, v);
- filter_filter_add_action (ff, fp);
+ em_filter_rule_add_action (ff, fp);
return FILTER_RULE(ff);
}
static void
-netscape_add_priority_workaround_filters (FilterContext *fc)
+netscape_add_priority_workaround_filters (EMFilterContext *fc)
{
FilterRule *fr;
@@ -822,11 +822,11 @@ netscape_filter_score_set (NsFilterCondition *cond, FilterInt *el)
}
-static FilterFilter *
-netscape_filter_to_evol_filter (FilterContext *fc, NsFilter *nsf, gboolean *priority_needed)
+static EMFilterRule *
+netscape_filter_to_evol_filter (EMFilterContext *fc, NsFilter *nsf, gboolean *priority_needed)
{
RuleContext *rc = RULE_CONTEXT(fc);
- FilterFilter *ff = NULL;
+ EMFilterRule *ff = NULL;
FilterPart *fp;
FilterRule *fr;
FilterElement *el;
@@ -834,7 +834,7 @@ netscape_filter_to_evol_filter (FilterContext *fc, NsFilter *nsf, gboolean *prio
gboolean part_added = FALSE, action_added = FALSE;
- ff = filter_filter_new ();
+ ff = em_filter_rule_new ();
fr = FILTER_RULE(ff);
filter_rule_set_name (fr, nsf->name);
@@ -1113,13 +1113,13 @@ netscape_filter_to_evol_filter (FilterContext *fc, NsFilter *nsf, gboolean *prio
char *evol_folder;
char *evol_folder_uri;
- fp = filter_context_create_action (fc, "move-to-folder");
- filter_filter_add_action (ff, fp);
+ fp = em_filter_context_create_action (fc, "move-to-folder");
+ em_filter_rule_add_action (ff, fp);
el = filter_part_find_element (fp, "folder");
evol_folder = netscape_filter_strip_sbd (nsf->action_val_str);
evol_folder_uri = netscape_filter_map_folder_to_uri (evol_folder);
- filter_folder_set_value ((FilterFolder *)el, evol_folder_uri);
+ em_filter_folder_element_set_value ((EMFilterFolderElement *)el, evol_folder_uri);
g_free (evol_folder);
g_free (evol_folder_uri);
@@ -1127,7 +1127,7 @@ netscape_filter_to_evol_filter (FilterContext *fc, NsFilter *nsf, gboolean *prio
}
break;
case CHANGE_PRIORITY:
- fp = filter_context_create_action (fc, "score");
+ fp = em_filter_context_create_action (fc, "score");
el = filter_part_find_element (fp, "score");
switch (nsf->action_val_id) {
@@ -1156,19 +1156,19 @@ netscape_filter_to_evol_filter (FilterContext *fc, NsFilter *nsf, gboolean *prio
}
if (action_added) {
*priority_needed = TRUE;
- filter_filter_add_action (ff, fp);
+ em_filter_rule_add_action (ff, fp);
}
break;
case DELETE:
- fp = filter_context_create_action (fc, "delete");
- filter_filter_add_action (ff, fp);
+ fp = em_filter_context_create_action (fc, "delete");
+ em_filter_rule_add_action (ff, fp);
action_added = TRUE;
break;
case MARK_READ:
- fp = filter_context_create_action (fc, "set-status");
+ fp = em_filter_context_create_action (fc, "set-status");
el = filter_part_find_element (fp, "flag");
filter_option_set_current ((FilterOption *)el, "Seen");
- filter_filter_add_action (ff, fp);
+ em_filter_rule_add_action (ff, fp);
action_added = TRUE;
break;
case IGNORE_THREAD:
@@ -1191,12 +1191,12 @@ netscape_filter_to_evol_filter (FilterContext *fc, NsFilter *nsf, gboolean *prio
static void
netscape_import_filters (NsImporter *importer)
{
- FilterContext *fc;
+ EMFilterContext *fc;
char *user, *system;
FILE *mailrule_handle;
char *ns_mailrule;
NsFilter *nsf;
- FilterFilter *ff;
+ EMFilterRule *ff;
gboolean priority_needed = FALSE;
ns_mailrule = gnome_util_prepend_user_home (".netscape/mailrule");
@@ -1209,7 +1209,7 @@ netscape_import_filters (NsImporter *importer)
return;
}
- fc = filter_context_new ();
+ fc = em_filter_context_new ();
user = g_build_filename(g_get_home_dir (), "evolution/filters.xml");
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
diff --git a/mail/mail-autofilter.c b/mail/mail-autofilter.c
index 469c7ce55b..c660a03204 100644
--- a/mail/mail-autofilter.c
+++ b/mail/mail-autofilter.c
@@ -43,13 +43,13 @@
#include "em-utils.h"
#include "widgets/misc/e-error.h"
-#include "filter/vfolder-context.h"
-#include "filter/vfolder-rule.h"
-#include "filter/vfolder-editor.h"
+#include "em-vfolder-context.h"
+#include "em-vfolder-rule.h"
+#include "em-vfolder-editor.h"
-#include "filter/filter-context.h"
-#include "filter/filter-filter.h"
-#include "filter/filter-editor.h"
+#include "em-filter-context.h"
+#include "em-filter-rule.h"
+#include "em-filter-editor.h"
#include "filter/filter-option.h"
@@ -277,13 +277,13 @@ rule_from_message (FilterRule *rule, RuleContext *context, CamelMimeMessage *msg
}
FilterRule *
-vfolder_rule_from_message (VfolderContext *context, CamelMimeMessage *msg, int flags, const char *source)
+em_vfolder_rule_from_message (EMVFolderContext *context, CamelMimeMessage *msg, int flags, const char *source)
{
- VfolderRule *rule;
+ EMVFolderRule *rule;
char *euri = em_uri_from_camel(source);
- rule = vfolder_rule_new ();
- vfolder_rule_add_source (rule, euri);
+ rule = em_vfolder_rule_new ();
+ em_vfolder_rule_add_source (rule, euri);
rule_from_message ((FilterRule *)rule, (RuleContext *)context, msg, flags);
g_free(euri);
@@ -291,16 +291,16 @@ vfolder_rule_from_message (VfolderContext *context, CamelMimeMessage *msg, int f
}
FilterRule *
-filter_rule_from_message (FilterContext *context, CamelMimeMessage *msg, int flags)
+filter_rule_from_message (EMFilterContext *context, CamelMimeMessage *msg, int flags)
{
- FilterFilter *rule;
+ EMFilterRule *rule;
FilterPart *part;
- rule = filter_filter_new ();
+ rule = em_filter_rule_new ();
rule_from_message ((FilterRule *)rule, (RuleContext *)context, msg, flags);
- part = filter_context_next_action (context, NULL);
- filter_filter_add_action (rule, filter_part_clone (part));
+ part = em_filter_context_next_action (context, NULL);
+ em_filter_rule_add_action (rule, filter_part_clone (part));
return (FilterRule *)rule;
}
@@ -308,13 +308,13 @@ filter_rule_from_message (FilterContext *context, CamelMimeMessage *msg, int fla
void
filter_gui_add_from_message (CamelMimeMessage *msg, const char *source, int flags)
{
- FilterContext *fc;
+ EMFilterContext *fc;
char *user, *system;
FilterRule *rule;
g_return_if_fail (msg != NULL);
- fc = filter_context_new ();
+ fc = em_filter_context_new ();
user = g_strdup_printf ("%s/mail/filters.xml",
mail_component_peek_base_directory (mail_component_peek ()));
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
@@ -331,7 +331,7 @@ filter_gui_add_from_message (CamelMimeMessage *msg, const char *source, int flag
void
mail_filter_rename_uri(CamelStore *store, const char *olduri, const char *newuri)
{
- FilterContext *fc;
+ EMFilterContext *fc;
char *user, *system;
GList *changed;
char *eolduri, *enewuri;
@@ -339,7 +339,7 @@ mail_filter_rename_uri(CamelStore *store, const char *olduri, const char *newuri
eolduri = em_uri_from_camel(olduri);
enewuri = em_uri_from_camel(newuri);
- fc = filter_context_new ();
+ fc = em_filter_context_new ();
user = g_strdup_printf ("%s/mail/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
rule_context_load ((RuleContext *)fc, system, user);
@@ -362,14 +362,14 @@ mail_filter_rename_uri(CamelStore *store, const char *olduri, const char *newuri
void
mail_filter_delete_uri(CamelStore *store, const char *uri)
{
- FilterContext *fc;
+ EMFilterContext *fc;
char *user, *system;
GList *deleted;
char *euri;
euri = em_uri_from_camel(uri);
- fc = filter_context_new ();
+ fc = em_filter_context_new ();
user = g_strdup_printf ("%s/mail/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
rule_context_load ((RuleContext *)fc, system, user);
diff --git a/mail/mail-autofilter.h b/mail/mail-autofilter.h
index 45f9d15863..c6501e2fa1 100644
--- a/mail/mail-autofilter.h
+++ b/mail/mail-autofilter.h
@@ -26,8 +26,8 @@
#define _MAIL_AUTOFILTER_H
#include <filter/filter-rule.h>
-#include <filter/filter-context.h>
-#include <filter/vfolder-context.h>
+#include "em-filter-context.h"
+#include "em-vfolder-context.h"
#include <camel/camel-mime-message.h>
enum {
@@ -37,8 +37,8 @@ enum {
AUTO_MLIST = 8,
};
-FilterRule *vfolder_rule_from_message(VfolderContext *context, CamelMimeMessage *msg, int flags, const char *source);
-FilterRule *filter_rule_from_message(FilterContext *context, CamelMimeMessage *msg, int flags);
+FilterRule *em_vfolder_rule_from_message(EMVFolderContext *context, CamelMimeMessage *msg, int flags, const char *source);
+FilterRule *filter_rule_from_message(EMFilterContext *context, CamelMimeMessage *msg, int flags);
/* easiest place to put this */
void filter_gui_add_from_message (CamelMimeMessage *msg, const char *source, int flags);
diff --git a/mail/mail-component.c b/mail/mail-component.c
index 8a72dbba4d..76212813eb 100644
--- a/mail/mail-component.c
+++ b/mail/mail-component.c
@@ -49,7 +49,7 @@
#include "widgets/misc/e-info-label.h"
#include "widgets/misc/e-error.h"
-#include "filter/rule-context.h"
+#include "em-search-context.h"
#include "mail-config.h"
#include "mail-component.h"
#include "mail-folder-cache.h"
@@ -172,7 +172,6 @@ static void
mc_add_store(MailComponent *component, CamelStore *store, const char *name, void (*done)(CamelStore *store, CamelFolderInfo *info, void *data))
{
struct _store_info *si;
- char *uri;
MAIL_COMPONENT_DEFAULT(component);
@@ -297,18 +296,9 @@ setup_search_context (MailComponent *component)
char *user = g_build_filename(component->priv->base_directory, "mail/searches.xml", NULL);
char *system = g_strdup (EVOLUTION_PRIVDATADIR "/searchtypes.xml");
- priv->search_context = rule_context_new ();
- /* This is a sort of hack, but saves us having to have a search context just to do it for us */
- priv->search_context->flags |= RULE_CONTEXT_THREADING;
+ priv->search_context = (RuleContext *)em_search_context_new ();
g_object_set_data_full (G_OBJECT (priv->search_context), "user", user, g_free);
g_object_set_data_full (G_OBJECT (priv->search_context), "system", system, g_free);
-
- rule_context_add_part_set (priv->search_context, "partset", filter_part_get_type (),
- rule_context_add_part, rule_context_next_part);
-
- rule_context_add_rule_set (priv->search_context, "ruleset", filter_rule_get_type (),
- rule_context_add_rule, rule_context_next_rule);
-
rule_context_load (priv->search_context, system, user);
}
}
diff --git a/mail/mail-config.glade b/mail/mail-config.glade
index 967a7a6d71..aabf7d60ee 100644
--- a/mail/mail-config.glade
+++ b/mail/mail-config.glade
@@ -8374,4 +8374,282 @@ for display purposes only. </property>
</child>
</widget>
+<widget class="GtkDialog" id="vfolder-source">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="title" translatable="yes"></property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="apply_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-apply</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">0</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vfolder_source_frame">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;vFolder Sources&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"> </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkOptionMenu" id="source_option">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="history">0</property>
+
+ <child internal-child="menu">
+ <widget class="GtkMenu" id="convertwidget8">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">specific folders only</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">with all local folders</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget11">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">with all active remote folders</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="convertwidget12">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">with all local and active remote folders</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="Custom" id="source_list">
+ <property name="visible">True</property>
+ <property name="creation_function">em_vfolder_editor_sourcelist_new</property>
+ <property name="int1">0</property>
+ <property name="int2">0</property>
+ <property name="last_modification_time">Fri, 13 Dec 2002 00:22:39 GMT</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">1</property>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="source_add">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="source_remove">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">3</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
</glade-interface>
diff --git a/mail/mail-errors.xml b/mail/mail-errors.xml
index 0a9996dc8b..5190fbf9ad 100644
--- a/mail/mail-errors.xml
+++ b/mail/mail-errors.xml
@@ -282,6 +282,23 @@ Used the now removed folder:
And have been updated.</secondary>
</error>
+ <error id="no-folder" type="error">
+ <primary>Missing folder.</primary>
+ <secondary>You must specify a folder.</secondary>
+ </error>
+
+ <error id="no-name-vfolder" type="error">
+ <primary>Missing name.</primary>
+ <secondary>You must name this vFolder.</secondary>
+ </error>
+
+ <error id="vfolder-no-source" type="error">
+ <primary>No sources selected.</primary>
+ <secondary>You must specify at least one folder as a source.
+Either by selecting the folders individually, and/or by selecting
+all local folders, all remote folders, or both.</secondary>
+ </error>
+
<error id="ask-migrate-existing" type="question" default="GTK_RESPONSE_CANCEL">
<primary>Problem migrating old mail folder &quot;{0}&quot;.</primary>
<secondary>A non-empty folder at &quot;{1}&quot; already exists.
diff --git a/mail/mail-errors.xml.h b/mail/mail-errors.xml.h
index 8e1b4acec1..f3db9cac7f 100644
--- a/mail/mail-errors.xml.h
+++ b/mail/mail-errors.xml.h
@@ -206,6 +206,20 @@ char *s = N_("The following filter rule(s):\n"
"Used the now removed folder:\n"
" \"{1}\"\n"
"And have been updated.");
+/* mail:no-folder primary */
+char *s = N_("Missing folder.");
+/* mail:no-folder secondary */
+char *s = N_("You must specify a folder.");
+/* mail:no-name-vfolder primary */
+char *s = N_("Missing name.");
+/* mail:no-name-vfolder secondary */
+char *s = N_("You must name this vFolder.");
+/* mail:vfolder-no-source primary */
+char *s = N_("No sources selected.");
+/* mail:vfolder-no-source secondary */
+char *s = N_("You must specify at least one folder as a source.\n"
+ "Either by selecting the folders individually, and/or by selecting\n"
+ "all local folders, all remote folders, or both.");
/* mail:ask-migrate-existing primary */
char *s = N_("Problem migrating old mail folder \"{0}\".");
/* mail:ask-migrate-existing secondary */
diff --git a/mail/mail-ops.c b/mail/mail-ops.c
index 0a112947d1..d6351fe435 100644
--- a/mail/mail-ops.c
+++ b/mail/mail-ops.c
@@ -56,7 +56,7 @@
#include "mail-session.h"
#include "composer/e-msg-composer.h"
-#include "filter/filter-filter.h"
+#include "em-filter-rule.h"
#include "mail-mt.h"
@@ -93,7 +93,7 @@ struct _fetch_mail_msg {
};
static char *
-filter_folder_describe (struct _mail_msg *mm, int complete)
+em_filter_folder_element_describe (struct _mail_msg *mm, int complete)
{
return g_strdup (_("Filtering Folder"));
}
@@ -101,7 +101,7 @@ filter_folder_describe (struct _mail_msg *mm, int complete)
/* filter a folder, or a subset thereof, uses source_folder/source_uids */
/* this is shared with fetch_mail */
static void
-filter_folder_filter (struct _mail_msg *mm)
+em_filter_folder_element_filter (struct _mail_msg *mm)
{
struct _filter_mail_msg *m = (struct _filter_mail_msg *)mm;
CamelFolder *folder;
@@ -154,12 +154,12 @@ filter_folder_filter (struct _mail_msg *mm)
}
static void
-filter_folder_filtered (struct _mail_msg *mm)
+em_filter_folder_element_filtered (struct _mail_msg *mm)
{
}
static void
-filter_folder_free (struct _mail_msg *mm)
+em_filter_folder_element_free (struct _mail_msg *mm)
{
struct _filter_mail_msg *m = (struct _filter_mail_msg *)mm;
@@ -181,11 +181,11 @@ filter_folder_free (struct _mail_msg *mm)
mail_session_flush_filter_log ();
}
-static struct _mail_msg_op filter_folder_op = {
- filter_folder_describe, /* we do our own progress reporting? */
- filter_folder_filter,
- filter_folder_filtered,
- filter_folder_free,
+static struct _mail_msg_op em_filter_folder_element_op = {
+ em_filter_folder_element_describe, /* we do our own progress reporting? */
+ em_filter_folder_element_filter,
+ em_filter_folder_element_filtered,
+ em_filter_folder_element_free,
};
void
@@ -195,7 +195,7 @@ mail_filter_folder (CamelFolder *source_folder, GPtrArray *uids,
{
struct _filter_mail_msg *m;
- m = mail_msg_new (&filter_folder_op, NULL, sizeof (*m));
+ m = mail_msg_new (&em_filter_folder_element_op, NULL, sizeof (*m));
m->source_folder = source_folder;
camel_object_ref (source_folder);
m->source_uids = uids;
@@ -315,7 +315,7 @@ fetch_mail_fetch (struct _mail_msg *mm)
camel_uid_cache_free_uids (cache_uids);
fm->cache = cache;
- filter_folder_filter (mm);
+ em_filter_folder_element_filter (mm);
/* save the cache of uids that we've just downloaded */
camel_uid_cache_save (cache);
@@ -330,7 +330,7 @@ fetch_mail_fetch (struct _mail_msg *mm)
camel_uid_cache_destroy (cache);
camel_folder_free_uids (folder, folder_uids);
} else {
- filter_folder_filter (mm);
+ em_filter_folder_element_filter (mm);
}
/* we unref the source folder here since we
@@ -371,7 +371,7 @@ fetch_mail_free (struct _mail_msg *mm)
if (m->cancel)
camel_operation_unref (m->cancel);
- filter_folder_free (mm);
+ em_filter_folder_element_free (mm);
}
static struct _mail_msg_op fetch_mail_op = {
diff --git a/mail/mail-send-recv.c b/mail/mail-send-recv.c
index a45290f924..99dbe5620b 100644
--- a/mail/mail-send-recv.c
+++ b/mail/mail-send-recv.c
@@ -43,7 +43,7 @@
#include "e-util/e-account-list.h"
#include "widgets/misc/e-clipped-label.h"
-#include "filter/filter-filter.h"
+#include "em-filter-rule.h"
#include "camel/camel-filter-driver.h"
#include "camel/camel-folder.h"
#include "camel/camel-operation.h"
@@ -317,7 +317,7 @@ build_dialog (EAccountList *accounts, CamelFolder *outbox, const char *destinati
GdkPixbuf *pixbuf;
GList *icon_list;
- gd = (GtkDialog *)send_recv_dialog = gtk_dialog_new_with_buttons(_("Send & Receive Mail"), NULL, GTK_DIALOG_NO_SEPARATOR, NULL);
+ gd = (GtkDialog *)(send_recv_dialog = gtk_dialog_new_with_buttons(_("Send & Receive Mail"), NULL, GTK_DIALOG_NO_SEPARATOR, NULL));
gtk_window_set_modal ((GtkWindow *) gd, FALSE);
stop = (GtkButton *)e_gtk_button_new_with_icon(_("Cancel _All"), GTK_STOCK_CANCEL);
diff --git a/mail/mail-session.c b/mail/mail-session.c
index dc36dbc7c7..6516ca541c 100644
--- a/mail/mail-session.c
+++ b/mail/mail-session.c
@@ -43,8 +43,8 @@
#include <camel/camel.h> /* FIXME: this is where camel_init is defined, it shouldn't include everything else */
#include "camel/camel-filter-driver.h"
-#include "filter/filter-context.h"
-#include "filter/filter-filter.h"
+#include "em-filter-context.h"
+#include "em-filter-rule.h"
#include "mail-component.h"
#include "mail-config.h"
#include "mail-session.h"
@@ -615,7 +615,7 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException
user = g_strdup_printf ("%s/mail/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
- fc = (RuleContext *) filter_context_new ();
+ fc = (RuleContext *) em_filter_context_new ();
rule_context_load (fc, system, user);
g_free (user);
@@ -664,7 +664,7 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException
g_string_truncate (faction, 0);
filter_rule_build_code (rule, fsearch);
- filter_filter_build_action ((FilterFilter *) rule, faction);
+ em_filter_rule_build_action ((EMFilterRule *) rule, faction);
camel_filter_driver_add_rule (driver, rule->name, fsearch->str, faction->str);
}
diff --git a/mail/mail-tools.c b/mail/mail-tools.c
index 5a6f13d5a4..7cf0878a2e 100644
--- a/mail/mail-tools.c
+++ b/mail/mail-tools.c
@@ -41,8 +41,8 @@
#include <camel/camel-file-utils.h>
#include <camel/camel-movemail.h>
-#include <filter/vfolder-rule.h>
-#include <filter/vfolder-context.h>
+#include "em-vfolder-rule.h"
+#include "em-vfolder-context.h"
#include <filter/filter-option.h>
#include <filter/filter-input.h>
diff --git a/mail/mail-vfolder.c b/mail/mail-vfolder.c
index 14fa477b63..c8ed01a9eb 100644
--- a/mail/mail-vfolder.c
+++ b/mail/mail-vfolder.c
@@ -45,12 +45,12 @@
#include "camel/camel-vee-store.h"
#include "camel/camel-vtrash-folder.h"
-#include "filter/vfolder-context.h"
-#include "filter/vfolder-editor.h"
+#include "em-vfolder-context.h"
+#include "em-vfolder-editor.h"
#define d(x) /*(printf("%s(%d):%s: ", __FILE__, __LINE__, __PRETTY_FUNCTION__), (x))*/
-static VfolderContext *context; /* context remains open all time */
+static EMVFolderContext *context; /* context remains open all time */
CamelStore *vfolder_store; /* the 1 static vfolder store */
/* lock for accessing shared resources (below) */
@@ -465,14 +465,14 @@ mail_vfolder_add_uri(CamelStore *store, const char *curi, int remove)
/* dont auto-add any sent/drafts folders etc, they must be explictly listed as a source */
if (rule->source
&& !is_ignore
- && ((((VfolderRule *)rule)->with == VFOLDER_RULE_WITH_LOCAL && !remote)
- || (((VfolderRule *)rule)->with == VFOLDER_RULE_WITH_REMOTE_ACTIVE && remote)
- || (((VfolderRule *)rule)->with == VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE)))
+ && ((((EMVFolderRule *)rule)->with == EM_VFOLDER_RULE_WITH_LOCAL && !remote)
+ || (((EMVFolderRule *)rule)->with == EM_VFOLDER_RULE_WITH_REMOTE_ACTIVE && remote)
+ || (((EMVFolderRule *)rule)->with == EM_VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE)))
found = TRUE;
/* we check using the store uri_cmp since its more accurate */
source = NULL;
- while (!found && (source = vfolder_rule_next_source((VfolderRule *)rule, source))) {
+ while (!found && (source = em_vfolder_rule_next_source((EMVFolderRule *)rule, source))) {
char *esource;
esource = em_uri_from_camel(source);
@@ -526,7 +526,7 @@ mail_vfolder_delete_uri(CamelStore *store, const char *curi)
rule = NULL;
while ((rule = rule_context_next_rule ((RuleContext *) context, rule, NULL))) {
source = NULL;
- while ((source = vfolder_rule_next_source ((VfolderRule *) rule, source))) {
+ while ((source = em_vfolder_rule_next_source ((EMVFolderRule *) rule, source))) {
/* Remove all sources that match, ignore changed events though
because the adduri call above does the work async */
if (uri_cmp (uri, source)) {
@@ -534,7 +534,7 @@ mail_vfolder_delete_uri(CamelStore *store, const char *curi)
g_assert (vf != NULL);
g_signal_handlers_disconnect_matched (rule, G_SIGNAL_MATCH_FUNC|G_SIGNAL_MATCH_DATA, 0,
0, NULL, rule_changed, vf);
- vfolder_rule_remove_source ((VfolderRule *)rule, source);
+ em_vfolder_rule_remove_source ((EMVFolderRule *)rule, source);
g_signal_connect (rule, "changed", G_CALLBACK(rule_changed), vf);
g_string_append_printf (changed, " %s\n", rule->name);
source = NULL;
@@ -600,7 +600,7 @@ mail_vfolder_rename_uri(CamelStore *store, const char *cfrom, const char *cto)
rule = NULL;
while ( (rule = rule_context_next_rule((RuleContext *)context, rule, NULL)) ) {
source = NULL;
- while ( (source = vfolder_rule_next_source((VfolderRule *)rule, source)) ) {
+ while ( (source = em_vfolder_rule_next_source((EMVFolderRule *)rule, source)) ) {
/* Remove all sources that match, ignore changed events though
because the adduri call above does the work async */
if (uri_cmp(from, source)) {
@@ -609,8 +609,8 @@ mail_vfolder_rename_uri(CamelStore *store, const char *cfrom, const char *cto)
g_assert(vf);
g_signal_handlers_disconnect_matched(rule, G_SIGNAL_MATCH_FUNC|G_SIGNAL_MATCH_DATA, 0,
0, NULL, rule_changed, vf);
- vfolder_rule_remove_source((VfolderRule *)rule, source);
- vfolder_rule_add_source((VfolderRule *)rule, to);
+ em_vfolder_rule_remove_source((EMVFolderRule *)rule, source);
+ em_vfolder_rule_add_source((EMVFolderRule *)rule, to);
g_signal_connect(rule, "changed", G_CALLBACK(rule_changed), vf);
changed++;
source = NULL;
@@ -693,12 +693,12 @@ rule_changed(FilterRule *rule, CamelFolder *folder)
d(printf("Filter rule changed? for folder '%s'!!\n", folder->name));
/* find any (currently available) folders, and add them to the ones to open */
- rule_add_sources(((VfolderRule *)rule)->sources, &sources_folder, &sources_uri);
+ rule_add_sources(((EMVFolderRule *)rule)->sources, &sources_folder, &sources_uri);
LOCK();
- if (((VfolderRule *)rule)->with == VFOLDER_RULE_WITH_LOCAL || ((VfolderRule *)rule)->with == VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE)
+ if (((EMVFolderRule *)rule)->with == EM_VFOLDER_RULE_WITH_LOCAL || ((EMVFolderRule *)rule)->with == EM_VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE)
rule_add_sources(source_folders_local, &sources_folder, &sources_uri);
- if (((VfolderRule *)rule)->with == VFOLDER_RULE_WITH_REMOTE_ACTIVE || ((VfolderRule *)rule)->with == VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE)
+ if (((EMVFolderRule *)rule)->with == EM_VFOLDER_RULE_WITH_REMOTE_ACTIVE || ((EMVFolderRule *)rule)->with == EM_VFOLDER_RULE_WITH_LOCAL_REMOTE_ACTIVE)
rule_add_sources(source_folders_remote, &sources_folder, &sources_uri);
UNLOCK();
@@ -870,7 +870,7 @@ vfolder_load_storage(void)
/* load our rules */
user = g_strdup_printf ("%s/mail/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ()));
- context = vfolder_context_new ();
+ context = em_vfolder_context_new ();
if (rule_context_load ((RuleContext *)context,
EVOLUTION_PRIVDATADIR "/vfoldertypes.xml", user) != 0) {
g_warning("cannot load vfolders: %s\n", ((RuleContext *)context)->error);
@@ -906,7 +906,7 @@ vfolder_revert(void)
static GtkWidget *vfolder_editor = NULL;
static void
-vfolder_editor_response (GtkWidget *dialog, int button, void *data)
+em_vfolder_editor_response (GtkWidget *dialog, int button, void *data)
{
char *user;
@@ -935,9 +935,9 @@ vfolder_edit (void)
return;
}
- vfolder_editor = GTK_WIDGET (vfolder_editor_new (context));
+ vfolder_editor = GTK_WIDGET (em_vfolder_editor_new (context));
gtk_window_set_title (GTK_WINDOW (vfolder_editor), _("vFolders"));
- g_signal_connect(vfolder_editor, "response", G_CALLBACK(vfolder_editor_response), NULL);
+ g_signal_connect(vfolder_editor, "response", G_CALLBACK(em_vfolder_editor_response), NULL);
gtk_widget_show (vfolder_editor);
}
@@ -1040,7 +1040,7 @@ vfolder_create_part(const char *name)
FilterRule *
vfolder_clone_rule(FilterRule *in)
{
- FilterRule *rule = (FilterRule *)vfolder_rule_new();
+ FilterRule *rule = (FilterRule *)em_vfolder_rule_new();
xmlNodePtr xml;
xml = filter_rule_xml_encode(in);
@@ -1052,7 +1052,7 @@ vfolder_clone_rule(FilterRule *in)
/* adds a rule with a gui */
void
-vfolder_gui_add_rule(VfolderRule *rule)
+vfolder_gui_add_rule(EMVFolderRule *rule)
{
GtkWidget *w;
GtkDialog *gd;
@@ -1082,11 +1082,11 @@ vfolder_gui_add_rule(VfolderRule *rule)
void
vfolder_gui_add_from_message(CamelMimeMessage *msg, int flags, const char *source)
{
- VfolderRule *rule;
+ EMVFolderRule *rule;
g_return_if_fail (msg != NULL);
- rule = (VfolderRule*)vfolder_rule_from_message(context, msg, flags, source);
+ rule = (EMVFolderRule*)em_vfolder_rule_from_message(context, msg, flags, source);
vfolder_gui_add_rule(rule);
}
diff --git a/mail/mail-vfolder.h b/mail/mail-vfolder.h
index f7fc684e56..f2b8f25766 100644
--- a/mail/mail-vfolder.h
+++ b/mail/mail-vfolder.h
@@ -6,7 +6,7 @@
#include "camel/camel-folder.h"
#include "camel/camel-mime-message.h"
-#include "filter/vfolder-rule.h"
+#include "em-vfolder-rule.h"
#include "filter/filter-part.h"
void vfolder_load_storage(void);
@@ -16,7 +16,7 @@ void vfolder_edit (void);
void vfolder_edit_rule(const char *name);
FilterPart *vfolder_create_part (const char *name);
FilterRule *vfolder_clone_rule (FilterRule *in);
-void vfolder_gui_add_rule (VfolderRule *rule);
+void vfolder_gui_add_rule (EMVFolderRule *rule);
void vfolder_gui_add_from_message (CamelMimeMessage *msg, int flags, const char *source);
/* add a uri that is now (un)available to vfolders in a transient manner */
diff --git a/mail/searchtypes.xml b/mail/searchtypes.xml
new file mode 100644
index 0000000000..8185b4a593
--- /dev/null
+++ b/mail/searchtypes.xml
@@ -0,0 +1,529 @@
+<?xml version="1.0"?>
+<filterdescription>
+<partset>
+ <part name="sender">
+ <title>Sender</title>
+ <input type="optionlist" name="sender-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>(match-all (header-contains "From" ${sender}))</code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>(match-all (not (header-contains "From" ${sender})))</code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>(match-all (header-matches "From" ${sender}))</code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>(match-all (not (header-matches "From" ${sender})))</code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with "From" ${sender}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with "From" ${sender})))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with "From" ${sender}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with "From" ${sender})))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="sender"/>
+ </part>
+
+ <part name="to">
+ <title>Recipients</title>
+ <input type="optionlist" name="recipient-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (match-all (or (header-contains "To" ${recipient})
+ (header-contains "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (match-all (not (or
+ (header-contains "To" ${recipient})
+ (header-contains "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (or (header-matches "To" ${recipient})
+ (header-matches "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (or
+ (header-matches "To" ${recipient})
+ (header-matches "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (or (header-starts-with "To" ${recipient})
+ (header-starts-with "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (or
+ (header-starts-with "To" ${recipient})
+ (header-starts-with "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (or (header-ends-with "To" ${recipient})
+ (header-ends-with "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (or
+ (header-ends-with "To" ${recipient})
+ (header-ends-with "Cc" ${recipient}))))
+ </code>
+ </option>
+ </input>
+ <input type="address" name="recipient"/>
+ </part>
+
+ <part name="subject">
+ <title>Subject</title>
+ <input type="optionlist" name="subject-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (match-all (header-contains "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (match-all (not (header-contains "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (header-matches "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (header-matches "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with "Subject" ${subject})))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="subject"/>
+ </part>
+ <part name="body">
+ <title>Message Body</title>
+ <input type="optionlist" name="body-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (body-contains ${word})
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (not (body-contains ${word}))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="word"/>
+ </part>
+ <part name="sexp">
+ <title>Expression</title>
+ <input type="code" name="code"/>
+ </part>
+
+ <part name="sent-date">
+ <title>Date sent</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (get-sent-date) ${versus})))
+ </code>
+ </option>
+ <option value="before">
+ <title>is before</title>
+ <code>
+ (match-all (&lt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="after">
+ <title>is after</title>
+ <code>
+ (match-all (&gt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus"/>
+ </part>
+
+ <part name="recv-date">
+ <title>Date received</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (get-received-date) ${versus})))
+ </code>
+ </option>
+ <option value="before">
+ <title>is before</title>
+ <code>
+ (match-all (&lt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="after">
+ <title>is after</title>
+ <code>
+ (match-all (&gt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus"/>
+ </part>
+
+ <part name="label">
+ <title>Label</title>
+ <input type="optionlist" name="label-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (user-tag "label") ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (user-tag "label") ${versus})))
+ </code>
+ </option>
+ </input>
+ <input type="label" name="versus"/>
+ </part>
+
+ <part name="score">
+ <title>Score</title>
+ <input type="optionlist" name="score-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (cast-int (user-tag "score")) ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (cast-int (user-tag "score")) ${versus})))
+ </code>
+ </option>
+ <option value="greater-than">
+ <title>is greater than</title>
+ <code>
+ (match-all (&gt; (cast-int (user-tag "score")) ${versus}))
+ </code>
+ </option>
+ <option value="less-than">
+ <title>is less than</title>
+ <code>
+ (match-all (&lt; (cast-int (user-tag "score")) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="score" name="versus"/>
+ </part>
+
+ <part name="size">
+ <title>Size (kB)</title>
+ <input type="optionlist" name="size-type">
+ <option value="greater-than">
+ <title>is greater than</title>
+ <code>
+ (match-all (&gt; (get-size) ${versus}))
+ </code>
+ </option>
+ <option value="less-than">
+ <title>is less than</title>
+ <code>
+ (match-all (&lt; (get-size) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="integer" name="versus"/>
+ </part>
+
+ <part name="status">
+ <title>Status</title>
+ <input type="optionlist" name="match-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (system-flag ${flag}))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (system-flag ${flag})))
+ </code>
+ </option>
+ </input>
+ <input type="optionlist" name="flag">
+ <option value="Answered">
+ <title>Replied to</title>
+ </option>
+ <option value="Deleted">
+ <title>Deleted</title>
+ </option>
+ <option value="Draft">
+ <title>Draft</title>
+ </option>
+ <option value="Flagged">
+ <title>Important</title>
+ </option>
+ <option value="Seen">
+ <title>Read</title>
+ </option>
+ </input>
+ </part>
+
+ <part name="follow-up">
+ <title>Follow Up</title>
+ <input type="optionlist" name="match-type">
+ <option value="is">
+ <title>is Flagged</title>
+ <code>
+ (match-all (not (= (user-tag "follow-up") "")))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not Flagged</title>
+ <code>
+ (match-all (= (user-tag "follow-up") ""))
+ </code>
+ </option>
+ </input>
+ </part>
+
+ <part name="attachments">
+ <title>Attachments</title>
+ <input type="optionlist" name="match-type">
+ <option value="exist">
+ <title>Exist</title>
+ <code>
+ (match-all (system-flag "Attachments"))
+ </code>
+ </option>
+ <option value="not exist">
+ <title>Do Not Exist</title>
+ <code>
+ (match-all (not (system-flag "Attachments")))
+ </code>
+ </option>
+ </input>
+ </part>
+
+ <part name="mlist">
+ <title>Mailing list</title>
+ <input type="optionlist" name="mlist-type">
+ <option value="is">
+ <title>is</title>
+ <code>(match-all (header-matches "x-camel-mlist" ${mlist}))</code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>(match-all (not (header-matches "x-camel-mlist" ${mlist})))</code>
+ </option>
+ <option value="contains">
+ <title>contains</title>
+ <code>(match-all (header-contains "x-camel-mlist" ${mlist}))</code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>(match-all (not (header-contains "x-camel-mlist" ${mlist})))</code>
+ </option>
+ </input>
+ <input type="string" name="mlist"/>
+ </part>
+
+</partset>
+
+ <ruleset>
+
+ <rule grouping="any" source="demand">
+ <_title>Subject contains</_title>
+ <partset>
+ <part name="subject">
+ <value name="subject-type" type="option" value="contains"/>
+ <value name="subject" type="string"/>
+ </part>
+ </partset>
+ <sources/>
+ </rule>
+
+ <rule grouping="any" source="demand">
+ <_title>Subject does not contain</_title>
+ <partset>
+ <part name="subject">
+ <value name="subject-type" type="option" value="not contains"/>
+ <value name="subject" type="string"/>
+ </part>
+ </partset>
+ <sources/>
+ </rule>
+
+ <rule grouping="any" source="demand">
+ <_title>Sender contains</_title>
+ <partset>
+ <part name="sender">
+ <value name="sender-type" type="option" value="contains"/>
+ <value name="sender" type="string"/>
+ </part>
+ </partset>
+ <sources/>
+ </rule>
+
+ <rule grouping="any" source="demand">
+ <_title>Recipients contain</_title>
+ <partset>
+ <part name="to">
+ <value name="recipient-type" type="option" value="contains"/>
+ <value name="recipient" type="address"/>
+ </part>
+ </partset>
+ <sources/>
+ </rule>
+
+ <rule grouping="any" source="demand">
+ <_title>Body contains</_title>
+ <partset>
+ <part name="body">
+ <value name="body-type" type="option" value="contains"/>
+ <value name="word" type="string"/>
+ </part>
+ </partset>
+ <sources/>
+ </rule>
+
+ <rule grouping="any" source="demand">
+ <_title>Body does not contain</_title>
+ <partset>
+ <part name="body">
+ <value name="body-type" type="option" value="not contains"/>
+ <value name="word" type="string"/>
+ </part>
+ </partset>
+ <sources/>
+ </rule>
+
+ <rule grouping="any" source="demand">
+ <_title>Body or subject contains</_title>
+ <partset>
+ <part name="subject">
+ <value name="subject-type" type="option" value="contains"/>
+ <value name="subject" type="string"/>
+ </part>
+ <part name="body">
+ <value name="body-type" type="option" value="contains"/>
+ <value name="word" type="string"/>
+ </part>
+ </partset>
+ <sources/>
+ </rule>
+
+ <rule grouping="any" source="demand">
+ <_title>Message contains</_title>
+ <partset>
+ <part name="subject">
+ <value name="subject-type" type="option" value="contains"/>
+ <value name="subject" type="string"/>
+ </part>
+ <part name="body">
+ <value name="body-type" type="option" value="contains"/>
+ <value name="word" type="string"/>
+ </part>
+ <part name="sender">
+ <value name="sender-type" type="option" value="contains"/>
+ <value name="sender" type="string"/>
+ </part>
+ <part name="to">
+ <value name="recipient-type" type="option" value="contains"/>
+ <value name="recipient" type="address"/>
+ </part>
+ </partset>
+ <sources/>
+ </rule>
+
+ </ruleset>
+</filterdescription>
diff --git a/mail/vfoldertypes.xml b/mail/vfoldertypes.xml
new file mode 100644
index 0000000000..d9e2b86841
--- /dev/null
+++ b/mail/vfoldertypes.xml
@@ -0,0 +1,424 @@
+<?xml version="1.0"?>
+<filterdescription>
+<partset>
+ <part name="sender">
+ <title>Sender</title>
+ <input type="optionlist" name="sender-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>(match-all (header-contains "From" ${sender}))</code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>(match-all (not (header-contains "From" ${sender})))</code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>(match-all (header-matches "From" ${sender}))</code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>(match-all (not (header-matches "From" ${sender})))</code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with "From" ${sender}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with "From" ${sender})))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with "From" ${sender}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with "From" ${sender})))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="sender"/>
+ </part>
+
+ <part name="to">
+ <title>Recipients</title>
+ <input type="optionlist" name="recipient-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (match-all (or (header-contains "To" ${recipient})
+ (header-contains "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (match-all (not (or
+ (header-contains "To" ${recipient})
+ (header-contains "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (or (header-matches "To" ${recipient})
+ (header-matches "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (or
+ (header-matches "To" ${recipient})
+ (header-matches "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (or (header-starts-with "To" ${recipient})
+ (header-starts-with "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (or
+ (header-starts-with "To" ${recipient})
+ (header-starts-with "Cc" ${recipient}))))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (or (header-ends-with "To" ${recipient})
+ (header-ends-with "Cc" ${recipient})))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (or
+ (header-ends-with "To" ${recipient})
+ (header-ends-with "Cc" ${recipient}))))
+ </code>
+ </option>
+ </input>
+ <input type="address" name="recipient"/>
+ </part>
+
+ <part name="subject">
+ <title>Subject</title>
+ <input type="optionlist" name="subject-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (match-all (header-contains "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (match-all (not (header-contains "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (header-matches "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (header-matches "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="starts with">
+ <title>starts with</title>
+ <code>
+ (match-all (header-starts-with "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not starts with">
+ <title>does not start with</title>
+ <code>
+ (match-all (not (header-starts-with "Subject" ${subject})))
+ </code>
+ </option>
+ <option value="ends with">
+ <title>ends with</title>
+ <code>
+ (match-all (header-ends-with "Subject" ${subject}))
+ </code>
+ </option>
+ <option value="not ends with">
+ <title>does not end with</title>
+ <code>
+ (match-all (not (header-ends-with "Subject" ${subject})))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="subject"/>
+ </part>
+ <part name="body">
+ <title>Message Body</title>
+ <input type="optionlist" name="body-type">
+ <option value="contains">
+ <title>contains</title>
+ <code>
+ (body-contains ${word})
+ </code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>
+ (not (body-contains ${word}))
+ </code>
+ </option>
+ </input>
+ <input type="string" name="word"/>
+ </part>
+ <part name="sexp">
+ <title>Expression</title>
+ <input type="code" name="code"/>
+ </part>
+
+ <part name="sent-date">
+ <title>Date sent</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (get-sent-date) ${versus})))
+ </code>
+ </option>
+ <option value="before">
+ <title>is before</title>
+ <code>
+ (match-all (&lt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="after">
+ <title>is after</title>
+ <code>
+ (match-all (&gt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus"/>
+ </part>
+
+ <part name="recv-date">
+ <title>Date received</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (get-received-date) ${versus})))
+ </code>
+ </option>
+ <option value="before">
+ <title>is before</title>
+ <code>
+ (match-all (&lt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="after">
+ <title>is after</title>
+ <code>
+ (match-all (&gt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus"/>
+ </part>
+
+ <part name="label">
+ <title>Label</title>
+ <input type="optionlist" name="label-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (user-tag "label") ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (user-tag "label") ${versus})))
+ </code>
+ </option>
+ </input>
+ <input type="label" name="versus"/>
+ </part>
+
+ <part name="score">
+ <title>Score</title>
+ <input type="optionlist" name="score-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (= (cast-int (user-tag "score")) ${versus}))
+ </code>
+ </option>
+ <option value="is-not">
+ <title>is not</title>
+ <code>
+ (match-all (not (= (cast-int (user-tag "score")) ${versus})))
+ </code>
+ </option>
+ <option value="greater-than">
+ <title>is greater than</title>
+ <code>
+ (match-all (&gt; (cast-int (user-tag "score")) ${versus}))
+ </code>
+ </option>
+ <option value="less-than">
+ <title>is less than</title>
+ <code>
+ (match-all (&lt; (cast-int (user-tag "score")) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="score" name="versus"/>
+ </part>
+
+ <part name="size">
+ <title>Size (kB)</title>
+ <input type="optionlist" name="size-type">
+ <option value="greater-than">
+ <title>is greater than</title>
+ <code>
+ (match-all (&gt; (get-size) ${versus}))
+ </code>
+ </option>
+ <option value="less-than">
+ <title>is less than</title>
+ <code>
+ (match-all (&lt; (get-size) ${versus}))
+ </code>
+ </option>
+ </input>
+ <input type="integer" name="versus"/>
+ </part>
+
+ <part name="status">
+ <title>Status</title>
+ <input type="optionlist" name="match-type">
+ <option value="is">
+ <title>is</title>
+ <code>
+ (match-all (system-flag ${flag}))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>
+ (match-all (not (system-flag ${flag})))
+ </code>
+ </option>
+ </input>
+ <input type="optionlist" name="flag">
+ <option value="Answered">
+ <title>Replied to</title>
+ </option>
+ <option value="Deleted">
+ <title>Deleted</title>
+ </option>
+ <option value="Draft">
+ <title>Draft</title>
+ </option>
+ <option value="Flagged">
+ <title>Important</title>
+ </option>
+ <option value="Seen">
+ <title>Read</title>
+ </option>
+ <option value="Junk">
+ <title>Junk</title>
+ </option>
+ </input>
+ </part>
+
+ <part name="follow-up">
+ <title>Follow Up</title>
+ <input type="optionlist" name="match-type">
+ <option value="is">
+ <title>is Flagged</title>
+ <code>
+ (match-all (not (= (user-tag "follow-up") "")))
+ </code>
+ </option>
+ <option value="is not">
+ <title>is not Flagged</title>
+ <code>
+ (match-all (= (user-tag "follow-up") ""))
+ </code>
+ </option>
+ </input>
+ </part>
+
+ <part name="attachments">
+ <title>Attachments</title>
+ <input type="optionlist" name="match-type">
+ <option value="exist">
+ <title>Exist</title>
+ <code>
+ (match-all (system-flag "Attachments"))
+ </code>
+ </option>
+ <option value="not exist">
+ <title>Do Not Exist</title>
+ <code>
+ (match-all (not (system-flag "Attachments")))
+ </code>
+ </option>
+ </input>
+ </part>
+
+ <part name="mlist">
+ <title>Mailing list</title>
+ <input type="optionlist" name="mlist-type">
+ <option value="is">
+ <title>is</title>
+ <code>(match-all (header-matches "x-camel-mlist" ${mlist}))</code>
+ </option>
+ <option value="is not">
+ <title>is not</title>
+ <code>(match-all (not (header-matches "x-camel-mlist" ${mlist})))</code>
+ </option>
+ <option value="contains">
+ <title>contains</title>
+ <code>(match-all (header-contains "x-camel-mlist" ${mlist}))</code>
+ </option>
+ <option value="not contains">
+ <title>does not contain</title>
+ <code>(match-all (not (header-contains "x-camel-mlist" ${mlist})))</code>
+ </option>
+ </input>
+ <input type="string" name="mlist"/>
+ </part>
+
+</partset>
+</filterdescription>