/* -*- Mode: C -*-
 * $Id: gnome-canvas-grid.c,v 1.7 2000/02/02 04:10:50 gregm Exp $
 *
 * GXSNMP -- An snmp mangament application
 * Copyright 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.
 *
 */ 
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "gnome-canvas-grid.h"
#include <libgnomeui/gnome-canvas-util.h>
#include <libart_lgpl/art_rgb.h>
#include <math.h>

#include "debug.h"

/****************************************************************************
 * Forward references
 **/
static void     gnome_canvas_grid_class_init    (GnomeCanvasGridClass *klass);
static void     gnome_canvas_grid_init          (GnomeCanvasGrid      *grid);
static void     gnome_canvas_grid_destroy       (GtkObject            *object);
static void     gnome_canvas_grid_set_arg       (GtkObject            *object,
						 GtkArg               *arg,
						 guint                arg_id);
static void     gnome_canvas_grid_get_arg       (GtkObject            *object,
						 GtkArg               *arg,
						 guint                arg_id);
static void     gnome_canvas_grid_realize       (GnomeCanvasItem      *item);
static void     gnome_canvas_grid_unrealize     (GnomeCanvasItem      *item);
static void     gnome_canvas_grid_draw          (GnomeCanvasItem      *item,
						 GdkDrawable          *drawbl,
						 int                  x,
						 int                  y,
						 int                  width,
						 int                  height);
static void     gnome_canvas_grid_render       (GnomeCanvasItem       *item,
						GnomeCanvasBuf        *buf);
static void     gnome_canvas_grid_update       (GnomeCanvasItem       *item,
						double                *affline,
						ArtSVP                *clip,
						int                   flags);
/****************************************************************************
 * Local definitions
 **/
/** stipple pattern */
#define grey50_width  2
#define grey50_height 2
static const char grey50_bits[] =
{
  0x02, 0x01
};
/** Arguments **/
enum {
  ARG_0,
  ARG_GRID_X,
  ARG_GRID_Y,
  ARG_LINE_COLOR
};
/** Signals **/
enum {
  LAST_SIGNAL
};
/** The parent class */
static GnomeCanvasItemClass      *parent_class;
/**
 * gnome_canvas_grid_get_type:
 *
 * Registers the &GnomeCanvasGrid class if necessary, and returns the unique
 * type ID associated to it.
 *
 * Return value: The unique type ID of the &GnomeCanvasGrid class.
 **/
GtkType
gnome_canvas_grid_get_type (void)
{
  static GtkType canvas_grid_type = 0;
  D_FUNC_START;
  if (!canvas_grid_type) 
    {
      static const GtkTypeInfo canvas_grid_info = {
	"GnomeCanvasGrid",
	sizeof (GnomeCanvasGrid),
	sizeof (GnomeCanvasGridClass),
	(GtkClassInitFunc) gnome_canvas_grid_class_init,
	(GtkObjectInitFunc) gnome_canvas_grid_init,
	NULL, /* reserved_1 */
	NULL, /* reserved_2 */
	(GtkClassInitFunc) NULL
      };
      canvas_grid_type = gtk_type_unique (gnome_canvas_item_get_type (),
					  &canvas_grid_info);
    }
  D_FUNC_END;
  return canvas_grid_type;
}
/****************************************************************************
 * Class initialization function for GnomeCanvasGridClass 
 **/
static void
gnome_canvas_grid_class_init (GnomeCanvasGridClass *klass)
{
  GtkObjectClass       *object_class;
  GnomeCanvasItemClass *item_class;
  D_FUNC_START;
  object_class = (GtkObjectClass *)klass;
  item_class   = (GnomeCanvasItemClass *)klass;
  parent_class = gtk_type_class (gnome_canvas_item_get_type ());

  gtk_object_add_arg_type ("GnomeCanvasGrid::grid_x",
			   GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_GRID_X);
  gtk_object_add_arg_type ("GnomeCanvasGrid::grid_y", 
			   GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_GRID_Y);
  gtk_object_add_arg_type ("GnomeCanvasGrid::line_color",
			   GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LINE_COLOR);

  object_class->destroy  = gnome_canvas_grid_destroy;
  object_class->set_arg  = gnome_canvas_grid_set_arg;
  object_class->get_arg  = gnome_canvas_grid_get_arg;

  item_class->realize    = gnome_canvas_grid_realize;
  item_class->unrealize  = gnome_canvas_grid_unrealize;
  item_class->draw       = gnome_canvas_grid_draw;
  item_class->render     = gnome_canvas_grid_render;
  item_class->update     = gnome_canvas_grid_update;

  D_FUNC_END;
}
/****************************************************************************
 * Object initialization function for GnomeCanvasGrid
 **/
