#include <string.h>
#include <math.h>
#include <gtk/gtk.h>

#include "guiutils.h"
#include "piechart.h"


static void PieChartDrawArc(
	GdkPixmap *pixmap, GdkGC *gc,
	gint x, gint y, gint width, gint height,
	gfloat start_angle,     /* 0.0 to 1.0 */
	gfloat end_angle,       /* 0.0 to 1.0 */
	gboolean outline_circumferance,
	gboolean outline_radius, gboolean outline_height
);
static void PieChartDrawPieIterate(
	pie_chart_struct *pc, GtkStyle *style, GdkPixmap *pixmap,
	gint state, gint x, gint y,
	gint width, gint height,
	gboolean outline_circumference, gboolean outline_radius,
	gboolean outline_height,
	gboolean shaded, gboolean shadow
);
static void PieChartCreateValueLabel(
	pie_chart_struct *pc,
	GdkColor *c, const gchar *type_label, const gchar *value_label,
	GtkWidget **type_label_hbox_rtn, GtkWidget **value_label_hbox_rtn,
	GtkWidget **drawing_area_rtn
);
static void PieChartDoRealize(pie_chart_struct *pc);
static void PieChartRedrawPixmap(pie_chart_struct *pc);

static gint PieChartValueLabelExposeCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
);
static gint PieChartExposeCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
);

gint PieChartValueAdd(
	pie_chart_struct *pc,
	GtkAdjustment *adj, GdkColor *c,
	const gchar *type_label, const gchar *value_label
);
void PieChartValueSet(
	pie_chart_struct *pc, gint value_num,
	GtkAdjustment *adj, GdkColor *c,
	const gchar *type_label, const gchar *value_label
);
void PieChartValueRemove(
	pie_chart_struct *pc, gint value_num
);
void PieChartValueRemoveAll(pie_chart_struct *pc);

pie_chart_struct *PieChartNew(
	GtkAdjustment *adj, GdkColor *c, gint width, gint height,
	const gchar *title, const gchar *footer,
	const gchar *base_type_label, const gchar *base_value_label
);
void PieChartMap(pie_chart_struct *pc);
void PieChartUnmap(pie_chart_struct *pc);
void PieChartDelete(pie_chart_struct *pc);


#ifndef PI
# define PI	3.14159265
#endif

#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)

#define PIE_CHART_PIE_HEIGHT	10


/*
 *	Draws a segment of a pie arc.
 *
 *	Angles are in units from 0.0 to 1.0 starting at the 12 o'clock
 *	position being 0.0.
 */
