/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see
*
*
* Authors:
* Not Zed
* Jeffrey Stedfast
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "e-alert-dialog.h"
#include "e-filter-code.h"
#include "e-filter-color.h"
#include "e-filter-datespec.h"
#include "e-filter-file.h"
#include "e-filter-input.h"
#include "e-filter-int.h"
#include "e-filter-option.h"
#include "e-filter-rule.h"
#include "e-rule-context.h"
#include "e-xml-utils.h"
#define E_RULE_CONTEXT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_RULE_CONTEXT, ERuleContextPrivate))
struct _ERuleContextPrivate {
gint frozen;
};
enum {
RULE_ADDED,
RULE_REMOVED,
CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
struct _revert_data {
GHashTable *rules;
gint rank;
};
G_DEFINE_TYPE (
ERuleContext,
e_rule_context,
G_TYPE_OBJECT)
static void
rule_context_set_error (ERuleContext *context,
gchar *error)
{
g_free (context->error);
context->error = error;
}
static void
new_rule_response (GtkWidget *dialog,
gint button,
ERuleContext *context)
{
if (button == GTK_RESPONSE_OK) {
EFilterRule *rule = g_object_get_data ((GObject *) dialog, "rule");
gchar *user = g_object_get_data ((GObject *) dialog, "path");
EAlert *alert = NULL;
if (!e_filter_rule_validate (rule, &alert)) {
e_alert_run_dialog (GTK_WINDOW (dialog), alert);
g_object_unref (alert);
return;
}
if (e_rule_context_find_rule (context, rule->name, rule->source)) {
e_alert_run_dialog_for_args ((GtkWindow *) dialog,
"filter:bad-name-notunique",
rule->name, NULL);
return;
}
g_object_ref (rule);
e_rule_context_add_rule (context, rule);
if (user)
e_rule_context_save (context, user);
}
gtk_widget_destroy (dialog);
}
static void
revert_rule_remove (gpointer key,
EFilterRule *rule,
ERuleContext *context)
{
e_rule_context_remove_rule (context, rule);
g_object_unref (rule);
}
static void
revert_source_remove (gpointer key,
struct _revert_data *rest_data,
ERuleContext *context)
{
g_hash_table_foreach (
rest_data->rules, (GHFunc) revert_rule_remove, context);
g_hash_table_destroy (rest_data->rules);
g_free (rest_data);
}
static guint
source_hashf (const gchar *a)
{
return (a != NULL) ? g_str_hash (a) : 0;
}
static gint
source_eqf (const gchar *a,
const gchar *b)
{
return (g_strcmp0 (a, b) == 0);
}
static void
free_part_set (struct _part_set_map *map)
{
g_free (map->name);
g_free (map);
}
static void
free_rule_set (struct _rule_set_map *map)
{
g_free (map->name);
g_free (map);
}
static void
rule_context_finalize (GObject *obj)
{
ERuleContext *context =(ERuleContext *) obj;
g_list_foreach (context->rule_set_list, (GFunc) free_rule_set, NULL);
g_list_free (context->rule_set_list);
g_hash_table_destroy (context->rule_set_map);
g_list_foreach (context->part_set_list, (GFunc) free_part_set, NULL);
g_list_free (context->part_set_list);
g_hash_table_destroy (context->part_set_map);
g_free (context->error);
g_list_foreach (context->parts, (GFunc) g_object_unref, NULL);
g_list_free (context->parts);
g_list_foreach (context->rules, (GFunc) g_object_unref, NULL);
g_list_free (context->rules);
G_OBJECT_CLASS (e_rule_context_parent_class)->finalize (obj);
}
static gint
rule_context_load (ERuleContext *context,
const gchar *system,
const gchar *user)
{
xmlNodePtr set, rule, root;
xmlDocPtr systemdoc, userdoc;
struct _part_set_map *part_map;
struct _rule_set_map *rule_map;
rule_context_set_error (context, NULL);
systemdoc = e_xml_parse_file (system);
if (systemdoc == NULL) {
gchar * err_msg;
err_msg = g_strdup_printf (
"Unable to load system rules '%s': %s",
system, g_strerror (errno));
g_warning ("%s: %s", G_STRFUNC, err_msg);
rule_context_set_error (context, err_msg);
/* no need to free err_msg here */
return -1;
}
root = xmlDocGetRootElement (systemdoc);
if (root == NULL || strcmp ((gchar *) root->name, "filterdescription")) {
gchar * err_msg;
err_msg = g_strdup_printf (
"Unable to load system rules '%s': "
"Invalid format", system);
g_warning ("%s: %s", G_STRFUNC, err_msg);
rule_context_set_error (context, err_msg);
/* no need to free err_msg here */
xmlFreeDoc (systemdoc);
return -1;
}
/* doesn't matter if this doens't exist */
userdoc = NULL;
if (g_file_test (user, G_FILE_TEST_IS_REGULAR))
userdoc = e_xml_parse_file (user);
/* now parse structure */
/* get rule parts */
set = root->children;
while (set) {
part_map = g_hash_table_lookup (context->part_set_map, set->name);
if (part_map) {
rule = set->children;
while (rule) {
if (!strcmp ((gchar *) rule->name, "part")) {
EFilterPart *part =
E_FILTER_PART (g_object_new (
part_map->type, NULL, NULL));
if (e_filter_part_xml_create (part, rule, context) == 0) {
part_map->append (context, part);
} else {
g_object_unref (part);
g_warning ("Cannot load filter part");
}
}
rule = rule->next;
}
} else if ((rule_map = g_hash_table_lookup (
context->rule_set_map, set->name))) {
rule = set->children;
while (rule) {
if (!strcmp ((gchar *) rule->name, "rule")) {
EFilterRule *part =
E_FILTER_RULE (g_object_new (
rule_map->type, NULL, NULL));
if (e_filter_rule_xml_decode (part, rule, context) == 0) {
part->system = TRUE;
rule_map->append (context, part);
} else {
g_object_unref (part);
g_warning ("Cannot load filter part");
}
}
rule = rule->next;
}
}
set = set->next;
}
/* now load actual rules */
if (userdoc) {
root = xmlDocGetRootElement (userdoc);
set = root ? root->children : NULL;
while (set) {
rule_map = g_hash_table_lookup (context->rule_set_map, set->name);
if (rule_map) {
rule = set->children;
while (rule) {
if (!strcmp ((gchar *) rule->name, "rule")) {
EFilterRule *part =
E_FILTER_RULE (g_object_new (
rule_map->type, NULL, NULL));
if (e_filter_rule_xml_decode (part, rule, context) == 0) {
rule_map->append (context, part);
} else {
g_object_unref (part);
g_warning ("Cannot load filter part");
}
}
rule = rule->next;
}
}
set = set->next;
}
}
xmlFreeDoc (userdoc);
xmlFreeDoc (systemdoc);
return 0;
}
static gint
rule_context_save (ERuleContext *context,
const gchar *user)
{
xmlDocPtr doc;
xmlNodePtr root, rules, work;
GList *l;
EFilterRule *rule;
struct _rule_set_map *map;
gint ret;
doc = xmlNewDoc ((xmlChar *)"1.0");
/* FIXME: set character encoding to UTF-8? */
root = xmlNewDocNode (doc, NULL, (xmlChar *)"filteroptions", NULL);
xmlDocSetRootElement (doc, root);
l = context->rule_set_list;
while (l) {
map = l->data;
rules = xmlNewDocNode (doc, NULL, (xmlChar *) map->name, NULL);
xmlAddChild (root, rules);
rule = NULL;
while ((rule = map->next (context, rule, NULL))) {
if (!rule->system) {
work = e_filter_rule_xml_encode (rule);
xmlAddChild (rules, work);
}
}
l = g_list_next (l);
}
ret = e_xml_save_file (user, doc);
xmlFreeDoc (doc);
return ret;
}
static gint
rule_context_revert (ERuleContext *context,
const gchar *user)
{
xmlNodePtr set, rule;
/*struct _part_set_map *part_map;*/
struct _rule_set_map *rule_map;
struct _revert_data *rest_data;
GHashTable *source_hash;
xmlDocPtr userdoc;
EFilterRule *frule;
rule_context_set_error (context, NULL);
userdoc = e_xml_parse_file (user);
if (userdoc == NULL)
/* clear out anythign we have? */
return 0;
source_hash = g_hash_table_new (
(GHashFunc) source_hashf,
(GCompareFunc) source_eqf);
/* setup stuff we have now */
/* Note that we assume there is only 1 set of rules in a given rule context,
* although other parts of the code dont assume this */
frule = NULL;
while ((frule = e_rule_context_next_rule (context, frule, NULL))) {
rest_data = g_hash_table_lookup (source_hash, frule->source);
if (rest_data == NULL) {
rest_data = g_malloc0 (sizeof (*rest_data));
rest_data->rules = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (source_hash, frule->source, rest_data);
}
g_hash_table_insert (rest_data->rules, frule->name, frule);
}
/* make what we have, match what we load */
set = xmlDocGetRootElement (userdoc);
set = set ? set->children : NULL;
while (set) {
rule_map = g_hash_table_lookup (context->rule_set_map, set->name);
if (rule_map) {
rule = set->children;
while (rule) {
if (!strcmp ((gchar *) rule->name, "rule")) {
EFilterRule *part =
E_FILTER_RULE (g_object_new (
rule_map->type, NULL, NULL));
if (e_filter_rule_xml_decode (part, rule, context) == 0) {
/* Use the revert data to keep
* track of the right rank of
* this rule part. */
rest_data = g_hash_table_lookup (source_hash, part->source);
if (rest_data == NULL) {
rest_data = g_malloc0 (sizeof (*rest_data));
rest_data->rules = g_hash_table_new (
g_str_hash,
g_str_equal);
g_hash_table_insert (
source_hash,
part->source,
rest_data);
}
frule = g_hash_table_lookup (
rest_data->rules,
part->name);
if (frule) {
if (context->priv->frozen == 0 &&
!e_filter_rule_eq (frule, part))
e_filter_rule_copy (frule, part);
g_object_unref (part);
e_rule_context_rank_rule (
context, frule,
frule->source,
rest_data->rank);
g_hash_table_remove (rest_data->rules, frule->name);
} else {
e_rule_context_add_rule (context, part);
e_rule_context_rank_rule (
context,
part,
part->source,
rest_data->rank);
}
rest_data->rank++;
} else {
g_object_unref (part);
g_warning ("Cannot load filter part");
}
}
rule = rule->next;
}
}
set = set->next;
}
xmlFreeDoc (userdoc);
/* remove any we still have that weren't in the file */
g_hash_table_foreach (source_hash, (GHFunc) revert_source_remove, context);
g_hash_table_destroy (source_hash);
return 0;
}
static EFilterElement *
rule_context_new_element (ERuleContext *context,
const gchar *type)
{
if (!strcmp (type, "string")) {
return (EFilterElement *) e_filter_input_new ();
} else if (!strcmp (type, "address")) {
/* FIXME: temporary ... need real address type */
return (EFilterElement *) e_filter_input_new_type_name (type);
} else if (!strcmp (type, "code")) {
return (EFilterElement *) e_filter_code_new (FALSE);
} else if (!strcmp (type, "rawcode")) {
return (EFilterElement *) e_filter_code_new (TRUE);
} else if (!strcmp (type, "colour")) {
return (EFilterElement *) e_filter_color_new ();
} else if (!strcmp (type, "optionlist")) {
return (EFilterElement *) e_filter_option_new ();
} else if (!strcmp (type, "datespec")) {
return (EFilterElement *) e_filter_datespec_new ();
} else if (!strcmp (type, "command")) {
return (EFilterElement *) e_filter_file_new_type_name (type);
} else if (!strcmp (type, "file")) {
return (EFilterElement *) e_filter_file_new_type_name (type);
} else if (!strcmp (type, "integer")) {
return (EFilterElement *) e_filter_int_new ();
} else if (!strcmp (type, "regex")) {
return (EFilterElement *) e_filter_input_new_type_name (type);
} else if (!strcmp (type, "completedpercent")) {
return (EFilterElement *) e_filter_int_new_type (
"completedpercent", 0,100);
} else {
g_warning ("Unknown filter type '%s'", type);
return NULL;
}
}
static void
e_rule_context_class_init (ERuleContextClass *class)
{
GObjectClass *object_class;
g_type_class_add_private (class, sizeof (ERuleContextPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->finalize = rule_context_finalize;
class->load = rule_context_load;
class->save = rule_context_save;
class->revert = rule_context_revert;
class->new_element = rule_context_new_element;
signals[RULE_ADDED] = g_signal_new (
"rule-added",
E_TYPE_RULE_CONTEXT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ERuleContextClass, rule_added),
NULL,
NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1,
G_TYPE_POINTER);
signals[RULE_REMOVED] = g_signal_new (
"rule-removed",
E_TYPE_RULE_CONTEXT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ERuleContextClass, rule_removed),
NULL,
NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1,
G_TYPE_POINTER);
signals[CHANGED] = g_signal_new (
"changed",
E_TYPE_RULE_CONTEXT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ERuleContextClass, changed),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
e_rule_context_init (ERuleContext *context)
{
context->priv = E_RULE_CONTEXT_GET_PRIVATE (context);
context->part_set_map = g_hash_table_new (g_str_hash, g_str_equal);
context->rule_set_map = g_hash_table_new (g_str_hash, g_str_equal);
context->flags = E_RULE_CONTEXT_GROUPING;
}
/**
* e_rule_context_new:
*
* Create a new ERuleContext object.
*
* Return value: A new #ERuleContext object.
**/
ERuleContext *
e_rule_context_new (void)
{
return g_object_new (E_TYPE_RULE_CONTEXT, NULL);
}
void
e_rule_context_add_part_set (ERuleContext *context,
const gchar *setname,
GType part_type,
ERuleContextPartFunc append,
ERuleContextNextPartFunc next)
{
struct _part_set_map *map;
g_return_if_fail (E_IS_RULE_CONTEXT (context));
g_return_if_fail (setname != NULL);
g_return_if_fail (append != NULL);
g_return_if_fail (next != NULL);
map = g_hash_table_lookup (context->part_set_map, setname);
if (map != NULL) {
g_hash_table_remove (context->part_set_map, setname);
context->part_set_list = g_list_remove (context->part_set_list, map);
free_part_set (map);
map = NULL;
}
map = g_malloc0 (sizeof (*map));
map->type = part_type;
map->append = append;
map->next = next;
map->name = g_strdup (setname);
g_hash_table_insert (context->part_set_map, map->name, map);
context->part_set_list = g_list_append (context->part_set_list, map);
}
void
e_rule_context_add_rule_set (ERuleContext *context,
const gchar *setname,
GType rule_type,
ERuleContextRuleFunc append,
ERuleContextNextRuleFunc next)
{
struct _rule_set_map *map;
g_return_if_fail (E_IS_RULE_CONTEXT (context));
g_return_if_fail (setname != NULL);
g_return_if_fail (append != NULL);
g_return_if_fail (next != NULL);
map = g_hash_table_lookup (context->rule_set_map, setname);
if (map != NULL) {
g_hash_table_remove (context->rule_set_map, setname);
context->rule_set_list = g_list_remove (context->rule_set_list, map);
free_rule_set (map);
map = NULL;
}
map = g_malloc0 (sizeof (*map));
map->type = rule_type;
map->append = append;
map->next = next;
map->name = g_strdup (setname);
g_hash_table_insert (context->rule_set_map, map->name, map);
context->rule_set_list = g_list_append (context->rule_set_list, map);
}
/**
* e_rule_context_load:
* @context:
* @system:
* @user:
*
* Load a rule context from a system and user description file.
*
* Return value:
**/
gint
e_rule_context_load (ERuleContext *context,
const gchar *system,
const gchar *user)
{
ERuleContextClass *class;
gint result;
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
g_return_val_if_fail (system != NULL, -1);
g_return_val_if_fail (user != NULL, -1);
class = E_RULE_CONTEXT_GET_CLASS (context);
g_return_val_if_fail (class->load != NULL, -1);
context->priv->frozen++;
result = class->load (context, system, user);
context->priv->frozen--;
return result;
}
/**
* e_rule_context_save:
* @context:
* @user:
*
* Save a rule context to disk.
*
* Return value:
**/
gint
e_rule_context_save (ERuleContext *context,
const gchar *user)
{
ERuleContextClass *class;
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
g_return_val_if_fail (user != NULL, -1);
class = E_RULE_CONTEXT_GET_CLASS (context);
g_return_val_if_fail (class->save != NULL, -1);
return class->save (context, user);
}
/**
* e_rule_context_revert:
* @context:
* @user:
*
* Reverts a rule context from a user description file. Assumes the
* system description file is unchanged from when it was loaded.
*
* Return value:
**/
gint
e_rule_context_revert (ERuleContext *context,
const gchar *user)
{
ERuleContextClass *class;
g_return_val_if_fail (E_RULE_CONTEXT (context), 0);
g_return_val_if_fail (user != NULL, 0);
class = E_RULE_CONTEXT_GET_CLASS (context);
g_return_val_if_fail (class->revert != NULL, 0);
return class->revert (context, user);
}
EFilterPart *
e_rule_context_find_part (ERuleContext *context,
const gchar *name)
{
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
g_return_val_if_fail (name != NULL, NULL);
return e_filter_part_find_list (context->parts, name);
}
EFilterPart *
e_rule_context_create_part (ERuleContext *context,
const gchar *name)
{
EFilterPart *part;
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
g_return_val_if_fail (name != NULL, NULL);
part = e_rule_context_find_part (context, name);
if (part == NULL)
return NULL;
return e_filter_part_clone (part);
}
EFilterPart *
e_rule_context_next_part (ERuleContext *context,
EFilterPart *last)
{
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
return e_filter_part_next_list (context->parts, last);
}
EFilterRule *
e_rule_context_next_rule (ERuleContext *context,
EFilterRule *last,
const gchar *source)
{
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
return e_filter_rule_next_list (context->rules, last, source);
}
EFilterRule *
e_rule_context_find_rule (ERuleContext *context,
const gchar *name,
const gchar *source)
{
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
g_return_val_if_fail (name != NULL, NULL);
return e_filter_rule_find_list (context->rules, name, source);
}
void
e_rule_context_add_part (ERuleContext *context,
EFilterPart *part)
{
g_return_if_fail (E_IS_RULE_CONTEXT (context));
g_return_if_fail (E_IS_FILTER_PART (part));
context->parts = g_list_append (context->parts, part);
}
void
e_rule_context_add_rule (ERuleContext *context,
EFilterRule *rule)
{
g_return_if_fail (E_IS_RULE_CONTEXT (context));
g_return_if_fail (E_IS_FILTER_RULE (rule));
context->rules = g_list_append (context->rules, rule);
if (context->priv->frozen == 0) {
g_signal_emit (context, signals[RULE_ADDED], 0, rule);
g_signal_emit (context, signals[CHANGED], 0);
}
}
/* Add a rule, with a gui, asking for confirmation first,
* and optionally save to path. */
void
e_rule_context_add_rule_gui (ERuleContext *context,
EFilterRule *rule,
const gchar *title,
const gchar *path)
{
GtkDialog *dialog;
GtkWidget *widget;
GtkWidget *content_area;
g_return_if_fail (E_IS_RULE_CONTEXT (context));
g_return_if_fail (E_IS_FILTER_RULE (rule));
widget = e_filter_rule_get_widget (rule, context);
gtk_widget_show (widget);
dialog =(GtkDialog *) gtk_dialog_new ();
gtk_dialog_add_buttons (
dialog,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
gtk_window_set_title ((GtkWindow *) dialog, title);
gtk_window_set_default_size ((GtkWindow *) dialog, 600, 400);
gtk_window_set_resizable ((GtkWindow *) dialog, TRUE);
content_area = gtk_dialog_get_content_area (dialog);
gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
g_object_set_data_full ((GObject *) dialog, "rule", rule, g_object_unref);
if (path)
g_object_set_data_full ((GObject *) dialog, "path", g_strdup (path), g_free);
g_signal_connect (
dialog, "response",
G_CALLBACK (new_rule_response), context);
g_object_ref (context);
g_object_set_data_full ((GObject *) dialog, "context", context, g_object_unref);
gtk_widget_show ((GtkWidget *) dialog);
}
void
e_rule_context_remove_rule (ERuleContext *context,
EFilterRule *rule)
{
g_return_if_fail (E_IS_RULE_CONTEXT (context));
g_return_if_fail (E_IS_FILTER_RULE (rule));
context->rules = g_list_remove (context->rules, rule);
if (context->priv->frozen == 0) {
g_signal_emit (context, signals[RULE_REMOVED], 0, rule);
g_signal_emit (context, signals[CHANGED], 0);
}
}
void
e_rule_context_rank_rule (ERuleContext *context,
EFilterRule *rule,
const gchar *source,
gint rank)
{
GList *node;
gint i = 0, index = 0;
g_return_if_fail (E_IS_RULE_CONTEXT (context));
g_return_if_fail (E_IS_FILTER_RULE (rule));
if (e_rule_context_get_rank_rule (context, rule, source) == rank)
return;
context->rules = g_list_remove (context->rules, rule);
node = context->rules;
while (node) {
EFilterRule *r = node->data;
if (i == rank) {
context->rules = g_list_insert (context->rules, rule, index);
if (context->priv->frozen == 0)
g_signal_emit (context, signals[CHANGED], 0);
return;
}
index++;
if (source == NULL || (r->source && strcmp (r->source, source) == 0))
i++;
node = node->next;
}
context->rules = g_list_append (context->rules, rule);
if (context->priv->frozen == 0)
g_signal_emit (context, signals[CHANGED], 0);
}
gint
e_rule_context_get_rank_rule (ERuleContext *context,
EFilterRule *rule,
const gchar *source)
{
GList *node;
gint i = 0;
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
g_return_val_if_fail (E_IS_FILTER_RULE (rule), -1);
node = context->rules;
while (node) {
EFilterRule *r = node->data;
if (r == rule)
return i;
if (source == NULL || (r->source && strcmp (r->source, source) == 0))
i++;
node = node->next;
}
return -1;
}
EFilterRule *
e_rule_context_find_rank_rule (ERuleContext *context,
gint rank,
const gchar *source)
{
GList *node;
gint i = 0;
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
node = context->rules;
while (node) {
EFilterRule *r = node->data;
if (source == NULL || (r->source && strcmp (r->source, source) == 0)) {
if (rank == i)
return r;
i++;
}
node = node->next;
}
return NULL;
}
GList *
e_rule_context_rename_uri (ERuleContext *context,
const gchar *old_uri,
const gchar *new_uri,
GCompareFunc compare)
{
ERuleContextClass *class;
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
g_return_val_if_fail (old_uri != NULL, NULL);
g_return_val_if_fail (new_uri != NULL, NULL);
g_return_val_if_fail (compare != NULL, NULL);
class = E_RULE_CONTEXT_GET_CLASS (context);
/* This method is optional. */
if (class->rename_uri == NULL)
return NULL;
return class->rename_uri (context, old_uri, new_uri, compare);
}
GList *
e_rule_context_delete_uri (ERuleContext *context,
const gchar *uri,
GCompareFunc compare)
{
ERuleContextClass *class;
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
g_return_val_if_fail (uri != NULL, NULL);
g_return_val_if_fail (compare != NULL, NULL);
class = E_RULE_CONTEXT_GET_CLASS (context);
/* This method is optional. */
if (class->delete_uri == NULL)
return NULL;
return class->delete_uri (context, uri, compare);
}
void
e_rule_context_free_uri_list (ERuleContext *context,
GList *uris)
{
g_return_if_fail (E_IS_RULE_CONTEXT (context));
/* TODO: should be virtual */
g_list_foreach (uris, (GFunc) g_free, NULL);
g_list_free (uris);
}
/**
* e_rule_context_new_element:
* @context:
* @name:
*
* create a new filter element based on name.
*
* Return value:
**/
EFilterElement *
e_rule_context_new_element (ERuleContext *context,
const gchar *name)
{
ERuleContextClass *class;
g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
g_return_val_if_fail (name != NULL, NULL);
class = E_RULE_CONTEXT_GET_CLASS (context);
g_return_val_if_fail (class->new_element != NULL, NULL);
return class->new_element (context, name);
}