/*****
* gtk.c : XmHTML/gtk ToolkitAbstraction
*
* This file Version	$Revision$
*
* Creation date:		Thu Oct 16 09:54:29 CEST 1998
* Last modification: 	$Date$
* By:					$Author$
* Current State:		$State$
*
* Author:				newt
*
* Copyright (C) 1994-1998 by Ripley Software Development 
* All Rights Reserved
*
* This file is part of the XmHTML Widget Library
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*****/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>

/* Our private header files */
#include "toolkit.h"
#include XmHTMLPrivateHeader

/* and we need access to some of gdk's private vars as well (see below) */
#include <gdk/gdkprivate.h>
#include <gdk/gdkx.h>

/*** External Function Prototype Declarations ***/

/*** Public Variable Declarations ***/

/*** Private Datatype Declarations ****/

/*** Private Function Prototype Declarations ****/

/*** Private Variable Declarations ***/

static GdkFont *curr_font = NULL;

/*****************************************************************************
* Start of wrapper functions
*
* Note:
* Wherever possible I try to use the real Xlib functions instead of
* using the gtk/gdk equivalent functions by accessing private members of
* gdk/gtk datastructures.
*
*****************************************************************************/

/**********
***** Gtk GC wrapper functions
**********/

static GdkGC*
gdk_gc_new_wrapper(struct _ToolkitAbstraction* tka, GdkWindow *drawable,
	GdkGCValuesMask valuemask, GdkGCValues *values)
{
	return(gdk_gc_new_with_values(drawable, values, valuemask));
}

static void
gdk_gc_destroy_wrapper(void *display, GdkGC *gc)
{
	gdk_gc_destroy(gc);
}

static void
gdk_gc_copy_wrapper(void *display, GdkGC *src, gint valuemask, GdkGC *dest)
{
	/*****
	* Why doesn't gdk allow you to specify which fields of the gc to
	* copy?
	*****/
	gdk_gc_copy(dest, src);
}

static void
gdk_gc_set_function_wrapper(void *display, GdkGC *gc, GdkFunction function)
{
	gdk_gc_set_function(gc, function);
}


static void
gdk_gc_set_clip_origin_and_mask_wrapper(
	struct _ToolkitAbstraction* tka, GdkGC *gc, gint clip_x_origin, gint clip_y_origin, GdkBitmap *pixmap
) {
	/* set clip origin */
#ifdef NO_GDK_ILLEGAL_ACCESS
	gdk_gc_set_clip_origin(gc, clip_x_origin, clip_y_origin);
#else
	GdkGCPrivate *pgc = (GdkGCPrivate*)gc;

	XSetClipOrigin(pgc->xdisplay, pgc->xgc, clip_x_origin, clip_y_origin);
#endif

	/* set clip mask */
	gdk_gc_set_clip_mask(gc, pixmap);		
}

static void
gdk_gc_set_tile_wrapper(void *display, GdkGC *gc, GdkPixmap *tile)
{
	gdk_gc_set_tile(gc, tile);
}

static void
gdk_gc_set_ts_origin_wrapper(void *display, GdkGC *gc,
	gint ts_x_origin, gint ts_y_origin)
{
#ifdef NO_GDK_ILLEGAL_ACCESS
	gdk_gc_set_ts_origin(gc, ts_x_origin, ts_y_origin);
#else
	GdkGCPrivate *pgc = (GdkGCPrivate*)gc;

	XSetTSOrigin(pgc->xdisplay, pgc->xgc, ts_x_origin, ts_y_origin);
#endif
}

static void
gdk_gc_set_fill_wrapper(void *display, GdkGC *gc, GdkFill fill_style)
{
	gdk_gc_set_fill(gc, fill_style);
}

static void
gdk_gc_set_font_wrapper(void *display, GdkGC *gc, XmHTMLFont *font)
{
	curr_font = font;
	gdk_gc_set_font(gc, font->xfont);
}

static void
gdk_gc_set_foreground_wrapper(void *display, GdkGC *gc, gulong foreground)
{
#ifdef NO_GDK_ILLEGAL_ACCESS
	GdkColor m;
	m.pixel = background;
	gdk_gc_set_foreground(gc, &m);
#else
	GdkGCPrivate *pgc = (GdkGCPrivate*)gc;

	XSetForeground(pgc->xdisplay, pgc->xgc, foreground);
#endif
}