static void PieChartDrawArc(
	GdkPixmap *pixmap, GdkGC *gc,
	gint x, gint y, gint width, gint height,
	gfloat start_angle,	/* 0.0 to 1.0 */
	gfloat end_angle,	/* 0.0 to 1.0 */
	gboolean outline_circumferance,
	gboolean outline_radius, gboolean outline_height
)
{
	gfloat delta_angle;

	if((pixmap == NULL) || (gc == NULL))
	    return;

        /* Calculate delta angle */
        delta_angle = start_angle - end_angle;
	if(delta_angle == 0.0f)
	    return;

	/* Outline radius? */
	if(outline_radius &&
	   ((start_angle != 0.0f) || (end_angle != 1.0f))
	)
	{
	    gfloat	ostart_angle = start_angle,
			oend_angle = end_angle;
	    gint	rx = (width / 2),
			ry = (height / 2),
			cx = x + rx,
			cy = y + ry;

	    /* Sanitize outline start and end angles */
	    while(ostart_angle > 1.0f)
		ostart_angle -= 1.0f;
	    while(ostart_angle < 0.0f)
		ostart_angle += 1.0f;

	    while(oend_angle > 1.0f)
		oend_angle -= 1.0f;
	    while(oend_angle < 0.0f)
		oend_angle += 1.0f;

	    gdk_draw_line(
		pixmap, gc,
		cx, cy,
		cx + (rx * sin(ostart_angle * 2.0f * PI)),
		cy + (ry * -cos(ostart_angle * 2.0f * PI))
	    );
	    gdk_draw_line(
		pixmap, gc,
		cx, cy,
		cx + (rx * sin(oend_angle * 2.0f * PI)),
		cy + (ry * -cos(oend_angle * 2.0f * PI))
	    );
	}

	/* Outline height? */
	if(outline_height)
	{
	    gfloat      ostart_angle = start_angle,
			oend_angle = end_angle;
	    gint        rx = (width / 2),
			ry = (height / 2),
			cx = x + rx,
			cy = y + ry;

	    /* Sanitize outline start and end angles */
	    while(ostart_angle > 1.0f)
		ostart_angle -= 1.0f;
	    while(ostart_angle < 0.0f)
		ostart_angle += 1.0f;

	    while(oend_angle > 1.0f)
		oend_angle -= 1.0f;
	    while(oend_angle < 0.0f)
		oend_angle += 1.0f;

	    /* Draw height lines dividing pie segments */
	    if((ostart_angle > 0.25f) && (ostart_angle < 0.75f))
		gdk_draw_line(
		    pixmap, gc,
		    cx + (rx * sin(ostart_angle * 2.0f * PI)),
		    cy + (ry * -cos(ostart_angle * 2.0f * PI)),
		    cx + (rx * sin(ostart_angle * 2.0f * PI)),
		    cy + (ry * -cos(ostart_angle * 2.0f * PI)) +
			PIE_CHART_PIE_HEIGHT
		);
	    if((oend_angle > 0.25f) && (oend_angle < 0.75f))
		gdk_draw_line(
		    pixmap, gc,
		    cx + (rx * sin(oend_angle * 2.0f * PI)),
		    cy + (ry * -cos(oend_angle * 2.0f * PI)),
		    cx + (rx * sin(oend_angle * 2.0f * PI)),
		    cy + (ry * -cos(oend_angle * 2.0f * PI)) +
			PIE_CHART_PIE_HEIGHT
		);

	    /* Draw height lines at far left and right edges */
	    gdk_draw_line(
		pixmap, gc,
		x, y + ry,
		x, y + ry + PIE_CHART_PIE_HEIGHT - 1
	    );
	    gdk_draw_line(
		pixmap, gc,
		x + width, y + ry,
		x + width, y + ry + PIE_CHART_PIE_HEIGHT - 1
	    );
	}

	/* Convert angles to degrees and make it relative to the 3
	 * o'clock position
	 */
	start_angle = 90.0f - (start_angle * 360.0f);
	while(start_angle < 0.0f)
	    start_angle += 360.0f;
	while(start_angle >= 360.0f)
	    start_angle -= 360.0f;

	delta_angle *= 360.0f;

	gdk_draw_arc(
	    pixmap, gc, !outline_circumferance,
	    x, y, width, height,
	    (gint)(start_angle * 64.0f),
	    (gint)(delta_angle * 64.0f)
	);
}

/*
 *	Creates a new value label and parents it to the given pie chart's
 *	value_labels_vbox.
 *
 *	Returns the pointer to the hbox containing the new label.
 */
static void PieChartCreateValueLabel(
	pie_chart_struct *pc,
	GdkColor *c, const gchar *type_label, const gchar *value_label,
	GtkWidget **type_label_hbox_rtn, GtkWidget **value_label_hbox_rtn,
	GtkWidget **drawing_area_rtn
)
{
	const gint	border_minor = 2;
	gint da_width = 12, da_height = 12;
	GdkFont *font;
	GtkStyle *style;
	GtkWidget *w, *parent, *parent2;
	GtkWidget *type_labels_vbox, *value_labels_vbox;


	/* Reste returns */
	if(type_label_hbox_rtn != NULL)
	    *type_label_hbox_rtn = NULL;
	if(value_label_hbox_rtn != NULL)
	    *value_label_hbox_rtn = NULL;
	if(drawing_area_rtn != NULL)
	    *drawing_area_rtn = NULL;

	if((pc == NULL) || STRISEMPTY(type_label))
	    return;

	w = pc->toplevel;
	style = (w != NULL) ? gtk_widget_get_style(w) : NULL;
	if(style == NULL)
	    return;

	font = style->font;

	/* Get parent for labels */
	type_labels_vbox = parent = pc->type_labels_vbox;
	if(parent == NULL)
	    return;

	/* Hbox for labels */
	w = gtk_hbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent = w;
	if(type_label_hbox_rtn != NULL)
	    *type_label_hbox_rtn = w;

	/* Frame for color */
	w = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Drawing area to display color */
	if(font != NULL)
	{
	    da_height = MAX(font->ascent + font->descent - 2, 2);
	    da_width = da_height;
	}
	w = gtk_drawing_area_new();
	gtk_widget_set_usize(w, da_width, da_height);
	gtk_widget_add_events(w, GDK_EXPOSURE_MASK);
	gtk_signal_connect(
	    GTK_OBJECT(w), "expose_event",
	    GTK_SIGNAL_FUNC(PieChartValueLabelExposeCB), pc
	);
	gtk_container_add(GTK_CONTAINER(parent2), w);
	gtk_widget_show(w);
	if(drawing_area_rtn != NULL)
	    *drawing_area_rtn = w;

	/* Type label */
	w = gtk_label_new(type_label);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);


	/* Value label */
	value_labels_vbox = parent = pc->value_labels_vbox;
	if((value_label != NULL) && (parent != NULL))
	{
	    w = gtk_hbox_new(FALSE, 0);
	    gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	    gtk_widget_show(w);
	    parent2 = w;
	    if(value_label_hbox_rtn != NULL)
		*value_label_hbox_rtn = w;

	    w = gtk_alignment_new(0.0f, 0.5f, 0.0f, 0.0f);
	    gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	    gtk_widget_show(w);
	    parent2 = w;

	    w = gtk_label_new(value_label);
	    gtk_container_add(GTK_CONTAINER(parent2), w);
	    gtk_widget_show(w);
	}
}

