diff options
Diffstat (limited to 'mail')
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 (< (get-sent-date) ${versus})) + </code> + </option> + <option value="after"> + <title>is after</title> + <code> + (match-all (> (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 (< (get-received-date) ${versus})) + </code> + </option> + <option value="after"> + <title>is after</title> + <code> + (match-all (> (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 (> (cast-int (user-tag "score")) ${versus})) + </code> + </option> + <option value="less-than"> + <title>is less than</title> + <code> + (match-all (< (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 (> (get-size) ${versus})) + </code> + </option> + <option value="less-than"> + <title>is less than</title> + <code> + (match-all (< (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 (> (pipe-message "/bin/sh" "-c" ${command}) ${retval})) + </code> + </option> + <option value="less-than"> + <title>returns less than</title> + <code> + (match-all (< (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"><b>vFolder Sources</b></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 "{0}".</primary> <secondary>A non-empty folder at "{1}" 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 (< (get-sent-date) ${versus})) + </code> + </option> + <option value="after"> + <title>is after</title> + <code> + (match-all (> (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 (< (get-received-date) ${versus})) + </code> + </option> + <option value="after"> + <title>is after</title> + <code> + (match-all (> (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 (> (cast-int (user-tag "score")) ${versus})) + </code> + </option> + <option value="less-than"> + <title>is less than</title> + <code> + (match-all (< (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 (> (get-size) ${versus})) + </code> + </option> + <option value="less-than"> + <title>is less than</title> + <code> + (match-all (< (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 (< (get-sent-date) ${versus})) + </code> + </option> + <option value="after"> + <title>is after</title> + <code> + (match-all (> (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 (< (get-received-date) ${versus})) + </code> + </option> + <option value="after"> + <title>is after</title> + <code> + (match-all (> (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 (> (cast-int (user-tag "score")) ${versus})) + </code> + </option> + <option value="less-than"> + <title>is less than</title> + <code> + (match-all (< (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 (> (get-size) ${versus})) + </code> + </option> + <option value="less-than"> + <title>is less than</title> + <code> + (match-all (< (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> |