static void
gdk_gc_set_background_wrapper(void *display, GdkGC *gc,
	gulong background)
{
#ifdef NO_GDK_ILLEGAL_ACCESS
	GdkColor m;
	m.pixel = background;
	gdk_gc_set_background(gc, &m);
#else
	GdkGCPrivate *pgc = (GdkGCPrivate*)gc;

	XSetBackground(pgc->xdisplay, pgc->xgc, foreground);
#endif
}

static void
gdk_gc_set_line_attributes_wrapper(void *display, GdkGC *gc,
	guint line_width, gint line_style, gint cap_style, gint join_style)
{
	/*****
	* gdk has enums for all of these. Yuck.
	*****/
	gdk_gc_set_line_attributes(gc, (gint)line_width, (GdkLineStyle)line_style,
		(GdkCapStyle)cap_style, (GdkJoinStyle)join_style);
}

/**********
***** Gtk Font Functions
**********/
static GdkFont*
gdk_font_load_wrapper(void *display, gchar *name)
{
	return(gdk_font_load(name));
}

static void
gdk_font_free_wrapper(void *display, GdkFont *font)
{
	gdk_font_unref(font);
}

static void
gdk_get_font_property_wrapper(GdkFont *font_struct, GdkAtom atom,
	gulong *value_return)
{
	/* gdk/gtk doesn't support font properties, so do it the hard way. */
	XGetFontProperty(GDK_FONT_XFONT(font_struct), atom, value_return);
}

/**********
***** Cursor & Pointer functions
**********/
static void
gdk_pointer_ungrab_wrapper(void *display, guint32 time)
{
	gdk_pointer_ungrab(time);
}

static void
gdk_window_set_cursor_wrapper(void *display, *GdkWindow win, GdkCursor *cursor)
{
	gdk_window_set_cursor(win, cursor);
}

/*****
* gdk doesn't have an UndefineCursor equivalent so we have to implement
* our own.
*
* Note: gdk_window_set_cursor allows one to provide a NULL cursor
* argument but then it passes the None cursor to XDefineCursor. I don't
* know and doubt that that is similar to calling XUndefineCursor.
*****/
static void
gdk_window_unset_cursor_wrapper(void *display, GdkWindow win)
{
#ifdef NO_GDK_ILLEGAL_ACCESS
	gdk_window_set_cursor(win, NULL);
#else
	GdkWindowPrivate *wp;

	wp = (GdkWindowPrivate*)win;

	if(!wp->destroyed)
		XUndefineCursor(wp->xdisplay, wp->window);
#endif
}

static void
gdk_cursor_destroy_wrapper(void *display, GdkCursor *cursor)
{
	gdk_cursor_destroy(cursor);
}

/**********
***** Color functions
**********/
static gint
gdk_color_parse_wrapper(void *display, GdkColormap *colormap,
	const gchar *spec, GdkColor *exact_def_return)
{
	return(gdk_color_parse(spec, exact_def_return));
}

static gint
gdk_color_alloc_wrapper(void *display, GdkColormap *colormap,
	GdkColor *screen_in_out)
{
	return(gdk_color_alloc(colormap, screen_in_out));
}

/*****
* Silly gdk/gtk doesn't have this!
*****/
static void
gdk_query_colors_wrapper(void *display, GdkColormap *colormap,
	GdkColor *defs_in_out, gint ncolors)
{
	XColor *xcolors;
	gint    i;

	xcolors = g_new(XColor, ncolors);
	for (i = 0; i < ncolors; i++)
		xcolors[i].pixel = GETP(defs_in_out[i]);

	XQueryColors(GDK_DISPLAY(), GDK_COLORMAP_XCOLORMAP(colormap), xcolors,
		ncolors);

	for (i = 0; i < ncolors; i++) {
		GETR(defs_in_out[i]) = xcolors[i].red;
		GETG(defs_in_out[i]) = xcolors[i].green;
		GETB(defs_in_out[i]) = xcolors[i].blue;
	}
	g_free(xcolors);
}

static void
gdk_query_color_wrapper(void *display, GdkColormap *colormap,
	GdkColor *def_in_out)
{
	gdk_query_colors_wrapper(dpy, colormap, def_in_out, 1);
}

static void
gdk_colors_free_wrapper(void *display, GdkColormap *colormap,
	gulong *pixels, gint npixels, gulong nplanes)
{
	gdk_colors_free(colormap, pixels, npixels, nplanes);
}

