/* This is -*- C -*- */
/* vim: set sw=2: */
/*
* guppi-seq-categorical.c
*
* Copyright (C) 2000 EMC Capital Management, Inc.
* Copyright (C) 2001 The Free Software Foundation
*
* Developed by Jon Trowbridge <trow@gnu.org>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include "guppi-seq-categorical.h"
/* #include <gnome.h> */
#include <gtk/gtksignal.h>
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>
#include <guppi-convenient.h>
typedef struct _GuppiSeqCategoricalPrivate GuppiSeqCategoricalPrivate;
struct _GuppiSeqCategoricalPrivate {
GuppiCategory *category;
GuppiSeqInteger *seq;
gboolean auto_add;
GHashTable *freq_table;
};
static GtkObjectClass *parent_class = NULL;
static void
guppi_seq_categorical_finalize (GtkObject *obj)
{
GuppiSeqCategorical *seq = GUPPI_SEQ_CATEGORICAL (obj);
guppi_unref0 (seq->priv->category);
if (seq->priv->freq_table)
g_hash_table_foreach (seq->priv->freq_table, guppi_free_hash_val, NULL);
g_hash_table_destroy (seq->priv->freq_table);
seq->priv->freq_table = NULL;
guppi_free0 (seq->priv);
if (parent_class->finalize)
parent_class->finalize (obj);
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
/* GuppiSeqInteger virtual functions */
static void
range (GuppiSeqInteger *si, gint *min, gint *max)
{
GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq;
GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass);
klass->range (proxy, min, max);
}
static gint
frequency (GuppiSeqInteger *si, gint x)
{
GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq;
GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass);
return klass->frequency (proxy, x);
}
static gint
get (GuppiSeqInteger *si, gint i)
{
GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq;
GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass);
return klass->get (proxy, i);
}
static void
set (GuppiSeqInteger *si, gint i, gint val)
{
GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq;
GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass);
klass->set (proxy, i, val);
}
static void
insert (GuppiSeqInteger *si, gint i, const gint *ptr, gsize N)
{
GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq;
GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass);
klass->insert (proxy, i, ptr, N);
}
/* GuppiSeq virtual functions */
static void
size_hint (GuppiSeq *seq, gsize N)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
klass->size_hint (proxy, N);
}
static void
get_bounds (GuppiSeq *seq, gint *min, gint *max)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
klass->get_bounds (proxy, min, max);
}
static void
shift_indices (GuppiSeq *seq, gint delta)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
klass->shift_indices (proxy, delta);
}
static void
insert_generic (GuppiSeq *seq, gint i, gsize N)
{
GuppiSeqInteger *proxy = GUPPI_SEQ_INTEGER (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass);
code_t c;
gint j;
c = guppi_category_min_code (GUPPI_SEQ_CATEGORICAL (seq)->priv->category);
for (j = 0; j < (gint) N; ++j) {
klass->insert (proxy, i, &c, 1);
}
/* We don't want to chain insert_generic */
}
static void
delete_many (GuppiSeq *seq, gint i, gsize N)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
klass->delete_many (proxy, i, N);
}
static gboolean
has_missing (GuppiSeq *seq)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
return klass->has_missing (proxy);
}
static gsize
missing_count (GuppiSeq *seq)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
return klass->missing_count (proxy);
}
static gboolean
missing (GuppiSeq *seq, gint i)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
return klass->missing (proxy, i);
}
static void
set_missing (GuppiSeq *seq, gint i, gboolean x)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
klass->set_missing (proxy, i, x);
}
static void
set_range_missing (GuppiSeq *seq, gint i0, gint i1, gboolean x)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
klass->set_range_missing (proxy, i0, i1, x);
}
static void
set_many_missing (GuppiSeq *seq, gint *ind, gsize N, gboolean x)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
klass->set_many_missing (proxy, ind, N, x);
}
static void
set_all_missing (GuppiSeq *seq, gboolean x)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
klass->set_all_missing (proxy, x);
}
static void
insert_missing (GuppiSeq *seq, gint i, gboolean x, gsize N)
{
GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq);
GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass);
g_message ("guppi-seq-categorical insert_missing");
klass->insert_missing (seq, i, x, N);
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
/*** Frequency Table Updating **** */
/*
By doing all of this in signals, we ensure that the tables get updated
even if we directly manipulate the underlying GuppiSeqInteger object.
Cool, 'eh?
*/
static void
freq_adjust (GuppiSeqCategorical *seq, gint i0, gint i1, gint adj)
{
GuppiSeqCategoricalPrivate *p = seq->priv;
GHashTable *freq = p->freq_table;
gint i;
gboolean has_missing;
code_t last_code = GUPPI_INVALID_CODE;
gpointer last_val = NULL;
i0 = MAX (i0, guppi_seq_min_index (GUPPI_SEQ (seq)));
i1 = MIN (i1, guppi_seq_max_index (GUPPI_SEQ (seq)));
has_missing = guppi_seq_has_missing (GUPPI_SEQ (seq));
for (i = i0; i <= i1; ++i) {
code_t c;
gpointer val;
if (!has_missing || guppi_seq_available (GUPPI_SEQ (seq), i)) {
c = (code_t) guppi_seq_integer_get (GUPPI_SEQ_INTEGER (seq), i);
if (last_val && (c == last_code)) {
val = last_val;
} else {
gpointer ptr = GUPPI_CATEGORY_CODE_TO_POINTER (c);
val = g_hash_table_lookup (freq, ptr);
if (val == NULL) {
val = guppi_new0 (gint, 1);
g_hash_table_insert (freq, ptr, val);
}
last_code = c;
last_val = val;
}
*(gint *) val += adj;
}
}
}
static void
set_before_cb (GuppiSeq *seq, gint i0, gint i1, gpointer foo)
{
freq_adjust (GUPPI_SEQ_CATEGORICAL (seq), i0, i1, -1);
}
static void
set_after_cb (GuppiSeq *seq, gint i0, gint i1, gpointer foo)
{
freq_adjust (GUPPI_SEQ_CATEGORICAL (seq), i0, i1, 1);
}
static void
delete_before_cb (GuppiSeq *seq, gint i, gsize N, gpointer foo)
{
freq_adjust (GUPPI_SEQ_CATEGORICAL (seq), i, i - 1 + N, -1);
}
static void
insert_after_cb (GuppiSeq *seq, gint i, gsize N, gpointer foo)
{
freq_adjust (GUPPI_SEQ_CATEGORICAL (seq), i, i - 1 + N, 1);
}
static void
guppi_seq_categorical_class_init (GuppiSeqCategoricalClass *klass)
{
GtkObjectClass *object_class = (GtkObjectClass *) klass;
GuppiDataClass *data_class = GUPPI_DATA_CLASS (klass);
GuppiSeqIntegerClass *seq_int_class = GUPPI_SEQ_INTEGER_CLASS (klass);
GuppiSeqClass *seq_class = GUPPI_SEQ_CLASS (klass);
parent_class = gtk_type_class (GUPPI_TYPE_SEQ_INTEGER);
seq_int_class->range = range;
seq_int_class->frequency = frequency;
seq_int_class->get = get;
seq_int_class->set = set;
seq_int_class->insert = insert;
seq_class->size_hint = size_hint;
seq_class->get_bounds = get_bounds;
seq_class->shift_indices = shift_indices;
seq_class->insert_generic = insert_generic;
seq_class->delete_many = delete_many;
seq_class->has_missing = has_missing;
seq_class->missing_count = missing_count;
seq_class->missing = missing;
seq_class->set_missing = set_missing;
seq_class->set_many_missing = set_many_missing;
seq_class->set_range_missing = set_range_missing;
seq_class->set_all_missing = set_all_missing;
seq_class->insert_missing = insert_missing;
seq_class->support_missing_values = TRUE;
data_class->is_leaf_type = TRUE;
object_class->finalize = guppi_seq_categorical_finalize;
}
static void
guppi_seq_categorical_init (GuppiSeqCategorical *obj)
{
GuppiSeqCategoricalPrivate *p;
p = obj->priv = guppi_new0 (GuppiSeqCategoricalPrivate, 1);
/* FIXME: Underlying implementation should be used-definable */
p->seq = GUPPI_SEQ_INTEGER (guppi_data_new ("GuppiSeqIntegerCore"));
p->category = NULL;
p->auto_add = TRUE;
p->freq_table = g_hash_table_new (NULL, NULL);
gtk_signal_connect (GTK_OBJECT (obj),
"changed_set",
GTK_SIGNAL_FUNC (set_before_cb),
NULL);
gtk_signal_connect_after (GTK_OBJECT (obj),
"changed_set",
GTK_SIGNAL_FUNC (set_after_cb),
NULL);
gtk_signal_connect_after (GTK_OBJECT (obj),
"changed_insert",
GTK_SIGNAL_FUNC (insert_after_cb),
NULL);
gtk_signal_connect (GTK_OBJECT (obj),
"changed_delete",
GTK_SIGNAL_FUNC (delete_before_cb),
NULL);
}
GtkType guppi_seq_categorical_get_type (void)
{
static GtkType guppi_seq_categorical_type = 0;
if (!guppi_seq_categorical_type) {
static const GtkTypeInfo guppi_seq_categorical_info = {
"GuppiSeqCategorical",
sizeof (GuppiSeqCategorical),
sizeof (GuppiSeqCategoricalClass),
(GtkClassInitFunc) guppi_seq_categorical_class_init,
(GtkObjectInitFunc) guppi_seq_categorical_init,
NULL, NULL, (GtkClassInitFunc) NULL
};
guppi_seq_categorical_type = gtk_type_unique (GUPPI_TYPE_SEQ_INTEGER,
&guppi_seq_categorical_info);
}
return guppi_seq_categorical_type;
}
GuppiCategory *
guppi_seq_categorical_category (GuppiSeqCategorical *seq)
{
g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), NULL);
return seq->priv->category;
}
void
guppi_seq_categorical_set_category (GuppiSeqCategorical *seq,
GuppiCategory *cat)
{
GuppiSeqCategoricalPrivate *p;
g_return_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq));
g_return_if_fail (cat != NULL && GUPPI_IS_CATEGORY (cat));
p = seq->priv;
guppi_refcounting_assign (p->category, cat);
p->auto_add = FALSE;
}
gboolean guppi_seq_categorical_auto_add (GuppiSeqCategorical *seq)
{
g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE);
return seq->priv->auto_add;
}
void
guppi_seq_categorical_set_auto_add (GuppiSeqCategorical *seq, gboolean x)
{
g_return_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq));
seq->priv->auto_add = x;
}
const gchar *
guppi_seq_categorical_get (GuppiSeqCategorical *seq, gint i)
{
code_t c;
g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), NULL);
g_return_val_if_fail (guppi_seq_in_bounds (GUPPI_SEQ (seq), i), NULL);
g_return_val_if_fail (seq->priv->category != NULL, NULL);
c = (code_t) guppi_seq_integer_get (GUPPI_SEQ_INTEGER (seq), i);
return guppi_category_find_by_code (seq->priv->category, c);
}
gboolean
guppi_seq_categorical_set (GuppiSeqCategorical *seq, gint i,
const gchar *str)
{
code_t c;
g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE);
g_return_val_if_fail (guppi_seq_in_bounds (GUPPI_SEQ (seq), i), FALSE);
g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (seq)), FALSE);
g_return_val_if_fail (seq->priv->category != NULL, FALSE);
c = guppi_category_find_by_name (seq->priv->category, str);
if (seq->priv->auto_add && c == GUPPI_INVALID_CODE)
c = guppi_category_add_by_name (seq->priv->category, str);
if (c != GUPPI_INVALID_CODE)
guppi_seq_integer_set (GUPPI_SEQ_INTEGER (seq), i, c);
return c != GUPPI_INVALID_CODE;
}
gboolean
guppi_seq_categorical_prepend (GuppiSeqCategorical *seq, const gchar *str)
{
code_t c;
g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE);
g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (seq)), FALSE);
g_return_val_if_fail (seq->priv->category != NULL, FALSE);
c = guppi_category_find_by_name (seq->priv->category, str);
if (seq->priv->auto_add && c == GUPPI_INVALID_CODE)
c = guppi_category_add_by_name (seq->priv->category, str);
if (c != GUPPI_INVALID_CODE)
guppi_seq_integer_prepend (GUPPI_SEQ_INTEGER (seq), c);
return c != GUPPI_INVALID_CODE;
}
gboolean
guppi_seq_categorical_append (GuppiSeqCategorical *seq, const gchar *str)
{
code_t c;
g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE);
g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (seq)), FALSE);
g_return_val_if_fail (seq->priv->category != NULL, FALSE);
c = guppi_category_find_by_name (seq->priv->category, str);
if (seq->priv->auto_add && c == GUPPI_INVALID_CODE)
c = guppi_category_add_by_name (seq->priv->category, str);
if (c != GUPPI_INVALID_CODE)
guppi_seq_integer_append (GUPPI_SEQ_INTEGER (seq), c);
return c != GUPPI_INVALID_CODE;
}
gboolean
guppi_seq_categorical_insert (GuppiSeqCategorical *seq, gint i,
const gchar *str)
{
code_t c;
g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE);
g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (seq)), FALSE);
g_return_val_if_fail (seq->priv->category != NULL, FALSE);
c = guppi_category_find_by_name (seq->priv->category, str);
if (seq->priv->auto_add && c == GUPPI_INVALID_CODE)
c = guppi_category_add_by_name (seq->priv->category, str);
if (c != GUPPI_INVALID_CODE)
guppi_seq_integer_insert (GUPPI_SEQ_INTEGER (seq), i, c);
return c != GUPPI_INVALID_CODE;
}
gint
guppi_seq_categorical_frequency (GuppiSeqCategorical *seq, const gchar *str)
{
code_t c;
gpointer ptr;
g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), 0);
g_return_val_if_fail (str != NULL, 0);
g_return_val_if_fail (seq->priv->category != NULL, 0);
c = guppi_category_find_by_name (seq->priv->category, str);
ptr = g_hash_table_lookup (seq->priv->freq_table,
GUPPI_CATEGORY_CODE_TO_POINTER (c));
return ptr ? *(gint *) ptr : 0;
}
double
guppi_seq_categorical_percentage (GuppiSeqCategorical *seq,
const gchar *str)
{
gsize N;
g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), 0);
g_return_val_if_fail (str != NULL, 0);
g_return_val_if_fail (seq->priv->category != NULL, 0);
N = guppi_seq_count (GUPPI_SEQ (seq));
if (N == 0)
return -1;
return guppi_seq_categorical_frequency (seq, str) / (double) N;
}
syntax highlighted by Code2HTML, v. 0.9.1