/* -*- Mode: C -*-
 *  $Id: gxsnmp_table_dialog.c,v 1.3 2000/02/20 16:44:41 jochen Exp $
 *  GXSNMP -- An snmp mangament application
 *  Copyright (C) 1998 Gregory McLean
 *
 *  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, Cambridge, MA 02139, USA.
 *
 *  SNMP table dialog
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "gxsnmp_table_dialog.h"
#include "debug.h"
#include "gxsnmp_util.h"

enum {
  UPDATE,
  LAST_SIGNAL
};

enum _coltype {
	COL_DIRECT,
	COL_ENUM,
	COL_INDIRECT
};

struct _colinfo {
  gchar         *title;
  enum _coltype  type;
  gulong        *oid;
  gint           oidlen;
  gchar         *format;
  gulong        *ioid;
  gint           ioidlen;
  gchar         *iformat;
  GHashTable    *enumber;
};

enum
{
  TARGET_COLUMN,
  TARGET_OID
};

static const GtkTargetEntry drop_targets[] = {
  { "application/x-gxsnmp-column", 0, TARGET_COLUMN},
  { "application/x-gxsnmp-oid", 0, TARGET_OID }
};

static const GtkTargetEntry drag_targets[] = {
  { "application/x-gxsnmp-column", 0, TARGET_COLUMN}
};

/******************************************************************************
 *
 *  Forward references
 *
 **/
static void	table_class_init        (GxSNMPTableDialogClass *klass);
static void	table_init              (GxSNMPTableDialog      *dialog);
static void	table_dialog_cb		(GnomeDialog		*dialog,
					 gint			 button,
					 gpointer		 data);
static void	table_drag_get		(GtkWidget		*widget,
					 GdkDragContext		*context,
					 GtkSelectionData	*sel_data,
					 guint			 info,
					 guint			 dtime,
					 gpointer		 data);
static void	table_data		(GtkWidget		*widget,
					 GdkDragContext		*context,
					 gint			 x,
					 gint			 y,
					 GtkSelectionData	*sel_data,
					 guint			 info,
					 guint			 dtime,
					 gpointer		 data);
static void	table_drop		(GtkWidget		*widget,
					 GdkDragContext		*context,
					 gint			 x,
					 gint			 y,
					 guint			 dtime,
					 gpointer		 data);
static void	table_update		(GxSNMPTableDialog	*dialog);
/******************************************************************************
 *
 *  gxsnmp_table_dialog_get_type ()
 *
 **/
GtkType
gxsnmp_table_dialog_get_type()
{
  static GtkType table_type = 0;
  D_FUNC_START;
  if (!table_type)
    {
      GtkTypeInfo table_info =
      {
        "GxSNMPTableDialog",
        sizeof (GxSNMPTableDialog),
        sizeof (GxSNMPTableDialogClass),
        (GtkClassInitFunc) table_class_init,
        (GtkObjectInitFunc) table_init,
        /* reserved 1 */ NULL,
        /* reserved 2 */ NULL,
	(GtkClassInitFunc) NULL,
      };
      table_type = gtk_type_unique (gnome_dialog_get_type (), &table_info);
    }
  D_FUNC_END;
  return table_type;
}

/******************************************************************************
 *
 *  The class initialization subroutine
 *
 **/
static GnomeDialog *parent_class;
static gint handle_signals[LAST_SIGNAL] = { 0 };

static void
table_class_init (GxSNMPTableDialogClass *klass)
{
  GtkObjectClass *object_class;
  D_FUNC_START;
  object_class = (GtkObjectClass *) klass;

  parent_class = gtk_type_class (gnome_dialog_get_type ());

  handle_signals[UPDATE] =
    gtk_signal_new ("update",
    		     GTK_RUN_LAST,
		     object_class->type,
		     GTK_SIGNAL_OFFSET (GxSNMPTableDialogClass, update),
		     gtk_marshal_NONE__NONE,
		     GTK_TYPE_NONE, 0);

  gtk_object_class_add_signals (object_class, handle_signals, LAST_SIGNAL);

  D_FUNC_END;
}
/*****************************************************************************
 *
 *  The widget initialization subroutine
 *
 **/
