/* This is -*- C -*- */
/* $Id: guppi-data.c,v 1.44 2001/11/19 05:40:40 trow Exp $ */

/*
 * guppi-data.c
 *
 * Copyright (C) 2000 EMC Capital Management, Inc.
 * Copyright (C) 2001 The Free Software Foundation
 *
 * Developed by Jon Trowbridge <trow@gnu.org> and
 * Havoc Pennington <hp@pobox.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; 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.h"

#include <string.h>

#include <zlib.h>
/* #include <gnome.h> */
#include <gtk/gtksignal.h>
#include <gtk/gtkentry.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkvbox.h>
#include <gtk/gtkhseparator.h>
#include <gtk/gtkvseparator.h>
#include <gtk/gtkfilesel.h>
#include <gtk/gtkmenuitem.h>

#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>
#include <libgnomeui/gnome-dialog.h>
#include <libgnomeui/gnome-stock.h>
#include <gnome-xml/xmlmemory.h>

#include <guppi-useful.h>
#include <guppi-dialogs.h>
#include "guppi-data-plug-in.h"

#if 0
#include "guppi-data-tree.h"
#include "guppi-data-transform.h"
#endif


static GtkObjectClass *parent_class = NULL;

enum {
  ARG_0
};

enum {
  CHANGED,
  CHANGED_LABEL,
  CHANGED_SUBDATA,
  LAST_SIGNAL
};
static guint data_signals[LAST_SIGNAL] = { 0 };


static void
process_pending_op (GuppiData *data)
{
  GList *op_list;
  GuppiDataOp *op;

  g_return_if_fail (data != NULL && GUPPI_IS_DATA (data));

  op_list = (GList *) data->pending_ops;
  g_return_if_fail (op_list != NULL);

  op = (GuppiDataOp *) op_list->data;
  g_return_if_fail (op != NULL);

  if (op->op)
    op->op (data, op);

  data->pending_ops = g_list_remove_link (op_list, op_list);
  g_list_free_1 (op_list);
}

static void
changed (GuppiData *data)
{
  process_pending_op (data);
}

static void
changed_label (GuppiData *data)
{
  /* no op */
}

static void
changed_subdata (GuppiData *data)
{
  process_pending_op (data);
}

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

GuppiData *
guppi_data_new (const gchar *type)
{
  GuppiPlugIn *pi;
  GuppiData *data;
  GuppiDataClass *klass;
  GtkType t;

  g_return_val_if_fail (type && *type, NULL);

  t = gtk_type_from_name (type);

  if (t) {

    if (gtk_type_is_a (t, GUPPI_TYPE_DATA)) {
      data = (GuppiData *) guppi_type_new (t);
    } else {
      g_warning ("Type '%s' is-not-a GuppiData", type);
      return NULL;
    }

  } else {

    pi = guppi_plug_in_lookup ("data_impl", type);
    if (pi == NULL) {
      g_warning("Plug-in data_impl::%s not found", type);
      return NULL;
    }
  
    g_return_val_if_fail (GUPPI_IS_DATA_PLUG_IN (pi), NULL);
    
    data = guppi_data_plug_in_create_data (GUPPI_DATA_PLUG_IN (pi));
    if (data == NULL) {
      g_warning ("%s's constructor returned NULL", type);
      return NULL;
    }
  }
 
  g_return_val_if_fail (GUPPI_IS_DATA (data), NULL);

  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);
  
  if (!klass->class_validated && klass->validate_class != NULL) {
    klass->class_validated = klass->validate_class (klass);
    if (! klass->class_validated)
      g_warning ("Class '%s' is not fully specified.\n", gtk_type_name (GTK_OBJECT_CLASS (klass)->type));
    klass->class_validated = TRUE; /* only complain once */
  }

  if (!klass->is_leaf_type) {
    g_warning ("Constructed non-leaf type '%s'", type);
    guppi_unref (data);
    return NULL;
  }

  if (klass->plug_in_code == NULL) {
    klass->plug_in_code = guppi_strdup (type);
    guppi_permanent_alloc (klass->plug_in_code);
  }

  return data;
}

