/*
 * e-cell-toggle.c: Multi-state image toggle cell object.
 *
 * Author:
 *   Miguel de Icaza (miguel@kernel.org)
 *
 * (C) 1999 Helix Code, Inc
 */
#include <config.h>
#include <gtk/gtkenums.h>
#include <gtk/gtkentry.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtksignal.h>
#include <gdk/gdkkeysyms.h>
#include <libgnomeui/gnome-canvas.h>
#include "e-cell-toggle.h"
#include "e-util/e-util.h"
#include "e-table-item.h"

#define PARENT_TYPE e_cell_get_type()

typedef struct {
	ECellView    cell_view;
	GdkGC       *gc;
	GnomeCanvas *canvas;
} ECellToggleView;

static ECellClass *parent_class;

static void
etog_queue_redraw (ECellToggleView *text_view, int view_col, int view_row)
{
	e_table_item_redraw_range (
		text_view->cell_view.e_table_item_view,
		view_col, view_row, view_col, view_row);
}

/*
 * ECell::realize method
 */
static ECellView *
etog_new_view (ECell *ecell, ETableModel *table_model, void *e_table_item_view)
{
	ECellToggleView *toggle_view = g_new0 (ECellToggleView, 1);
	ETableItem *eti = E_TABLE_ITEM (e_table_item_view);
	GnomeCanvas *canvas = GNOME_CANVAS_ITEM (eti)->canvas;
	
	toggle_view->cell_view.ecell = ecell;
	toggle_view->cell_view.e_table_model = table_model;
	toggle_view->cell_view.e_table_item_view = e_table_item_view;
	toggle_view->canvas = canvas;
	
	return (ECellView *) toggle_view;
}

static void
etog_kill_view (ECellView *ecell_view)
{
	g_free (ecell_view);
}	

static void
etog_realize (ECellView *ecell_view)
{
	ECellToggleView *toggle_view = (ECellToggleView *) ecell_view;

	toggle_view->gc = gdk_gc_new (GTK_WIDGET (toggle_view->canvas)->window);
}

/*
 * ECell::unrealize method
 */
static void
etog_unrealize (ECellView *ecv)
{
	ECellToggleView *toggle_view = (ECellToggleView *) ecv;

	gdk_gc_unref (toggle_view->gc);
	toggle_view->gc = NULL;
}

/*
 * ECell::draw method
 */
static void
etog_draw (ECellView *ecell_view, GdkDrawable *drawable,
	  int model_col, int view_col, int row, gboolean selected,
	  int x1, int y1, int x2, int y2)
{
	ECellToggle *toggle = E_CELL_TOGGLE (ecell_view->ecell);
	ECellToggleView *toggle_view = (ECellToggleView *) ecell_view;
	GdkPixbuf *image;
	ArtPixBuf *art;
	int x, y, width, height;
	const int value = GPOINTER_TO_INT (
		e_table_model_value_at (ecell_view->e_table_model, model_col, row));

	if (value >= toggle->n_states){
		g_warning ("Value from the table model is %d, the states we support are [0..%d)\n",
			   value, toggle->n_states);
		return;
	}

	/*
	 * Paint the background
	 */
	gdk_draw_rectangle (drawable, GTK_WIDGET (toggle_view->canvas)->style->white_gc, TRUE, x1, y1, x2 - x1, y2 - y1);
			    
	image = toggle->images [value];
	art = image->art_pixbuf;

	if ((x2 - x1) < art->width){
		x = x1;
		width = x2 - x1;
	} else {
		x = x1 + ((x2 - x1) - art->width) / 2;
		width = art->width;
	}

	if ((y2 - y1) < art->height){
		y = y1;
		height = y2 - y1;
	} else {
		y = y1 + ((y2 - y1) - art->height) / 2;
		height = art->height;
	}

	width = y2 - y1;

	if (image->art_pixbuf->has_alpha){
		GdkColor background;
		guchar *buffer;
		int alpha, ix, iy;

		buffer = g_malloc (art->rowstride * art->height * 3);

		background.red = 255;
		background.green = 255;
		background.blue = 255;
		
		for (iy = 0; iy < art->height; iy++){
			unsigned char *dest;
			unsigned char *src;
		
			dest = buffer + (iy * art->rowstride);
			src = art->pixels + (iy * art->rowstride);

			for (ix = 0; ix < art->width; ix++){
				alpha = src [3];
				if (alpha == 0){
					*dest++ = background.red;
					*dest++ = background.green;
					*dest++ = background.blue;
					src += 3;
				} else if (alpha == 255){
					*dest++ = *src++;
					*dest++ = *src++;
					*dest++ = *src++;
				} else {
					*dest++ = (background.red +   ((*src++ - background.red) * alpha + 0x80)) >> 8;
					*dest++ = (background.green + ((*src++ - background.green) * alpha + 0x80)) >> 8;
					*dest++ = (background.blue +  ((*src++ - background.blue) * alpha + 0x80)) >> 8;
				}
				src++;
			}
		}
		
		gdk_draw_rgb_image_dithalign (
			drawable, toggle_view->gc, x, y, width, height,
			GDK_RGB_DITHER_NORMAL, buffer, art->rowstride, 0, 0);

		g_free (buffer);
	} else
		gdk_draw_rgb_image_dithalign (
			drawable, toggle_view->gc, x, y, width, height,
			GDK_RGB_DITHER_NORMAL, art->pixels, art->rowstride, 0, 0);
}