static void
table_init (GxSNMPTableDialog *dialog)
{
  GtkWidget * table;
  D_FUNC_START;
  g_return_if_fail (dialog != NULL);
  g_return_if_fail (GXSNMP_IS_TABLE_DIALOG (dialog));
  dialog->clist = NULL;
  /* 
   *  Do the widget type initialization.
   */
  table = gtk_table_new (1, 2, FALSE);
  gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
		      table, TRUE, TRUE, 0);
  dialog->label = gtk_label_new ("");
  gtk_table_attach (GTK_TABLE (table), dialog->label, 0, 1, 0, 1, 0, 0, 0, 0);
  gtk_widget_show (dialog->label);
  dialog->scrolled_win = gtk_scrolled_window_new (NULL, NULL);
  gtk_container_set_border_width (GTK_CONTAINER (dialog->scrolled_win), 5);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dialog->scrolled_win),
				  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_table_attach (GTK_TABLE (table), 
		    dialog->scrolled_win, 0, 1, 1, 2, 0, 0, 0, 0);
  gtk_widget_show (dialog->scrolled_win);
  gtk_widget_show (table);
  /* Button 0 */
  gnome_dialog_append_button_with_pixmap (GNOME_DIALOG (dialog),
					  "Settings...",
					  GNOME_STOCK_PIXMAP_FONT);
  /* Button 1 */
  gnome_dialog_append_button_with_pixmap (GNOME_DIALOG (dialog),
					  "Abort",
					  GNOME_STOCK_PIXMAP_STOP);
  /* Button 2 */
  gnome_dialog_append_button_with_pixmap (GNOME_DIALOG (dialog),
					  "Reload",
					  GNOME_STOCK_PIXMAP_REFRESH);
  /* Button 3 & 4 */
  gnome_dialog_append_buttons (GNOME_DIALOG (dialog),
			       GNOME_STOCK_BUTTON_CLOSE,
			       GNOME_STOCK_BUTTON_HELP,
			       NULL);
  gtk_signal_connect (GTK_OBJECT(dialog), "clicked",
		      GTK_SIGNAL_FUNC(table_dialog_cb), NULL);
  gtk_signal_connect (GTK_OBJECT(dialog), "update",
		      GTK_SIGNAL_FUNC(table_update), NULL);
  D_FUNC_END;
}
static void
table_update(GxSNMPTableDialog *dialog)
{
  gint        c_width;          /* Average width of a character */
  gint        c_height;         /* Average height of a character */
  gint        colnum, i;
  GtkWidget  *head;
  gchar     **coltitle;
  struct _colinfo *col;
  GSList     *colinfo;
  D_FUNC_START;
  gtk_label_set_text(GTK_LABEL(dialog->label), dialog->title);
  if (dialog->clist)
    {
      gtk_container_remove (GTK_CONTAINER (dialog->scrolled_win), 
      			    dialog->clist);
    }
  c_width  = gdk_string_width (dialog->label->style->font, "xW") / 2;
  c_height = dialog->label->style->font->ascent + 
	     dialog->label->style->font->descent;
  colinfo = dialog->colinfo;
  colnum = g_slist_length(colinfo);
  if (colnum)
    {
      coltitle = g_malloc(colnum * sizeof (gpointer));
      for (i=0;i<colnum;i++)
	{
	  col = (struct _colinfo*) colinfo->data;
	  coltitle[i] = col->title;
	  colinfo = colinfo->next;
	}
      dialog->clist = gtk_clist_new_with_titles (colnum, coltitle);
      g_free(coltitle);
      gtk_container_add (GTK_CONTAINER (dialog->scrolled_win), dialog->clist);
      gtk_widget_set_usize (dialog->clist, 80 * c_width , 25 * c_height);
      gtk_widget_show (dialog->clist);
      for (i=0;i<colnum;i++)
	{
	  head = gtk_clist_get_column_widget (GTK_CLIST(dialog->clist), i);
	  gtk_drag_dest_set (head, GTK_DEST_DEFAULT_ALL,
		drop_targets, ELEMENTS (drop_targets), GDK_ACTION_COPY);
	  gtk_drag_source_set (head->parent, GDK_BUTTON1_MASK,
	        drag_targets, ELEMENTS (drag_targets), GDK_ACTION_COPY);
	  gtk_signal_connect (GTK_OBJECT(head), "drag_data_received",
		(GtkSignalFunc) table_data, dialog);
	  gtk_signal_connect (GTK_OBJECT(head), "drag_drop",
		(GtkSignalFunc) table_drop, dialog);
	  gtk_signal_connect (GTK_OBJECT(head->parent), "drag_data_get",
		(GtkSignalFunc) table_drag_get, dialog);
	}
    }
  D_FUNC_END;
}
static void
gxsnmp_table_dialog_set_data(GxSNMPTableDialog *dialog, GSList *colinfo, 
			     gchar *title)
{
  D_FUNC_START;
  dialog->title = title;
  dialog->colinfo = colinfo;
  gtk_signal_emit_by_name(dialog,"update");
  D_FUNC_END;
}
static void
table_dialog_cb (GnomeDialog *dialog, gint button, gpointer data)
{
  gchar *tmp;
  D_FUNC_START;
  switch (button)
    {
    case 0:
      g_print ("Settings...\n");
      break;
    case 1:
      g_print ("Abort!\n");
      break;
    case 2:
      g_print ("Reload\n");
      break;
    case 3:
      gnome_dialog_close (GNOME_DIALOG (dialog));
      break;
    case 4:
      tmp = gnome_help_file_find_file ("gxsnmp", "table-help.html");
      if (tmp)
	{
	  gnome_help_goto (0, tmp);
	  g_free (tmp);
	}
      break;
    default:
      g_print ("Button %d hit!\n", button);
      break;
    }
  D_FUNC_END;
}
static void
table_drag_get(GtkWidget* widget, GdkDragContext* context,
               GtkSelectionData* sel_data, guint info, guint dtime,
               gpointer data)
{
  gint i;
  struct _colinfo *col;
  GSList *colinfo;
  GxSNMPTableDialog *dialog;
  D_FUNC_START;
  g_return_if_fail (data != NULL);
  dialog = GXSNMP_TABLE_DIALOG(data);
  colinfo = dialog->colinfo;
  i = 0;
  while (colinfo)
    {
      col = (struct _colinfo*) colinfo->data;
      if (gtk_clist_get_column_widget(GTK_CLIST(dialog->clist), i)->parent == widget)
	{
	  switch(info)
	    {
	      case TARGET_COLUMN:
		gtk_selection_data_set (sel_data, sel_data->target, 8, 
					col->title, strlen(col->title));
		break;
	      default:
	    }
	}
      i++;
      colinfo = colinfo->next;
    }
  D_FUNC_END;
}
static void
table_data (GtkWidget* widget, GdkDragContext* context, gint x, gint y,
	    GtkSelectionData* sel_data, guint info, guint dtime, gpointer data)
{
  gint i;
  struct _colinfo *col;
  GSList *colinfo, *colinfo2, *colinfo3;
  GxSNMPTableDialog *dialog;
  gchar *buffer;
  SmiNode * entry;
  D_FUNC_START;
  dialog = GXSNMP_TABLE_DIALOG(data);
  colinfo = dialog->colinfo;
  i = 0;
  while (colinfo)
    {
      col = (struct _colinfo*) colinfo->data;
      if (gtk_clist_get_column_widget (GTK_CLIST(dialog->clist), i) == widget)
	{
	  switch(info)
	    {
	      case TARGET_COLUMN:
	        g_print("Move %s after %s\n", (gchar *)sel_data->data, 
			 col->title);
		colinfo2 = dialog->colinfo;
		colinfo3 = NULL;
		while(colinfo2)
		  {
		    if (!strcmp(((struct _colinfo*) colinfo2->data)->title, 
		    	       (gchar *)sel_data->data))
		      break;
		    colinfo3 = colinfo2;
		    colinfo2 = colinfo2->next;
		  }
		if (colinfo == colinfo2) colinfo2 = NULL;
		if (colinfo2)
		  {
		    if (colinfo3)
		      colinfo3->next = colinfo2->next;
		    else
		      dialog->colinfo = colinfo2->next;
		    colinfo2->next = colinfo->next;
		    colinfo->next = colinfo2;
		  }
		break;
	      default:
		g_print("Make column %s indirect to %s\n", col->title,
			(gchar *)sel_data->data);
		buffer = strchr((gchar *)sel_data->data,'.');
		if (buffer)
		  {
		    buffer++;
		    g_free(col->title);
		    col->title = g_strdup(buffer);
		  }
		entry=smiGetNode(NULL, (gchar *)sel_data->data);
		col->ioidlen = entry->oidlen;
		col->ioid = g_malloc(entry->oidlen * sizeof(int));
		g_memmove(col->oid, entry->oid, entry->oidlen * sizeof(int));
		col->type=COL_INDIRECT;
		break;
	    }
	}
      i++;
      colinfo = colinfo->next;
    }
  gtk_drag_finish (context, TRUE, TRUE, dtime);
  D_FUNC_END;
}
static void
table_drop (GtkWidget* widget, GdkDragContext* context, gint x, gint y,
	    guint dtime, gpointer data)
{
  GxSNMPTableDialog *dialog;
  D_FUNC_START;
  dialog = GXSNMP_TABLE_DIALOG(data);
  gtk_signal_emit_by_name(dialog,"update");
  D_FUNC_END;
}
/****************************************************************************
 *
 *  Public function to create a new table dialog.
 *
 **/