/**
 * guppi_data_unique_id
 * @data: A #GuppiData object.
 *
 * Returns a unique identifier associated with @data.
 */
guppi_uniq_t
guppi_data_unique_id (GuppiData *data)
{
  g_return_val_if_fail (data && GUPPI_IS_DATA (data), 0);

  return data->id;
}

/**
 * guppi_data_get_label
 * @data: A #GuppiData object.
 *
 * Returns the label associated with @data.
 */
const gchar *
guppi_data_get_label (GuppiData *data)
{
  g_return_val_if_fail (GUPPI_IS_DATA (data), NULL);

  return data->label;
}

/**
 * guppi_data_set_label
 * @data: A #GuppiData object.
 * @label: A descriptive label.
 *
 * Associates the label @label with @data.
 */
void
guppi_data_set_label (GuppiData *data, const gchar *label)
{
  g_return_if_fail (GUPPI_IS_DATA (data));
  g_return_if_fail (label != NULL);

  if (data->label && strcmp (label, data->label)) {
    guppi_free (data->label);
    data->label = guppi_strdup (label);
    gtk_signal_emit (GTK_OBJECT (data), data_signals[CHANGED_LABEL]);
  }
}

/**
 * guppi_data_is_read_only
 * @data: A #GuppiData object.
 *
 * Returns %TRUE is @data is a read-only object.  If an object is read-only,
 * all potentially state-altering operations are illegal.
 */
gboolean
guppi_data_is_read_only (GuppiData *data)
{
  GuppiDataClass *klass;

  g_return_val_if_fail (GUPPI_IS_DATA (data), FALSE);

  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);
  return data->read_only || klass->read_only;
}

/**
 * guppi_data_can_change 
 * @data: A #GuppiData object.
 *
 * Returns %TRUE is @data is not a read-only object.  This is the opposite
 * of guppi_data_can_change().
 */
gboolean 
guppi_data_can_change (GuppiData *data)
{
  return !guppi_data_is_read_only (data);
}

/**
 * guppi_data_add_pending_op
 * @data: A #GuppiData object.
 * @op: A chunk of black magic.
 *
 * You should only call this function if you really know what you are doing.
 */
void
guppi_data_add_pending_op (GuppiData *data, GuppiDataOp *op)
{
  GList *op_list;

  g_return_if_fail (data != NULL && GUPPI_IS_DATA (data));
  g_return_if_fail (op != NULL);

  op_list = (GList *) data->pending_ops;
  data->pending_ops = g_list_prepend (op_list, op);
}

/**
 * guppi_data_changed
 * @data: A #GuppiData object.
 *
 * To force @data to emit a "changed" signal, don't call
 * this function.  Call guppi_data_touch() instead.
 *
 * If you ignore this advice, bad things will probably happen.
 */
void
guppi_data_changed (GuppiData *data)
{
  g_return_if_fail (data != NULL && GUPPI_IS_DATA (data));
  g_assert (guppi_data_can_change (data));
  g_assert (data->pending_ops != NULL);

  gtk_signal_emit (GTK_OBJECT (data), data_signals[CHANGED]);
}

static void
op_nop (GuppiData *d, GuppiDataOp *op)
{

}

/**
 * guppi_data_touch
 * @data: A #GuppiData object.
 *
 * Force @data to emit a "changed" signal.
 */
void
guppi_data_touch (GuppiData *data)
{
  GuppiDataOp op;

  g_return_if_fail (data != NULL && GUPPI_IS_DATA (data));

  op.op = op_nop;

  guppi_data_add_pending_op (data, &op);
  gtk_signal_emit (GTK_OBJECT (data), data_signals[CHANGED]);
}