/*
 *	Checks if the given pie chart is not realized, if it is not
 *	realized then all colors and pixmap will be created and the pie
 *	chart will be marked as realized.
 */
static void PieChartDoRealize(pie_chart_struct *pc)
{
	gint i, width, height;
	GdkColor *c;
	GdkColormap *colormap;
	GdkWindow *window;
	GdkPixmap *pixmap;
	GtkWidget *w = (pc != NULL) ? pc->drawing_area : NULL;
	pie_chart_value_struct *value_ptr;
	if(w == NULL)
	    return;

	/* Pie chart already realized or drawing area not realized? */
	if(pc->realized || !GTK_WIDGET_REALIZED(w))
	    return;

	window = w->window;
	if(window == NULL)
	    return;

	gdk_window_get_size(window, &width, &height);


	/* Get colormap */
	colormap = pc->colormap;
	if(colormap == NULL)
	{
	    pc->colormap = colormap = gdk_window_get_colormap(window);
	    if(colormap != NULL)
		gdk_colormap_ref(colormap);
	}
	if(colormap == NULL)
	{
	    pc->colormap = colormap = gtk_widget_get_default_colormap();
	    if(colormap != NULL)
		gdk_colormap_ref(colormap);
	}
	if(colormap == NULL)
	    return;


	/* Create graphics context */
	if(pc->gc == NULL)
	    pc->gc = GDK_GC_NEW();


	/* Create off screen buffer pixmap for drawing area */
	pixmap = pc->pixmap;
	if(pixmap == NULL)
	    pc->pixmap = pixmap = GDK_PIXMAP_NEW(width, height);

	/* Set pie base colors */
	c = &pc->c;
	GDK_COLORMAP_ALLOC_COLOR(colormap, c)
	c = &pc->c_shade;
	GDK_COLORMAP_ALLOC_COLOR(colormap, c)
	c = &pc->c_shadow;
	GDK_COLORMAP_ALLOC_COLOR(colormap, c)
	/* Create base value label */
	PieChartCreateValueLabel(
	    pc, &pc->c, pc->base_type_label, pc->base_value_label,
	    NULL, NULL, NULL
	);

	/* Realize all values */
	for(i = 0; i < pc->total_values; i++)
	{
	    value_ptr = pc->value[i];
	    if(value_ptr == NULL)
		continue;

	    /* Allocate color of this value */
	    c = &value_ptr->c;
	    GDK_COLORMAP_ALLOC_COLOR(colormap, c)
	    c = &value_ptr->c_shade;
	    GDK_COLORMAP_ALLOC_COLOR(colormap, c)

	    /* Create label widget for this value */
	    PieChartCreateValueLabel(
		pc, &value_ptr->c, value_ptr->type_label, value_ptr->value_label,
		&value_ptr->type_label_hbox, &value_ptr->value_label_hbox,
		&value_ptr->drawing_area
	    );
	}


	/* Mark as realized */
	pc->realized = TRUE;
}


/*
 *	Draws one itteration of a complete pie disk at the given
 *	coordinates.
 */