GtkWidget *
gxsnmp_table_dialog_new (GSList *colinfo, gchar *title)
{
  GxSNMPTableDialog * dialog;
  D_FUNC_START;
  dialog = gtk_type_new (gxsnmp_table_dialog_get_type ());
  gxsnmp_table_dialog_set_data(dialog, colinfo, title);
  D_FUNC_END;
  return GTK_WIDGET (dialog);
}
GtkWidget *
gxsnmp_table_dialog_new_from_node (SmiNode *node)
{
  GxSNMPTableDialog * dialog;
  GSList * colinfo;
  SmiNode * entry;
  SmiNode * child;
  SmiType * type;
  gchar * title;
  struct _colinfo *col;
  D_FUNC_START;
  title = node->name;
  colinfo = NULL;
  entry = smiGetFirstChildNode(node); /* There can be only one :) */
  g_return_val_if_fail(entry, NULL);
  child = smiGetFirstChildNode(entry);
  while(child)
    {
      type = smiGetNodeType(child);
      col = g_malloc(sizeof(struct _colinfo));
      col->title = child->name;
      col->format = child->format;
      col->enumber = NULL;
      col->ioidlen = 0;
      col->ioid = NULL;
      col->iformat = NULL;
      col->oidlen = child->oidlen;
      col->oid = g_malloc(child->oidlen * sizeof(int));
      g_memmove(col->oid, child->oid, child->oidlen * sizeof(int));
      if (type->basetype == SMI_BASETYPE_ENUM)
	{
	  SmiNamedNumber *num;
	  col->type = COL_ENUM;
	  col->enumber = g_hash_table_new(g_int_hash, g_int_equal);
	  num = smiGetFirstNamedNumber(type);
	  while (num)
	    {
	      int *i;
	      i = g_malloc(sizeof(int));
	      *i = num->value.value.integer32;
	      g_hash_table_insert(col->enumber, i, num->name);
	      num = smiGetNextNamedNumber(num);
	    }
	}
      else
	col->type = COL_DIRECT;
      colinfo = g_slist_append(colinfo, col);
      child = smiGetNextChildNode(child);
    }
  dialog = gtk_type_new (gxsnmp_table_dialog_get_type ());
  gxsnmp_table_dialog_set_data(dialog, colinfo, title);
  D_FUNC_END;
  return GTK_WIDGET (dialog);
}
/* EOF */