/**
 * guppi_data_touch_subdata
 * @data: A #GuppiData object.
 *
 * Force @data to emit a "changed_subdata" signal.
 */
void
guppi_data_touch_subdata (GuppiData *data)
{
  g_return_if_fail (data && GUPPI_IS_DATA (data));

  gtk_signal_emit (GTK_OBJECT (data), data_signals[CHANGED_SUBDATA]);
}

/**
 * guppi_data_copy
 * @data: A #GuppiData object.
 *
 * Returns a copy of @data.
 */
GuppiData *
guppi_data_copy (GuppiData *data)
{
  GuppiDataClass *klass;
  GuppiData *copy;

  g_return_val_if_fail (GUPPI_IS_DATA (data), NULL);

  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);
  g_return_val_if_fail (klass->copy != NULL, NULL);

  copy = klass->copy (data);

  copy->read_only = data->read_only;
  copy->label     = guppi_strdup_printf (_("Copy of %s"), guppi_data_get_label (data));

  return copy;
}

/**
 * guppi_data_get_size_in_bytes
 * @data: A #GuppiData object.
 *
 * Returns the total quantity of memory being used by @data, in bytes.
 * For some data interfaces and implementations, this is only an estimate,
 * not an exact figure.
 */
gint
guppi_data_get_size_in_bytes (GuppiData *data)
{
  GuppiDataClass *klass;

  g_return_val_if_fail (GUPPI_IS_DATA (data), -1);
  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);

  return klass->get_size_in_bytes ? klass->get_size_in_bytes (data) : -1;
}

/**
 * guppi_data_get_size_info
 * @data: A #GuppiData object.
 *
 * Returns a short, human-readable description of @data.
 * The exact nature of the description depends on the
 * @data's type.
 */
gchar *
guppi_data_get_size_info (GuppiData *data)
{
  GuppiDataClass *klass;

  g_return_val_if_fail (GUPPI_IS_DATA (data), NULL);
  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);

  return klass->get_size_info ? klass->get_size_info (data) : NULL;
}

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

/**
 * guppi_data_export_xml
 * @data: A #GuppiData object.
 * @doc: The #GuppiXMLDocument that will contain the exported XML.
 *
 * Build an XML tree describing @data.
 * Returns: the top node of the constructed XML.
 */
xmlNodePtr
guppi_data_export_xml (GuppiData *data, GuppiXMLDocument *doc)
{
  GuppiDataClass *klass;
  xmlNodePtr data_node = NULL;

  g_return_val_if_fail (data && GUPPI_IS_DATA (data), NULL);
  g_return_val_if_fail (doc, NULL);

  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);

  data_node = xmlNewNode (doc->ns, "Data");
  xmlNewProp (data_node, "Type", klass->plug_in_code);

  if (data->label)
    xmlNewTextChild (data_node, doc->ns, "Label", data->label);

  if (klass->export_xml)
    klass->export_xml (data, doc, data_node);

  return data_node;
}
/**
 * guppi_data_import_xml
 * @doc: An XML document.
 * @node: The top node of a description of some data from @doc.
 *
 * Scan the XML tree contained in @node and use it to construct a #GuppiData object.
 * If the XML is malformed or any other errors occur in processing, %NULL is returned.
 *
 * Returns: the #GuppiData object constructed from the XML in @node.
 */