static void PieChartDrawPieIterate(
	pie_chart_struct *pc, GtkStyle *style, GdkPixmap *pixmap,
	gint state, gint x, gint y,
	gint width, gint height,
	gboolean outline_circumference, gboolean outline_radius,
	gboolean outline_height,
	gboolean shaded, gboolean shadow
)
{
	gint i;
	gboolean outline = (outline_circumference || outline_radius || outline_height) ?
	    TRUE : FALSE;
	gfloat values_range, total_range, values_range_coeff;
	GdkGC *gc;
	GtkAdjustment *adj;
	pie_chart_value_struct *value_ptr;


	if((pc == NULL) || (style == NULL) || (pixmap == NULL) ||
	   (width < 1) || (height < 1)
	)
	    return;

	if(outline)
	    gc = style->fg_gc[state];
	else
	    gc = pc->gc;
	if(gc == NULL)
	    return;

	/* Set foreground color only if not drawing for an outline */
	if(!outline)
	{
	    if(shaded)
		gdk_gc_set_foreground(gc, &pc->c_shade);
	    else if(shadow)
		gdk_gc_set_foreground(gc, &pc->c_shadow);
	    else
		gdk_gc_set_foreground(gc, &pc->c);
	    gdk_gc_set_fill(gc, GDK_SOLID);
	}

	/* Calculate total range of all values */
	values_range = 0.0;
	for(i = 0; i < pc->total_values; i++)
	{
	    value_ptr = pc->value[i];
	    if(value_ptr == NULL)
		continue;

	    adj = value_ptr->adj;
	    values_range += (adj != NULL) ? (adj->upper - adj->lower) : 0.0;
	}

	/* Calculate absolute total range */
	adj = pc->adj;
	total_range = (adj != NULL) ? (adj->upper - adj->lower) : 0.0;

	/* Check if more of the values range is visible on the `lower'
	 * side
	 *
	 * Note that angles are in units from 0.0 to 1.0 starting at
	 * the 12 o'clock position being 0.0 going clockwise
	 */
	values_range_coeff = (total_range > 0.0) ?
	    (values_range / total_range) : 0.0;
	if(values_range_coeff < 0.5)
	{
	    gfloat value_pos_coeff = 0.5 + (values_range_coeff / 2.0);
	    gfloat value_pos_delta;


	    /* Draw base disk */
	    PieChartDrawArc(
		pixmap, gc, x, y, width, height,
		0.0, 1.0,
		outline_circumference, outline_radius,
		outline_height
	    );
	    /* Do not draw beyond this point if this disk is to be a
	     * shadow.
	     */
	    if(shadow)
		return;

	    /* Draw each value */
	    for(i = 0; i < pc->total_values; i++)
	    {
		value_ptr = pc->value[i];
		if(value_ptr == NULL)
		    continue;

		adj = value_ptr->adj;
		if((adj != NULL) && (total_range > 0.0))
		    value_pos_delta = (adj->upper - adj->lower) /
			total_range;
		else
		    value_pos_delta = 0.0;

		if(!outline)
		{
		    if(shaded)
			gdk_gc_set_foreground(gc, &value_ptr->c_shade);
		    else
			gdk_gc_set_foreground(gc, &value_ptr->c);
		    gdk_gc_set_fill(gc, GDK_SOLID);
		}

		PieChartDrawArc(
		    pixmap, gc, x, y, width, height,
		    value_pos_coeff, value_pos_coeff - value_pos_delta,
		    outline_circumference, outline_radius,
		    outline_height
		);

		value_pos_coeff -= value_pos_delta;
	    }
	}
	else
	{
	    gfloat value_pos_coeff = 1.0 - (values_range_coeff / 2.0);
	    gfloat value_pos_delta;


	    /* Draw base disk */
	    PieChartDrawArc(
		pixmap, gc, x, y, width, height,
		0.0, 1.0,
		outline_circumference, outline_radius,
		outline_height
	    );
	    /* Do not draw beyond this point if this disk is to be a
	     * shadow.
	     */
	    if(shadow)
		return;

	    /* Draw each value */
	    for(i = 0; i < pc->total_values; i++)
	    {
		value_ptr = pc->value[i];
		if(value_ptr == NULL)
		    continue;

		adj = value_ptr->adj;
		if((adj != NULL) && (total_range > 0.0))
		    value_pos_delta = (adj->upper - adj->lower) /
			total_range;
		else
		    value_pos_delta = 0.0;

		if(!outline)
		{
		    if(shaded)
			gdk_gc_set_foreground(gc, &value_ptr->c_shade);
		    else
			gdk_gc_set_foreground(gc, &value_ptr->c);
		    gdk_gc_set_fill(gc, GDK_SOLID);
		}

		PieChartDrawArc(
		    pixmap, gc, x, y, width, height,
		    value_pos_coeff, value_pos_coeff + value_pos_delta,
		    outline_circumference, outline_radius,
		    outline_height
		);

		value_pos_coeff += value_pos_delta;
	    }
	}
}

/*
 *	Redraws the off screen buffer pixmap on the given pie chart.
 *
 *	The pie chart will be realized as needed.
 */
