/* This is -*- C -*- */
/* $Id: guppi-data-tree-view.c,v 1.22 2001/09/27 05:07:29 trow Exp $ */

/*
 * guppi-data-tree-view.c
 *
 * Copyright (C) 2000 EMC Capital Management, Inc.
 *
 * 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 <gtk/gtkclist.h>
#include <gtk/gtkctree.h>
#include <gtk/gtksignal.h>
#include <gtk/gtkmenuitem.h>
#include <gtk/gtkhseparator.h>
#include <gtk/gtkselection.h>
#include <gtk/gtkdnd.h>
#include <gtk/gtkwindow.h>

#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>

#include <gdk-pixbuf/gdk-pixbuf.h>
#include <guppi-paths.h>
#include <guppi-convenient.h>
#if 0
#include <guppi-data-transform.h>
#endif
#include "guppi-data-tree-view.h"

static GtkObjectClass *parent_class = NULL;

static void
guppi_data_tree_view_destroy (GtkObject * obj)
{
  GuppiDataTreeView *tv = GUPPI_DATA_TREE_VIEW (obj);

  gtk_signal_disconnect_by_data (GTK_OBJECT (tv->tree), tv);
  guppi_unref0 (tv->tree);

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

static void
guppi_data_tree_view_finalize (GtkObject * obj)
{
  guppi_finalized (obj);

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

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

static GtkMenu *
build_data_popup (GuppiData * data, GuppiDataTreeView * tv,
		  GuppiDataTreeNode * node)
{
  GtkMenu *menu = GTK_MENU (gtk_menu_new ());
  GtkWidget *mi;

  mi = gtk_menu_item_new_with_label (guppi_data_get_label (data));
  gtk_menu_append (menu, mi);
  gtk_widget_show (mi);

  mi = gtk_menu_item_new ();
  gtk_container_add (GTK_CONTAINER (mi), gtk_hseparator_new ());
  gtk_menu_append (menu, mi);
  gtk_widget_show_all (mi);

  guppi_data_build_menu (data, menu, tv->tree);

  return menu;
}

static GtkTargetEntry drag_target_table = { "guppi/data", 0, 0 };

/* Unselect the data object we are manipulating when the popup menu
   disappears. */
static void
popup_destroy_cb (GtkWidget *menu, gpointer user_data)
{
  GtkCTree *ctree;
  GtkCTreeNode *ctree_node;
  
  ctree = GTK_CTREE (user_data);
  ctree_node = (GtkCTreeNode *)gtk_object_get_data (GTK_OBJECT (menu),
						    "ctree_node");

  /* If our popup action deleted the element from the tree, it might
     not still be there to unselect... */
  if (!gtk_ctree_find (ctree, NULL, ctree_node))
    return;

  gtk_ctree_unselect (ctree, ctree_node);
  gtk_ctree_node_set_selectable (ctree, ctree_node, FALSE);
}

static gint
button_press_event (GtkWidget * w, GdkEventButton * ev)
{
  GuppiDataTreeView *tv;
  GtkCTreeNode *ctree_node;
  GuppiDataTreeNode *node;
  gint row, col;
  GList *lnode;

  g_return_val_if_fail (w != NULL && GUPPI_IS_DATA_TREE_VIEW (w), FALSE);
  g_return_val_if_fail (ev != NULL, FALSE);

  tv = GUPPI_DATA_TREE_VIEW (w);

  gtk_clist_get_selection_info (GTK_CLIST (w), ev->x, ev->y, &row, &col);

  /*
   * Let clicks on the tree expanders chain up to the CTree event handlers.
   */
  if (gtk_ctree_is_hot_spot (GTK_CTREE (w), ev->x, ev->y)) {
    if (GTK_WIDGET_CLASS (parent_class)->button_press_event)
      GTK_WIDGET_CLASS (parent_class)->button_press_event (w, ev);
    return TRUE;
  }

  lnode = g_list_nth (GTK_CLIST (w)->row_list, row);
  if (lnode == NULL || lnode->data == NULL)
    return FALSE;

  ctree_node = GTK_CTREE_NODE (lnode);
  node = (GuppiDataTreeNode *) gtk_ctree_node_get_row_data (GTK_CTREE (w),
							    ctree_node);
  g_return_val_if_fail (node != NULL, FALSE);

  if (ev->button == 1) {
    GtkTargetList *target_list;

    gtk_ctree_node_set_selectable (GTK_CTREE (w), ctree_node, TRUE);
    gtk_ctree_select (GTK_CTREE (w), ctree_node);

    target_list = gtk_target_list_new (&drag_target_table, 1);
    gtk_drag_begin (w, target_list, GDK_ACTION_COPY,
		    ev->button, (GdkEvent *) ev);


    tv->pending_dragged_data = node->data;
    tv->pending_dragged_node = ctree_node;

    return TRUE;

  } else if (ev->button == 3) {
    GtkMenu *popup_menu = build_data_popup (node->data, tv, node);
    gtk_widget_show (GTK_WIDGET (popup_menu));
    gtk_menu_popup (popup_menu, NULL, NULL, NULL, NULL, ev->button, ev->time);

    gtk_ctree_node_set_selectable (GTK_CTREE (w), ctree_node, TRUE);
    gtk_ctree_select (GTK_CTREE (w), ctree_node);

    gtk_object_set_data (GTK_OBJECT (popup_menu), "ctree_node", ctree_node);

    /* Connect a callback to selection-done that will un-select the
       particular ctree node when our popup menu goes away. */
    gtk_signal_connect (GTK_OBJECT (popup_menu),
			"selection-done",
			GTK_SIGNAL_FUNC (popup_destroy_cb),
			w);

    return TRUE;

  }

  return FALSE;
}