/**********
***** Pixmap functions
**********/

/*****
* Under Xlib, pixmaps can be created on any drawable.
* Under gdk/gtk, this doesn't seem possible as it requires a GdkWindow when
* creating pixmaps. But it does know a GdkDrawable, so I wonder why this
* can't be used for creating pixmaps. I consider this a limitation.
*****/

static GdkPixmap
gdk_pixmap_new_wrapper(void *display, GdkWindow *d, gint width, gint height,
	gint depth)
{
	return(gdk_pixmap_new(d, width, height, depth));
}

static GdkPixmap*
gdk_pixmap_create_from_data_wrapper(void *display, GdkWindow *d,
	guchar *data, guint width, guint height, gulong fg, gulong bg,
	guint depth)
{
	/*****
	* Gdk insists on working with GdkColor instead of plain pixels.
	*****/
	GdkColor fg_color, bg_color;
	fg_color.pixel = fg;
	bg_color.pixel = bg;

	return(gdk_pixmap_create_from_data(d, data, width, height, depth,
		&fg_color, &bg_color));
}

static void
gdk_pixmap_unref_wrapper(void *display, GdkPixmap *pixmap)
{
	gdk_pixmap_unref(pixmap);
}

/**********
***** Ximage functions
*****
***** No wrappers for:
***** DestroyImage: the type and number of arguments correspond with Xlib.
**********/

static GdkImage*
gdk_image_new_wrapper(void *display, GdkVisual *visual, guint depth,
	gint format, gint offset, gchar *data, guint width, guint height,
	gint bitmap_pad, gint bytes_per_line)
{
	/*****
	* I wonder why gdk only allows you to create images with a depth
	* equal to the current visual. Now I have to differentiate when
	* I want a bitmap.
	*****/
	if(depth == 1)
		return(gdk_image_new_bitmap(visual, width, height));
	else
		return(gdk_image_new(GDK_IMAGE_FASTEST, visual, width, height));
}

static void
gdk_draw_image_wrapper(void *display, GdkDrawable *d, GdkGC *gc,
	GdkImage *image, gint src_x, gint src_y, gint dest_x, gint dest_y,
	guint width, guint height)
{
	/* why can width and height be negative? */
	gdk_draw_image(d, gc, image, src_x, src_y, dest_x, dest_y,
		(gint)width, (gint)height);
}

/**********
***** String/Text functions
*****
***** No wrappers for:
***** TextWidth
**********/
static gint
gdk_text_extents_wrapper(GdkFont *font_struct, const gchar *string,
	gint nchars, gint *direction_return, gint *font_ascent_return,
	gint *font_descent_return, XCharStruct *overall_return)
{
	return(XTextExtents(GDK_FONT_XFONT(font_struct), string, nchars,
		direction_return, font_ascent_return, overall_return));
}

/**********
***** Render functions
**********/

static void
gdk_draw_text_wrapper(void *display, GdkWindow *d, XmHTMLFont *font,
	GdkGC *gc, gint x, gint y, char *string, gint length)
{
	gdk_draw_text(d, (GdkFont*)font->xfont, gc, x, y, string, length);
}

static void
gdk_draw_line_wrapper(void *display, GdkWindow *d, GdkGC *gc, int x1,
	int y1, int x2, int y2)
{
	gdk_draw_line(d, gc, x1, y1, x2, y2);
}

static void
gdk_draw_lines_wrapper(void *display, GdkWindow *d, GdkGC *gc,
	GdkPoint *points, gint npoints, gint mode)
{
	gdk_draw_polygon(d, gc, FALSE, points, npoints);
}

static void
gdk_draw_rectangle_wrapper(void *display, GdkWindow *d, GdkGC *gc,
	gint x, gint y, gint width, gint height)
{
	gdk_draw_rectangle(d, gc, FALSE, x, y, (gint)width, (gint)height);
}

static void
gdk_fill_rectangle_wrapper(void *display, GdkWindow *d, GdkGC *gc,
	gint x, gint y, guint width, guint height)
{
	gdk_draw_rectangle(d, gc, TRUE, x, y, (gint)width, (gint)height);
}

static void
gdk_draw_arc_wrapper(void *display, GdkWindow *d, GdkGC *gc, gint x,
	gint y, guint width, guint height, gint angle1, gint angle2)
{
	gdk_draw_arc(d, gc, FALSE, x, y, (gint)width, (gint)height, angle1, angle2);
}

