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

article-find.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 <gtk/gtk.h>

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

#include <pan/article-find.h>
#include <pan/articlelist.h>
#include <pan/globals.h>
#include <pan/util.h>

enum
{
      PAN_RESPONSE_FIND_NEXT = 1,
      PAN_RESPONSE_FIND_PREV = 2
};

static GtkWidget *find_dialog;

static void
find_dialog_destroy_cb (void)
{
      find_dialog = NULL;
}

typedef struct
{
      gboolean unread_only;
      gboolean case_sensitive;

      GPatternSpec * author;
      GPatternSpec * subject;
      GPatternSpec * msgid;
} 
header_compare_data;

static gboolean
test_phrase (GPatternSpec * pattern, const char * phrase, gboolean case_sensitive)
{
      gboolean retval;
      char * down = NULL;

      if (!case_sensitive)
            phrase = down = g_utf8_strdown (phrase, -1);
      retval = g_pattern_match (pattern, strlen(phrase), phrase, NULL);
      if (down != NULL)
            g_free (down);

      return retval;
}

/**
 * Return 0 if equal
 */
static gboolean
compare_article (const Article * article, gpointer compare_data_gpointer)
{
      const header_compare_data * header = (const header_compare_data*) compare_data_gpointer;

      /* if it ain't an article, it ain't a match */
      if (!article)
            return 0;

      /* make sure the unread state (if user specified) matches... */
      if (header->unread_only && article_is_read (article))
            return 0;

      /* make sure the author (if user specified one) matches.. */
      if (header->author != NULL) {
            char buf[512] = { '\0' };
            article_get_author_str (article, buf, sizeof(buf));
            if (!test_phrase (header->author, buf, header->case_sensitive))
                  return 0;
      }

      /* make sure the subject (if user specified one) matches.. */
      if (header->subject && !test_phrase (header->subject, article_get_subject(article), header->case_sensitive))
            return 0;

      /* make sure the message-id (if user specified one) matches.. */
      if (header->msgid && !test_phrase (header->msgid, article_get_message_id(article), header->case_sensitive))
            return 0;

      /* hooray, a match */
      return 1;
}

static GPatternSpec*
create_pattern (const char * pch, gboolean case_sensitive)
{
      GPatternSpec * retval = NULL;

      if (pch != NULL)
      {
            char * tmp = g_strdup (pch);
            g_strstrip (tmp);
            if (is_nonempty_string (tmp))
            {
                  /* wrap the key in wildcards to make it a substring search...
                     unless there are already wildcards present, indicating the
                     user already knows what he wants */
                  if (!strchr (tmp, '*'))
                        replace_gstr (&tmp, g_strdup_printf ("*%s*", tmp));
                  if (!case_sensitive)
                        replace_gstr (&tmp, g_utf8_strdown (tmp, -1));
                  retval = g_pattern_spec_new (tmp);
            }

            g_free (tmp);
      }

      return retval;
}

static void
find_article (const char   * author,
              const char   * subject,
              const char   * message_id,
              gboolean       case_sensitive,
              gboolean       unread_only,
              gboolean       reverse)
{
      header_compare_data header_data;

      /* set up search function struct */
      header_data.author = create_pattern (author, case_sensitive);
      header_data.subject = create_pattern (subject, case_sensitive);
      header_data.msgid = create_pattern (message_id, case_sensitive);
      header_data.unread_only = unread_only;
      header_data.case_sensitive = case_sensitive;

      /* select the article */
      if (reverse)
            header_pane_select_prev_if (compare_article, &header_data);
      else
            header_pane_select_next_if (compare_article, &header_data);

      /* cleanup */
      if (header_data.author)
            g_pattern_spec_free (header_data.author);
      if (header_data.subject)
            g_pattern_spec_free (header_data.subject);
      if (header_data.msgid)
            g_pattern_spec_free (header_data.msgid);
}

static char * find_article__author = NULL;
static char * find_article__subject = NULL;
static char * find_article__message_id = NULL;
static gboolean find_article__case_sensitive = FALSE;
static gboolean find_article__unread_only = FALSE;
static gboolean find_article__reverse = FALSE;