static void
gnome_canvas_grid_init (GnomeCanvasGrid *grid)
{
  D_FUNC_START;
  /* Start off with a 20 x 20 grid */
  grid->x = 20;
  grid->y = 20;
  D_FUNC_END;
}
/****************************************************************************
 * destroy handler.
 **/
static void
gnome_canvas_grid_destroy (GtkObject *object)
{
  GnomeCanvasGrid   *grid;
  D_FUNC_START;
  grid = (GnomeCanvasGrid *)object;
  if (grid->stipple)
    gdk_bitmap_unref (grid->stipple);

  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
  D_FUNC_END;
}
/****************************************************************************
 * set_arg handler for the GnomeCanvasGrid 
 **/
static void
gnome_canvas_grid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
  GnomeCanvasGrid  *grid;
  GnomeCanvasItem  *item;
  GdkColor         color;

  D_FUNC_START;
  g_return_if_fail (GNOME_IS_CANVAS_GRID (object));
  grid = GNOME_CANVAS_GRID (object);
  item = GNOME_CANVAS_ITEM (object);
  switch (arg_id)
    {
    case ARG_GRID_X:
      grid->x = GTK_VALUE_INT (*arg);
      gnome_canvas_item_request_update (item);
      break;
    case ARG_GRID_Y:
      grid->y = GTK_VALUE_INT (*arg);
      gnome_canvas_item_request_update (item);
      break;
    case ARG_LINE_COLOR:
      gdk_color_parse (GTK_VALUE_STRING (*arg), &color);
      grid->line_color = ((color.red &0xff00) << 8 |
			  (color.green & 0xff00)   |
			  (color.blue & 0xff00) >> 8);
      gnome_canvas_item_request_update (item);
      break;
    default:
      g_warning ("Invalid arg recieved.\n");
      break;
    }
  D_FUNC_END;
}
/****************************************************************************
 * get_arg handler for the GnomeCanvasGrid
 **/
static void
gnome_canvas_grid_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
  GnomeCanvasGrid   *grid;
  D_FUNC_START;
  g_return_if_fail (GNOME_IS_CANVAS_GRID (object));
  grid = GNOME_CANVAS_GRID (object);
  switch (arg_id)
    {
    case ARG_GRID_X:
      GTK_VALUE_INT (*arg) = grid->x;
      break;
    case ARG_GRID_Y:
      GTK_VALUE_INT (*arg) = grid->y;
      break;
    default:
      arg->type = GTK_TYPE_INVALID;
      g_warning ("Unknown arg type recieved.");
      break;
    }
  D_FUNC_END;
}
/****************************************************************************
 * realize handler.
 **/
static void
gnome_canvas_grid_realize (GnomeCanvasItem *item)
{
  GnomeCanvasGrid    *grid;
  D_FUNC_START;
  grid = GNOME_CANVAS_GRID (item);

  if (parent_class->realize)
    (* parent_class->realize) (item);

  /* default color */
  gdk_color_parse ("black", &grid->color);
  grid->line_color = ((grid->color.red &0xff00) << 8 |
		      (grid->color.green & 0xff00)   |
		      (grid->color.blue & 0xff00) >> 8);
  if (!item->canvas->aa)
    {
      grid->gc      = gdk_gc_new (item->canvas->layout.bin_window);
      grid->stipple = gdk_bitmap_create_from_data (NULL, grey50_bits,
						grey50_width, grey50_height);
      gdk_gc_set_foreground (grid->gc, &grid->color);
      gdk_gc_set_stipple (grid->gc, grid->stipple);
    }
  D_FUNC_END;
}
/****************************************************************************
 * unrealize handler.
 **/
static void
gnome_canvas_grid_unrealize (GnomeCanvasItem *item)
{
  GnomeCanvasGrid    *grid;
  D_FUNC_START;
  grid = GNOME_CANVAS_GRID (item);
  if (!item->canvas->aa)
    {
      gdk_gc_unref (grid->gc);
      grid->gc = NULL;
    }
  if (parent_class->unrealize)
    (* parent_class->unrealize) (item);
  D_FUNC_END;
}
/****************************************************************************
 * draw handler.
 **/
static void
gnome_canvas_grid_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
			int x, int y, int width, int height)
{
  GnomeCanvasGrid   *grid;
  gint              pos;
  gdouble           ppu;

  D_FUNC_START;
  grid = (GnomeCanvasGrid *)item;
  ppu = item->canvas->pixels_per_unit;
  pos = ((x + grid->x - 1) / grid->x) * grid->x  - x;
  /* vertical lines */
  while (pos <= width)
    {
      gdk_draw_line (drawable, grid->gc, pos, 0, pos, height);
      pos += grid->x;
    }
  /* horizontal lines */
  pos = ((y + grid->y - 1) / grid->y) * grid->y - y;
  while (pos <= height)
    {
      gdk_draw_line (drawable, grid->gc, 0, pos, width, pos);
      pos += grid->y;
    }
  D_FUNC_END;
}
/****************************************************************************
 * antialias render handler.
 **/