static void
gdk_fill_arc_wrapper(void *display, GdkWindow *d, GdkGC *gc, gint x,
	gint y, guint width, guint height, gint angle1, gint angle2)
{
	gdk_draw_arc(d, gc, TRUE, x, y, (gint)width, (gint)height, angle1, angle2);
}

/**********
***** Misc. Xlib functions
**********/
static void
gdk_window_copy_area_wrapper(void *display, GdkWindow *src, GdkWindow *dest,
	GdkGC *gc, gint src_x, gint src_y, guint width, guint height,
	gint dest_x, gint dest_y)
{
	/*****
	* Why did they mixed up the order of arguments? They need to rearrange
	* them again internally. Inefficient.
	*****/
	gdk_window_copy_area(dest, gc, dest_x, dest_y, src, src_x, src_y,
		(gint)width, (gint)height);
}

static void
gdk_clear_area_wrapper(void *display, GdkWindow *w, gint x, gint y,
	guint width, guint height, gboolean exposures)
{
	/*****
	* Why did they drop the exposures argument? Now I need to do this
	* myself if I want to keep it as a efficient as possible.
	*
	* Doing this the hard way saves at least three conditionals and two
	* function calls. I'm beginning to wonder if all of gdk's Xlib
	* wrappers are this inefficient...
	*****/
#ifdef NO_GDK_ILLEGAL_ACCESS
	if(exposures)
		gdk_window_clear_area_e(w, x, y, width, height);
	else
		gdk_window_clear_area(w, x, y, width, height);
#else
	GdkWindowPrivate *private;

	private = (GdkWindowPrivate*)w;

	if(!private->destroyed)
		XClearArea(private->xdisplay, private->xwindow, x, y, width, height,
			exposures);
#endif
}

static void
gdk_flush_wrapper(void *display, Bool discard)
{
	/*****
	* This is beginning to look like a dummy's toolkit to me.
	* Here's what gdk_flush does:
	*
	* XSync(gdk_display, False);
	*
	* How am I supposed to discard outstanding events???
	*****/
#ifdef NO_GDK_ILLEGAL_ACCESS
	gdk_flush();
#else
	XSync(GDK_DISPLAY(), discard);
#endif
}

/**********
***** X Library Intrinsics function wrappers
*****
***** No wrappers for:
***** ManageChild, UnmanageChild, MoveWidget, ResizeWidget, DestroyWidget,
***** RemoveTimeOut.
**********/

static Boolean
gtk_widget_is_realized_wrapper(GdkWidget *w)
{
	return(GTK_WIDGET_REALIZED (GTK_WIDGET (w)));
}


static Boolean
gtk_widget_is_managed_wrapper(GdkWidget *w)
{
	return(GTK_WIDGET_MAPPED (GTK_WIDGET (w)));
}

static void
gtk_widget_configure_wrapper(GdkWidget *w, gint x, gint h, gint width,
	gint height, gint border_width)
{
	/* gdk/gtk doesn't care about borderwidth, unless we are a container */
	gdk_window_move_resize(w, x, h, width, height);
}

static int
gtk_widget_mapped_when_managed_wrapper(GtkWidget *w, Boolean map_state)
{
	if(map_state)
	{
		if(GTK_WIDGET_MAPPED (w))
			return(0);
		gtk_widget_show(w);
	}
	else
	{
		if(!GTK_WIDGET_MAPPED (w))
			return(0);
		gtk_widget_hide(w);
	}
	return(1);
}

static guint
gtk_timeout_add_wrapper(void *context, guint32 interval, GtkFunction function,
	gpointer data)
{
	return(gtk_timeout_add(interval, function, data));
}