static void PieChartRedrawPixmap(pie_chart_struct *pc)
{
	GtkStateType state;
	gint x, y, width, height, pwidth, pheight;
	GdkGC *gc;
	GdkWindow *window;
	GdkPixmap *pixmap;
	GtkStyle *style;
	GtkWidget *w;


	if(pc == NULL)
	    return;

	w = pc->drawing_area;
	if(w == NULL)
	    return;

	if(!GTK_WIDGET_REALIZED(w) || !GTK_WIDGET_VISIBLE(w))
	    return;

	window = w->window;
	style = gtk_widget_get_style(w);
	if((window == NULL) || (style == NULL))
	    return;

	if(!pc->realized)
	    PieChartDoRealize(pc);

	/* Get offscreen buffer pixmap that we will be drawing to,
	 * this pixmap should be created in PieChartDoRealize()
	 */
	pixmap = pc->pixmap;
	if(pixmap == NULL)
	    return;

	state = GTK_WIDGET_STATE(w);
	gdk_window_get_size(pixmap, &width, &height);


	/* Get graphics context */
	gc = pc->gc;
	if(gc == NULL)
	    return;


	/* Begin drawing */

	/* Draw background */
	gtk_style_apply_default_background(
	    style, pixmap, FALSE, state,
	    NULL,
	    0, 0, width, height
	);


	/* Begin drawing pie chart */

	/* Shadow */
	x = 4;
	y = PIE_CHART_PIE_HEIGHT + 2;
	pwidth = width - 6;
	pheight = height - PIE_CHART_PIE_HEIGHT - 4;
	if(pc->shadows)
	    PieChartDrawPieIterate(
		pc, style, pixmap,
		state, x, y, pwidth, pheight,
		FALSE,		/* Outline circumference */
		FALSE, FALSE,	/* Outline radius and height */
		FALSE,		/* Is shaded */
		TRUE		/* Is shadow */
	    );

	/* Base outline */
	x = 0;
	y = PIE_CHART_PIE_HEIGHT;
	if(pc->outline)
	{
	    PieChartDrawPieIterate(
		pc, style, pixmap,
		state, x, y, pwidth, pheight,
		FALSE,		/* Outline circumference */
		FALSE, FALSE,	/* Outline radius and height */
		TRUE,		/* Is shaded */
		FALSE		/* Is shadow */
	    );
	    PieChartDrawPieIterate(
		pc, style, pixmap,
		state, x, y, pwidth, pheight,
		TRUE,		/* Outline circumference */
		FALSE, FALSE,	/* Outline radius and height */
		TRUE,		/* Is shaded */
		FALSE		/* Is shadow */
	    );
	    y--;
	}

	while(y > 0)
	{
	    PieChartDrawPieIterate(
		pc, style, pixmap,
		state, x, y, pwidth, pheight,
		FALSE, 		/* Outline circumference */
		FALSE, FALSE,	/* Outline radius and height */
		TRUE,		/* Is shaded */
		FALSE		/* Is shadow */
	    );
	    y--;
	}

	y = 0;
	PieChartDrawPieIterate(
	    pc, style, pixmap,
	    state, x, y, pwidth, pheight,
	    FALSE,		/* Outline circumference */
	    FALSE, FALSE,	/* Outline radius and height */
	    FALSE,		/* Is shaded */
	    FALSE		/* Is shadow */
	);

	if(pc->outline)
	    PieChartDrawPieIterate(
		pc, style, pixmap,
		state, x, y, pwidth, pheight,
		TRUE,		/* Outline circumference */
		TRUE, TRUE,	/* Outline radius and height */
		FALSE,		/* Is shaded */
		FALSE		/* Is shadow */
	    );


}

/*
 *	Value label drawing area "expose_event" signal callback.
 */
static gint PieChartValueLabelExposeCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
)
{
	gint i, state, width, height;
	GdkColor *c_bg;
	GdkGC *gc;
	GdkWindow *window;
	GtkStyle *style;
	pie_chart_value_struct *value_ptr;
	pie_chart_struct *pc = PIE_CHART(data);
	if((widget == NULL) || (pc == NULL))
	    return(FALSE);


	/* Find value structure that contains this drawing area */
	value_ptr = NULL;
	for(i = 0; i < pc->total_values; i++)
	{
	    value_ptr = pc->value[i];
	    if(value_ptr == NULL)
		continue;

	    if(value_ptr->drawing_area == widget)
		break;
	}
	if(i >= pc->total_values)
	    value_ptr = NULL;
	if(value_ptr != NULL)
	{
	    c_bg = &value_ptr->c;
	}
	else
	{
	    c_bg = &pc->c;
	}


	state = GTK_WIDGET_STATE(widget);
	window = widget->window;
	style = gtk_widget_get_style(widget);
	if((window == NULL) || (style == NULL))
	    return(FALSE);


	gdk_window_get_size(window, &width, &height);
	
	gc = pc->gc;
	if(gc != NULL)
	{
	    gdk_gc_set_foreground(gc, c_bg);
	    gdk_gc_set_fill(gc, GDK_SOLID);
	    gdk_draw_rectangle(
		(GdkDrawable *)window, gc, TRUE,
		0, 0, width, height
	    );
	}
#if 0
	gc = style->fg_gc[state];
	if(gc != NULL)
	{
	    gdk_draw_rectangle(
		(GdkDrawable *)window, gc, FALSE,
		0, 0, width - 1, height - 1
	    );
	}
#endif

	return(TRUE);
}