static void
etog_set_value (ECellToggleView *toggle_view, int model_col, int view_col, int row, int value)
{
	ECell *ecell = toggle_view->cell_view.ecell;
	ECellToggle *toggle = E_CELL_TOGGLE (ecell);

	if (value >= toggle->n_states)
		value = 0;

	e_table_model_set_value_at (toggle_view->cell_view.e_table_model,
				    model_col, row, GINT_TO_POINTER (value));
	etog_queue_redraw (toggle_view, view_col, row);
}

/*
 * ECell::event method
 */
static gint
etog_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col, int row)
{
	ECellToggleView *toggle_view = (ECellToggleView *) ecell_view;
	void *_value = e_table_model_value_at (ecell_view->e_table_model, model_col, row);
	const int value = GPOINTER_TO_INT (_value);
	
	switch (event->type){
	case GDK_BUTTON_RELEASE:
		etog_set_value (toggle_view, model_col, view_col, row, value + 1);
		return TRUE;

	case GDK_KEY_PRESS:
		if (event->key.keyval == GDK_space){
			etog_set_value (toggle_view, model_col, view_col, row, value + 1);
			return TRUE;
		}
		return FALSE;
		
	default:
		return FALSE;
	}
	return TRUE;
}

/*
 * ECell::height method
 */
static int
etog_height (ECellView *ecell_view, int model_col, int view_col, int row)
{
	ECellToggle *toggle = E_CELL_TOGGLE (ecell_view->ecell);

	return toggle->height;
}

static void
etog_destroy (GtkObject *object)
{
	ECellToggle *etog = E_CELL_TOGGLE (object);
	int i;
	
	for (i = 0; i < etog->n_states; i++)
		gdk_pixbuf_unref (etog->images [i]);

	g_free (etog->images);

	GTK_OBJECT_CLASS (parent_class)->destroy (object);
}

static void
e_cell_toggle_class_init (GtkObjectClass *object_class)
{
	ECellClass *ecc = (ECellClass *) object_class;

	object_class->destroy = etog_destroy;

	ecc->new_view   = etog_new_view;
	ecc->kill_view  = etog_kill_view;
	ecc->realize    = etog_realize;
	ecc->unrealize  = etog_unrealize;
	ecc->draw       = etog_draw;
	ecc->event      = etog_event;
	ecc->height     = etog_height;

	parent_class = gtk_type_class (PARENT_TYPE);
}

E_MAKE_TYPE(e_cell_toggle, "ECellToggle", ECellToggle, e_cell_toggle_class_init, NULL, PARENT_TYPE);

void
e_cell_toggle_construct (ECellToggle *etog, int border, int n_states, GdkPixbuf **images)
{
	int max_height =  0;
	int i;
	
	etog->border = border;
	etog->n_states = n_states;

	etog->images = g_new (GdkPixbuf *, n_states);

	for (i = 0; i < n_states; i++){
		etog->images [i] = images [i];
		gdk_pixbuf_ref (images [i]);

		if (images [i]->art_pixbuf->height > max_height)
			max_height = images [i]->art_pixbuf->height;
	}

	etog->height = max_height;
}

ECell *
e_cell_toggle_new (int border, int n_states, GdkPixbuf **images)
{
	ECellToggle *etog = gtk_type_new (e_cell_toggle_get_type ());

	e_cell_toggle_construct (etog, border, n_states, images);

	return (ECell *) etog;
}