/**********
***** Motif functions
**********/
static void
gtk_draw_shadows_wrapper(void *display, GdkWindow *drawable,
	GdkGC *top_shadow_gc, GdkGC *bottom_shadow_gc,
	gshort x, gshort y, gushort width, gushort height,
	gshort shadow_tick, Byte shadow_type)
{
	GdkWindowPrivate *wp = (GdkWindowPrivate*)drawable;
	GdkGCPrivate *ptsgc  = (GdkGCPrivate*)top_shadow_gc;
	GdkGCPrivate *pbsgc  = (GdkGCPrivate*)bottom_shadow_gc;

	switch(shadow_type)
	{
		case XmSHADOW_IN:
			/* top & left border */
			gdk_draw_rectangle(d, bottom_shadow_gc, TRUE, x, y, width, 1);
			gdk_draw_rectangle(d, bottom_shadow_gc, TRUE, x, y, 1, height - 1);

			/* bottom & right border */
			gdk_draw_rectangle(d, top_shadow_gc, TRUE, x + 1, y + height - 1,
				width - 1, 1);
			gdk_draw_rectangle(d, top_shadow_gc, TRUE, x - 1, y + 1, 1,
				height - 2);
			break;
		case XmSHADOW_OUT:
			/* top & left border */
			gdk_draw_rectangle(d, top_shadow_gc, TRUE, x, y, width, 1);
			gdk_draw_rectangle(d, top_shadow_gc, TRUE, x, y, 1, height - 1);

			/* bottom & right border */
			gdk_draw_rectangle(d, bottom_shadow_gc, TRUE, x + 1,
				y + height - 1, width - 1, 1);
			gdk_draw_rectangle(d, bottom_shadow_gc, TRUE, x - 1,
				y + 1, 1, height - 2);
			break;
		default:
			break;
	}
}

/*****************************************************************************
* End of wrapper functions
*****************************************************************************/

static ToolkitAbstraction*
_CreateGtkToolkitAbstraction(void)
{
	static ToolkitAbstraction *tka;

	tka = (ToolkitAbstraction*)malloc(sizeof(ToolkitAbstraction));

	/* GC properties */
	tka->fill_style[GC_FILL_SOLID]           = GDK_SOLID;
	tka->fill_style[GC_FILL_TILED]           = GDK_TILED;
	tka->fill_style[GC_FILL_STIPPLED]        = GDK_STIPPLED;
	tka->fill_style[GC_FILL_OPAQUE_STIPPLED] = GDK_OPAQUE_STIPPLED;

	tka->cap_style[GC_CAP_NOT_LAST]   = GDK_CAP_NOT_LAST;
	tka->cap_style[GC_CAP_BUTT]       = GDK_CAP_BUTT;
	tka->cap_style[GC_CAP_ROUND]      = GDK_CAP_ROUND;
	tka->cap_style[GC_CAP_PROJECTING] = GDK_CAP_PROJECTING;

	tka->line_style[GC_LINE_SOLID]       = GDK_LINE_SOLID;
	tka->line_style[GC_LINE_ON_OFF_DASH] = GDK_LINE_ON_OFF_DASH;
	tka->line_style[GC_LINE_DOUBLE_DASH] = GDK_LINE_DOUBLE_DASH;

	tka->join_style[GC_JOIN_MITER] = GDK_JOIN_MITER;
	tka->join_style[GC_JOIN_ROUND] = GDK_JOIN_ROUND;
	tka->join_style[GC_JOIN_BEVEL] = GDK_JOIN_BEVEL;

	/* GC functions */
	tka->gc_func[GC_GXcopy] = GDK_COPY;

	/* GdkGC functions */
	tka->CreateGC      = gdk_gc_new_wrapper;
	tka->FreeGC        = gdk_gc_destroy_wrapper;
	tka->CopyGC        = gdk_gc_copy_wrapper;
	tka->SetFunction   = gdk_gc_set_function_wrapper;
	tka->SetClipOriginAndMask   = gdk_gc_set_clip_origin_and_mask_wrapper;
	tka->SetTile       = gdk_gc_set_tile_wrapper;
	tka->SetTSOrigin   = gdk_gc_set_ts_origin_wrapper;
	tka->SetFillStyle  = gdk_gc_set_fill_wrapper;
	tka->SetFont       = gdk_gc_set_font_wrapper;
	tka->SetForeground = gdk_gc_set_foreground_wrapper;
	tka->SetBackground = gdk_gc_set_background_wrapper;
	tka->SetLineAttributes = gdk_gc_set_line_attributes_wrapper;

	/* Font Allocation functions */
	tka->LoadQueryFont = gdk_font_load_wrapper;
	tka->FreeFont      = gdk_font_free_wrapper;
	tka->GetFontProperty = gdk_get_font_property_wrapper;

	/* Cursor & pointer functions */
	tka->UngrabPointer = gdk_pointer_ungrab_wrapper;
	tka->DefineCursor  = gdk_window_set_cursor_wrapper;
	tka->UndefineCursor= gdk_window_unset_cursor_wrapper;
	tka->FreeCursor    = gdk_cursor_destroy_wrapper;

	/* Color functions */
	tka->ParseColor    = gdk_color_parse_wrapper;
	tka->AllocColor    = gdk_color_alloc_wrapper;
	tka->QueryColor    = gdk_query_color_wrapper;
	tka->QueryColors   = gdk_query_colors_wrapper;
	tka->FreeColors    = gdk_colors_free_wrapper;

	/* Pixmap functions */
	tka->CreatePixmap  = gdk_pixmap_new_wrapper;
	tka->FreePixmap    = gdk_pixmap_unref_wrapper;
	tka->CreatePixmapFromBitmapData = gdk_pixmap_create_from_data_wrapper;

	/* XImage functions */
	tka->CreateImage   = gdk_image_new_wrapper;
	tka->DestroyImage  = gdk_image_destroy;
	tka->PutImage      = gdk_draw_image_wrapper;

	/* string/text functions */
	tka->TextWidth     = gdk_text_width;
	tka->TextExtents   = gdk_text_extents_wrapper;

	/* Render functions */
	tka->DrawString    = gdk_draw_text_wrapper;
	tka->DrawLine      = gdk_draw_line_wrapper;
	tka->DrawLines     = gdk_draw_lines_wrapper;
	tka->DrawRectangle = gdk_draw_rectangle_wrapper;
	tka->FillRectangle = gdk_fill_rectangle_wrapper;
	tka->DrawArc       = gdk_draw_arc_wrapper;
	tka->FillArc       = gdk_fill_arc_wrapper;

	/* misc. functions */
	tka->CopyArea      = gdk_window_copy_area_wrapper;
	tka->ClearArea     = gdk_clear_area_wrapper;
	tka->Sync          = gdk_flush_wrapper;

	/* X Intrinsic wrappers */
	tka->IsRealized      = gtk_widget_is_realized_wrapper;
	tka->IsManaged       = gtk_widget_is_managed_wrapper;
	tka->ManageChild     = gtk_widget_show;
	tka->UnmanageChild   = gtk_widget_hide;
	tka->MoveWidget      = gtk_widget_move_wrapper;
	tka->ResizeWidget    = gtk_widget_resize_wrapper;
	tka->ConfigureWidget = gtk_widget_configure_wrapper;
	tka->DestroyWidget   = gtk_widget_destroy_wrapper;
	tka->SetMappedWhenManaged = gtk_widget_mapped_when_managed_wrapper;
	tka->RemoveTimeOut   = gtk_timeout_remove;
	tka->AddTimeOut      = gtk_timeout_add_wrapper;

	/* Motif wrappers */
	tka->DrawShadows     = gtk_draw_shadows_wrapper;
	return(tka);
}