/*
 *	Drawing area "expose_event" signal callback.
 */
static gint PieChartExposeCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
)
{
	gint status = FALSE;
	GtkStateType state;
	gint width, height;
	GdkWindow *window;
	GdkPixmap *pixmap;
	GdkDrawable *drawable;
	GtkStyle *style;
	pie_chart_struct *pc = PIE_CHART(data);
	if((widget == NULL) || (pc == NULL))
	    return(status);

	state = GTK_WIDGET_STATE(widget);
	window = widget->window;
	style = gtk_widget_get_style(widget);
	if((window == NULL) || (style == NULL))
	    return(status);

	/* Get pixmap for drawing area
	 *
	 * If the pixmap is NULL then it implies it has not been
	 * realized or redrawn, so realize and redraw it as needed
	 */
	pixmap = pc->pixmap;
	if(pixmap == NULL)
	{
	    PieChartRedrawPixmap(pc);
	    pixmap = pc->pixmap;
	}
	if(pixmap == NULL)
	    return(status);

	drawable = (GdkDrawable *)pixmap;
	gdk_window_get_size(drawable, &width, &height);

	/* Send drawable to window if drawable is not the window */
	if(drawable != window)
	    gdk_draw_pixmap(
		window, style->fg_gc[state], drawable,
		0, 0, 0, 0, width, height
	    );

	status = TRUE;
	return(status);
}


/*
 *	Adds a new value to the pie chart, returns an index id to that
 *	value or -1 on failure.
 */
gint PieChartValueAdd(
	pie_chart_struct *pc,
	GtkAdjustment *adj, GdkColor *c,
	const gchar *type_label, const gchar *value_label
)
{
	int i;
	GdkColormap *colormap;
	pie_chart_value_struct *value_ptr;


	if((pc == NULL) || (adj == NULL) || (c == NULL))
	    return(-1);

	/* Sanitize total */
	if(pc->total_values < 0)
	    pc->total_values = 0;

	/* Allocate more pointers */
	i = pc->total_values;
	pc->total_values = i + 1;
	pc->value = (pie_chart_value_struct **)g_realloc(
	    pc->value,
	    pc->total_values * sizeof(pie_chart_value_struct *)
	);
	if(pc->value == NULL)
	{
	    pc->total_values = 0;
	    return(-1);
	}

	/* Allocate a new value structure */
	pc->value[i] = value_ptr = (pie_chart_value_struct *)g_malloc0(
	    sizeof(pie_chart_value_struct)
	);
	if(value_ptr != NULL)
	{
	    GdkColor *tc;

	    /* Add a refcount to the given adjustment and assign it */
	    gtk_object_ref(GTK_OBJECT(adj));
	    value_ptr->adj = adj;

	    /* Set given color, allocate it if the colormap is set */
	    tc = &value_ptr->c;
	    memcpy(tc, c, sizeof(GdkColor));

	    tc = &value_ptr->c_shade;
	    tc->red = c->red / 2;
	    tc->green = c->green / 2;
	    tc->blue = c->blue / 2;

	    colormap = pc->colormap;
	    if(colormap != NULL)
	    {
		GDK_COLORMAP_ALLOC_COLOR(
		    colormap, &value_ptr->c
		)
		GDK_COLORMAP_ALLOC_COLOR(
		    colormap, &value_ptr->c_shade
		)
	    }

	    value_ptr->type_label = STRDUP(type_label);
	    value_ptr->value_label = STRDUP(value_label);

	    return(i);
	}
	else
	{
	    return(-1);
	}
}

/*
 *	Sets the value specified by index value_num on the given pie 
 *	chart.
 */