static void
drag_end (GtkWidget * w, GdkDragContext * c)
{
  GtkCTreeNode *ctree_node;

  g_return_if_fail (w != NULL && GUPPI_IS_DATA_TREE_VIEW (w));

  ctree_node = GUPPI_DATA_TREE_VIEW (w)->pending_dragged_node;

  gtk_ctree_unselect (GTK_CTREE (w), ctree_node);
  gtk_ctree_node_set_selectable (GTK_CTREE (w), ctree_node, FALSE);
}

static void
drag_data_get (GtkWidget * w, GdkDragContext * context,
	       GtkSelectionData * selection_data, guint info, guint time)
{
  GuppiDataTreeView *tv;

  g_return_if_fail (w != NULL && GUPPI_IS_DATA_TREE_VIEW (w));
  tv = GUPPI_DATA_TREE_VIEW (w);

  g_return_if_fail (tv->pending_dragged_data != NULL);

  gtk_selection_data_set (selection_data,
			  gdk_atom_intern ("guppi/data", FALSE),
			  (sizeof (tv->pending_dragged_data) == 4 ? 32 : 64),	/* ? */
			  (guchar *) & (tv->pending_dragged_data),
			  sizeof (tv->pending_dragged_data));
  tv->pending_dragged_data = NULL;
}


static void
guppi_data_tree_view_class_init (GuppiDataTreeViewClass * klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *) klass;
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  parent_class = gtk_type_class (GTK_TYPE_CTREE);

  object_class->destroy = guppi_data_tree_view_destroy;
  object_class->finalize = guppi_data_tree_view_finalize;

  widget_class->button_press_event = button_press_event;
  widget_class->drag_data_get = drag_data_get;
  widget_class->drag_end = drag_end;

}

static void
guppi_data_tree_view_init (GuppiDataTreeView * obj)
{

}

GtkType guppi_data_tree_view_get_type (void)
{
  static GtkType guppi_data_tree_view_type = 0;
  if (!guppi_data_tree_view_type) {
    static const GtkTypeInfo guppi_data_tree_view_info = {
      "GuppiDataTreeView",
      sizeof (GuppiDataTreeView),
      sizeof (GuppiDataTreeViewClass),
      (GtkClassInitFunc) guppi_data_tree_view_class_init,
      (GtkObjectInitFunc) guppi_data_tree_view_init,
      NULL, NULL, (GtkClassInitFunc) NULL
    };
    guppi_data_tree_view_type = gtk_type_unique (GTK_TYPE_CTREE,
						 &guppi_data_tree_view_info);
  }
  return guppi_data_tree_view_type;
}

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

