Logo Search packages:      
Sourcecode: pan version File versions  Download package

filter.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Pan - A Newsreader for Gtk+
 * Copyright (C) 2002  Charles Kerr <charles@rebelbase.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; version 2 of the License.
 *
 * 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
 */

#include <config.h>

#include <string.h>

#include <glib.h>

#include <pan/base/debug.h>
#include <pan/base/pan-i18n.h>
#include <pan/base/pan-glib-extensions.h>

#include <pan/filters/filter.h>
#include <pan/filters/filter-aggregate.h>

const gchar * FILTER_CLASS_ID = "PanObject::Filter";

/************
*************  PROTECTED
************/

void
filter_class_dup (const Filter          * filter_original,
                Filter                * filter_new)
{
      g_return_if_fail (filter_new!=NULL);
      g_return_if_fail (filter_original!=NULL);

      filter_new->negate = filter_original->negate;
      filter_new->class_id = filter_original->class_id;
}

/*static gint filters_alive = 0;*/

void
filter_constructor (Filter                  * filter,
                    PanObjectDestructor       dtor,
                    FilterTestArticlesFunc    test_func,
                    FilterToStringFunc        to_string_func,
                    FilterDupFunc             dup_func,
                    const gchar             * class_id)
{
      debug_enter ("filter_constructor");
      g_return_if_fail (filter!=NULL);

      pan_object_constructor (PAN_OBJECT(filter), dtor);
      filter->negate = FALSE;
      filter->test_articles_func = test_func;
      filter->to_string_func = to_string_func;
      filter->dup_func = dup_func;
      filter->class_id = class_id;

      debug_exit ("filter_constructor");
}

void
filter_destructor (PanObject * obj)
{
      pan_object_destructor (obj);
}

/************
*************  PUBLIC
************/

/***
****  TESTING ARTICLES
***/

gboolean
filter_test_article (Filter * filter,
                     const Article * article)
{
      gboolean retval;

      /* sanity clause */
      g_return_val_if_fail (filter!=NULL, FALSE);
      g_return_val_if_fail (article!=NULL, FALSE);

      /* evaluate the article */
      (*filter->test_articles_func)(filter,&article,1,&retval);
      if (filter->negate)
            retval = !retval;

      return retval;
}

void
filter_test_articles  (Filter               * filter,
                       const Article       ** articles,
                       gint                   qty,
                       gboolean             * passfail)
{
      g_return_if_fail (filter!=NULL);
      g_return_if_fail (articles!=NULL);
      g_return_if_fail (qty>=0);
      g_return_if_fail (passfail!=NULL);

      (*filter->test_articles_func)(filter,articles,qty,passfail);

      if (filter->negate) {
            gboolean * end;
            for (end = passfail+qty; passfail!=end; ++passfail)
                  *passfail = !*passfail;
      }
}

char*
filter_to_string (const Filter * filter)
{
      g_return_val_if_fail (filter!=NULL, NULL);
      return (*filter->to_string_func)(filter);
}


static void
filter_to_string_recursive (GString * gstr,
                            const Filter * filter,
                            gint depth)
{
      /* sanity clause */
      g_return_if_fail (gstr!=NULL);
      g_return_if_fail (filter!=NULL);
      g_return_if_fail (depth>=0);

      /* add this filter */
      if (1)
      {
            gint i;
            gchar * s = filter_to_string (filter);

            for (i=0; i<depth; ++i)
                  g_string_append (gstr, "          ");
            g_string_append (gstr,  s);
            g_string_append_c (gstr, '\n');

            g_free (s);
      }

      /* maybe add children */
      if (filter_isa (filter, FILTER_AGGREGATE_CLASS_ID))
      {
            const FilterAggregate * fa = FILTER_AGGREGATE (filter);
            const gint children = filter_aggregate_child_size (fa);

            if (children == 0)
            {
                  if (filter->negate)
                        g_string_append (gstr, _("  (No conditions -- no articles will match)"));
                  else
                        g_string_append (gstr, _("  (No conditions -- all articles will match)"));
            }
            else
            {
                  guint i;
                  for (i=0; i<fa->children->len; ++i)
                  {
                        const Filter * child = FILTER(g_ptr_array_index(fa->children,i));
                        filter_to_string_recursive (gstr, child, depth+1);
                  }
            }
      }
}

char*
filter_to_string_deep (const Filter * filter)
{
      GString * gstr = g_string_new (NULL);
      filter_to_string_recursive (gstr, filter, 0);
      return g_string_free (gstr, FALSE);
}

void
filter_remove_failures (Filter * filter,
                        GPtrArray * articles)
{
      gint i;
      GPtrArray * tmp;
      gboolean * passfail;

      /* sanity clause */
      g_return_if_fail (articles!=NULL);
      if (filter==NULL) {
            articles->len = 0;
            g_return_if_fail (filter!=NULL);
      }
      if (articles->len == 0)
            return;

      /* test the articles */
      passfail = g_new0 (gboolean, articles->len);
      filter_test_articles (filter,
                            (const Article**)articles->pdata,
                            articles->len,
                            passfail);

      /* only keep those that passed */
      tmp = g_ptr_array_sized_new (articles->len);
      for (i=0; i<articles->len; ++i)
            if (passfail[i])
                  g_ptr_array_add (tmp, g_ptr_array_index(articles,i));
      pan_g_ptr_array_assign (articles, tmp->pdata, tmp->len);

      /* cleanup */
      g_free (passfail);
      g_ptr_array_free (tmp, TRUE);
}

/***
****  META
***/

Filter*
filter_dup (const Filter * filter)
{
      Filter * f;
      debug_enter ("filter_dup");
      g_return_val_if_fail (filter!=NULL, NULL);

      f = (*filter->dup_func)(filter);

      debug_exit ("filter_dup");
      return f;
}

/**
 * Yes I know this RTTI is very primitive.
 * This is all going to be ported to GObjects when glib 2.0 comes out.
 */
gboolean
filter_isa (const Filter * f, const gchar * class_id)
{
      gboolean retval;
      g_return_val_if_fail (f!=NULL, FALSE);
      g_return_val_if_fail (is_nonempty_string(class_id), FALSE);
      g_return_val_if_fail (is_nonempty_string(f->class_id), FALSE);
      retval = !strncmp (f->class_id, class_id, strlen(class_id));
      return retval;
}

gpointer
filter_cast (Filter * filter, const gchar * class_id)
{
      gpointer retval = filter_isa (filter,class_id) ? filter : NULL;
      if (!retval)
            g_warning ("Tried to cast a \"%s\" as a \"%s\"", (filter==NULL?"NULL":filter->class_id), (class_id==NULL?"NULL":class_id));
      return retval;
}

/***
****  ACCESSORS / MUTATORS
***/

void
filter_negate (Filter * filter)
{
      g_return_if_fail (filter != NULL);

      filter->negate = !filter->negate;
}

Generated by  Doxygen 1.6.0   Back to index