static void
find_dialog_response_cb (GtkDialog     * dialog,
                         int             response,
                         GtkWidget     * table)
{
      g_return_if_fail (table!=NULL);

      if (response==PAN_RESPONSE_FIND_PREV || response==PAN_RESPONSE_FIND_NEXT)
      {
            GtkWidget * author_entry;
            GtkWidget * subject_entry;
            GtkWidget * message_id_entry;
            GtkWidget * case_sensitive_checkbox;
            GtkWidget * unread_only_checkbox;
            char * author;
            char * subject;
            char * message_id;
            gboolean case_sensitive;
            gboolean unread_only;

            /* get author to search for, or NULL if none... */
            author_entry = (GtkWidget*) gtk_object_get_data ( GTK_OBJECT(find_dialog), "author" );
            g_return_if_fail ( author_entry!=NULL );
            author = g_strdup ( gtk_entry_get_text ( GTK_ENTRY(author_entry) ) );
            g_strstrip (author);
            if (!*author)
                  replace_gstr (&author, NULL);

            /* get subject to search for, or NULL if none... */
            subject_entry = (GtkWidget*) gtk_object_get_data ( GTK_OBJECT(find_dialog), "subject" );
            g_return_if_fail ( subject_entry!=NULL );
            subject = g_strdup ( gtk_entry_get_text ( GTK_ENTRY(subject_entry) ) );
            g_strstrip (subject);
            if (!*subject)
                  replace_gstr (&subject, NULL);

            /* get message-id to search for, or NULL if none...*/
            message_id_entry = (GtkWidget*) gtk_object_get_data ( GTK_OBJECT(find_dialog), "message-id" );
            g_return_if_fail ( message_id_entry != NULL );
            message_id = g_strdup ( gtk_entry_get_text ( GTK_ENTRY(message_id_entry) ) );
            g_strstrip (message_id);
            if (!*message_id)
                  replace_gstr (&message_id, NULL);

            /* get case sensitivity... */
            case_sensitive_checkbox = (GtkWidget*) gtk_object_get_data ( GTK_OBJECT(find_dialog), "case" );
            g_return_if_fail ( case_sensitive_checkbox!=NULL );
            case_sensitive = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(case_sensitive_checkbox) );

            /* get case sensitivity... */
            unread_only_checkbox = (GtkWidget*) gtk_object_get_data ( GTK_OBJECT(find_dialog), "unread" );
            g_return_if_fail ( unread_only_checkbox!=NULL );
            unread_only = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(unread_only_checkbox) );

            /* update the find_article fields with these widget values */
            replace_gstr (&find_article__author, author);
            replace_gstr (&find_article__subject, subject);
            replace_gstr (&find_article__message_id, message_id);
            find_article__case_sensitive = case_sensitive;
            find_article__unread_only = unread_only;

            find_article__reverse = response == PAN_RESPONSE_FIND_PREV;
            articlelist_find_next_cb ( );
      }
      else
      {
            gtk_widget_unmap (GTK_WIDGET(find_dialog));
      }
}

void
articlelist_find_next_cb (void)
{
      /* user may have hit "find again" without hitting "find" first */
      if (!find_article__subject && !find_article__author && !find_article__message_id) {
            articlelist_find_text_cb();
            return;
      }

      /* got a subject and/or an author.. let's go find 'em */
      find_article (find_article__author,
                    find_article__subject,
                    find_article__message_id,
                    find_article__case_sensitive,
                    find_article__unread_only,
                    find_article__reverse);

      find_article__reverse = FALSE;
}


void
articlelist_find_text_cb (void)
{
      GtkWidget * t;
      GtkWidget * author_entry;
      GtkWidget * subject_entry;
      GtkWidget * message_id_entry;
      GtkWidget * case_sensitive_checkbutton;
      GtkWidget * unread_only_checkbutton;
      guint       row = 0;

      /* There can be only one! (find window) */
      if (find_dialog != NULL)
      {
            gtk_widget_show_all (find_dialog);
            gtk_widget_map (find_dialog);
            return;
      }

      find_dialog = gtk_dialog_new_with_buttons (_("Pan: Find Article"),
                                                 GTK_WINDOW(Pan.window),
                                                 GTK_DIALOG_DESTROY_WITH_PARENT,
                                                 GTK_STOCK_GO_BACK, PAN_RESPONSE_FIND_PREV,
                                                 GTK_STOCK_GO_FORWARD, PAN_RESPONSE_FIND_NEXT,
                                                 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
                                                 NULL);

      t = pan_hig_workarea_create ();
      pan_hig_workarea_add_section_spacer (t, row, 3);

      subject_entry = gtk_entry_new ( );
      g_object_set_data (G_OBJECT(find_dialog), "subject", subject_entry);
      pan_hig_workarea_add_row (t, &row, _("_Subject:"), subject_entry, NULL);

      author_entry = gtk_entry_new ( );
      g_object_set_data (G_OBJECT(find_dialog), "author", author_entry);
      pan_hig_workarea_add_row (t, &row, _("_Author:"), author_entry, NULL);

      message_id_entry = gtk_entry_new ();
      g_object_set_data (G_OBJECT(find_dialog), "message-id", message_id_entry);
      pan_hig_workarea_add_row (t, &row, _("_Message-ID:"), message_id_entry, NULL);

      pan_hig_workarea_add_section_divider (t, &row);

      case_sensitive_checkbutton = pan_hig_workarea_add_wide_checkbutton (t, &row, _("Case Sensiti_ve"), FALSE);
      g_object_set_data (G_OBJECT(find_dialog), "case", case_sensitive_checkbutton);

      unread_only_checkbutton = pan_hig_workarea_add_wide_checkbutton (t, &row, _("_Unread Only"), FALSE);
      g_object_set_data (G_OBJECT(find_dialog), "unread", unread_only_checkbutton);
      
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG(find_dialog)->vbox), t, FALSE, FALSE, 0);
      
      g_signal_connect (find_dialog, "response", G_CALLBACK(find_dialog_response_cb), t);
      g_signal_connect (find_dialog, "destroy", G_CALLBACK(find_dialog_destroy_cb), NULL);

      gtk_widget_grab_focus (subject_entry);
      gtk_widget_show_all (find_dialog);
}

Generated by  Doxygen 1.6.0   Back to index