static GtkCTreeNode *
guppi_data_tree_view_build_node (GuppiDataTreeView * tv,
				 GtkCTreeNode * parent,
				 GuppiDataTreeNode * node)
{
  static GdkPixmap *lock_pixmap = NULL;
  static GdkBitmap *lock_bitmap = NULL;

  gchar *text[5];
  GtkCTreeNode *sib = NULL;
  GtkCTreeNode *us;
  gint mem;
  float mem_val;
  gchar buf[32];
  gchar *sz_info;

  if (lock_pixmap == NULL) {
    gchar *path = guppi_find_pixmap ("lock.png");
    GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (path);
    gdk_pixbuf_render_pixmap_and_mask (pixbuf,
				       &lock_pixmap, &lock_bitmap, 20);
    guppi_free (path);
  }

  if (node->sibling_next)
    sib = guppi_data_tree_view_build_node (tv, parent, node->sibling_next);

  text[0] = (gchar *) guppi_data_get_label (node->data);
  text[1] = " ";
  /*  text[2] = (gchar *) guppi_data_get_type_name (node->data); */
  text[2] = "-TypeName-";

  sz_info = guppi_data_get_size_info (node->data);
  text[3] = sz_info;

  mem = guppi_data_get_size_in_bytes (node->data);
  if (mem > 0) {
    if (mem < 1024)
      g_snprintf (buf, 32, "%db", mem);
    else {
      mem_val = mem / 1024.0;
      if (mem_val < 1024)
	g_snprintf (buf, 32, "%0.2fk", mem_val);
      else
	g_snprintf (buf, 32, "%0.2fM", mem_val / 1024);
    }
  }

  text[4] = mem > 0 ? buf : "?";


  us = gtk_ctree_insert_node (GTK_CTREE (tv), parent, sib,
			      text, 6,
			      NULL,
			      NULL, NULL, NULL, node->child == NULL, TRUE);

  if (guppi_data_is_read_only (node->data)) {
    gtk_ctree_node_set_pixmap (GTK_CTREE (tv),
			       us, 1, lock_pixmap, lock_bitmap);
  }

  gtk_ctree_node_set_selectable (GTK_CTREE (tv), us, FALSE);
  gtk_ctree_node_set_row_data (GTK_CTREE (tv), us, node);

  if (node->child)
    guppi_data_tree_view_build_node (tv, us, node->child);

  g_free (sz_info);

  return us;
}

static void
guppi_data_tree_view_build (GuppiDataTreeView * tv)
{
  GuppiDataTreeNode *root;

  g_return_if_fail (tv != NULL);

  gtk_clist_freeze (GTK_CLIST (tv));
  gtk_clist_clear (GTK_CLIST (tv));

  root = tv->tree->root;
  if (root)
    guppi_data_tree_view_build_node (tv, NULL, root);

  gtk_clist_thaw (GTK_CLIST (tv));

}

void
guppi_data_tree_view_construct (GuppiDataTreeView * tv, GuppiDataTree * tree)
{
  static gchar *titles[5] = { NULL };
  gint column_count = 5;
  gint i;

  if (titles[0] == NULL) {
    titles[0] = _("Data");
    titles[1] = "";
    titles[2] = _("Type");
    titles[3] = _("Size");
    titles[4] = _("Mem");
  }


  g_return_if_fail (tv != NULL);
  g_return_if_fail (tree != NULL);
  g_return_if_fail (tv->tree == NULL);

  tv->tree = tree;
  guppi_ref (tv->tree);

  gtk_signal_connect_object (GTK_OBJECT (tv->tree),
			     "changed",
			     GTK_SIGNAL_FUNC (guppi_data_tree_view_build),
			     GTK_OBJECT (tv));


  gtk_ctree_construct (GTK_CTREE (tv), column_count, 0, titles);
  for (i = 0; i < column_count; ++i)
    gtk_clist_set_column_auto_resize (GTK_CLIST (tv), i, TRUE);
  gtk_ctree_set_line_style (GTK_CTREE (tv), GTK_CTREE_LINES_DOTTED);
  gtk_ctree_set_expander_style (GTK_CTREE (tv), GTK_CTREE_EXPANDER_TRIANGLE);

  guppi_data_tree_view_build (tv);
}

GtkWidget *
guppi_data_tree_view_new (GuppiDataTree * tree)
{
  GuppiDataTreeView *tv;

  g_return_val_if_fail (tree != NULL, NULL);
  tv = GUPPI_DATA_TREE_VIEW (guppi_type_new (guppi_data_tree_view_get_type ()));

  guppi_data_tree_view_construct (tv, tree);

  return GTK_WIDGET (tv);
}

/* For debugging */
void
guppi_data_tree_view_popup (GuppiDataTree * tree)
{
  GtkWidget *w = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  GtkWidget *tv = guppi_data_tree_view_new (tree);
  gtk_container_add (GTK_CONTAINER (w), tv);
  gtk_widget_show_all (w);
}



/* $Id: guppi-data-tree-view.c,v 1.22 2001/09/27 05:07:29 trow Exp $ */


syntax highlighted by Code2HTML, v. 0.9.1