GuppiData *
guppi_data_import_xml (GuppiXMLDocument *doc, xmlNodePtr node)
{
  gchar *type_name;
  GuppiDataClass *klass;
  GuppiData *data = NULL;
  gboolean loaded_label = FALSE;

  g_return_val_if_fail (doc != NULL, NULL);
  g_return_val_if_fail (node != NULL, NULL);

  if (strcmp (node->name, "Data")) 
    return NULL;

  /* Construct base object from type and impl information */

  type_name = xmlGetProp (node, "Type");
  if (type_name == NULL) {
    g_message ("Missing data Type property");
    return NULL;
  }

  data = guppi_data_new (type_name);

  if (data == NULL) {
    g_message ("Unknown type \"%s\"", type_name);
    xmlFree (type_name);
    return NULL;
  }
  
  xmlFree (type_name);

  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);

  node = node->xmlChildrenNode;

  while (node != NULL) {

    if (!loaded_label && !strcmp (node->name, "Label")) {

      /* We do this song & dance to avoid g_free vs. xmlFree problems. */
      gchar *tmp = xmlNodeGetContent (node->xmlChildrenNode);
      guppi_free (data->label);
      data->label = g_strdup (tmp);
      xmlFree (tmp);

      loaded_label = TRUE;

    } else if (klass->import_xml) {
      /* If we fail, fail silently. */
      klass->import_xml (data, doc, node);
    }
    
    node = node->next;
  }

  return data;
}

/**
 * guppi_data_spew_xml
 * @data: A #GuppiData object.
 *
 * Build an XML tree describing @data and write it out to the console.
 * (This can be useful for debugging purposes.)
 */
void
guppi_data_spew_xml (GuppiData *data)
{
  GuppiXMLDocument *doc;

  g_return_if_fail (data && GUPPI_IS_DATA (data));

  doc = guppi_xml_document_new ();
  guppi_xml_document_set_root (doc, guppi_data_export_xml (data, doc));
  guppi_xml_document_spew (doc);
  guppi_xml_document_free (doc);
}

/* Subdata handling */

/**
 * guppi_data_has_subdata
 * @data: A #GuppiData object.
 *
 * Returns: %TRUE is @data contains any embedded subdata objects.
 */
gboolean
guppi_data_has_subdata (GuppiData *data)
{
  g_return_val_if_fail (data && GUPPI_IS_DATA (data), FALSE);
  
  return guppi_data_subdata_count (data) != 0;
}

static void
count_subdata_fn (GuppiData *d, gpointer user_data)
{
  ++*(gint *)user_data;
}

/**
 * guppi_data_subdata_count
 * @data: A #GuppiData object.
 *
 * Returns: the number of embedded subdata objects in @data.
 */
gint
guppi_data_subdata_count (GuppiData *data)
{
  gint count = 0;

  g_return_val_if_fail (data && GUPPI_IS_DATA (data), 0);
  
  guppi_data_foreach_subdata (data, count_subdata_fn, &count);

  return count;
}

/**
 * guppi_data_foreach_subdata
 * @data: A #GuppiData object.
 * @fn: A function.
 * @user_data: 
 *
 * Iterate over @data's embedded subdata objects, applying the function @fn
 * to each.  For every call, @user_data is passed to @fn as its second argument.
 */

void
guppi_data_foreach_subdata (GuppiData *data, GuppiDataFn fn, gpointer closure)
{
  GuppiDataClass *klass;

  g_return_if_fail (GUPPI_IS_DATA (data));
  g_return_if_fail (fn != NULL);

  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);

  /* If foreach_subdata isn't defined in the implementation class,
     we assume that that type can never have subdata, and the
     guppi_data_foreach_subdata () call is just a NOP. */
  if (klass->foreach_subdata)
    klass->foreach_subdata (data, fn, closure);
}

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

/*** Data Menu Items ***/

/**
 * guppi_data_build_menu
 * @data: A #GuppiData object.
 * @menu: A menu.
 * @data_tree:
 *
 * Add menu items to @menu that correspond to data-type-specific operations
 * on @data.
 */
void
guppi_data_build_menu (GuppiData *data, GtkMenu *menu, gpointer data_tree)
{
  GuppiDataClass *klass;

  g_return_if_fail (data && GUPPI_IS_DATA (data));
  g_return_if_fail (menu && GTK_IS_MENU (menu));

  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);

  if (klass->build_menu)
    klass->build_menu (data, menu, data_tree);
}

/*
 * Implement a well-behaved "rename data" dialog.
 */

