/* * Copyright © 2002 Olivier Martin * (C) 2002 Jorn Baayen * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "config.h" #include #include #include #include "ephy-node-filter.h" static void ephy_node_filter_class_init (EphyNodeFilterClass *klass); static void ephy_node_filter_init (EphyNodeFilter *node); static void ephy_node_filter_finalize (GObject *object); static gboolean ephy_node_filter_expression_evaluate (EphyNodeFilterExpression *expression, EphyNode *node); enum { CHANGED, LAST_SIGNAL }; #define EPHY_NODE_FILTER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_NODE_FILTER, EphyNodeFilterPrivate)) struct _EphyNodeFilterPrivate { GPtrArray *levels; }; struct _EphyNodeFilterExpression { EphyNodeFilterExpressionType type; union { struct { EphyNode *a; EphyNode *b; } node_args; struct { int prop_id; union { EphyNode *node; char *string; int number; } second_arg; } prop_args; } args; }; static GObjectClass *parent_class = NULL; static guint ephy_node_filter_signals[LAST_SIGNAL] = { 0 }; GType ephy_node_filter_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { const GTypeInfo our_info = { sizeof (EphyNodeFilterClass), NULL, NULL, (GClassInitFunc) ephy_node_filter_class_init, NULL, NULL, sizeof (EphyNodeFilter), 0, (GInstanceInitFunc) ephy_node_filter_init }; type = g_type_register_static (G_TYPE_OBJECT, "EphyNodeFilter", &our_info, 0); } return type; } static void ephy_node_filter_class_init (EphyNodeFilterClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->finalize = ephy_node_filter_finalize; ephy_node_filter_signals[CHANGED] = g_signal_new ("changed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (EphyNodeFilterClass, changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_type_class_add_private (object_class, sizeof (EphyNodeFilterPrivate)); } static void ephy_node_filter_init (EphyNodeFilter *filter) { filter->priv = EPHY_NODE_FILTER_GET_PRIVATE (filter); filter->priv->levels = g_ptr_array_new (); } static void ephy_node_filter_finalize (GObject *object) { EphyNodeFilter *filter = EPHY_NODE_FILTER (object); ephy_node_filter_empty (filter); g_ptr_array_free (filter->priv->levels, TRUE); G_OBJECT_CLASS (parent_class)->finalize (object); } EphyNodeFilter * ephy_node_filter_new (void) { return EPHY_NODE_FILTER (g_object_new (EPHY_TYPE_NODE_FILTER, NULL)); } void ephy_node_filter_add_expression (EphyNodeFilter *filter, EphyNodeFilterExpression *exp, int level) { while (level >= filter->priv->levels->len) g_ptr_array_add (filter->priv->levels, NULL); /* FIXME bogosity! This only works because g_list_append (x, data) == x */ g_ptr_array_index (filter->priv->levels, level) = g_list_append (g_ptr_array_index (filter->priv->levels, level), exp); } void ephy_node_filter_empty (EphyNodeFilter *filter) { int i; for (i = filter->priv->levels->len - 1; i >= 0; i--) { GList *list, *l; list = g_ptr_array_index (filter->priv->levels, i); for (l = list; l != NULL; l = g_list_next (l)) { EphyNodeFilterExpression *exp; exp = (EphyNodeFilterExpression *) l->data; ephy_node_filter_expression_free (exp); } g_list_free (list); g_ptr_array_remove_index (filter->priv->levels, i); } } void ephy_node_filter_done_changing (EphyNodeFilter *filter) { g_signal_emit (G_OBJECT (filter), ephy_node_filter_signals[CHANGED], 0); } /* * We go through each level evaluating the filter expressions. * Every time we get a match we immediately do a break and jump * to the next level. We'll return FALSE if we arrive to a level * without matches, TRUE otherwise. */ gboolean ephy_node_filter_evaluate (EphyNodeFilter *filter, EphyNode *node) { int i; for (i = 0; i < filter->priv->levels->len; i++) { GList *l, *list; gboolean handled; handled = FALSE; list = g_ptr_array_index (filter->priv->levels, i); for (l = list; l != NULL; l = g_list_next (l)) { if (ephy_node_filter_expression_evaluate (l->data, node) == TRUE) { handled = TRUE; break; } } if (list != NULL && handled == FALSE) return FALSE; } return TRUE; } EphyNodeFilterExpression * ephy_node_filter_expression_new (EphyNodeFilterExpressionType type, ...) { EphyNodeFilterExpression *exp; va_list valist; va_start (valist, type); exp = g_new0 (EphyNodeFilterExpression, 1); exp->type = type; switch (type) { case EPHY_NODE_FILTER_EXPRESSION_NODE_EQUALS: exp->args.node_args.a = va_arg (valist, EphyNode *); exp->args.node_args.b = va_arg (valist, EphyNode *); break; case EPHY_NODE_FILTER_EXPRESSION_EQUALS: case EPHY_NODE_FILTER_EXPRESSION_HAS_PARENT: case EPHY_NODE_FILTER_EXPRESSION_HAS_CHILD: exp->args.node_args.a = va_arg (valist, EphyNode *); break; case EPHY_NODE_FILTER_EXPRESSION_NODE_PROP_EQUALS: case EPHY_NODE_FILTER_EXPRESSION_CHILD_PROP_EQUALS: exp->args.prop_args.prop_id = va_arg (valist, int); exp->args.prop_args.second_arg.node = va_arg (valist, EphyNode *); break; case EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_CONTAINS: case EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_EQUALS: exp->args.prop_args.prop_id = va_arg (valist, int); exp->args.prop_args.second_arg.string = g_utf8_casefold (va_arg (valist, char *), -1); break; case EPHY_NODE_FILTER_EXPRESSION_KEY_PROP_CONTAINS: case EPHY_NODE_FILTER_EXPRESSION_KEY_PROP_EQUALS: { char *folded; exp->args.prop_args.prop_id = va_arg (valist, int); folded = g_utf8_casefold (va_arg (valist, char *), -1); exp->args.prop_args.second_arg.string = g_utf8_collate_key (folded, -1); g_free (folded); break; } case EPHY_NODE_FILTER_EXPRESSION_INT_PROP_EQUALS: case EPHY_NODE_FILTER_EXPRESSION_INT_PROP_BIGGER_THAN: case EPHY_NODE_FILTER_EXPRESSION_INT_PROP_LESS_THAN: exp->args.prop_args.prop_id = va_arg (valist, int); exp->args.prop_args.second_arg.number = va_arg (valist, int); break; default: break; } va_end (valist); return exp; } void ephy_node_filter_expression_free (EphyNodeFilterExpression *exp) { switch (exp->type) { case EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_CONTAINS: case EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_EQUALS: case EPHY_NODE_FILTER_EXPRESSION_KEY_PROP_CONTAINS: case EPHY_NODE_FILTER_EXPRESSION_KEY_PROP_EQUALS: g_free (exp->args.prop_args.second_arg.string); break; default: break; } g_free (exp); } static gboolean ephy_node_filter_expression_evaluate (EphyNodeFilterExpression *exp, EphyNode *node) { switch (exp->type) { case EPHY_NODE_FILTER_EXPRESSION_ALWAYS_TRUE: return TRUE; case EPHY_NODE_FILTER_EXPRESSION_NODE_EQUALS: return (exp->args.node_args.a == exp->args.node_args.b); case EPHY_NODE_FILTER_EXPRESSION_EQUALS: return (exp->args.node_args.a == node); case EPHY_NODE_FILTER_EXPRESSION_HAS_PARENT: return ephy_node_has_child (exp->args.node_args.a, node); case EPHY_NODE_FILTER_EXPRESSION_HAS_CHILD: return ephy_node_has_child (node, exp->args.node_args.a); case EPHY_NODE_FILTER_EXPRESSION_NODE_PROP_EQUALS: { EphyNode *prop; prop = ephy_node_get_property_node (node, exp->args.prop_args.prop_id); return (prop == exp->args.prop_args.second_arg.node); } case EPHY_NODE_FILTER_EXPRESSION_CHILD_PROP_EQUALS: { EphyNode *prop; GPtrArray *children; int i; children = ephy_node_get_children (node); for (i = 0; i < children->len; i++) { EphyNode *child; child = g_ptr_array_index (children, i); prop = ephy_node_get_property_node (child, exp->args.prop_args.prop_id); if (prop == exp->args.prop_args.second_arg.node) { return TRUE; } } return FALSE; } case EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_CONTAINS: { const char *prop; char *folded_case; gboolean ret; prop = ephy_node_get_property_string (node, exp->args.prop_args.prop_id); if (prop == NULL) return FALSE; folded_case = g_utf8_casefold (prop, -1); ret = (strstr (folded_case, exp->args.prop_args.second_arg.string) != NULL); g_free (folded_case); return ret; } case EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_EQUALS: { const char *prop; char *folded_case; gboolean ret; prop = ephy_node_get_property_string (node, exp->args.prop_args.prop_id); if (prop == NULL) return FALSE; folded_case = g_utf8_casefold (prop, -1); ret = (strcmp (folded_case, exp->args.prop_args.second_arg.string) == 0); g_free (folded_case); return ret; } case EPHY_NODE_FILTER_EXPRESSION_KEY_PROP_CONTAINS: { const char *prop; prop = ephy_node_get_property_string (node, exp->args.prop_args.prop_id); if (prop == NULL) return FALSE; return (strstr (prop, exp->args.prop_args.second_arg.string) != NULL); } case EPHY_NODE_FILTER_EXPRESSION_KEY_PROP_EQUALS: { const char *prop; prop = ephy_node_get_property_string (node, exp->args.prop_args.prop_id); if (prop == NULL) return FALSE; return (strcmp (prop, exp->args.prop_args.second_arg.string) == 0); } case EPHY_NODE_FILTER_EXPRESSION_INT_PROP_EQUALS: { int prop; prop = ephy_node_get_property_int (node, exp->args.prop_args.prop_id); return (prop == exp->args.prop_args.second_arg.number); } case EPHY_NODE_FILTER_EXPRESSION_INT_PROP_BIGGER_THAN: { int prop; prop = ephy_node_get_property_int (node, exp->args.prop_args.prop_id); return (prop > exp->args.prop_args.second_arg.number); } case EPHY_NODE_FILTER_EXPRESSION_INT_PROP_LESS_THAN: { int prop; prop = ephy_node_get_property_int (node, exp->args.prop_args.prop_id); return (prop < exp->args.prop_args.second_arg.number); } default: break; } return FALSE; }