/* This is -*- C -*- */
/* vim: set sw=2: */
/* $Id: guppi-data-table-bundle.c,v 1.4 2001/10/07 19:56:04 trow Exp $ */

/*
 * guppi-data-table-bundle.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-memory.h>
#include "guppi-data-table-bundle.h"

static GtkObjectClass *parent_class = NULL;

static void
free_everything (GuppiDataTableBundle *bundle)
{
  gint i;

  guppi_data_table_bundle_set_row_labels (bundle, NULL);
  guppi_data_table_bundle_set_col_labels (bundle, NULL);
  for (i = 0; i < bundle->C; ++i)
    guppi_data_table_bundle_set_col_data (bundle, i, NULL);

  guppi_free0 (bundle->col);
}

static gboolean
get_bounds (GuppiDataTable *dt, gint *r, gint *c)
{
  GuppiDataTableBundle *bundle = (GuppiDataTableBundle *) dt;
  if (r)
    *r = bundle->R;
  if (c)
    *c = bundle->C;
  
  return TRUE;
}

static void
set_bounds (GuppiDataTable *dt, gint r, gint c)
{
  GuppiDataTableBundle *bundle = (GuppiDataTableBundle *) dt;

  free_everything (bundle);

  bundle->R = r;
  bundle->C = c;
}

static double
get_entry (GuppiDataTable *dt, gint r, gint c)
{
  GuppiDataTableBundle *bundle = (GuppiDataTableBundle *) dt;
  GuppiSeqScalar *seq;

  if (bundle->col == NULL)
    return 0;

  if (bundle->col[c] == NULL)
    return 0;

  seq  = bundle->col[c]->data;

  return guppi_seq_in_bounds (GUPPI_SEQ (seq), r) ? guppi_seq_scalar_get (seq, r) : 0;
}

static void
set_entry (GuppiDataTable *dt, gint r, gint c, double x)
{
  GuppiDataTableBundle *bundle = (GuppiDataTableBundle *) dt;
  GuppiSeqScalar *seq;

  if (bundle->col == NULL)
    return;

  if (bundle->col[c]->data == NULL)
    return;

  seq  = bundle->col[c]->data;
  if (! guppi_seq_in_bounds (GUPPI_SEQ (seq), r))
    return;

  guppi_seq_scalar_set (seq, r, x);
}

static const gchar *
get_label (GuppiDataTable *dt, GuppiDataTableSpan span, gint i)
{
  GuppiDataTableBundle *bundle = (GuppiDataTableBundle *) dt;
  GuppiSeqString *str_seq;
  
  if (span == GUPPI_TABLE_ROW)
    str_seq = bundle->row_labels;
  else if (span == GUPPI_TABLE_COL)
    str_seq = bundle->col_labels;
  else
    return NULL;

  if (str_seq == NULL)
    return "";

  return guppi_seq_string_get (str_seq, i);
}

static GuppiData *
copy (GuppiData *data)
{
  return NULL;
}

static gint
get_size_in_bytes (GuppiData *data)
{
  GuppiDataTableBundle *bundle = (GuppiDataTableBundle *) data;
  gint sz = 0;
  gint i;

  if (bundle->col) {
    for (i = 0; i < bundle->C; ++i) {
      if (bundle->col[i] && bundle->col[i]->data)
	sz += guppi_data_get_size_in_bytes (GUPPI_DATA (bundle->col[i]->data));
    }
    
    if (bundle->row_labels)
      sz += guppi_data_get_size_in_bytes (GUPPI_DATA (bundle->row_labels));

    if (bundle->col_labels)
      sz += guppi_data_get_size_in_bytes (GUPPI_DATA (bundle->col_labels));
  }
	  
  if (GUPPI_DATA_CLASS (parent_class)->get_size_in_bytes)
    sz += GUPPI_DATA_CLASS (parent_class)->get_size_in_bytes (data);

  return sz;
}

static void
set_label (GuppiDataTable *dt, GuppiDataTableSpan span, gint i, const gchar *label)
{
  GuppiDataTableBundle *bundle = (GuppiDataTableBundle *) dt;
  GuppiSeqString *str_seq;
  
  if (span == GUPPI_TABLE_ROW)
    str_seq = bundle->row_labels;
  else if (span == GUPPI_TABLE_COL)
    str_seq = bundle->col_labels;
  else
    return;

  guppi_seq_string_set (str_seq, i, label);
}


static void
guppi_data_table_bundle_finalize (GtkObject *obj)
{
  GuppiDataTableBundle *x = GUPPI_DATA_TABLE_BUNDLE(obj);

  x->block_signals_hack = TRUE;
  free_everything (x);

  guppi_finalized (obj);

  if (parent_class->finalize)
    parent_class->finalize (obj);
}

static void
guppi_data_table_bundle_class_init (GuppiDataTableBundleClass *klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *)klass;
  GuppiDataClass *data_class = GUPPI_DATA_CLASS (klass);
  GuppiDataTableClass *data_table_class = GUPPI_DATA_TABLE_CLASS (klass);

  parent_class = gtk_type_class (GUPPI_TYPE_DATA_TABLE);

  object_class->finalize = guppi_data_table_bundle_finalize;

  data_class->copy = copy;
  data_class->get_size_in_bytes = get_size_in_bytes;
  data_class->is_leaf_type = TRUE;

  data_table_class->get_bounds = get_bounds;
  data_table_class->set_bounds = set_bounds;
  data_table_class->get_entry  = get_entry;
  data_table_class->set_entry  = set_entry;
  data_table_class->get_label  = get_label;
  data_table_class->set_label  = set_label;
}

static void
guppi_data_table_bundle_init (GuppiDataTableBundle *obj)
{

}

GtkType
guppi_data_table_bundle_get_type (void)
{
  static GtkType guppi_data_table_bundle_type = 0;
  if (!guppi_data_table_bundle_type) {
    static const GtkTypeInfo guppi_data_table_bundle_info = {
      "GuppiDataTableBundle",
      sizeof (GuppiDataTableBundle),
      sizeof (GuppiDataTableBundleClass),
      (GtkClassInitFunc)guppi_data_table_bundle_class_init,
      (GtkObjectInitFunc)guppi_data_table_bundle_init,
      NULL, NULL, (GtkClassInitFunc)NULL
    };
    guppi_data_table_bundle_type = gtk_type_unique (GUPPI_TYPE_DATA_TABLE, &guppi_data_table_bundle_info);
  }
  return guppi_data_table_bundle_type;
}

GuppiDataTable *
guppi_data_table_bundle_new (void)
{
  guppi_data_table_bundle_get_type ();
  return (GuppiDataTable *) guppi_data_new ("GuppiDataTableBundle");
}

/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

static void
guppi_data_table_bundle_column_changed (GuppiDataTableBundle *bundle, gint c)
{
  GuppiDataOp noop;
 
  if (bundle->block_signals_hack)
    return;

  noop.op = NULL;
  guppi_data_table_changed_table_entries ((GuppiDataTable *) bundle, c, 0, c, bundle->R - 1, &noop);
}

static void
col_changed_cb (GuppiData *data, gpointer closure)
{
  GuppiDataTableBundleColumn *column = closure;
  guppi_data_table_bundle_column_changed (column->bundle, column->col_num);
}

static GuppiDataTableBundleColumn *
column_new (GuppiDataTableBundle *tb, gint col_num, GuppiSeqScalar *ss)
{
  GuppiDataTableBundleColumn *column;

  if (col_num < 0 || col_num >= tb->C)
    return NULL;

  if (ss == NULL)
    return NULL;

  column = g_new (GuppiDataTableBundleColumn, 1);
  column->bundle = tb; /* no ref -- don't want a circular data structure */
  column->col_num = col_num;
  column->data = ss;
  guppi_ref (column->data);

  column->changed_tag = gtk_signal_connect (GTK_OBJECT (column->data),
					    "changed",
					    GTK_SIGNAL_FUNC (col_changed_cb),
					    column);
  return column;
}