struct rename_info {
  GuppiData *data;
  gchar *original_label;
  GtkWidget *entry;
};

static void
rename_info_free (struct rename_info *ri)
{
  gtk_signal_disconnect_by_data (GTK_OBJECT (ri->data), ri->entry);
  guppi_unref (ri->data);
  guppi_free (ri->original_label);
  guppi_free (ri);
}

static void
rename_clicked_cb (GtkWidget *w, gint num, gpointer user_data)
{
  struct rename_info *ri = (struct rename_info *)user_data;

  if (num == 0) { /* OK */
    /* We don't need to do anything in this case. */
  } else if (num == 1) { /* Cancel */
    guppi_data_set_label (ri->data, ri->original_label);
  } else {
    g_assert_not_reached ();
  }

  rename_info_free (ri);

  gtk_widget_destroy (w);
}

static void
rename_close_cb (GtkWidget *w, gpointer user_data)
{
  struct rename_info *ri = (struct rename_info *)user_data;

  /* Revert to our original label */
  guppi_data_set_label (ri->data, ri->original_label);

  rename_info_free (ri);
}

static void
rename_edit_cb (GtkWidget *w, gpointer user_data)
{
  struct rename_info *ri = (struct rename_info *)user_data;
  gchar *s;

  s = gtk_editable_get_chars (GTK_EDITABLE (w), 0, -1);
  guppi_outside_alloc (s);
  guppi_data_set_label (ri->data, s);
  guppi_free (s);
}

static void
rename_changed_cb (GuppiData *data, const gchar* label, gpointer user_data)
{
  GtkEntry *entry = GTK_ENTRY (user_data);
  gchar *s;

  s = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);

  if (strcmp (s, label))
    gtk_entry_set_text (entry, label);
}

static void
rename_cb (GtkWidget *w, GuppiData *data)
{
  GtkWidget *dialog;
  GtkWidget *entry;
  struct rename_info *ri;

  ri = guppi_new0 (struct rename_info, 1);
  ri->data = data;
  guppi_ref (ri->data);
  ri->original_label = guppi_strdup (guppi_data_get_label (data));

  dialog = gnome_dialog_new (_("Edit Data Label"),
			     GNOME_STOCK_BUTTON_OK,
			     GNOME_STOCK_BUTTON_CANCEL,
			     NULL);

  ri->entry = entry = gtk_entry_new ();
  gtk_entry_set_text (GTK_ENTRY (entry), ri->original_label);
  gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox),
		     entry, TRUE, TRUE, 2);

  gtk_signal_connect (GTK_OBJECT (entry),
		      "changed",
		      GTK_SIGNAL_FUNC (rename_edit_cb),
		      ri);

  gtk_signal_connect (GTK_OBJECT (dialog),
		      "clicked",
		      GTK_SIGNAL_FUNC (rename_clicked_cb),
		      ri);

  gtk_signal_connect (GTK_OBJECT (dialog),
		      "close",
		      GTK_SIGNAL_FUNC (rename_close_cb),
		      ri);
  gtk_signal_connect (GTK_OBJECT (data),
		      "changed_label",
		      GTK_SIGNAL_FUNC (rename_changed_cb),
		      entry);

  gtk_widget_show_all (dialog);
}

static void
data_info_cb (GtkWidget *w, GuppiData *data)
{
  GtkWidget *win;
  GtkWidget *box;
  GtkWidget *info;
  GtkWidget *close_button;

  win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (win), "Data Information");

  info = guppi_data_info_display (data);
  if (info == NULL)
    info = gtk_label_new (_("No information available."));

  box = gtk_vbox_new (FALSE, 2);

  gtk_box_pack_start (GTK_BOX (box), info, TRUE, TRUE, 2);

  gtk_box_pack_start (GTK_BOX (box), gtk_hseparator_new (),
		      FALSE, TRUE, 3);

  close_button = gnome_stock_button (GNOME_STOCK_BUTTON_CLOSE);
  gtk_box_pack_start (GTK_BOX (box), close_button, FALSE, FALSE, 2);

  gtk_container_add (GTK_CONTAINER (win), box);

  gtk_signal_connect_object (GTK_OBJECT (close_button),
			     "clicked",
			     GTK_SIGNAL_FUNC (gtk_widget_destroy),
			     GTK_OBJECT (win));
  
  gtk_widget_show_all (win);

}

