/* This is -*- C -*- */ /* vim: set sw=2: */ /* $Id: guppi-data-table.c,v 1.6 2002/01/08 06:28:58 trow Exp $ */ /* * guppi-data-table.c * * Copyright (C) 2001 The Free Software Foundation * * Developed by Jon Trowbridge * * 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 #include "guppi-data-table.h" #include #include static GtkObjectClass *parent_class = NULL; enum { CHANGED_DIMENSIONS, CHANGED_TABLE_ENTRIES, CHANGED_TABLE_LABELS, LAST_SIGNAL }; static guint guppi_data_table_signals[LAST_SIGNAL] = { 0 }; typedef struct _GuppiDataTableStats GuppiDataTableStats; struct _GuppiDataTableStats { gboolean have_sum, have_abs_sum, have_min, have_max; double sum, abs_sum, min, max; }; struct _GuppiDataTablePrivate { GuppiDataTableStats *row_stats; GuppiDataTableStats *col_stats; }; #define perp_span(x) ((x) == GUPPI_TABLE_ROW ? GUPPI_TABLE_COL : GUPPI_TABLE_ROW) /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ static void stats_init (GuppiDataTableStats *stats, gint N) { gint i; for (i = 0; i < N; ++i) { stats[i].have_sum = FALSE; stats[i].have_abs_sum = FALSE; stats[i].have_min = FALSE; stats[i].have_max = FALSE; } } static void guppi_data_table_finalize (GtkObject *obj) { GuppiDataTable *x = GUPPI_DATA_TABLE(obj); g_free (x->priv); x->priv = NULL; guppi_finalized (obj); if (parent_class->finalize) parent_class->finalize (obj); } static gchar * get_size_info (GuppiData *data) { GuppiDataTable *table = GUPPI_DATA_TABLE (data); gint R, C; guppi_data_table_get_dimensions (table, &R, &C); return guppi_strdup_printf ("%d x %d", R, C); } static gboolean validate_class (GuppiDataClass *klass) { GuppiDataTableClass *table_class = GUPPI_DATA_TABLE_CLASS (klass); gboolean ok = TRUE; if (table_class->get_bounds == NULL) { g_warning ("Method GuppiDataTable::get_bounds not defined."); ok = FALSE; } if (table_class->set_bounds == NULL && ! klass->read_only) { g_warning ("Method GuppiDataTable::set_bounds not defined."); ok = FALSE; } if (table_class->get_entry == NULL) { g_warning ("Method GuppiDataTable::get_entry not defined."); ok = FALSE; } if (table_class->set_entry == NULL && ! klass->read_only) { g_warning ("Method GuppiDataTable::set_entry not defined."); ok = FALSE; } if (table_class->get_label == NULL) { g_warning ("Method GuppiDataTable::get_label not defined."); ok = FALSE; } if (table_class->set_label == NULL && ! klass->read_only) { g_warning ("Method GuppiDataTable::set_label not defined."); ok = FALSE; } if (GUPPI_DATA_CLASS (parent_class)->validate_class && ! GUPPI_DATA_CLASS (parent_class)->validate_class (klass)) ok = FALSE; return ok; } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /* Table Operations */ typedef struct _GuppiDataOp_Table GuppiDataOp_Table; struct _GuppiDataOp_Table { GuppiDataOp op; gint r, c, i; GuppiDataTableSpan span; double x; const gchar *str; }; static void op_set (GuppiData *d, GuppiDataOp *op) { GuppiDataTable *tab = GUPPI_DATA_TABLE (d); GuppiDataTableClass *klass = GUPPI_DATA_TABLE_CLASS (GTK_OBJECT (tab)->klass); GuppiDataOp_Table *table_op = (GuppiDataOp_Table *) op; g_assert (klass->set_entry); klass->set_entry (tab, table_op->r, table_op->c, table_op->x); } static void op_set_label (GuppiData *d, GuppiDataOp *op) { GuppiDataTable *tab = GUPPI_DATA_TABLE (d); GuppiDataTableClass *klass = GUPPI_DATA_TABLE_CLASS (GTK_OBJECT (tab)->klass); GuppiDataOp_Table *table_op = (GuppiDataOp_Table *) op; g_assert (klass->set_label); klass->set_label (tab, table_op->span, table_op->i, table_op->str); } static void op_set_dim (GuppiData *d, GuppiDataOp *op) { GuppiDataTable *tab = GUPPI_DATA_TABLE (d); GuppiDataTableClass *klass = GUPPI_DATA_TABLE_CLASS (GTK_OBJECT (tab)->klass); GuppiDataOp_Table *table_op = (GuppiDataOp_Table *) op; g_assert (klass->set_bounds); klass->set_bounds (tab, table_op->r, table_op->c); } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ gboolean guppi_data_table_get_dimensions (GuppiDataTable *tab, gint *rows, gint *cols) { GuppiDataTableClass *klass; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), FALSE); if (rows == NULL && cols == NULL) return TRUE; klass = GUPPI_DATA_TABLE_CLASS (GTK_OBJECT (tab)->klass); g_return_val_if_fail (klass->get_bounds, FALSE); return klass->get_bounds (tab, rows, cols); } gint guppi_data_table_get_span_count (GuppiDataTable *tab, GuppiDataTableSpan span) { gint N = -1; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), -1); switch (span) { case GUPPI_TABLE_ROW: guppi_data_table_get_dimensions (tab, &N, NULL); break; case GUPPI_TABLE_COL: guppi_data_table_get_dimensions (tab, NULL, &N); break; default: g_assert_not_reached (); } return N; } gint guppi_data_table_get_row_count (GuppiDataTable *tab) { gint r; return guppi_data_table_get_dimensions (tab, &r, NULL) ? r : -1; } gint guppi_data_table_get_col_count (GuppiDataTable *tab) { gint c; return guppi_data_table_get_dimensions (tab, NULL, &c) ? c : -1; } gboolean guppi_data_table_in_bounds (GuppiDataTable *tab, gint r, gint c) { gint rows, cols; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), FALSE); if (r < 0 || c < 0) return FALSE; if (! guppi_data_table_get_dimensions (tab, &rows, &cols)) return FALSE; return r < rows && c < cols; } gboolean guppi_data_table_in_span_bounds (GuppiDataTable *tab, GuppiDataTableSpan span, gint i) { g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), FALSE); if (i < 0) return FALSE; return i < guppi_data_table_get_span_count (tab, span); } gboolean guppi_data_table_in_row_bounds (GuppiDataTable *tab, gint r) { return guppi_data_table_in_span_bounds (tab, GUPPI_TABLE_ROW, r); } gboolean guppi_data_table_in_col_bounds (GuppiDataTable *tab, gint c) { return guppi_data_table_in_span_bounds (tab, GUPPI_TABLE_COL, c); } void guppi_data_table_set_dimensions (GuppiDataTable *tab, gint r, gint c) { GuppiDataOp_Table op; g_return_if_fail (GUPPI_IS_DATA_TABLE (tab)); g_return_if_fail (guppi_data_can_change (GUPPI_DATA (tab))); op.op.op = op_set_dim; op.r = r; op.c = c; guppi_data_table_changed_dimensions (tab, r, c, (GuppiDataOp *) &op); } double guppi_data_table_get_entry (GuppiDataTable *tab, gint r, gint c) { GuppiDataTableClass *klass; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), 0.0); g_return_val_if_fail (guppi_data_table_in_bounds (tab, r, c), 0.0); klass = GUPPI_DATA_TABLE_CLASS (GTK_OBJECT (tab)->klass); g_assert (klass->get_entry); return klass->get_entry (tab, r, c); } void guppi_data_table_set_entry (GuppiDataTable *tab, gint r, gint c, double x) { GuppiDataOp_Table op; g_return_if_fail (GUPPI_IS_DATA_TABLE (tab)); g_return_if_fail (guppi_data_can_change (GUPPI_DATA (tab))); g_return_if_fail (guppi_data_table_in_bounds (tab, r, c)); op.op.op = op_set; op.r = r; op.c = c; op.x = x; guppi_data_table_changed_table_entries (tab, r, c, r, c, (GuppiDataOp *) &op); } const gchar * guppi_data_table_get_label (GuppiDataTable *tab, GuppiDataTableSpan span, gint i) { GuppiDataTableClass *klass; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), NULL); g_return_val_if_fail (guppi_data_table_in_span_bounds (tab, span, i), NULL); klass = GUPPI_DATA_TABLE_CLASS (GTK_OBJECT (tab)->klass); g_assert (klass->get_label); return klass->get_label (tab, span, i); } const gchar * guppi_data_table_get_row_label (GuppiDataTable *tab, gint i) { return guppi_data_table_get_label (tab, GUPPI_TABLE_ROW, i); } const gchar * guppi_data_table_get_col_label (GuppiDataTable *tab, gint i) { return guppi_data_table_get_label (tab, GUPPI_TABLE_COL, i); } void guppi_data_table_set_label (GuppiDataTable *tab, GuppiDataTableSpan span, gint i, const gchar *str) { GuppiDataOp_Table op; g_return_if_fail (GUPPI_IS_DATA_TABLE (tab)); g_return_if_fail (guppi_data_can_change (GUPPI_DATA (tab))); g_return_if_fail (guppi_data_table_in_span_bounds (tab, span, i)); op.op.op = op_set_label; op.span = span; op.i = i; op.str = str; guppi_data_table_changed_table_labels (tab, span, i, i, (GuppiDataOp *) &op); } void guppi_data_table_set_row_label (GuppiDataTable *tab, gint r, const gchar *str) { guppi_data_table_set_label (tab, GUPPI_TABLE_ROW, r, str); } void guppi_data_table_set_col_label (GuppiDataTable *tab, gint c, const gchar *str) { guppi_data_table_set_label (tab, GUPPI_TABLE_COL, c, str); } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ static GuppiDataTableStats * get_stats (GuppiDataTable *tab, GuppiDataTableSpan span, gint i) { return &(span == GUPPI_TABLE_ROW ? tab->priv->row_stats : tab->priv->col_stats)[i]; } static void calc_stats (GuppiDataTable *tab, GuppiDataTableSpan span, gint i) { GuppiDataTableClass *klass = GUPPI_DATA_TABLE_CLASS (GTK_OBJECT (tab)->klass); GuppiDataTableStats *stats; gint j = 0, N, r, c; double x, run_sum=0, run_abs_sum=0, run_min=0, run_max=0; stats = get_stats (tab, span, i); N = guppi_data_table_get_span_count (tab, perp_span (span)); for (j = 0; j < N; ++j) { if (span == GUPPI_TABLE_ROW) { r = i; c = j; } else { r = j; c = i; } x = klass->get_entry (tab, r, c); run_sum += x; run_abs_sum += fabs(x); if (j == 0 || x < run_min) run_min = x; if (j == 0 || x > run_max) run_max = x; } stats->have_sum = stats->have_abs_sum = stats->have_min = stats->have_max = TRUE; stats->sum = run_sum; stats->abs_sum = run_abs_sum; stats->min = run_min; stats->max = run_max; } double guppi_data_table_get_sum (GuppiDataTable *tab, GuppiDataTableSpan span, gint i) { GuppiDataTableStats *stats; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), 0); g_return_val_if_fail (guppi_data_table_in_span_bounds (tab, span, i), 0); stats = get_stats (tab, span, i); if (! stats->have_sum) calc_stats (tab, span, i); return stats->sum; } double guppi_data_table_get_abs_sum (GuppiDataTable *tab, GuppiDataTableSpan span, gint i) { GuppiDataTableStats *stats; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), 0); g_return_val_if_fail (guppi_data_table_in_span_bounds (tab, span, i), 0); stats = get_stats (tab, span, i); if (! stats->have_abs_sum) calc_stats (tab, span, i); return stats->abs_sum; } double guppi_data_table_get_min (GuppiDataTable *tab, GuppiDataTableSpan span, gint i) { GuppiDataTableStats *stats; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), 0); g_return_val_if_fail (guppi_data_table_in_span_bounds (tab, span, i), 0); stats = get_stats (tab, span, i); if (! stats->have_min) calc_stats (tab, span, i); return stats->min; } double guppi_data_table_get_max (GuppiDataTable *tab, GuppiDataTableSpan span, gint i) { GuppiDataTableStats *stats; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), 0); g_return_val_if_fail (guppi_data_table_in_span_bounds (tab, span, i), 0); stats = get_stats (tab, span, i); if (! stats->have_max) calc_stats (tab, span, i); return stats->max; } double guppi_data_table_get_range_sum (GuppiDataTable *tab, gint r0, gint c0, gint r1, gint c1) { gint R, C, r, c; double val; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), 0); guppi_data_table_get_dimensions (tab, &R, &C); r0 = CLAMP (r0, 0, R-1); r1 = CLAMP (r1, 0, R-1); c0 = CLAMP (c0, 0, C-1); c1 = CLAMP (c1, 0, C-1); val = 0; for (r = r0; r <= r1; ++r) { for (c = c0; c <= c1; ++c) { val += guppi_data_table_get_entry (tab, r, c); } } return val; } double guppi_data_table_get_range_abs_sum (GuppiDataTable *tab, gint r0, gint c0, gint r1, gint c1) { gint R, C, r, c; double val; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), 0); guppi_data_table_get_dimensions (tab, &R, &C); r0 = CLAMP (r0, 0, R-1); r1 = CLAMP (r1, 0, R-1); c0 = CLAMP (c0, 0, C-1); c1 = CLAMP (c1, 0, C-1); val = 0; for (r = r0; r <= r1; ++r) { for (c = c0; c <= c1; ++c) { val += fabs (guppi_data_table_get_entry (tab, r, c)); } } return val; } double guppi_data_table_get_range_min (GuppiDataTable *tab, gint r0, gint c0, gint r1, gint c1) { gint R, C, r, c; double val = 0; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), 0); guppi_data_table_get_dimensions (tab, &R, &C); r0 = CLAMP (r0, 0, R-1); r1 = CLAMP (r1, 0, R-1); c0 = CLAMP (c0, 0, C-1); c1 = CLAMP (c1, 0, C-1); val = 0; for (r = r0; r <= r1; ++r) { for (c = c0; c <= c1; ++c) { double v = guppi_data_table_get_entry (tab, r, c); if ((r == r0 && c == c0) || (v < val)) val = v; } } return val; } double guppi_data_table_get_range_max (GuppiDataTable *tab, gint r0, gint c0, gint r1, gint c1) { gint R, C, r, c; double val = 0; g_return_val_if_fail (GUPPI_IS_DATA_TABLE (tab), 0); guppi_data_table_get_dimensions (tab, &R, &C); r0 = CLAMP (r0, 0, R-1); r1 = CLAMP (r1, 0, R-1); c0 = CLAMP (c0, 0, C-1); c1 = CLAMP (c1, 0, C-1); val = 0; for (r = r0; r <= r1; ++r) { for (c = c0; c <= c1; ++c) { double v = guppi_data_table_get_entry (tab, r, c); if ((r == r0 && c == c0) || (v > val)) val = v; } } return val; } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ void guppi_data_table_changed_dimensions (GuppiDataTable *tab, gint r, gint c, GuppiDataOp *op) { g_return_if_fail (GUPPI_IS_DATA_TABLE (tab)); g_return_if_fail (op != NULL); guppi_data_add_pending_op (GUPPI_DATA (tab), op); gtk_signal_emit (GTK_OBJECT (tab), guppi_data_table_signals[CHANGED_DIMENSIONS], r, c); } void guppi_data_table_changed_table_entries (GuppiDataTable *tab, gint r0, gint c0, gint r1, gint c1, GuppiDataOp *op) { g_return_if_fail (GUPPI_IS_DATA_TABLE (tab)); g_return_if_fail (op != NULL); guppi_data_add_pending_op (GUPPI_DATA (tab), op); gtk_signal_emit (GTK_OBJECT (tab), guppi_data_table_signals[CHANGED_TABLE_ENTRIES], r0, c0, r1, c1); } void guppi_data_table_changed_table_labels (GuppiDataTable *tab, GuppiDataTableSpan span, gint i0, gint i1, GuppiDataOp *op) { g_return_if_fail (GUPPI_IS_DATA_TABLE (tab)); g_return_if_fail (op != NULL); guppi_data_add_pending_op (GUPPI_DATA (tab), op); gtk_signal_emit (GTK_OBJECT (tab), guppi_data_table_signals[CHANGED_TABLE_LABELS], span, i0, i1); } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ static void changed (GuppiData *data) { GuppiDataTable *tab = GUPPI_DATA_TABLE (data); gint r, c; guppi_data_table_get_dimensions (tab, &r, &c); stats_init (tab->priv->row_stats, r); stats_init (tab->priv->col_stats, c); if (GUPPI_DATA_CLASS (parent_class)->changed) GUPPI_DATA_CLASS (parent_class)->changed (data); } static void changed_dimensions (GuppiDataTable *tab, gint r, gint c) { guppi_data_changed (GUPPI_DATA (tab)); guppi_free (tab->priv->row_stats); guppi_free (tab->priv->col_stats); tab->priv->row_stats = guppi_new (GuppiDataTableStats, r); tab->priv->col_stats = guppi_new (GuppiDataTableStats, c); } static void changed_table_entries (GuppiDataTable *tab, gint r0, gint c0, gint r1, gint c1) { guppi_data_changed (GUPPI_DATA (tab)); } static void changed_table_labels (GuppiDataTable *tab, GuppiDataTableSpan span, gint i0, gint i1) { guppi_data_changed (GUPPI_DATA (tab)); } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ typedef void (*GuppiSignal_NONE__INT_INT_INT) (GtkObject *, gint, gint, gint, gpointer closure); static void guppi_marshal_NONE__INT_INT_INT (GtkObject *object, GtkSignalFunc func, gpointer func_data, GtkArg *args) { GuppiSignal_NONE__INT_INT_INT rfunc = (GuppiSignal_NONE__INT_INT_INT) func; rfunc (object, GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]), GTK_VALUE_INT (args[2]), func_data); } typedef void (*GuppiSignal_NONE__INT_INT_INT_INT) (GtkObject *, gint, gint, gint, gint, gpointer closure); static void guppi_marshal_NONE__INT_INT_INT_INT (GtkObject *object, GtkSignalFunc func, gpointer func_data, GtkArg *args) { GuppiSignal_NONE__INT_INT_INT_INT rfunc = (GuppiSignal_NONE__INT_INT_INT_INT) func; rfunc (object, GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]), GTK_VALUE_INT (args[2]), GTK_VALUE_INT (args[3]), func_data); } static void guppi_data_table_class_init (GuppiDataTableClass *klass) { GtkObjectClass *object_class = (GtkObjectClass *)klass; GuppiDataClass *data_class = GUPPI_DATA_CLASS (klass); parent_class = gtk_type_class (GUPPI_TYPE_DATA); object_class->finalize = guppi_data_table_finalize; data_class->get_size_info = get_size_info; data_class->validate_class = validate_class; data_class->changed = changed; klass->changed_dimensions = changed_dimensions; klass->changed_table_entries = changed_table_entries; klass->changed_table_labels = changed_table_labels; guppi_data_table_signals[CHANGED_DIMENSIONS] = gtk_signal_new ("changed_dimensions", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GuppiDataTableClass, changed_dimensions), gtk_marshal_NONE__INT_INT, GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); guppi_data_table_signals[CHANGED_TABLE_ENTRIES] = gtk_signal_new ("changed_table_entries", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GuppiDataTableClass, changed_table_entries), guppi_marshal_NONE__INT_INT_INT_INT, GTK_TYPE_NONE, 4, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT); guppi_data_table_signals[CHANGED_TABLE_LABELS] = gtk_signal_new ("changed_table_labels", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GuppiDataTableClass, changed_table_labels), guppi_marshal_NONE__INT_INT_INT, GTK_TYPE_NONE, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT); gtk_object_class_add_signals (object_class, guppi_data_table_signals, LAST_SIGNAL); } static void guppi_data_table_init (GuppiDataTable *obj) { obj->priv = g_new0 (GuppiDataTablePrivate, 1); } GtkType guppi_data_table_get_type (void) { static GtkType guppi_data_table_type = 0; if (!guppi_data_table_type) { static const GtkTypeInfo guppi_data_table_info = { "GuppiDataTable", sizeof (GuppiDataTable), sizeof (GuppiDataTableClass), (GtkClassInitFunc)guppi_data_table_class_init, (GtkObjectInitFunc)guppi_data_table_init, NULL, NULL, (GtkClassInitFunc)NULL }; guppi_data_table_type = gtk_type_unique (GUPPI_TYPE_DATA, &guppi_data_table_info); } return guppi_data_table_type; }