void PieChartValueSet(
	pie_chart_struct *pc, gint value_num,
	GtkAdjustment *adj, GdkColor *c,
	const gchar *type_label, const gchar *value_label
)
{
	GdkColormap *colormap;
	pie_chart_value_struct *value_ptr;


	if((pc == NULL) || (adj == NULL) || (c == NULL))
	    return;

	if((value_num < 0) || (value_num >= pc->total_values))
	    return;

	value_ptr = pc->value[value_num];
	if(value_ptr == NULL)
	    return;

	/* Add refcount to given adjustment, remove refcount from existing
	 * adjustment and assign given adjustment.
	 */
	gtk_object_ref(GTK_OBJECT(adj));
	if(value_ptr->adj != NULL)
	    gtk_object_unref(GTK_OBJECT(value_ptr->adj));
	value_ptr->adj = adj;

	/* Delete old color if colormap is available, set new color
	 * and allocate it if colormap is available
	 */
	colormap = pc->colormap;
	if(colormap != NULL)
	{
	    GdkColor *tc;

	    tc = &value_ptr->c;
	    GDK_COLORMAP_FREE_COLOR(colormap, tc)
	    memcpy(tc, c, sizeof(GdkColor));
	    GDK_COLORMAP_ALLOC_COLOR(colormap, tc)

	    tc = &value_ptr->c_shade;
	    GDK_COLORMAP_FREE_COLOR(colormap, tc)
	    tc->red = c->red / 2;
	    tc->green = c->green / 2;
	    tc->blue = c->blue / 2;
	    GDK_COLORMAP_ALLOC_COLOR(colormap, tc)
	}
	else
	{
	    GdkColor *tc;

	    tc = &value_ptr->c;
	    memcpy(tc, c, sizeof(GdkColor));

	    tc = &value_ptr->c_shade;
	    tc->red = c->red / 2;
	    tc->green = c->green / 2;
	    tc->blue = c->blue / 2;
	}


	/* Set new label text */
	g_free(value_ptr->type_label);
	value_ptr->type_label = STRDUP(type_label);

	g_free(value_ptr->value_label);
	value_ptr->value_label = STRDUP(value_label);

/* Need to recreate pixmap */
}

/*
 *	Removes the value specified by index value_num on the given pie
 *	chart.
 */
void PieChartValueRemove(
	pie_chart_struct *pc, gint value_num
)
{
	pie_chart_value_struct *value_ptr;
	GdkColormap *colormap;


	if(pc == NULL)
	    return;

	if((value_num < 0) || (value_num >= pc->total_values))
	    return;

	value_ptr = pc->value[value_num];
	if(value_ptr == NULL)
	    return;


	/* Unref adjustment */
	if(value_ptr->adj != NULL)
	{
	    gtk_object_unref(GTK_OBJECT(value_ptr->adj));
	    value_ptr->adj = NULL;
	}

	/* Free color */
	colormap = pc->colormap;
	if(colormap != NULL)
	{
	    GDK_COLORMAP_FREE_COLOR(colormap, &value_ptr->c)
	    memset(&value_ptr->c, 0x00, sizeof(GdkColor));

	    GDK_COLORMAP_FREE_COLOR(colormap, &value_ptr->c_shade)
	    memset(&value_ptr->c_shade, 0x00, sizeof(GdkColor));
	}

	/* Label hbox */
	if(value_ptr->type_label_hbox != NULL)
	{
	    gtk_widget_destroy(value_ptr->type_label_hbox);
	    value_ptr->type_label_hbox = NULL;
	}
	if(value_ptr->value_label_hbox != NULL)
	{
	    gtk_widget_destroy(value_ptr->value_label_hbox);
	    value_ptr->value_label_hbox = NULL;
	}
	value_ptr->drawing_area = NULL;

	/* Labels */
	g_free(value_ptr->type_label);
	value_ptr->type_label = NULL;
	g_free(value_ptr->value_label);
	value_ptr->value_label = NULL;


	/* Deallocate structure itself and mark it as having been 
	 * deallocated.
	 */
	g_free(value_ptr);
	pc->value[value_num] = value_ptr = NULL;
}

/*
 *	Removes all values on the given pie chart.
 */
void PieChartValueRemoveAll(pie_chart_struct *pc)
{
	gint i;


	if(pc == NULL)
	    return;

	for(i = 0; i < pc->total_values; i++)
	    PieChartValueRemove(pc, i);

	g_free(pc->value);
	pc->value = NULL;
	pc->total_values = 0;
}



/*
 *	Creates a new pie chart widget.
 */
