1027 lines
25 KiB
C
1027 lines
25 KiB
C
/* -*- 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 <http://www.gnu.org/licenses/>
|
|
*
|
|
*
|
|
* Authors:
|
|
* Not Zed <notzed@lostzed.mmc.com.au>
|
|
* Jeffrey Stedfast <fejj@ximian.com>
|
|
*
|
|
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <glib/gi18n.h>
|
|
|
|
#include <libedataserver/libedataserver.h>
|
|
|
|
#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);
|
|
}
|