/*****
* Name:
* Return Type:
* Description:
* In:
*
* Returns:
*
*****/
ToolkitAbstraction*
XmHTMLTkaCreate(void)
{
	return(_CreateGtkToolkitAbstraction(void));
}

/*****
* Name: 
* Return Type: 
* Description: 
* In: 
*
* Returns:
*
*****/
void
XmHTMLTkaSetDrawable(ToolkitAbstraction *tka, GdkDrawable *d)
{
	tka->win = d;
}

/*****
* Name: 
* Return Type: 
* Description: 
* In: 
*
* Returns:
*
*****/
void
XmHTMLTkaSetDisplay(ToolkitAbstraction *tka, void *display)
{
	int screen = DefaultScreen(GDK_DISPLAY());

	tka->dpy = (void*)GDK_DISPLAY();

	/* set display dimensions (we use it to determine the dpi of the screen)*/
	tka->width    = DisplayWidth(GDK_DISPLAY(), screen);
	tka->height   =	DisplayHeight(GDK_DISPLAY(), screen);
	tka->widthMM  = DisplayWidthMM(GDK_DISPLAY(), screen);
	tka->heightMM = DisplayheightMM(GDK_DISPLAY(), screen);

	/* fallback window */
	tka->defaultRoot = DefaultRootWindow(GDK_DISPLAY());
}

/*****
* Color computation
*****/

/*****
* This color shading method is taken from gtkstyle.c.  Some
* modifications made by me - Federico
*****/

