/* 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 <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-data-table.h"

#include <math.h>
#include <guppi-memory.h>


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;
}



syntax highlighted by Code2HTML, v. 0.9.1