struct save_info {
  GtkFileSelection *fs;
  GuppiData *data;
};

static void
save_info_free (struct save_info *si)
{
  gtk_widget_destroy (GTK_WIDGET (si->fs));
  guppi_unref (si->data);
  guppi_free (si);
}

static void
save_ok_cb (GtkWidget *w, gpointer user_data)
{
  struct save_info *si = (struct save_info *)user_data;
  GuppiXMLDocument *doc;
  gchar *filename;

  filename = gtk_file_selection_get_filename (si->fs);

  doc = guppi_xml_document_new ();
  guppi_xml_document_set_root (doc, guppi_data_export_xml (si->data, doc));
  guppi_xml_document_write_file (doc, filename);
  guppi_xml_document_free (doc);

  save_info_free (si);
}

static void
save_cancel_cb (GtkWidget *w, gpointer user_data)
{
  struct save_info *si = (struct save_info *)user_data;

  save_info_free (si);
}

static gboolean
save_delete_event_cb (GtkWidget *w, GdkEvent *ev, gpointer user_data)
{
  struct save_info *si = (struct save_info *)user_data;

  save_info_free (si);

  return TRUE;
}

static void
data_save_cb (GtkWidget *w, GuppiData *data)
{
  GtkWidget *fs;
  gchar *s;
  gchar *tmp;
  gchar *filename;
  struct save_info *si;

  s = guppi_strdup_printf (_("Save \"%s\""), guppi_data_get_label (data));
  fs = gtk_file_selection_new (s);
  guppi_free (s);

  si = guppi_new0 (struct save_info, 1);
  si->fs = GTK_FILE_SELECTION (fs);
  si->data = data;
  guppi_ref (data);

  tmp = guppi_string_canonize_filename (guppi_data_get_label (data));
  filename = guppi_strdup_printf ("%s.gpi", tmp);
  guppi_free (tmp);
  gtk_file_selection_set_filename (si->fs, filename);
  guppi_free (filename);

  gtk_signal_connect (GTK_OBJECT (si->fs->ok_button),
		      "clicked",
		      GTK_SIGNAL_FUNC (save_ok_cb),
		      si);

  gtk_signal_connect (GTK_OBJECT (si->fs->cancel_button),
		      "clicked",
		      GTK_SIGNAL_FUNC (save_cancel_cb),
		      si);

  gtk_signal_connect (GTK_OBJECT (fs),
		      "delete_event",
		      GTK_SIGNAL_FUNC (save_delete_event_cb),
		      si);

  gtk_widget_show_all (fs);
}

#if 0
static void
data_delete_cb (GtkWidget *w, GuppiData *data)
{
  GuppiDataTree *tree;

  tree = GUPPI_DATA_TREE (gtk_object_get_data (GTK_OBJECT (w), "tree"));

  guppi_data_tree_remove (tree, data);
}

static void
data_copy_cb (GtkWidget *w, GuppiData *data)
{
  GuppiData *copy;
  GuppiDataTree *tree;

  copy = guppi_data_copy (data);
  if (copy == NULL) {
    guppi_warning_dialog ("Can't copy \"%s\"", guppi_data_get_label (data));
  } else {
    tree = GUPPI_DATA_TREE (gtk_object_get_data (GTK_OBJECT (w), "tree"));
    guppi_data_tree_add (tree, copy);
  }

}