static void
gnome_canvas_grid_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
{
  guchar          *bufptr;
  GnomeCanvasGrid *grid;
  gint            x, y;
  gint		  w, h;
  gdouble         ppu;
  D_FUNC_START;

  grid = (GnomeCanvasGrid *)item;
  gnome_canvas_buf_ensure_buf (buf);
  /* Vertical */ 
  d_print (DEBUG_DUMP, "x0 = %d, y0 = %d, x1 = %d, y1 = %d\n", 
	  buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1);
  ppu = item->canvas->pixels_per_unit;
  d_print (DEBUG_DUMP, "ppu = %.2f\n", ppu);
//  x = ((buf->rect.x0 + grid->x  - 1) / grid->x) * grid->x - buf->rect.x0;
  w = buf->rect.x1 - buf->rect.x0;
  h = buf->rect.y1 - buf->rect.y0;
  x = grid->x - buf->rect.x0%grid->x;
  for ( y = 0; y <= h; y++)
    {
      bufptr = (buf->buf + y * buf->buf_rowstride + x * 3);
      bufptr[0] = grid->line_color >> 16; 
      bufptr[1] = (grid->line_color >> 8) & 0xff; 
      bufptr[2] = grid->line_color & 0xff;
      bufptr = (buf->buf + y * buf->buf_rowstride + buf->rect.x1 * 3);
      bufptr[0] = grid->line_color >> 16; 
      bufptr[1] = (grid->line_color >> 8) & 0xff; 
      bufptr[2] = grid->line_color & 0xff;      
    }
  y = grid->y - buf->rect.y0%grid->x;
  for ( x = 0; x <= w; x++)
    {
      bufptr = (buf->buf + y * buf->buf_rowstride + x * 3);
      bufptr[0] = grid->line_color >> 16; 
      bufptr[1] = (grid->line_color >> 8) & 0xff; 
      bufptr[2] = grid->line_color &0xff;
      bufptr = (buf->buf + 1  * buf->buf_rowstride + x * 3);
      bufptr[0] = grid->line_color >> 16; 
      bufptr[1] = (grid->line_color >> 8) & 0xff; 
      bufptr[2] = grid->line_color &0xff;
    }
  x = grid->x - buf->rect.x0%grid->x;
#if 0
  while ( x <= w )
    {
      d_print (DEBUG_DUMP, "x = %d\n", x);
      for (y = 0; y <= h; y++)
	{
          if (y%2) 
	    {
	      bufptr = (buf->buf + y * buf->buf_rowstride + x * 3);
	      bufptr[0] = grid->line_color >> 16; 
	      bufptr[1] = (grid->line_color >> 8) & 0xff; 
	      bufptr[2] = grid->line_color & 0xff;
	    }
        }
      x += grid->x;
    }
  /* Horizontal */
//  y = ((buf->rect.y0 + grid->y - 1) / grid->y) * grid->y - buf->rect.y0;
  y = grid->y - buf->rect.y0%grid->x;
  while (y <= h)
    {
      d_print (DEBUG_DUMP, "y = %d\n", y);
      for (x = 0; x <= w; x++)
	{
          if (x%2) 
	    {
	      bufptr = (buf->buf + y * buf->buf_rowstride + x * 3);
	      bufptr[0] = grid->line_color >> 16; 
	      bufptr[1] = (grid->line_color >> 8) & 0xff; 
	      bufptr[2] = grid->line_color &0xff;
	    }
        }
      y += grid->y;
    }
#endif 
  buf->is_bg = 0; /* tell the canvas to render it */
  D_FUNC_END;
}
/****************************************************************************
 * update handler.
 **/
static void
gnome_canvas_grid_update (GnomeCanvasItem *item, double *affine, 
			  ArtSVP *clip_path, gint flags)
{
  GnomeCanvasGrid    *grid;
  GnomeCanvas        *canvas;

  if (parent_class->update)
    (* parent_class->update) (item, affine, clip_path, flags);
  item->x1 = 0;
  item->y1 = 0;
  item->x2 = INT_MAX;
  item->y2 = INT_MAX;
  
  gnome_canvas_group_child_bounds (GNOME_CANVAS_GROUP (item->parent), item);
  D_FUNC_END;
}
/* EOF */