static void
rgb_to_hls (gdouble *r, gdouble *g, gdouble *b)
{
	gdouble min;
	gdouble max;
	gdouble red;
	gdouble green;
	gdouble blue;
	gdouble h, l, s;
	gdouble delta;

	red = *r;
	green = *g;
	blue = *b;

	if (red > green) {
		if (red > blue)
			max = red;
		else
			max = blue;

		if (green < blue)
			min = green;
		else
			min = blue;
	} else {
		if (green > blue)
			max = green;
		else
			max = blue;

		if (red < blue)
			min = red;
		else
			min = blue;
	}

	l = (max + min) / 2;
	s = 0;
	h = 0;

	if (max != min) {
		if (l <= 0.5)
			s = (max - min) / (max + min);
		else
			s = (max - min) / (2 - max - min);

		delta = max -min;
		if (red == max)
			h = (green - blue) / delta;
		else if (green == max)
			h = 2 + (blue - red) / delta;
		else if (blue == max)
			h = 4 + (red - green) / delta;

		h *= 60;
		if (h < 0.0)
			h += 360;
	}

	*r = h;
	*g = l;
	*b = s;
}

static void
hls_to_rgb (gdouble *h, gdouble *l, gdouble *s)
{
	gdouble hue;
	gdouble lightness;
	gdouble saturation;
	gdouble m1, m2;
	gdouble r, g, b;

	lightness = *l;
	saturation = *s;

	if (lightness <= 0.5)
		m2 = lightness * (1 + saturation);
	else
		m2 = lightness + saturation - lightness * saturation;
	m1 = 2 * lightness - m2;

	if (saturation == 0) {
		*h = lightness;
		*l = lightness;
		*s = lightness;
	} else {
		hue = *h + 120;
		while (hue > 360)
			hue -= 360;
		while (hue < 0)
			hue += 360;

		if (hue < 60)
			r = m1 + (m2 - m1) * hue / 60;
		else if (hue < 180)
			r = m2;
		else if (hue < 240)
			r = m1 + (m2 - m1) * (240 - hue) / 60;
		else
			r = m1;

		hue = *h;
		while (hue > 360)
			hue -= 360;
		while (hue < 0)
			hue += 360;

		if (hue < 60)
			g = m1 + (m2 - m1) * hue / 60;
		else if (hue < 180)
			g = m2;
		else if (hue < 240)
			g = m1 + (m2 - m1) * (240 - hue) / 60;
		else
			g = m1;

		hue = *h - 120;
		while (hue > 360)
			hue -= 360;
		while (hue < 0)
			hue += 360;

		if (hue < 60)
			b = m1 + (m2 - m1) * hue / 60;
		else if (hue < 180)
			b = m2;
		else if (hue < 240)
			b = m1 + (m2 - m1) * (240 - hue) / 60;
		else
			b = m1;

		*h = r;
		*l = g;
		*s = b;
	}
}

static void
shade_color(GdkColor *a, GdkColor *b, gdouble k)
{
	gdouble red;
	gdouble green;
	gdouble blue;

	/* Special case for black, so that it looks pretty... */

	if ((a->red == 0) && (a->green == 0) && (a->blue == 0)) {
		a->red = 32768;
		a->green = 32768;
		a->blue = 32768;
	}

	red = (gdouble) a->red / 65535.0;
	green = (gdouble) a->green / 65535.0;
	blue = (gdouble) a->blue / 65535.0;

	rgb_to_hls (&red, &green, &blue);

	green *= k;

	if (green > 1.0)
		green = 1.0;
	else if (green < 0.0)
		green = 0.0;

	blue *= k;

	if (blue > 1.0)
		blue = 1.0;
	else if (blue < 0.0)
		blue = 0.0;

	hls_to_rgb (&red, &green, &blue);

	b->red = (int) (red * 65535.0 + 0.5);
	b->green = (int) (green * 65535.0 + 0.5);
	b->blue = (int) (blue * 65535.0 + 0.5);
}

#define HIGHLIGHT_MULT 0.7
#define LIGHT_MULT     1.5
#define DARK_MULT      0.5