static void
data_transform_cb (GtkWidget *w, GuppiData *data)
{
  GuppiDataTransform *xform;
  GuppiDataTree *tree;
  GuppiData *result;

  xform =
    GUPPI_DATA_TRANSFORM (gtk_object_get_data (GTK_OBJECT (w), "xform"));
  tree = GUPPI_DATA_TREE (gtk_object_get_data (GTK_OBJECT (w), "tree"));

  result = guppi_data_transform_process (xform, data);
  if (result)
    guppi_data_tree_add_beside (tree, data, result);
}
#endif

static void
build_menu (GuppiData *data, GtkMenu *menu, gpointer data_tree)
{
  GtkWidget *mi;
  GuppiDataClass *klass;
  GList *xforms;
  GList *iter;

  g_return_if_fail (data && GUPPI_IS_DATA (data));
  g_return_if_fail (menu && GTK_IS_MENU (menu));

  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);


  /* Need to hook up signals... */

  if (klass->info_display) {
    mi = gtk_menu_item_new_with_label (_("Information"));
    gtk_menu_append (menu, mi);
    gtk_signal_connect (GTK_OBJECT (mi),
			"activate",
			GTK_SIGNAL_FUNC (data_info_cb),
			data);
    gtk_widget_show (mi);
  }

  mi = gtk_menu_item_new_with_label (_("Rename"));
  gtk_menu_append (menu, mi);
  gtk_signal_connect (GTK_OBJECT (mi),
		      "activate",
		      GTK_SIGNAL_FUNC (rename_cb),
		      data);
  gtk_widget_show (mi);

#if 0
  mi = gtk_menu_item_new_with_label (_("Save"));
  gtk_menu_append (menu, mi);
  gtk_signal_connect (GTK_OBJECT (mi),
		      "activate",
		      GTK_SIGNAL_FUNC (data_save_cb),
		      data);
  gtk_widget_show (mi);


  mi = gtk_menu_item_new_with_label (_("Delete"));
  gtk_object_set_data (GTK_OBJECT (mi), "tree", data_tree);
  gtk_menu_append (menu, mi);
  gtk_signal_connect (GTK_OBJECT (mi),
		      "activate",
		      GTK_SIGNAL_FUNC (data_delete_cb),
		      data);
  gtk_widget_show (mi);


  mi = gtk_menu_item_new_with_label (_("Copy"));
  gtk_object_set_data (GTK_OBJECT (mi), "tree", data_tree);
  gtk_menu_append (menu, mi);
  gtk_signal_connect (GTK_OBJECT (mi),
		      "activate",
		      GTK_SIGNAL_FUNC (data_copy_cb),
		      data);
  gtk_widget_show (mi);
#endif

#if 0
  xforms = iter = guppi_data_transforms_find_supported (data);
  if (xforms) {

    GtkMenu *xmenu = GTK_MENU (gtk_menu_new ());
    
    while (iter) {
      GuppiDataTransform *xform = GUPPI_DATA_TRANSFORM (iter->data);
      mi = gtk_menu_item_new_with_label (xform->name);

      gtk_object_set_data (GTK_OBJECT (mi), "xform", xform);
      gtk_object_set_data (GTK_OBJECT (mi), "tree", data_tree);

      gtk_signal_connect (GTK_OBJECT (mi),
			  "activate",
			  GTK_SIGNAL_FUNC (data_transform_cb),
			  data);

      gtk_menu_append (xmenu, mi);
      gtk_widget_show (mi);
      iter = g_list_next (iter);
    }
    g_list_free (xforms);

    mi = gtk_menu_item_new_with_label (_("Copy & Transform"));
    gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), GTK_WIDGET (xmenu));
    gtk_widget_show (GTK_WIDGET (xmenu));

    gtk_menu_append (menu, mi);
    gtk_widget_show (mi);
  }
#endif
}

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

/**
 * guppi_data_info_display
 * @data: A #GuppiData object.
 *
 * Returns: a widget containing useful human-readable information about
 * #data.
 */