static void
column_free (GuppiDataTableBundleColumn *column)
{
  if (column == NULL)
    return;

  if (column->changed_tag != 0) {
    gtk_signal_disconnect (GTK_OBJECT (column->data), column->changed_tag);
  }
  guppi_unref (column->data);
  guppi_free (column);
}

void
guppi_data_table_bundle_set_col_data (GuppiDataTableBundle *tb, gint c, GuppiSeqScalar *seq)
{
  GuppiDataTableBundleColumn *old_col;

  g_return_if_fail (GUPPI_IS_DATA_TABLE_BUNDLE (tb));
  g_return_if_fail (0 <= c && c < tb->C);
  g_return_if_fail (GUPPI_IS_SEQ_SCALAR0 (seq));

  if (tb->col == NULL) {
    if (seq == NULL)
      return;
    tb->col = guppi_new0 (GuppiDataTableBundleColumn *, tb->C);
  }

  old_col = tb->col[c];
  if (old_col && old_col->data == seq)
    return;
  if (old_col == NULL && seq == NULL)
    return;

  tb->col[c] = column_new (tb, c, seq);
  column_free (old_col);

  guppi_data_table_bundle_column_changed (tb, c);
}

/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

static void
guppi_data_table_bundle_labels_changed (GuppiDataTableBundle *bundle, GuppiDataTableSpan span)
{
  GuppiDataOp noop;
  gint N;

  /* Emit a bunch of crazy stuff */
  if (bundle->block_signals_hack)
    return;

  if (span == GUPPI_TABLE_ROW)
    N = bundle->R;
  else
    N = bundle->C;

  noop.op = NULL;
  guppi_data_table_changed_table_labels ((GuppiDataTable *) bundle, span, 0, N-1, &noop);
}