static void
my_get_colors(GdkColormap *colormap, gulong background, gulong *top,
	gulong *bottom, gulong *highlight, ToolkitAbstraction *tka)
{
	GdkColor cbackground;
	GdkColor ctop, cbottom, chighlight;

	/*****
	* I think this should use a ColorContext instead of allocating colors
	* itself... - Federico
	*****/

	/* FIXME: The colors that are allocated here are never freed.
	 * I think we can save the pixel values in static variables so that we
	 * can call gdk_colors_free() on the next invocation of the function.
	 */

	cbackground.pixel = background;
	tka->QueryColors(colormap, &cbackground, 1);

	if (top) {
		shade_color(&cbackground, &ctop, LIGHT_MULT);
		gdk_color_alloc(colormap, &ctop);
		*top = ctop.pixel;
	}

	if (bottom) {
		shade_color(&cbackground, &cbottom, DARK_MULT);
		gdk_color_alloc(colormap, &cbottom);
		*bottom = cbottom.pixel;
	}

	if (highlight) {
		shade_color(&cbackground, &chighlight, HIGHLIGHT_MULT);
		gdk_color_alloc(colormap, &chighlight);
		*highlight = chighlight.pixel;
	}
}

static void
set_widget_colors(GtkXmHTML *html, gulong *top, gulong *bottom,
	gulong *highlight)
{
	GdkColor c;
	
	if (top) {
		c.pixel = *top;
		gdk_gc_set_foreground(html->top_shadow_gc, &c);
	}

	if (bottom) {
		c.pixel = *bottom;
		gdk_gc_set_foreground(html->bottom_shadow_gc, &c);
	}

	if (highlight) {
		c.pixel = *highlight;
		gdk_gc_set_foreground(html->highlight_gc, &c);
		html->highlight_color = *highlight;
	}
}

/*****
* Name: 		XmHTMLTkaRecomputeColors
* Return Type: 	void
* Description: 	computes new values for top and bottom shadows and the
*				highlight color based on the current background color.
* In: 
*	html:		XmHTMLWidget id;
*	bg_pixel:	background color to be used (set as background for the
*				display area).
* Returns:
*	nothing.
*****/
void
XmHTMLTkaRecomputeColors(XmHTMLWidget html, Pixel bg_pixel) 
{
	/* 
	* We can only compute the colors when we have a GC. If we don't
	* have a GC, the widget is not yet realized. Use managers defaults
	* then.
	*/
	if(html->html.gc != NULL)
	{
		/*****
		* kdh: FIXME
		* Set background color of the render area
		*****/
		gulong top, bottom, highlight;
		my_get_colors(gtk_widget_get_colormap(GTK_WIDGET(html)),
			bg_color, &top, &bottom, &highlight, html->html.tka);
		set_widget_colors(html, &top, &bottom, &highlight);
	}
}

/*****
* Name: 		XmHTMLTkaRecomputeHighlightColor
* Return Type: 	void
* Description: 	computes the select color based upon the given color.
* In: 
*	html:		XmHTMLWidget id;
* Returns:
*	nothing.
*****/
void
XmHTMLTkaRecomputeHighlightColor(XmHTMLWidget html, Pixel bg_pixel) 
{
	if(html->html.gc != NULL)
	{
		gulong highlight;

		my_get_colors(gtk_widget_get_colormap(GTK_WIDGET(html)),
			bg_pixel, NULL, NULL, &highlight, html->html.tka);
		set_widget_colors(html, NULL, NULL, &highlight);
	}
}

/*****
* Name:			XmHTMLTkaRecomputeShadowColors
* Return Type:	void
* Description:	recomputes the top and bottom shadow colors based on the
*				given *foreground* color
* In:
*	html:		XmHTMLWidget id;
*	base:		base color to base computation on.
* Returns:
*	Nothing, but the GC's from Manager are updated to reflect the change.
*****/
void
XmHTMLTkaRecomputeShadowColors(XmHTMLWidget html, Pixel base)
{
	/*
	* We can only compute the colors when we have a GC. If we don't
	* have a GC, the widget is not yet realized. Use managers defaults
	* then.
	*/
	if(HTML_ATTR(gc) != NULL)
	{
		gulong top = None, bottom = None;

		my_get_colors(gtk_widget_get_colormap(GTK_WIDGET(html)),
			pages, &top, &bottom, NULL, HTML_ATTR(tka));
		set_widget_colors(html, &top, &bottom, NULL);
	}
}

#ifndef DMALLOC
#ifndef DEBUG
String
g_strdup_wrapper(String string)
{
	static String ret_val;

	/* sanity */
	if(string == NULL || *string == '\0')
		return(NULL);

	if((ret_val = strdup(string)) == NULL)
		g_error("strdup failed for %i bytes\n", strlen(string));

	return(ret_val);
}
#endif /* DEBUG */
#endif /* DMALLOC */