GtkWidget *
guppi_data_info_display (GuppiData *data)
{
  GuppiDataClass *klass;
  GtkWidget *w = NULL;
  GtkWidget *frame;
  GtkWidget *box = NULL;

  g_return_val_if_fail (data && GUPPI_IS_DATA (data), NULL);

  klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass);

  if (klass->info_display)
    w = klass->info_display (data);

  if (w == NULL)
    return NULL;

  frame = gtk_frame_new (guppi_data_get_label (data));
  box = gtk_vbox_new (FALSE, 2);
  gtk_container_add (GTK_CONTAINER (frame), box);

  if (w)
    gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0);

  return frame;
}

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

static void
guppi_data_finalize (GtkObject *obj)
{
  GuppiData *gd = GUPPI_DATA (obj);

  guppi_finalized (obj);

  /* We should never die with pending operations! */
  g_assert (gd->pending_ops == NULL);

  guppi_free0 (gd->label);

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

static gboolean
validate_class (GuppiDataClass *klass)
{
  gboolean ok = TRUE;

  if (klass->copy == NULL) {
    g_warning ("Method GuppiData::copy not defined.");
    ok = FALSE;
  }

  if (klass->get_size_in_bytes == NULL) {
    g_warning ("Method GuppiData::get_size_in_bytes not defined.");
    ok = FALSE;
  }

  if (klass->get_size_info == NULL) {
    g_warning ("Method GuppiData::get_size_info not defined.");
    ok = FALSE;
  }
  
  return ok;
}

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

static void
guppi_data_class_init (GuppiDataClass *klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *) klass;

  parent_class = gtk_type_class (GTK_TYPE_OBJECT);

  klass->changed = changed;
  klass->changed_label = changed_label;
  klass->changed_subdata = changed_subdata;

  klass->build_menu = build_menu;
  klass->validate_class = validate_class;

  data_signals[CHANGED] =
    gtk_signal_new ("changed",
		    GTK_RUN_LAST,
		    object_class->type,
		    GTK_SIGNAL_OFFSET (GuppiDataClass, changed),
		    gtk_marshal_NONE__NONE,
		    GTK_TYPE_NONE, 0);

  data_signals[CHANGED_LABEL] =
    gtk_signal_new ("changed_label",
		    GTK_RUN_LAST,
		    object_class->type,
		    GTK_SIGNAL_OFFSET (GuppiDataClass, changed_label),
		    gtk_marshal_NONE__NONE,
		    GTK_TYPE_NONE, 0);

  data_signals[CHANGED_SUBDATA] =
    gtk_signal_new ("changed_subdata",
		    GTK_RUN_LAST,
		    object_class->type,
		    GTK_SIGNAL_OFFSET (GuppiDataClass, changed_subdata),
		    gtk_marshal_NONE__NONE,
		    GTK_TYPE_NONE, 0);

  gtk_object_class_add_signals (object_class, data_signals, LAST_SIGNAL);

  object_class->finalize = guppi_data_finalize;
}

static void
guppi_data_init (GuppiData *gd)
{
  gd->id = guppi_unique_id ();
  gd->label = guppi_strdup (_("Unlabled"));
  gd->read_only = FALSE;
}

GtkType 
guppi_data_get_type (void)
{
  static GtkType guppi_data_type = 0;
  if (!guppi_data_type) {
    static const GtkTypeInfo guppi_data_info = {
      "GuppiData",
      sizeof (GuppiData),
      sizeof (GuppiDataClass),
      (GtkClassInitFunc) guppi_data_class_init,
      (GtkObjectInitFunc) guppi_data_init,
      NULL, NULL, (GtkClassInitFunc) NULL
    };
    guppi_data_type = gtk_type_unique (GTK_TYPE_OBJECT, &guppi_data_info);
  }
  return guppi_data_type;
}


/* $Id: guppi-data.c,v 1.44 2001/11/19 05:40:40 trow Exp $ */



syntax highlighted by Code2HTML, v. 0.9.1