static void
labels_changed_cb (GuppiData *data, gpointer closure)
{
  GuppiDataTableSpan span;
  GuppiDataTableBundle *bundle;

  if (GPOINTER_TO_INT (closure) & 1)
    span = GUPPI_TABLE_ROW;
  else
    span = GUPPI_TABLE_COL;

  bundle = GUPPI_DATA_TABLE_BUNDLE (GINT_TO_POINTER (GPOINTER_TO_INT (closure) & ~1));
  
  guppi_data_table_bundle_labels_changed (bundle, span);
}

static void
guppi_data_table_bundle_set_labels (GuppiDataTableBundle *tb, GuppiDataTableSpan span, GuppiSeqString *seq)
{
  GuppiSeqString *old;
  gpointer closure_hack;
  guint *changed_tag;

  g_return_if_fail (GUPPI_IS_DATA_TABLE_BUNDLE (tb));
  g_return_if_fail (GUPPI_IS_SEQ_STRING0 (seq));

  closure_hack = tb;

  if (span == GUPPI_TABLE_ROW) {
    old = tb->row_labels;
    changed_tag = &tb->row_labels_changed_tag;
    closure_hack = GINT_TO_POINTER (GPOINTER_TO_INT (closure_hack) | 1);
  } else if (span == GUPPI_TABLE_COL) {
    old = tb->col_labels;
    changed_tag = &tb->col_labels_changed_tag;
  } else
    return;

  if (old == seq)
    return;

  if (*changed_tag != 0) {
    gtk_signal_disconnect (GTK_OBJECT (old), *changed_tag);
    *changed_tag = 0;
  }

  if (seq) {
    *changed_tag = gtk_signal_connect (GTK_OBJECT (seq), 
				       "changed",
				       GTK_SIGNAL_FUNC (labels_changed_cb),
				       closure_hack);
  }
    
  if (span == GUPPI_TABLE_ROW)
    tb->row_labels = seq;
  else if (span == GUPPI_TABLE_COL)
    tb->col_labels = seq;
  
  guppi_ref (seq);
  guppi_unref (old);

  guppi_data_table_bundle_labels_changed (tb, span);
}

void
guppi_data_table_bundle_set_row_labels (GuppiDataTableBundle *tb, GuppiSeqString *seq)
{
  guppi_data_table_bundle_set_labels (tb, GUPPI_TABLE_ROW, seq);
}

void
guppi_data_table_bundle_set_col_labels (GuppiDataTableBundle *tb, GuppiSeqString *seq)
{
  guppi_data_table_bundle_set_labels (tb, GUPPI_TABLE_COL, seq);
}


syntax highlighted by Code2HTML, v. 0.9.1