From 7cf30eb79276d4f255c1d603e2c203bb054cf50e Mon Sep 17 00:00:00 2001 From: Peter Williams Date: Thu, 17 Aug 2000 17:42:21 +0000 Subject: Filtering on demand! booyeah! svn path=/trunk/; revision=4864 --- filter/ChangeLog | 42 +++++++++++++++++++++++++++ filter/filter-driver.c | 22 ++++++++++---- filter/filter-driver.h | 1 + filter/filter-editor.c | 79 +++++++++++++++++++++++++++++++++++++------------- filter/filter-rule.c | 31 ++++++++++++++++++++ filter/filter-rule.h | 8 +++++ filter/filter.glade | 3 +- filter/rule-context.c | 35 +++++++++++++++++++--- filter/rule-context.h | 9 ++++-- 9 files changed, 197 insertions(+), 33 deletions(-) (limited to 'filter') diff --git a/filter/ChangeLog b/filter/ChangeLog index 70950b51be..fff445dcf6 100644 --- a/filter/ChangeLog +++ b/filter/ChangeLog @@ -1,3 +1,45 @@ +2000-08-17 Peter Williams + + Implement filtering on demand. + + * rule-context.h: Add a new callback to rule_context_load + that allows the caller to hook on-demand rules into its UI. + + * rule-context.c (rule_context_load): Changed to pass the + extra parameters to load(). + (load): If the rule is successfully loaded, call the provided + callback so that the UI can be updated. + + * filter-editor.c (rule_add): Set the source of the new filter. + (rule_edit): Use the new rule_context_get_rank_rule_with_source() + so that we don't get a bad index into the GtkList. + (rule_delete): Same as above. + (rule_up): Same as above. + (rule_down): Same as above. + (select_source): New function. When the user changes the + dropdown list to select a new source type, repopulate the + list with rules of the appropriate type. + (filter_editor_construct): Code moved from here into + select_source(). Hook up all the elements of the source optionmenu + to callbacks to select_source(). + + * filter-rule.c (xml_encode): Save the rule's source type. + (xml_decode): Load it. Default to 'incoming' if unspecified. + + * filter-rule.h: New enumeration, _filter_source_t, the + specifies the rule's source. Add it to struct _FilterRule. + + * filter-driver.c (filter_driver_run): Add a new input, + sourcetype, that specifies which rules to run (only ones + with the same source will be run). struct filter_mail_input_t + changed to accomodate. + (do_filter_mail): Skip rules if they're not the specified source. + If source and dest are the same, don't delete the messages or + copy unnecessarily. + + * filter.glade: Make the optionmenu sensitive. Change "Outgoing" + to "On Demand" (outgoing should be added later). + 2000-08-15 Peter Williams * vfoldertype.xml, filtertypes.xml: Add entries defining the diff --git a/filter/filter-driver.c b/filter/filter-driver.c index e22fc7235b..e3e1c7df5e 100644 --- a/filter/filter-driver.c +++ b/filter/filter-driver.c @@ -37,11 +37,14 @@ #include "filter-filter.h" #include "e-util/e-sexp.h" +#define d(x) + /* mail-thread filter input data type */ typedef struct { FilterDriver *driver; CamelFolder *source; CamelFolder *inbox; + enum _filter_source_t sourcetype; gboolean self_destruct; gpointer unhook_func; gpointer unhook_data; @@ -414,6 +417,7 @@ static const mail_operation_spec op_filter_mail = void filter_driver_run (FilterDriver *d, CamelFolder *source, CamelFolder *inbox, + enum _filter_source_t sourcetype, gboolean self_destruct, gpointer unhook_func, gpointer unhook_data) { filter_mail_input_t *input; @@ -422,6 +426,7 @@ filter_driver_run (FilterDriver *d, CamelFolder *source, CamelFolder *inbox, input->driver = d; input->source = source; input->inbox = inbox; + input->sourcetype = sourcetype; input->self_destruct = self_destruct; input->unhook_func = unhook_func; input->unhook_data = unhook_data; @@ -502,15 +507,20 @@ do_filter_mail (gpointer in_data, gpointer op_data, CamelException *ex) rule = NULL; while ( (rule = (FilterFilter *)rule_context_next_rule((RuleContext *)p->context, (FilterRule *)rule)) ) { + + if (((FilterRule *)rule)->source != input->sourcetype) { + d(printf("skipping rule %s - wrong source type (%d %d)\n", ((FilterRule *)rule)->name, + ((FilterRule *)rule)->source, input->sourcetype)); + continue; + } + g_string_truncate(s, 0); g_string_truncate(a, 0); filter_rule_build_code((FilterRule *)rule, s); filter_filter_build_action(rule, a); -#if 0 - printf("applying rule %s\n action %s\n", s->str, a->str); -#endif + d(printf("applying rule %s\n action %s\n", s->str, a->str)); mail_tool_camel_lock_up (); p->matches = camel_folder_search_by_expression (p->source, s->str, p->ex); @@ -574,14 +584,16 @@ do_filter_mail (gpointer in_data, gpointer op_data, CamelException *ex) copies = tmp; } - if (!procuid) { + if (!procuid && inbox != source) { printf("Applying default rule to message %s\n", uid); camel_folder_append_message(inbox, mm, info, p->ex); } camel_object_unref (CAMEL_OBJECT (mm)); } - camel_folder_delete_message (p->source, uid); + + if (inbox != source) + camel_folder_delete_message (p->source, uid); mail_tool_camel_lock_down (); } diff --git a/filter/filter-driver.h b/filter/filter-driver.h index 338243212a..1ef62b7118 100644 --- a/filter/filter-driver.h +++ b/filter/filter-driver.h @@ -56,6 +56,7 @@ FilterDriver *filter_driver_new (FilterContext *ctx, FilterGetFolderFunc fe /* apply rules to a folder, unmatched messages goto inbox, if not NULL */ void filter_driver_run(FilterDriver *d, CamelFolder *source, CamelFolder *inbox, + enum _filter_source_t sourcetype, gboolean self_destruct, gpointer unhook_func, gpointer unhook_data); #if 0 diff --git a/filter/filter-editor.c b/filter/filter-editor.c index 368a5fbcfc..76fe9df704 100644 --- a/filter/filter-editor.c +++ b/filter/filter-editor.c @@ -129,6 +129,7 @@ struct _editor_data { FilterRule *current; GtkList *list; GtkButton *buttons[BUTTON_LAST]; + enum _filter_source_t current_source; }; static void set_sensitive(struct _editor_data *data); @@ -144,6 +145,7 @@ static void rule_add(GtkWidget *widget, struct _editor_data *data) d(printf("add rule\n")); /* create a new rule with 1 match and 1 action */ rule = filter_filter_new(); + ((FilterRule *)rule)->source = data->current_source; part = rule_context_next_part(data->f, NULL); filter_rule_add_part((FilterRule *)rule, filter_part_clone(part)); @@ -183,13 +185,14 @@ static void rule_edit(GtkWidget *widget, struct _editor_data *data) d(printf("edit rule\n")); rule = data->current; w = filter_rule_get_widget(rule, data->f); - gd = (GnomeDialog *)gnome_dialog_new("Edit Rule", "Ok", NULL); + gd = (GnomeDialog *)gnome_dialog_new("Edit Rule", GNOME_STOCK_BUTTON_OK, + GNOME_STOCK_BUTTON_CANCEL, NULL); gtk_box_pack_start((GtkBox *)gd->vbox, w, FALSE, TRUE, 0); gtk_widget_show((GtkWidget *)gd); result = gnome_dialog_run_and_close(gd); if (result == 0) { - pos = rule_context_get_rank_rule(data->f, data->current); + pos = rule_context_get_rank_rule_with_source (data->f, data->current, data->current_source); if (pos != -1) { GtkListItem *item = g_list_nth_data(data->list->children, pos); gtk_label_set_text((GtkLabel *)(((GtkBin *)item)->child), data->current->name); @@ -204,7 +207,7 @@ static void rule_delete(GtkWidget *widget, struct _editor_data *data) GtkListItem *item; d(printf("ddelete rule\n")); - pos = rule_context_get_rank_rule(data->f, data->current); + pos = rule_context_get_rank_rule_with_source (data->f, data->current, data->current_source); if (pos != -1) { rule_context_remove_rule(data->f, data->current); @@ -240,7 +243,7 @@ static void rule_up(GtkWidget *widget, struct _editor_data *data) int pos; d(printf("up rule\n")); - pos = rule_context_get_rank_rule(data->f, data->current); + pos = rule_context_get_rank_rule_with_source(data->f, data->current, data->current_source); if (pos>0) { rule_move(data, pos, pos-1); } @@ -251,7 +254,7 @@ static void rule_down(GtkWidget *widget, struct _editor_data *data) int pos; d(printf("down rule\n")); - pos = rule_context_get_rank_rule(data->f, data->current); + pos = rule_context_get_rank_rule_with_source(data->f, data->current, data->current_source); rule_move(data, pos, pos+1); } @@ -296,12 +299,49 @@ select_rule(GtkWidget *w, GtkWidget *child, struct _editor_data *data) set_sensitive(data); } +/* FIXME: we need a way to change a rule from one source type + * to a different type. Maybe keep the selected ones? + */ + +static void +select_source (GtkMenuItem *mi, struct _editor_data *data) +{ + FilterRule *rule = NULL; + GList *newitems = NULL; + enum _filter_source_t source; + + source = (enum _filter_source_t) GPOINTER_TO_INT ( + gtk_object_get_data (GTK_OBJECT (mi), "number")); + + gtk_list_clear_items (GTK_LIST (data->list), 0, -1); + + d(printf("Checking for rules that are of type %d\n", source)); + while ((rule = rule_context_next_rule (data->f, rule)) != NULL) { + GtkWidget *item; + + if (rule->source != source) { + d(printf(" skipping %s: %d != %d\n", rule->name, rule->source, source)); + continue; + } + + d(printf(" hit %s (%d)\n", rule->name, source)); + item = gtk_list_item_new_with_label (rule->name); + gtk_object_set_data (GTK_OBJECT (item), "rule", rule); + gtk_widget_show (GTK_WIDGET (item)); + newitems = g_list_append (newitems, item); + } + + gtk_list_append_items (data->list, newitems); + data->current_source = source; + data->current = NULL; + set_sensitive (data); +} + GtkWidget *filter_editor_construct (struct _FilterContext *f) { GladeXML *gui; - GtkWidget *d, *w; + GtkWidget *d, *w, *b, *firstitem = NULL; GList *l; - FilterRule *rule = NULL; struct _editor_data *data; int i; @@ -320,29 +360,28 @@ GtkWidget *filter_editor_construct (struct _FilterContext *f) gtk_signal_connect((GtkObject *)w, "clicked", edit_buttons[i].func, data); } - /* to be defined yet */ -#if 0 w = glade_xml_get_widget (gui, "filter_source"); l = GTK_MENU_SHELL(GTK_OPTION_MENU(w)->menu)->children; + i = 0; while (l) { - b = l->data; + b = GTK_WIDGET (l->data); + + if (i == 0) + firstitem = b; + + /* make sure that the glade is in sync with enum _filter_source_t! */ + gtk_object_set_data (GTK_OBJECT (b), "number", GINT_TO_POINTER (i)); + gtk_signal_connect (GTK_OBJECT (b), "activate", select_source, data); + + i++; l = l->next; } -#endif w = glade_xml_get_widget (gui, "rule_list"); data->list = (GtkList *)w; - l = NULL; - while ((rule = rule_context_next_rule((RuleContext *)f, rule))) { - GtkListItem *item = (GtkListItem *)gtk_list_item_new_with_label(rule->name); - gtk_object_set_data((GtkObject *)item, "rule", rule); - gtk_widget_show((GtkWidget *)item); - l = g_list_append(l, item); - } - gtk_list_append_items(data->list, l); gtk_signal_connect((GtkObject *)w, "select_child", select_rule, data); + select_source (GTK_MENU_ITEM (firstitem), data); - set_sensitive(data); gtk_object_unref((GtkObject *)gui); return d; diff --git a/filter/filter-rule.c b/filter/filter-rule.c index 9c47af6daf..58a2d76518 100644 --- a/filter/filter-rule.c +++ b/filter/filter-rule.c @@ -158,6 +158,19 @@ static xmlNodePtr xml_encode(FilterRule *fr) xmlSetProp(node, "grouping", "any"); break; } + + switch (fr->source) { + case FILTER_SOURCE_INCOMING: + xmlSetProp(node, "source", "incoming"); + break; + case FILTER_SOURCE_DEMAND: + xmlSetProp(node, "source", "ondemand"); + break; + case FILTER_SOURCE_OUTGOING: + xmlSetProp(node, "source", "outgoing"); + break; + } + if (fr->name) { work = xmlNewNode(NULL, "title"); xmlNodeSetContent(work, fr->name); @@ -209,16 +222,34 @@ static int xml_decode(FilterRule *fr, xmlNodePtr node, RuleContext *f) { xmlNodePtr work; char *grouping; + char *source; if (fr->name) { g_free(fr->name); fr->name = NULL; } + grouping = xmlGetProp(node, "grouping"); if (!strcmp(grouping, "any")) fr->grouping = FILTER_GROUP_ANY; else fr->grouping = FILTER_GROUP_ALL; + + /* FIXME: free source and grouping? */ + source = xmlGetProp (node, "source"); + if (!source) /*default to incoming*/ + fr->source = FILTER_SOURCE_INCOMING; + else if (!strcmp (source, "outgoing")) + fr->source = FILTER_SOURCE_OUTGOING; + else if (!strcmp (source, "ondemand")) + fr->source = FILTER_SOURCE_DEMAND; + else if (!strcmp (source, "incoming")) + fr->source = FILTER_SOURCE_INCOMING; + else { + g_warning ("Unknown filter source type \"%s\"", source); + fr->source = FILTER_SOURCE_INCOMING; + } + work = node->childs; while (work) { if (!strcmp(work->name, "partset")) { diff --git a/filter/filter-rule.h b/filter/filter-rule.h index 396af19e59..425d0ac350 100644 --- a/filter/filter-rule.h +++ b/filter/filter-rule.h @@ -39,6 +39,13 @@ enum _filter_grouping_t { FILTER_GROUP_ANY /* any rule must match */ }; +enum _filter_source_t { + FILTER_SOURCE_INCOMING, /* performed on incoming email */ + FILTER_SOURCE_DEMAND, /* performed on the selected folder + * when the user asks for it */ + FILTER_SOURCE_OUTGOING /* performed on outgoing mail */ +}; + struct _FilterRule { GtkObject parent; struct _FilterRulePrivate *priv; @@ -46,6 +53,7 @@ struct _FilterRule { char *name; enum _filter_grouping_t grouping; + enum _filter_source_t source; GList *parts; }; diff --git a/filter/filter.glade b/filter/filter.glade index d9b26e4489..5f8a04d64e 100644 --- a/filter/filter.glade +++ b/filter/filter.glade @@ -73,10 +73,9 @@ GtkOptionMenu filter_source - False True Incoming -Outgoing +On Demand 0 diff --git a/filter/rule-context.c b/filter/rule-context.c index c297ae1a61..8114d34197 100644 --- a/filter/rule-context.c +++ b/filter/rule-context.c @@ -26,7 +26,8 @@ #define d(x) x -static int load(RuleContext *f, const char *system, const char *user); +static int load(RuleContext *f, const char *system, const char *user, + RCRegisterFunc on_demand_cb, gpointer user_data); static int save(RuleContext *f, const char *user); static void rule_context_class_init (RuleContextClass *class); @@ -167,19 +168,25 @@ rule_context_set_error(RuleContext *f, char *error) * @f: * @system: * @user: + * @on_demand_cb: An optional callback to allow UI registration of on-demand rules + * @user_data: Extra data for the callback * * Load a rule context from a system and user description file. * * Return value: **/ -int rule_context_load(RuleContext *f, const char *system, const char *user) +int rule_context_load(RuleContext *f, const char *system, const char *user, + RCRegisterFunc on_demand_cb, gpointer user_data ) { printf("rule_context: loading %s %s\n", system, user); - return ((RuleContextClass *)((GtkObject *)f)->klass)->load(f, system, user); + return ((RuleContextClass *)((GtkObject *)f)->klass)->load(f, system, user, + on_demand_cb, + user_data); } -static int load(RuleContext *f, const char *system, const char *user) +static int load(RuleContext *f, const char *system, const char *user, + RCRegisterFunc on_demand_cb, gpointer user_data) { xmlNodePtr set, rule; struct _part_set_map *part_map; @@ -245,6 +252,9 @@ static int load(RuleContext *f, const char *system, const char *user) FilterRule *part = FILTER_RULE(gtk_type_new(rule_map->type)); if (filter_rule_xml_decode(part, rule, f) == 0) { rule_map->append(f, part); + + if (on_demand_cb && part->source == FILTER_SOURCE_DEMAND) + (on_demand_cb) (f, part, user_data); } else { gtk_object_unref((GtkObject *)part); g_warning("Cannot load filter part"); @@ -398,3 +408,20 @@ int rule_context_get_rank_rule(RuleContext *f, FilterRule *rule) { return g_list_index(f->rules, rule); } + +int +rule_context_get_rank_rule_with_source(RuleContext *f, FilterRule *rule, enum _filter_source_t source) +{ + int i; + GList *iter; + + i = 0; + for (iter = f->rules; iter; iter = iter->next) { + if (iter->data == rule) + return i; + if (((FilterRule *)iter->data)->source == source) + i++; + } + + return -1; +} diff --git a/filter/rule-context.h b/filter/rule-context.h index 06d32e0be3..27078adbcf 100644 --- a/filter/rule-context.h +++ b/filter/rule-context.h @@ -52,11 +52,14 @@ struct _RuleContext { GList *rule_set_list; }; +typedef void (*RCRegisterFunc)(RuleContext *f, FilterRule *rule, gpointer data); + struct _RuleContextClass { GtkObjectClass parent_class; /* virtual methods */ - int (*load)(RuleContext *f, const char *system, const char *user); + int (*load)(RuleContext *f, const char *system, const char *user, + RCRegisterFunc on_demand_cb, gpointer user_data); int (*save)(RuleContext *f, const char *user); /* signals */ @@ -85,7 +88,8 @@ guint rule_context_get_type (void); RuleContext *rule_context_new (void); /* methods */ -int rule_context_load(RuleContext *f, const char *system, const char *user); +int rule_context_load(RuleContext *f, const char *system, const char *user, + RCRegisterFunc on_demand_cb, gpointer user_data); int rule_context_save(RuleContext *f, const char *user); void rule_context_add_part(RuleContext *f, FilterPart *new); @@ -102,6 +106,7 @@ void rule_context_remove_rule(RuleContext *f, FilterRule *rule); /* get/set the rank (position) of a rule */ void rule_context_rank_rule(RuleContext *f, FilterRule *rule, int rank); int rule_context_get_rank_rule(RuleContext *f, FilterRule *rule); +int rule_context_get_rank_rule_with_source(RuleContext *f, FilterRule *rule, enum _filter_source_t source); void rule_context_delete_rule(RuleContext *f, FilterRule *rule); -- cgit v1.2.3