pie_chart_struct *PieChartNew(
	GtkAdjustment *adj, GdkColor *c, gint width, gint height,
	const gchar *title, const gchar *footer,
	const gchar *base_type_label, const gchar *base_value_label
)
{
	const gint	border_major = 5,
			border_minor = 2;
	GtkWidget *w, *parent, *parent2;
	pie_chart_struct *pc = PIE_CHART(g_malloc0(
	    sizeof(pie_chart_struct)
	));
	if(pc == NULL)
	    return(pc);

	/* Reset values */
	pc->initialized = TRUE;
	pc->map_state = FALSE;
	pc->width = width;
	pc->height = height;
	pc->pixmap = NULL;
	if(c != NULL)
	{
	    GdkColor *tc;
	    tc = &pc->c;
	    memcpy(tc, c, sizeof(GdkColor));
	    tc = &pc->c_shade;
	    tc->red	= c->red	/ 2;
	    tc->green	= c->green	/ 2;
	    tc->blue	= c->blue	/ 2;
	    tc = &pc->c_shadow;
	    tc->red	= 0.5f * (gushort)-1;
	    tc->green	= 0.5f * (gushort)-1;
	    tc->blue	= 0.5f * (gushort)-1;
	}
	pc->base_type_label = STRDUP(base_type_label);
	pc->base_value_label = STRDUP(base_value_label);
	pc->gc = NULL;
	pc->colormap = NULL;
	pc->realized = FALSE;
	pc->value = NULL;
	pc->total_values = 0;

	pc->show_labels = TRUE;
	pc->shadows = TRUE;
	pc->outline = TRUE;


	/* Transfer given adjustment over and add a refcount for 
	 * ourselves
	 */
	pc->adj = adj;
	if(adj != NULL)
	    gtk_object_ref(GTK_OBJECT(adj));


	/* Create toplevel vbox */
	pc->toplevel = w = gtk_vbox_new(FALSE, border_minor);
	gtk_widget_show(w);
	parent = w;

	/* Create title label if title string is given */
	if(title != NULL)
	{
	    pc->title_label = w = gtk_label_new(title);
	    gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	    gtk_widget_show(w);
	}


	/* Create hbox for columnizing type and value labels */
	w = gtk_hbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Create vbox for type labels */
	pc->type_labels_vbox = w = gtk_vbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	/* Hbox to center right column vbox */
	w = gtk_hbox_new(TRUE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent2 = w;
	/* Create vbox for value labels */
	pc->value_labels_vbox = w = gtk_vbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, FALSE, 0);
	gtk_widget_show(w);


	/* Create hbox to center pie chart drawing area */
	w = gtk_hbox_new(TRUE, 0);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Drawing area */
	pc->drawing_area = w = gtk_drawing_area_new();
	gtk_widget_set_usize(w, width, height);
	gtk_widget_add_events(w, GDK_EXPOSURE_MASK);
	gtk_signal_connect(
	    GTK_OBJECT(w), "expose_event",
	    GTK_SIGNAL_FUNC(PieChartExposeCB), pc
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, FALSE, 0);
	gtk_widget_show(w);

	/* Create footer label if title string is given */
	if(footer != NULL)
	{
	    pc->footer_label = w = gtk_label_new(footer);
	    gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	    gtk_widget_show(w);
	}



	return(pc);
}

/*
 *	Maps the pie chart.
 */
void PieChartMap(pie_chart_struct *pc)
{
	GtkWidget *w;


	if(pc == NULL)
	    return;

	w = pc->toplevel;
	if(w == NULL)
	    return;

	if(!pc->map_state)
	{
	    gtk_widget_show(w);
	    pc->map_state = TRUE;
	}
}

/*
 *	Unmaps the pie chart.
 */
void PieChartUnmap(pie_chart_struct *pc)
{
	GtkWidget *w;


	if(pc == NULL)
	    return;

	w = pc->toplevel;
	if(w == NULL)
	    return;

	if(pc->map_state)
	{
	    gtk_widget_hide(w);
	    pc->map_state = FALSE;
	}
}

/*
 *	Deallocates the given pie chart and all its resources.
 */
void PieChartDelete(pie_chart_struct *pc)
{
	GdkColormap *colormap;

	if(pc == NULL)
	    return;

	colormap = pc->colormap;

	/* Remove all value structures from the pie chart */
	PieChartValueRemoveAll(pc);

	/* Delete colors */
	GDK_COLORMAP_FREE_COLOR(colormap, &pc->c)
	GDK_COLORMAP_FREE_COLOR(colormap, &pc->c_shade)
	GDK_COLORMAP_FREE_COLOR(colormap, &pc->c_shadow)

	GTK_OBJECT_UNREF(pc->adj)

	GDK_PIXMAP_UNREF(pc->pixmap)

	/* Begin destroying widgets */
	GTK_WIDGET_DESTROY(pc->footer_label)
	GTK_WIDGET_DESTROY(pc->title_label)
	GTK_WIDGET_DESTROY(pc->type_labels_vbox)
	GTK_WIDGET_DESTROY(pc->value_labels_vbox)
	GTK_WIDGET_DESTROY(pc->drawing_area)
	GTK_WIDGET_DESTROY(pc->toplevel)

	GDK_GC_UNREF(pc->gc)

	GDK_COLORMAP_UNREF(pc->colormap)

	pc->realized = FALSE;

	g_free(pc->base_type_label);
	g_free(pc->base_value_label);

	g_free(pc);
}
