view src/gtkhtml.c @ 958:df664ea5eced

[gaim-migrate @ 968] This is mostly just a test. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Mon, 09 Oct 2000 09:18:19 +0000
parents 032a6e1553c1
children 056bd26615ea
line wrap: on
line source


/*
 * gaim
 *
 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
 *
 * 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, Boston, MA  02111-1307  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkprivate.h>
#include <gdk/gdkx.h>
#include <gdk/gdkkeysyms.h>
#include <ctype.h>

#ifndef _WIN32
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#endif

#include "gaim.h"
#include "gtkhtml.h"

#include "pixmaps/aol_icon.xpm"
#include "pixmaps/admin_icon.xpm"
#include "pixmaps/free_icon.xpm"
#include "pixmaps/dt_icon.xpm"
#define MAX_SIZE							7
#define MIN_HTML_WIDTH_LINES     20
#define MIN_HTML_HEIGHT_LINES    10
#define BORDER_WIDTH             2
#define SCROLL_TIME              100
#define SCROLL_PIXELS            5
#define KEY_SCROLL_PIXELS        10

int font_sizes[] = { 80, 100, 120, 140, 200, 300, 400 };
GtkHtmlBit *start_of_line = NULL;

/*
GdkFont *fixed_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
GdkFont *fixed_bold_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
GdkFont *fixed_italic_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
GdkFont *fixed_bold_italic_font[] =
	{ NULL, NULL, NULL, NULL, NULL, NULL, NULL };
GdkFont *prop_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
GdkFont *prop_bold_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
GdkFont *prop_italic_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
GdkFont *prop_bold_italic_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
*/

GData * font_cache;
static gboolean cache_init = FALSE;

struct font_state
{
	int size;
	int owncolor;
	int ownbg;
	gchar font[1024];
	GdkColor *color;
	GdkColor *bgcol;
	struct font_state *next;
};

struct font_state *push_state(struct font_state *current)
{
	struct font_state *tmp;
	tmp = (struct font_state *) g_new0(struct font_state, 1);
	tmp->next = current;
	tmp->color = current->color;
	tmp->bgcol = current->bgcol;
	tmp->size = current->size;
	tmp->owncolor = 0;
	tmp->ownbg = 0;
	strcpy( tmp->font, current->font );
	return tmp;
}

enum
{
	ARG_0,
	ARG_HADJUSTMENT,
	ARG_VADJUSTMENT,
};


enum
{
	TARGET_STRING,
	TARGET_TEXT,
	TARGET_COMPOUND_TEXT
};


static void gtk_html_class_init(GtkHtmlClass * klass);
static void gtk_html_set_arg(GtkObject * object, GtkArg * arg, guint arg_id);
static void gtk_html_get_arg(GtkObject * object, GtkArg * arg, guint arg_id);
static void gtk_html_init(GtkHtml * html);
static void gtk_html_destroy(GtkObject * object);
static void gtk_html_finalize(GtkObject * object);
static void gtk_html_realize(GtkWidget * widget);
static void gtk_html_unrealize(GtkWidget * widget);
static void gtk_html_style_set(GtkWidget * widget, GtkStyle * previous_style);
static void gtk_html_draw_focus(GtkWidget * widget);
static void gtk_html_size_request(GtkWidget * widget,
								  GtkRequisition * requisition);
static void gtk_html_size_allocate(GtkWidget * widget,
								   GtkAllocation * allocation);
static void gtk_html_adjustment(GtkAdjustment * adjustment, GtkHtml * html);
static void gtk_html_disconnect(GtkAdjustment * adjustment, GtkHtml * html);
static void gtk_html_add_seperator(GtkHtml *, GdkFont *, GdkColor *, GdkColor *);
static void gtk_html_add_text(GtkHtml * html,
							  GdkFont * font,
							  GdkColor * fore,
							  GdkColor * back,
							  gchar * chars,
							  gint length,
							  gint uline, gint strike, gchar * url);
static void gtk_html_draw_bit(GtkHtml * html,
							  GtkHtmlBit * htmlbit, gint redraw);
static void gtk_html_selection_get(GtkWidget * widget,
								   GtkSelectionData * selection_data,
								   guint sel_info, guint32 time);
static gint gtk_html_selection_clear(GtkWidget * widget,
									 GdkEventSelection * event);
static gint gtk_html_visibility_notify(GtkWidget * widget,
									   GdkEventVisibility * event);


/* Event handlers */
static void gtk_html_draw(GtkWidget * widget, GdkRectangle * area);
static gint gtk_html_expose(GtkWidget * widget, GdkEventExpose * event);
static gint gtk_html_button_press(GtkWidget * widget, GdkEventButton * event);
static gint gtk_html_button_release(GtkWidget * widget, GdkEventButton * event);
static gint gtk_html_motion_notify(GtkWidget * widget, GdkEventMotion * event);
static gint gtk_html_key_press(GtkWidget * widget, GdkEventKey * event);
static gint gtk_html_leave_notify(GtkWidget * widget, GdkEventCrossing * event);

static gint gtk_html_tooltip_timeout(gpointer data);


static void clear_area(GtkHtml * html, GdkRectangle * area);
static void expose_html(GtkHtml * html, GdkRectangle * area, gboolean cursor);
static void scroll_down(GtkHtml * html, gint diff0);
static void scroll_up(GtkHtml * html, gint diff0);

static void adjust_adj(GtkHtml * html, GtkAdjustment * adj);
static void resize_html(GtkHtml * html);
static gint html_bit_is_onscreen(GtkHtml * html, GtkHtmlBit * hb);
static void draw_cursor(GtkHtml * html);
static void undraw_cursor(GtkHtml * html);

static int get_line_max_height(GtkHtml *, GtkHtmlBit *);
static void get_line_max_extents(GtkHtml *, GtkHtmlBit *, int *, int *);

static GtkWidgetClass *parent_class = NULL;

GtkType gtk_html_get_type(void)
{
	static GtkType html_type = 0;

	if (!html_type)
	{
		static const GtkTypeInfo html_info = {
			"GtkHtml",
			sizeof(GtkHtml),
			sizeof(GtkHtmlClass),
			(GtkClassInitFunc) gtk_html_class_init,
			(GtkObjectInitFunc) gtk_html_init,
			NULL,
			NULL,
			NULL,
		};
		html_type = gtk_type_unique(GTK_TYPE_WIDGET, &html_info);
	}
	return html_type;
}


static void gtk_html_class_init(GtkHtmlClass * class)
{
	GtkObjectClass *object_class;
	GtkWidgetClass *widget_class;

	object_class = (GtkObjectClass *) class;
	widget_class = (GtkWidgetClass *) class;
	parent_class = gtk_type_class(GTK_TYPE_WIDGET);


	gtk_object_add_arg_type("GtkHtml::hadjustment",
							GTK_TYPE_ADJUSTMENT,
							GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
							ARG_HADJUSTMENT);

	gtk_object_add_arg_type("GtkHtml::vadjustment",
							GTK_TYPE_ADJUSTMENT,
							GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
							ARG_VADJUSTMENT);

	object_class->set_arg = gtk_html_set_arg;
	object_class->get_arg = gtk_html_get_arg;
	object_class->destroy = gtk_html_destroy;
	object_class->finalize = gtk_html_finalize;

	widget_class->realize = gtk_html_realize;
	widget_class->unrealize = gtk_html_unrealize;
	widget_class->style_set = gtk_html_style_set;
	widget_class->draw_focus = gtk_html_draw_focus;
	widget_class->size_request = gtk_html_size_request;
	widget_class->size_allocate = gtk_html_size_allocate;
	widget_class->draw = gtk_html_draw;
	widget_class->expose_event = gtk_html_expose;
	widget_class->button_press_event = gtk_html_button_press;
	widget_class->button_release_event = gtk_html_button_release;
	widget_class->motion_notify_event = gtk_html_motion_notify;
	widget_class->leave_notify_event = gtk_html_leave_notify;
	widget_class->selection_get = gtk_html_selection_get;
	widget_class->selection_clear_event = gtk_html_selection_clear;
	widget_class->key_press_event = gtk_html_key_press;
	widget_class->visibility_notify_event = gtk_html_visibility_notify;


	widget_class->set_scroll_adjustments_signal =
		gtk_signal_new("set_scroll_adjustments",
					   GTK_RUN_LAST,
					   object_class->type,
					   GTK_SIGNAL_OFFSET(GtkHtmlClass, set_scroll_adjustments),
					   gtk_marshal_NONE__POINTER_POINTER,
					   GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT,
					   GTK_TYPE_ADJUSTMENT);


	class->set_scroll_adjustments = gtk_html_set_adjustments;

}

static void gtk_html_set_arg(GtkObject * object, GtkArg * arg, guint arg_id)
{
	GtkHtml *html;

	html = GTK_HTML(object);

	switch (arg_id)
	{
	case ARG_HADJUSTMENT:
		gtk_html_set_adjustments(html, GTK_VALUE_POINTER(*arg), html->vadj);
		break;
	case ARG_VADJUSTMENT:
		gtk_html_set_adjustments(html, html->hadj, GTK_VALUE_POINTER(*arg));
		break;
	default:
		break;
	}
}

static void gtk_html_get_arg(GtkObject * object, GtkArg * arg, guint arg_id)
{
	GtkHtml *html;

	html = GTK_HTML(object);

	switch (arg_id)
	{
	case ARG_HADJUSTMENT:
		GTK_VALUE_POINTER(*arg) = html->hadj;
		break;
	case ARG_VADJUSTMENT:
		GTK_VALUE_POINTER(*arg) = html->vadj;
		break;
	default:
		arg->type = GTK_TYPE_INVALID;
		break;
	}
}

static void gtk_html_init(GtkHtml * html)
{
	static const GtkTargetEntry targets[] = {
		{"STRING", 0, TARGET_STRING},
		{"TEXT", 0, TARGET_TEXT},
		{"COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT}
	};

	static const gint n_targets = sizeof(targets) / sizeof(targets[0]);

	GTK_WIDGET_SET_FLAGS(html, GTK_CAN_FOCUS);

	html->html_area = NULL;
	html->hadj = NULL;
	html->vadj = NULL;
	html->current_x = 0;
	html->current_y = 0;
	html->start_sel = html->end_sel = NULL;
	html->start_sel_x = html->start_sel_y = -1;
	html->num_end = html->num_start = -1;

	html->html_bits = NULL;
	html->urls = NULL;
	html->selected_text = NULL;
	html->tooltip_hb = NULL;
	html->tooltip_timer = -1;
	html->tooltip_window = NULL;
	html->cursor_hb = NULL;
	html->cursor_pos = 0;

	html->pm = NULL;

	html->editable = 0;
	html->transparent = 0;

	html->frozen = 0;

	gtk_selection_add_targets(GTK_WIDGET(html), GDK_SELECTION_PRIMARY,
							  targets, n_targets);



}


GtkWidget *gtk_html_new(GtkAdjustment * hadj, GtkAdjustment * vadj)
{
	GtkWidget *html;
	if(!cache_init)
	{
		g_datalist_init(&font_cache);
		cache_init = TRUE;
	}

	if (hadj)
		g_return_val_if_fail(GTK_IS_ADJUSTMENT(hadj), NULL);
	if (vadj)
		g_return_val_if_fail(GTK_IS_ADJUSTMENT(vadj), NULL);

	html = gtk_widget_new(GTK_TYPE_HTML,
						  "hadjustment", hadj, "vadjustment", vadj, NULL);

	return html;
}


void gtk_html_set_editable(GtkHtml * html, gboolean is_editable)
{
	g_return_if_fail(html != NULL);
	g_return_if_fail(GTK_IS_HTML(html));


	html->editable = (is_editable != FALSE);

	if (is_editable)
		draw_cursor(html);
	else
		undraw_cursor(html);

}

void gtk_html_set_transparent(GtkHtml * html, gboolean is_transparent)
{
	GdkRectangle rect;
	gint width,
	  height;
	GtkWidget *widget;

	g_return_if_fail(html != NULL);
	g_return_if_fail(GTK_IS_HTML(html));


	widget = GTK_WIDGET(html);
	html->transparent = (is_transparent != FALSE);

	if (!GTK_WIDGET_REALIZED(widget))
		return;

	html->bg_gc = NULL;
	gdk_window_get_size(widget->window, &width, &height);
	rect.x = 0;
	rect.y = 0;
	rect.width = width;
	rect.height = height;
	gdk_window_clear_area(widget->window, rect.x, rect.y, rect.width,
						  rect.height);

	expose_html(html, &rect, FALSE);
	gtk_html_draw_focus((GtkWidget *) html);
}


void gtk_html_set_adjustments(GtkHtml * html,
							  GtkAdjustment * hadj, GtkAdjustment * vadj)
{
	g_return_if_fail(html != NULL);
	g_return_if_fail(GTK_IS_HTML(html));
	if (hadj)
		g_return_if_fail(GTK_IS_ADJUSTMENT(hadj));
	else
		hadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
	if (vadj)
		g_return_if_fail(GTK_IS_ADJUSTMENT(vadj));
	else
		vadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));

	if (html->hadj && (html->hadj != hadj))
	{
		gtk_signal_disconnect_by_data(GTK_OBJECT(html->hadj), html);
		gtk_object_unref(GTK_OBJECT(html->hadj));
	}

	if (html->vadj && (html->vadj != vadj))
	{
		gtk_signal_disconnect_by_data(GTK_OBJECT(html->vadj), html);
		gtk_object_unref(GTK_OBJECT(html->vadj));
	}

	if (html->hadj != hadj)
	{
		html->hadj = hadj;
		gtk_object_ref(GTK_OBJECT(html->hadj));
		gtk_object_sink(GTK_OBJECT(html->hadj));

		gtk_signal_connect(GTK_OBJECT(html->hadj), "changed",
						   (GtkSignalFunc) gtk_html_adjustment, html);
		gtk_signal_connect(GTK_OBJECT(html->hadj), "value_changed",
						   (GtkSignalFunc) gtk_html_adjustment, html);
		gtk_signal_connect(GTK_OBJECT(html->hadj), "disconnect",
						   (GtkSignalFunc) gtk_html_disconnect, html);
		gtk_html_adjustment(hadj, html);
	}

	if (html->vadj != vadj)
	{
		html->vadj = vadj;
		gtk_object_ref(GTK_OBJECT(html->vadj));
		gtk_object_sink(GTK_OBJECT(html->vadj));

		gtk_signal_connect(GTK_OBJECT(html->vadj), "changed",
						   (GtkSignalFunc) gtk_html_adjustment, html);
		gtk_signal_connect(GTK_OBJECT(html->vadj), "value_changed",
						   (GtkSignalFunc) gtk_html_adjustment, html);
		gtk_signal_connect(GTK_OBJECT(html->vadj), "disconnect",
						   (GtkSignalFunc) gtk_html_disconnect, html);
		gtk_html_adjustment(vadj, html);
	}
}



GdkColor *get_color(int colorv, GdkColormap * map)
{
	GdkColor *color;
#if 0
	fprintf(stdout, "color is %x\n", colorv);
#endif
	color = (GdkColor *) g_new0(GdkColor, 1);
	color->red = ((colorv & 0xff0000) >> 16) * 256;
	color->green = ((colorv & 0xff00) >> 8) * 256;
	color->blue = ((colorv & 0xff)) * 256;
#if 0
	fprintf(stdout, "Colors are %d, %d, %d\n", color->red, color->green,
			color->blue);
#endif
	gdk_color_alloc(map, color);
	return color;
}


int load_font_with_cache(const char *name, const char *weight, char slant,
	int size, GdkFont **font_return)
{
	gchar font_spec[1024];

	if (size > 0)
		g_snprintf(font_spec, sizeof font_spec,
			"-*-%s-%s-%c-*-*-*-%d-*-*-*-*-*-*",
			name, weight, slant, size);
	else
		g_snprintf(font_spec, sizeof font_spec,
			"-*-%s-%s-%c-*-*-*-*-*-*-*-*-*-*",
			name, weight, slant);

	if((*font_return = g_datalist_id_get_data(&font_cache,
				g_quark_from_string(font_spec)))) {
		return TRUE;
	} else if ((*font_return = gdk_font_load(font_spec))) {
		g_datalist_id_set_data(&font_cache,
			g_quark_from_string(font_spec), *font_return);
		return TRUE;
	} else {
		return FALSE;
	}
}


GdkFont *getfont(const char *font, int bold, int italic, int fixed, int size)
{
	GdkFont *my_font = NULL;
	gchar *weight, slant;

	if (!font || !strlen(font)) font = fixed ? "courier" : "helvetica";
	weight = bold ? "bold" : "medium";
	slant = italic ? 'i' : 'r';

	if (size > MAX_SIZE) size = MAX_SIZE;
	if (size < 1) size = 1;
	size = font_sizes[size-1];
	
	/* try both 'i'talic and 'o'blique for italic fonts */

	if (load_font_with_cache(font, weight, slant, size, &my_font))
		return my_font;
	if (load_font_with_cache(font, weight, 'o', size, &my_font))
		return my_font;
	if (italic && load_font_with_cache(font, weight, slant, 0, &my_font))
		return my_font;
	if (italic && load_font_with_cache(font, weight, 'o', 0, &my_font))
		return my_font;

	/* since we couldn't get the right font, fall back to the default fonts. */

	font = fixed ? "courier" : "helvetica";
	if (load_font_with_cache(font, weight, slant, size, &my_font))
		return my_font;
	if (load_font_with_cache(font, weight, slant, 0, &my_font))
		return my_font;
	
	font = fixed ? "helvetica" : "courier";
	if (load_font_with_cache(font, weight, slant, size, &my_font))
		return my_font;
	if (load_font_with_cache(font, weight, slant, 0, &my_font))
		return my_font;

	/* whoops, couldn't do any of those. maybe they have a default outgoing
	 * font? maybe we can use that. */
	if (fontface[0]) {
		/* woohoo! */
		if (load_font_with_cache(fontface, "medium", 'r', size, &my_font))
			return my_font;
		if (load_font_with_cache(fontface, "medium", 'r', 0, &my_font))
			return my_font;
	}

	/* ok, now we're in a pickle. if we can't do any of the above, let's
	 * try doing the most boring font we can find. */
	if (load_font_with_cache("helvetica", "medium", 'r', size, &my_font))
		return my_font;
	if (load_font_with_cache("helvetica", "medium", 'r', 0, &my_font))
		return my_font;

	if (load_font_with_cache("courier", "medium", 'r', size, &my_font))
		return my_font;
	if (load_font_with_cache("courier", "medium", 'r', 0, &my_font))
		return my_font;

	if (load_font_with_cache("times", "medium", 'r', size, &my_font))
		return my_font;
	if (load_font_with_cache("times", "medium", 'r', 0, &my_font))
		return my_font;

	/* my god, how did we end up here. is there a 'generic font' function
	 * in gdk? that would be incredibly useful here. there's gotta be a
	 * better way to do this. */
	
	/* well, if they can't do any of the fonts above, they'll take whatever
	 * they can get, and be happy about it, damn it. :) */
	load_font_with_cache("*", "*", '*', 0, &my_font);
	return my_font;
}


/* 'Borrowed' from ETerm */
GdkWindow *get_desktop_window(GtkWidget * widget)
{
#ifndef _WIN32
	GdkAtom prop,
	  type,
	  prop2;
	int format;
	gint length;
	guchar *data;
	GtkWidget *w;

	prop = gdk_atom_intern("_XROOTPMAP_ID", 1);
	prop2 = gdk_atom_intern("_XROOTCOLOR_PIXEL", 1);

	if (prop == None && prop2 == None)
	{
		return NULL;
	}



	for (w = widget; w; w = w->parent)
	{

		if (prop != None)
		{
			gdk_property_get(w->window, prop, AnyPropertyType, 0L, 1L, 0,
							 &type, &format, &length, &data);
		}
		else if (prop2 != None)
		{
			gdk_property_get(w->window, prop2, AnyPropertyType, 0L, 1L, 0,
							 &type, &format, &length, &data);
		}
		else
		{
			continue;
		}
		if (type != None)
		{
			return (w->window);
		}
	}
#endif
	return NULL;

}



GdkPixmap *get_desktop_pixmap(GtkWidget * widget)
{
#ifndef _WIN32
	GdkPixmap *p;
	GdkAtom prop,
	  type,
	  prop2;
	int format;
	gint length;
	guint32 id;
	guchar *data;

	prop = gdk_atom_intern("_XROOTPMAP_ID", 1);
	prop2 = gdk_atom_intern("_XROOTCOLOR_PIXEL", 1);


	if (prop == None && prop2 == None)
	{
		return NULL;
	}

	if (prop != None)
	{
		gdk_property_get(get_desktop_window(widget), prop, AnyPropertyType, 0L,
						 1L, 0, &type, &format, &length, &data);
		if (type == XA_PIXMAP)
		{
			id = data[0];
			id += data[1] << 8;
			id += data[2] << 16;
			id += data[3] << 24;
			p = gdk_pixmap_foreign_new(id);
			return p;
		}
	}
	if (prop2 != None)
	{

/*                XGetWindowProperty(Xdisplay, desktop_window, prop2, 0L, 1L, False, AnyPropertyType,
                                   &type, &format, &length, &after, &data);*/

/*                if (type == XA_CARDINAL) {*/
		/*
		 * D_PIXMAP(("  Solid color not yet supported.\n"));
		 */

/*                        return NULL;
                }*/
	}
	/*
	 * D_PIXMAP(("No suitable attribute found.\n"));
	 */
#endif
	return NULL;
}


static void clear_focus_area(GtkHtml * html,
							 gint area_x,
							 gint area_y, gint area_width, gint area_height)
{
	GtkWidget *widget = GTK_WIDGET(html);
	gint x,
	  y;

	gint ythick = BORDER_WIDTH + widget->style->klass->ythickness;
	gint xthick = BORDER_WIDTH + widget->style->klass->xthickness;

	gint width,
	  height;

	if (html->frozen > 0)
		return;

	if (html->transparent)
	{
		if (html->pm == NULL)
			html->pm = get_desktop_pixmap(widget);

		if (html->pm == NULL)
			return;

		if (html->bg_gc == NULL)
		{
			GdkGCValues values;

			values.tile = html->pm;
			values.fill = GDK_TILED;

			html->bg_gc = gdk_gc_new_with_values(html->html_area, &values,
												 GDK_GC_FILL | GDK_GC_TILE);

		}

		gdk_window_get_deskrelative_origin(widget->window, &x, &y);

		gdk_draw_pixmap(widget->window, html->bg_gc, html->pm,
						x + area_x, y + area_y, area_x, area_y, area_width,
						area_height);


	}
	else
	{
		gdk_window_get_size(widget->style->bg_pixmap[GTK_STATE_NORMAL], &width,
							&height);

		gdk_gc_set_ts_origin(html->bg_gc,
							 (-html->xoffset + xthick) % width,
							 (-html->yoffset + ythick) % height);

		gdk_draw_rectangle(widget->window, html->bg_gc, TRUE,
						   area_x, area_y, area_width, area_height);
	}
}

static void gtk_html_draw_focus(GtkWidget * widget)
{
	GtkHtml *html;
	gint width,
	  height;
	gint x,
	  y;

	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_HTML(widget));

	html = GTK_HTML(widget);

	if (GTK_WIDGET_DRAWABLE(widget))
	{
		gint ythick = widget->style->klass->ythickness;
		gint xthick = widget->style->klass->xthickness;
		gint xextra = BORDER_WIDTH;
		gint yextra = BORDER_WIDTH;

		x = 0;
		y = 0;
		width = widget->allocation.width;
		height = widget->allocation.height;

		if (GTK_WIDGET_HAS_FOCUS(widget))
		{
			x += 1;
			y += 1;
			width -= 2;
			height -= 2;
			xextra -= 1;
			yextra -= 1;

			gtk_paint_focus(widget->style, widget->window,
							NULL, widget, "text",
							0, 0,
							widget->allocation.width - 1,
							widget->allocation.height - 1);
		}

		gtk_paint_shadow(widget->style, widget->window,
						 GTK_STATE_NORMAL, GTK_SHADOW_IN,
						 NULL, widget, "text", x, y, width, height);

		x += xthick;
		y += ythick;
		width -= 2 * xthick;
		height -= 2 * ythick;


		if (widget->style->bg_pixmap[GTK_STATE_NORMAL] || html->transparent)
		{
			/*
			 * top rect 
			 */
			clear_focus_area(html, x, y, width, yextra);
			/*
			 * left rect 
			 */
			clear_focus_area(html, x, y + yextra,
							 xextra, y + height - 2 * yextra);
			/*
			 * right rect 
			 */
			clear_focus_area(html, x + width - xextra, y + yextra,
							 xextra, height - 2 * ythick);
			/*
			 * bottom rect 
			 */
			clear_focus_area(html, x, x + height - yextra, width, yextra);
		}
	}
}

static void gtk_html_size_request(GtkWidget * widget,
								  GtkRequisition * requisition)
{
	gint xthickness;
	gint ythickness;
	gint char_height;
	gint char_width;

	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_HTML(widget));
	g_return_if_fail(requisition != NULL);

	xthickness = widget->style->klass->xthickness + BORDER_WIDTH;
	ythickness = widget->style->klass->ythickness + BORDER_WIDTH;

	char_height = MIN_HTML_HEIGHT_LINES * (widget->style->font->ascent +
										   widget->style->font->descent);

	char_width = MIN_HTML_WIDTH_LINES * (gdk_text_width(widget->style->font,
														"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
														26) / 26);

	requisition->width = char_width + xthickness * 2;
	requisition->height = char_height + ythickness * 2;
}

static void gtk_html_size_allocate(GtkWidget * widget,
								   GtkAllocation * allocation)
{
	GtkHtml *html;

	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_HTML(widget));
	g_return_if_fail(allocation != NULL);

	html = GTK_HTML(widget);

	widget->allocation = *allocation;
	if (GTK_WIDGET_REALIZED(widget))
	{
		gdk_window_move_resize(widget->window,
							   allocation->x, allocation->y,
							   allocation->width, allocation->height);

		gdk_window_move_resize(html->html_area,
							   widget->style->klass->xthickness + BORDER_WIDTH,
							   widget->style->klass->ythickness + BORDER_WIDTH,
							   MAX(1, (gint) widget->allocation.width -
								   (gint) (widget->style->klass->xthickness +
										   (gint) BORDER_WIDTH) * 2),
							   MAX(1, (gint) widget->allocation.height -
								   (gint) (widget->style->klass->ythickness +
										   (gint) BORDER_WIDTH) * 2));

		resize_html(html);
	}
}

static void gtk_html_draw(GtkWidget * widget, GdkRectangle * area)
{
	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_HTML(widget));
	g_return_if_fail(area != NULL);

	if (GTK_WIDGET_DRAWABLE(widget))
	{
		expose_html(GTK_HTML(widget), area, TRUE);
		gtk_widget_draw_focus(widget);
	}
}


static gint gtk_html_expose(GtkWidget * widget, GdkEventExpose * event)
{
	GtkHtml *html;

	g_return_val_if_fail(widget != NULL, FALSE);
	g_return_val_if_fail(GTK_IS_HTML(widget), FALSE);
	g_return_val_if_fail(event != NULL, FALSE);

	html = GTK_HTML(widget);

	if (event->window == html->html_area)
	{
		expose_html(html, &event->area, TRUE);
	}
	else if (event->count == 0)
	{
		gtk_widget_draw_focus(widget);
	}

	return FALSE;

}


static gint gtk_html_selection_clear(GtkWidget * widget,
									 GdkEventSelection * event)
{
	GtkHtml *html;

	g_return_val_if_fail(widget != NULL, FALSE);
	g_return_val_if_fail(GTK_IS_HTML(widget), FALSE);
	g_return_val_if_fail(event != NULL, FALSE);

	/*
	 * Let the selection handling code know that the selection
	 * * has been changed, since we've overriden the default handler 
	 */
	if (!gtk_selection_clear(widget, event))
		return FALSE;

	html = GTK_HTML(widget);

	if (event->selection == GDK_SELECTION_PRIMARY)
	{
		if (html->selected_text)
		{
			GList *hbits = html->html_bits;
			GtkHtmlBit *hb;

			g_free(html->selected_text);
			html->selected_text = NULL;
			html->start_sel = NULL;
			html->end_sel = NULL;
			html->num_start = 0;
			html->num_end = 0;
			while (hbits)
			{
				hb = (GtkHtmlBit *) hbits->data;
				if (hb->was_selected)
					gtk_html_draw_bit(html, hb, 1);
				hbits = hbits->prev;
			}
			hbits = g_list_last(html->html_bits);
		}
	}

	return TRUE;
}



static void gtk_html_selection_get(GtkWidget * widget,
								   GtkSelectionData * selection_data,
								   guint sel_info, guint32 time)
{
	gchar *str;
	gint len;
	GtkHtml *html;

	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_HTML(widget));

	html = GTK_HTML(widget);


	if (selection_data->selection != GDK_SELECTION_PRIMARY)
		return;

	str = html->selected_text;

	if (!str)
		return;

	len = strlen(str);

	if (sel_info == TARGET_STRING)
	{
		gtk_selection_data_set(selection_data,
							   GDK_SELECTION_TYPE_STRING,
							   8 * sizeof(gchar), (guchar *) str, len);
	}
	else if ((sel_info == TARGET_TEXT) || (sel_info == TARGET_COMPOUND_TEXT))
	{
		guchar *text;
		GdkAtom encoding;
		gint format;
		gint new_length;

		gdk_string_to_compound_text(str, &encoding, &format, &text,
									&new_length);
		gtk_selection_data_set(selection_data, encoding, format, text,
							   new_length);
		gdk_free_compound_text(text);
	}



}

static void do_select(GtkHtml * html, int x, int y)
{
	GList *hbits = g_list_last(html->html_bits);
	int epos,
	  spos;
	GtkHtmlBit *hb;

	if (!hbits)
		return;

	hb = (GtkHtmlBit *) hbits->data;

	while (hbits)
	{
		hb = (GtkHtmlBit *) hbits->data;
		if (hb->type == HTML_BIT_TEXT)
			break;
		hbits = hbits->prev;
	}

	if (!hb)
		return;


	if (y > hb->y)
	{
		html->num_end = strlen(hb->text) - 1;
		html->end_sel = hb;
	}
	else if (y < 0)
	{
		html->num_end = 0;
		html->end_sel = (GtkHtmlBit *) html->html_bits->data;
	}
	else
		while (hbits)
		{
			hb = (GtkHtmlBit *) hbits->data;
			if ((y < hb->y && y > (hb->y - hb->height)) &&
				(x > hb->x + hb->width))
			{
				if (hb->type != HTML_BIT_TEXT)
				{
					html->num_end = 0;
					html->end_sel = hb;
					break;
				}

				html->num_end = strlen(hb->text) - 1;
				html->end_sel = hb;
				break;
			}
			else if ((x > hb->x && x < (hb->x + hb->width)) &&
					 (y < hb->y && y > (hb->y - hb->height)))
			{
				int i,
				  len;
				int w = x - hb->x;

				if (hb->type != HTML_BIT_TEXT)
				{
					html->num_end = 0;
					html->end_sel = hb;
					break;
				}

				len = strlen(hb->text);

				for (i = 1; i <= len; i++)
				{
					if (gdk_text_measure(hb->font, hb->text, i) > w)
					{
						html->num_end = i - 1;
						html->end_sel = hb;
						break;
					}
				}
				break;
			}
			hbits = hbits->prev;
		}

	if (html->end_sel == NULL)
		return;
	if (html->start_sel == NULL)
	{
		html->start_sel = html->end_sel;
		html->num_start = html->num_end;
	}

	epos = g_list_index(html->html_bits, html->end_sel);
	spos = g_list_index(html->html_bits, html->start_sel);
	g_free(html->selected_text);
	html->selected_text = NULL;

	if (epos == spos)
	{
		char *str;
		if (html->start_sel->type != HTML_BIT_TEXT)
		{
			html->selected_text = NULL;
			return;
		}
		if (html->num_end == html->num_start)
		{
			str = g_malloc(2);
			if (strlen(html->start_sel->text))
				str[0] = html->start_sel->text[html->num_end];
			else
				str[0] = 0;
			str[1] = 0;
			gtk_html_draw_bit(html, html->start_sel, 0);
			html->selected_text = str;
		}
		else
		{
			size_t st,
			  en;
			char *str;
			if (html->num_end > html->num_start)
			{
				en = html->num_end;
				st = html->num_start;
			}
			else
			{
				en = html->num_start;
				st = html->num_end;
			}

			str = g_malloc(en - st + 2);
			strncpy(str, html->start_sel->text + st, (en - st + 1));
			str[en - st + 1] = 0;
			gtk_html_draw_bit(html, html->start_sel, 0);
			html->selected_text = str;

		}
	}
	else
	{
		GtkHtmlBit *shb,
		 *ehb;
		size_t en,
		  st;
		int len,
		  nlen;
		char *str;
		if (epos > spos)
		{
			shb = html->start_sel;
			ehb = html->end_sel;
			en = html->num_end;
			st = html->num_start;
		}
		else
		{
			shb = html->end_sel;
			ehb = html->start_sel;
			en = html->num_start;
			st = html->num_end;
		}

		hbits = g_list_find(html->html_bits, shb);

		if (!hbits)
			return;

		if (shb->type == HTML_BIT_TEXT)
		{
			len = strlen(shb->text) - st + 1;
			str = g_malloc(len);
			strcpy(str, shb->text + st);
			str[len - 1] = 0;
			gtk_html_draw_bit(html, shb, 0);
			if (shb->newline)
			{
				len += 1;
				str = g_realloc(str, len);
				str[len - 2] = '\n';
				str[len - 1] = 0;
			}
		}
		else
		{
			len = 1;
			str = g_malloc(1);
			str[0] = 0;
		}
		if (hbits->next == NULL)
		{
			html->selected_text = str;
			return;
		}


		hbits = hbits->next;
		while (1)
		{						/*
								 * Yah I know is dangerous :P 
								 */
			hb = (GtkHtmlBit *) hbits->data;
			if (hb->type != HTML_BIT_TEXT)
			{
				if (hb == ehb)
					break;
				hbits = hbits->next;
				continue;
			}
			if (hb != ehb)
			{
				nlen = len + strlen(hb->text);
				str = g_realloc(str, nlen);
				strcpy(str + (len - 1), hb->text);
				len = nlen;
				str[len - 1] = 0;
				gtk_html_draw_bit(html, hb, 0);
				if (hb->newline)
				{
					len += 1;
					str = g_realloc(str, len);
					str[len - 2] = '\n';
					str[len - 1] = 0;
				}
			}
			else
			{
				nlen = len + en + 1;
				str = g_realloc(str, nlen);
				strncpy(str + (len - 1), hb->text, en + 1);
				len = nlen;
				str[len - 1] = 0;

				gtk_html_draw_bit(html, hb, 0);
				if (hb->newline && en == strlen(hb->text))
				{
					len += 1;
					str = g_realloc(str, len);
					str[len - 2] = '\n';
					str[len - 1] = 0;
				}
				break;
			}
			hbits = hbits->next;
		}
		html->selected_text = str;
	}

}

static gint scroll_timeout(GtkHtml * html)
{
	GdkEventMotion event;
	gint x,
	  y;
	GdkModifierType mask;

	html->timer = 0;
	gdk_window_get_pointer(html->html_area, &x, &y, &mask);

	if (mask & GDK_BUTTON1_MASK)
	{
		event.is_hint = 0;
		event.x = x;
		event.y = y;
		event.state = mask;

		gtk_html_motion_notify(GTK_WIDGET(html), &event);
	}

	return FALSE;

}


static gint gtk_html_tooltip_paint_window(GtkHtml * html)
{
	GtkStyle *style;
	gint y,
	  baseline_skip,
	  gap;

	style = html->tooltip_window->style;

	gap = (style->font->ascent + style->font->descent) / 4;
	if (gap < 2)
		gap = 2;
	baseline_skip = style->font->ascent + style->font->descent + gap;

	if (!html->tooltip_hb)
		return FALSE;

	gtk_paint_flat_box(style, html->tooltip_window->window,
					   GTK_STATE_NORMAL, GTK_SHADOW_OUT,
					   NULL, GTK_WIDGET(html->tooltip_window), "tooltip",
					   0, 0, -1, -1);

	y = style->font->ascent + 4;

	gtk_paint_string(style, html->tooltip_window->window,
					 GTK_STATE_NORMAL,
					 NULL, GTK_WIDGET(html->tooltip_window), "tooltip",
					 4, y, "HTML Link:");
	y += baseline_skip;
	gtk_paint_string(style, html->tooltip_window->window,
					 GTK_STATE_NORMAL,
					 NULL, GTK_WIDGET(html->tooltip_window), "tooltip",
					 4, y, html->tooltip_hb->url);

	return FALSE;


}

static gint gtk_html_tooltip_timeout(gpointer data)
{
	GtkHtml *html = (GtkHtml *) data;


	GDK_THREADS_ENTER();

	if (html->tooltip_hb && GTK_WIDGET_DRAWABLE(GTK_WIDGET(html)))
	{
		GtkWidget *widget;
		GtkStyle *style;
		gint gap,
		  x,
		  y,
		  w,
		  h,
		  scr_w,
		  scr_h,
		  baseline_skip;

		if (html->tooltip_window)
			gtk_widget_destroy(html->tooltip_window);

		html->tooltip_window = gtk_window_new(GTK_WINDOW_POPUP);
		gtk_widget_set_app_paintable(html->tooltip_window, TRUE);
		gtk_window_set_policy(GTK_WINDOW(html->tooltip_window), FALSE, FALSE,
							  TRUE);
		gtk_widget_set_name(html->tooltip_window, "gtk-tooltips");
		gtk_signal_connect_object(GTK_OBJECT(html->tooltip_window),
								  "expose_event",
								  GTK_SIGNAL_FUNC
								  (gtk_html_tooltip_paint_window),
								  GTK_OBJECT(html));
		gtk_signal_connect_object(GTK_OBJECT(html->tooltip_window), "draw",
								  GTK_SIGNAL_FUNC
								  (gtk_html_tooltip_paint_window),
								  GTK_OBJECT(html));

		gtk_widget_ensure_style(html->tooltip_window);
		style = html->tooltip_window->style;

		widget = GTK_WIDGET(html);

		scr_w = gdk_screen_width();
		scr_h = gdk_screen_height();

		gap = (style->font->ascent + style->font->descent) / 4;
		if (gap < 2)
			gap = 2;
		baseline_skip = style->font->ascent + style->font->descent + gap;

		w = 8 + MAX(gdk_string_width(style->font, _("HTML Link:")),
					gdk_string_width(style->font, html->tooltip_hb->url));
		;
		h = 8 - gap;
		h += (baseline_skip * 2);

		gdk_window_get_pointer(NULL, &x, &y, NULL);
		/*
		 * gdk_window_get_origin (widget->window, NULL, &y);
		 */
		if (GTK_WIDGET_NO_WINDOW(widget))
			y += widget->allocation.y;

		x -= ((w >> 1) + 4);

		if ((x + w) > scr_w)
			x -= (x + w) - scr_w;
		else if (x < 0)
			x = 0;

		if ((y + h + 4) > scr_h)
			y =
				y - html->tooltip_hb->font->ascent +
				html->tooltip_hb->font->descent;
		else
			y =
				y + html->tooltip_hb->font->ascent +
				html->tooltip_hb->font->descent;

		gtk_widget_set_usize(html->tooltip_window, w, h);
		gtk_widget_popup(html->tooltip_window, x, y);

	}

	html->tooltip_timer = -1;

	GDK_THREADS_LEAVE();

	return FALSE;
}


static gint gtk_html_leave_notify(GtkWidget * widget, GdkEventCrossing * event)
{
	GtkHtml *html;

	html = GTK_HTML(widget);

	if (html->tooltip_timer != -1)
		gtk_timeout_remove(html->tooltip_timer);
	if (html->tooltip_window)
	{
		gtk_widget_destroy(html->tooltip_window);
		html->tooltip_window = NULL;
	}


	html->tooltip_hb = NULL;
	return TRUE;
}


static gint gtk_html_motion_notify(GtkWidget * widget, GdkEventMotion * event)
{
	int x,
	  y;
	gint width,
	  height;
	GdkModifierType state;
	int realx,
	  realy;
	GtkHtml *html = GTK_HTML(widget);

	if (event->is_hint)
		gdk_window_get_pointer(event->window, &x, &y, &state);
	else
	{
		x = event->x;
		y = event->y;
		state = event->state;
	}

	gdk_window_get_size(html->html_area, &width, &height);

	realx = x;
	realy = y + html->yoffset;


	if (state & GDK_BUTTON1_MASK)
	{
		if (realx != html->start_sel_x || realy != html->start_sel_y)
		{
			char *tmp = NULL;

			if (y < 0 || y > height)
			{
				int diff;
				if (html->timer == 0)
				{
					html->timer = gtk_timeout_add(100,
												  (GtkFunction) scroll_timeout,
												  html);
					if (y < 0)
						diff = y / 2;
					else
						diff = (y - height) / 2;

					if (html->vadj->value + diff >
						html->vadj->upper - height + 20)
						gtk_adjustment_set_value(html->vadj,
												 html->vadj->upper - height +
												 20);
					else
						gtk_adjustment_set_value(html->vadj,
												 html->vadj->value + diff);

				}
			}

			if (html->selected_text != NULL)
				tmp = g_strdup(html->selected_text);
			do_select(html, realx, realy);
			if (tmp)
			{
				if (!html->selected_text || strcmp(tmp, html->selected_text))
				{
					GtkHtmlBit *hb;
					GList *hbits = html->html_bits;
					while (hbits)
					{
						hb = (GtkHtmlBit *) hbits->data;
						if (hb->was_selected)
							gtk_html_draw_bit(html, hb, 0);
						hbits = hbits->next;
					}
				}
				g_free(tmp);
			}
		}
	}
	else
	{
		GtkHtmlBit *hb;
		GList *urls;

		urls = html->urls;
		while (urls)
		{
			hb = (GtkHtmlBit *) urls->data;
			if ((realx > hb->x && realx < (hb->x + hb->width)) &&
				(realy < hb->y && realy > (hb->y - hb->height)))
			{
				GdkCursor *cursor = NULL;

				if (html->tooltip_hb != hb)
				{
					html->tooltip_hb = hb;
					if (html->tooltip_timer != -1)
						gtk_timeout_remove(html->tooltip_timer);
					if (html->tooltip_window)
					{
						gtk_widget_destroy(html->tooltip_window);
						html->tooltip_window = NULL;
					}
					html->tooltip_timer =
						gtk_timeout_add(HTML_TOOLTIP_DELAY,
										gtk_html_tooltip_timeout, html);
				}

				cursor = gdk_cursor_new(GDK_HAND2);
				gdk_window_set_cursor(html->html_area, cursor);
				gdk_cursor_destroy(cursor);

				return TRUE;
			}
			urls = urls->next;
		}
		if (html->tooltip_timer != -1)
			gtk_timeout_remove(html->tooltip_timer);
		if (html->tooltip_window)
		{
			gtk_widget_destroy(html->tooltip_window);
			html->tooltip_window = NULL;
		}


		html->tooltip_hb = NULL;
		gdk_window_set_cursor(html->html_area, NULL);


	}

	return TRUE;
}

static gint gtk_html_button_release(GtkWidget * widget, GdkEventButton * event)
{
	GtkHtml *html;

	html = GTK_HTML(widget);

	if (html->frozen > 0)
		return TRUE;

	if (event->button == 1)
	{
		int realx,
		  realy;
		GtkHtmlBit *hb;
		GList *urls = html->urls;

		realx = event->x;
		realy = event->y + html->yoffset;
		if (realx != html->start_sel_x || realy != html->start_sel_y)
		{
			if (gtk_selection_owner_set(widget,
										GDK_SELECTION_PRIMARY, event->time))
			{
			}
			else
			{
			}
		}
		else
		{
			if (gdk_selection_owner_get(GDK_SELECTION_PRIMARY) ==
				widget->window)
				gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY,
										event->time);


			while (urls)
			{
				void open_url_nw(GtkWidget * w, char *url);
				hb = (GtkHtmlBit *) urls->data;
				if ((realx > hb->x && realx < (hb->x + hb->width)) &&
					(realy < hb->y && realy > (hb->y - hb->height)))
				{
					open_url_nw(NULL, hb->url);
					// else
					//         open_url(NULL, hb->url);
					break;
				}
				urls = urls->next;
			}
		}
	}
	return TRUE;
}



static gint gtk_html_button_press(GtkWidget * widget, GdkEventButton * event)
{
	GtkHtml *html;
	gfloat value;


	html = GTK_HTML(widget);
	value = html->vadj->value;

	if (html->frozen > 0)
		return TRUE;

	if (event->button == 4)
	{
		value -= html->vadj->step_increment;
		if (value < html->vadj->lower)
			value = html->vadj->lower;
		gtk_adjustment_set_value(html->vadj, value);
	}
	else if (event->button == 5)
	{
		value += html->vadj->step_increment;
		if (value > html->vadj->upper)
			value = html->vadj->upper;
		gtk_adjustment_set_value(html->vadj, value);

	}
	else if (event->button == 1)
	{
		GList *hbits = g_list_last(html->html_bits);
		int realx,
		  realy;
		GtkHtmlBit *hb;

		realx = event->x;
		realy = event->y + html->yoffset;

		html->start_sel_x = realx;
		html->start_sel_y = realy;

		if (!hbits)
			return TRUE;

		if (html->selected_text)
		{
			gboolean forcedraw = FALSE;
			hbits = html->html_bits;
			g_free(html->selected_text);
			html->selected_text = NULL;
			html->start_sel = NULL;
			html->end_sel = NULL;
			html->num_start = 0;
			html->num_end = 0;
			while (hbits)
			{
				hb = (GtkHtmlBit *) hbits->data;
				if (hb->was_selected || forcedraw) {
					gtk_html_draw_bit(html, hb, 1);
					forcedraw = TRUE;
				}
				hbits = hbits->next;
			}
			hbits = g_list_last(html->html_bits);
		}

		hb = (GtkHtmlBit *) hbits->data;
		if (realy > hb->y)
		{
			if (hb->text)
				html->num_start = strlen(hb->text) - 1;
			else
				html->num_start = 0;
			html->start_sel = hb;
		}
		else
			while (hbits)
			{
				hb = (GtkHtmlBit *) hbits->data;
				if ((realy < hb->y && realy > (hb->y - hb->height)) &&
					(realx > hb->x + hb->width))
				{
					if (hb->type != HTML_BIT_TEXT)
					{
						html->num_end = 0;
						html->end_sel = hb;
						break;
					}

					if (hb->text)
						html->num_start = strlen(hb->text) - 1;
					else
						html->num_start = 0;

					html->start_sel = hb;
					break;
				}
				else if ((realx > hb->x && realx < (hb->x + hb->width)) &&
						 (realy < hb->y && realy > (hb->y - hb->height)))
				{
					int i,
					  len;
					int w = realx - hb->x;

					if (hb->type != HTML_BIT_TEXT)
					{
						html->num_end = 0;
						html->end_sel = hb;
						break;
					}

					if (hb->text)
						len = strlen(hb->text);
					else
						len = 0;

					for (i = 1; i <= len; i++)
					{
						if (gdk_text_measure(hb->font, hb->text, i) > w)
						{
							html->num_start = i - 1;
							html->start_sel = hb;
							break;
						}
					}
					break;
				}
				hbits = hbits->prev;
			}
	}
	else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
	{
		GtkHtmlBit *hb = NULL;
		int realx,
		  realy;
		GList *urls;

		realx = event->x;
		realy = event->y + html->yoffset;

		urls = html->urls;
		while (urls)
		{
			hb = (GtkHtmlBit *) urls->data;
			if ((realx > hb->x && realx < (hb->x + hb->width)) &&
				(realy < hb->y && realy > (hb->y - hb->height)))
			{
				break;
			}
			urls = urls->next;
			hb = NULL;
		}

		if (hb != NULL)
		{
			
			  GtkWidget *menu, *button;
			   
			   menu = gtk_menu_new();
			   
			   if (web_browser == BROWSER_NETSCAPE) {
			   
			   button = gtk_menu_item_new_with_label(_("Open URL in existing window"));
			   gtk_signal_connect(GTK_OBJECT(button), "activate",
			   GTK_SIGNAL_FUNC(open_url), hb->url);
			   gtk_menu_append(GTK_MENU(menu), button);
			   gtk_widget_show(button);
			   
			   }
			   
			   
			   button = gtk_menu_item_new_with_label(_("Open URL in new window"));
			   gtk_signal_connect(GTK_OBJECT(button), "activate",
			   GTK_SIGNAL_FUNC(open_url_nw), hb->url);
			   gtk_menu_append(GTK_MENU(menu), button);
			   gtk_widget_show(button);
			   
			   if (web_browser == BROWSER_NETSCAPE) {
			   
			   button = gtk_menu_item_new_with_label(_("Add URL as bookmark"));
			   gtk_signal_connect(GTK_OBJECT(button), "activate",
			   GTK_SIGNAL_FUNC(add_bookmark), hb->url);
			   gtk_menu_append(GTK_MENU(menu), button);
			   gtk_widget_show(button);
			   
			   }
			   
			   gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
			   event->button, event->time);                 
			   
		}
	}

	return TRUE;
}


static void gtk_html_draw_bit(GtkHtml * html, GtkHtmlBit * hb, int redraw)
{
	int mypos,
	  epos,
	  spos;
	GdkGC *gc = html->gc;
	int shift;
	GtkStateType selected_state;
	GtkWidget *widget = GTK_WIDGET(html);
	GdkRectangle area;
	GList *hbits;
	GtkHtmlBit *hbit;
	
	if (html->frozen > 0)
		return;

	if (hb->type == HTML_BIT_TEXT)
	{

		if (!(hb->text))
			return;
		/* this is possible, don't comment it out >:P */
		if (!strlen(hb->text))
			return;

		mypos = g_list_index(html->html_bits, hb);
		epos = g_list_index(html->html_bits, html->end_sel);
		spos = g_list_index(html->html_bits, html->start_sel);

		if (((html->end_sel == NULL) || (html->start_sel == NULL)) ||
			((epos < mypos) && (spos < mypos)) ||
			((epos > mypos) && (spos > mypos)))
		{
			selected_state = GTK_STATE_NORMAL;
		}
		else
		{
			selected_state = GTK_STATE_SELECTED;
		}

		gdk_text_extents(hb->font, hb->text, 1, &shift, NULL, NULL, NULL, NULL);

		if (selected_state == GTK_STATE_SELECTED)
		{
			int schar = 0,
			  echar = 0;
			int startx = 0,
			  xwidth = 0;

			if (epos > spos ||
				(epos == spos && html->num_end >= html->num_start))
			{
				if (mypos == epos)
				{
					echar = html->num_end;
					xwidth =
						gdk_text_width(hb->font, hb->text, html->num_end + 1);
				}
				else
				{
					echar = strlen(hb->text);
					xwidth = hb->width;
				}
				if (mypos == spos)
				{
					schar = html->num_start;
					startx =
						gdk_text_width(hb->font, hb->text, html->num_start);
					xwidth -= startx;
				}
			}
			else
			{
				if (mypos == spos)
				{
					echar = html->num_start;
					xwidth =
						gdk_text_width(hb->font, hb->text,
										 html->num_start + 1);
				}
				else
				{
					echar = strlen(hb->text);
					xwidth = hb->width;
				}
				if (mypos == epos)
				{
					schar = html->num_end;
					startx =
						gdk_text_width(hb->font, hb->text, html->num_end);
					xwidth -= startx;
				}
			}

			if (!redraw && echar == hb->sel_e && schar == hb->sel_s)
				return;

			hb->sel_e = echar;
			hb->sel_s = schar;

			startx += hb->x;

			area.x = hb->x - html->xoffset;
			area.y = hb->y - hb->height + 3 - html->yoffset;
			area.width = hb->width + 2;
			area.height = hb->height;
			clear_area(html, &area);

			gtk_paint_flat_box(widget->style, html->html_area,
							   selected_state, GTK_SHADOW_NONE,
							   NULL, widget, "text",
							   startx,
							   hb->y - hb->height + 3 - html->yoffset,
							   xwidth + 2, hb->height);
			hb->was_selected = 1;
		}
		else if (hb->was_selected)
		{
			area.x = hb->x - html->xoffset;
			area.y = hb->y - hb->height + 3 - html->yoffset;
			area.width = hb->width + 2;
			area.height = hb->height;
			clear_area(html, &area);

			hb->sel_e = -1;
			hb->sel_s = -1;

			hb->was_selected = 0;
		}

		if (selected_state == GTK_STATE_SELECTED && (mypos == epos
													 || mypos == spos))
		{
			char *s = hb->text;
			int num = 0,
			  width = 0,
			  fsel = 0,
			  esel = strlen(hb->text);
			int lbearing,
			  rbearing,
			  w;

			if (epos > spos ||
				(epos == spos && html->num_end >= html->num_start))
			{
				if (mypos == epos)
					esel = html->num_end;
				if (mypos == spos)
					fsel = html->num_start;
			}
			else
			{
				if (mypos == spos)
					esel = html->num_start;
				if (mypos == epos)
					fsel = html->num_end;
			}

			while (*s)
			{

				if (num < fsel || num > esel)
					selected_state = GTK_STATE_NORMAL;
				else
					selected_state = GTK_STATE_SELECTED;
				if (hb->fore != NULL)
					gdk_gc_set_foreground(gc, hb->fore);
				else
					gdk_gc_set_foreground(gc,
										  &widget->style->fg[selected_state]);

				gdk_gc_set_font(gc, hb->font);

				gdk_text_extents(hb->font, s, 1, &lbearing, &rbearing, &w, NULL,
								 NULL);

				gdk_draw_text(html->html_area, hb->font, gc,
							  shift + hb->x + width, hb->y - html->yoffset, s,
							  1);

				if (hb->uline)
					gdk_draw_line(html->html_area, gc, shift + hb->x + width,
								  hb->y - html->yoffset,
								  shift + hb->x + width + w,
								  hb->y - html->yoffset);

				if (hb->strike)
					gdk_draw_line(html->html_area, gc, shift + hb->x + width,
								  hb->y - html->yoffset - (hb->height / 3),
								  shift + hb->x + width + w,
								  hb->y - html->yoffset - (hb->height / 3));

				width += w;

				s++;
				num++;
			}


		}
		else
		{
			/*my stuff here*/
			
			if(!hb->was_selected)
			{
				area.x = hb->x - html->xoffset;
				area.y = hb->y - hb->height + 3 - html->yoffset;
				area.width = hb->width + 2;
				area.height = hb->height;
				clear_area(html, &area);
			}

			/*end my stuff*/

			if (hb->text && hb->back != NULL) {
				int hwidth, hheight, tmpcnt, max_ascent, max_descent;
				
				/* get the gtkhtmlbit at the start of each line, and use it as the starting point to calculate the
				max height of each line and set the background-rectangle's size accordingly */
				/* FIXME: should also include a method of eliminating 'gaps' between different line using
				different text sizes */
				if (!start_of_line)
					start_of_line = hb;
				else
				{
					hbits = g_list_find(html->html_bits, hb);
					if (hbits->prev)
					{
						hbit = hbits->prev->data;
						if (hbit && hbit->newline)
								start_of_line = hb;
					}
					else
						start_of_line = hb;
				}
				
				get_line_max_extents(html, start_of_line, &max_ascent, &max_descent);
				gdk_window_get_size(html->html_area, &hwidth, &hheight);
				gdk_gc_set_foreground(gc, hb->back);
				/* we use a 2-pixel window border */
				if (hb->x < 2)
					hb->x = 2;

				gdk_draw_rectangle(html->html_area, gc, TRUE /* filled */,
							hb->x, hb->y - html->yoffset - max_ascent - max_descent - 2,
							hwidth - shift - hb->x + 1, max_ascent + max_descent + 8);
				for (tmpcnt = 1; tmpcnt < hb->newline; tmpcnt++) {
					int eoff = max_ascent + max_descent + 2;
					eoff *= tmpcnt;
					gdk_draw_rectangle(html->html_area, gc, TRUE,
							2, hb->y - html->yoffset - max_ascent - max_descent - 2 + eoff,
							hwidth, max_ascent + max_descent + 2);
				}
			}

			if (hb->fore != NULL)
				gdk_gc_set_foreground(gc, hb->fore);
			else
				gdk_gc_set_foreground(gc, &widget->style->fg[selected_state]);

			gdk_gc_set_font(gc, hb->font);

			gdk_draw_string(html->html_area, hb->font, gc, shift + hb->x,
							hb->y - html->yoffset, hb->text);
		
			if (hb->uline)
				gdk_draw_line(html->html_area, gc, shift + hb->x,
							  hb->y - html->yoffset,
							  hb->x + gdk_string_measure(hb->font, hb->text),
							  hb->y - html->yoffset);

			if (hb->strike)
				gdk_draw_line(html->html_area, gc, shift + hb->x,
							  hb->y - html->yoffset - (hb->height / 3),
							  hb->x + gdk_string_measure(hb->font, hb->text),
							  hb->y - html->yoffset - (hb->height / 3));

		}
	}
	else if (hb->type == HTML_BIT_SEP)
	{
		if (hb->back != NULL) {
			int hwidth, hheight, hei, tmpcnt;
			
			if (!start_of_line)
				start_of_line = hb;
			else
			{
				hbits = g_list_find(html->html_bits, hb);
				if (hbits && hbits->prev)
				{
					hbit = hbits->prev->data;
					if (hbit && hbit->newline)
							start_of_line = hb;
				}
				else
					start_of_line = hb;
			}
					
			hei = get_line_max_height(html, start_of_line);

			gdk_window_get_size(html->html_area, &hwidth, &hheight);
			gdk_gc_set_foreground(gc, hb->back);
			gdk_draw_rectangle(html->html_area, gc, TRUE,
						2, hb->y - html->yoffset,
						hwidth, 5);
			for (tmpcnt = 0; tmpcnt < hb->newline; tmpcnt++) {
				int eoff = hei + hei + 2;
				eoff *= tmpcnt;
				eoff += 5;
				gdk_draw_rectangle(html->html_area, gc, TRUE,
						2, hb->y - html->yoffset + eoff,
						hwidth, hei + hei + 2);
			}
		}

		if (hb->fore != NULL)
			gdk_gc_set_foreground(gc, hb->fore);
		else
			gdk_gc_set_foreground(gc, &widget->style->fg[GTK_STATE_NORMAL]);

		gdk_draw_line(html->html_area, gc, hb->x + 2,
					  hb->y - html->yoffset - (hb->height / 2 - 1),
					  hb->x + hb->width,
					  hb->y - html->yoffset - (hb->height / 2 - 1));

	}
	else if (hb->type == HTML_BIT_PIXMAP)
	{
		area.x = hb->x - html->xoffset;
		area.y = hb->y - hb->height + 5 - html->yoffset;
		area.width = hb->width;
		area.height = hb->height;
		clear_area(html, &area);
		if (hb->back != NULL) {
			int hwidth, hheight, hei, tmpcnt;
			
			if (!start_of_line)
				start_of_line = hb;
			else
			{
				hbits = g_list_find(html->html_bits, hb);
				if (hbits && hbits->prev)
				{
					hbit = hbits->prev->data;
					if (hbit && hbit->newline)
							start_of_line = hb;
				}
				else
					start_of_line = hb;
			}
					
			hei = get_line_max_height(html, start_of_line);
			
			gdk_window_get_size(html->html_area, &hwidth, &hheight);
			gdk_gc_set_foreground(gc, hb->back);
			for (tmpcnt = 0; tmpcnt < hb->newline; tmpcnt++) {
				int eoff = hei + hei + 2;
				eoff *= tmpcnt;
				eoff += 5;
				gdk_draw_rectangle(html->html_area, gc, TRUE,
						2, hb->y - html->yoffset + eoff,
						hwidth, hei + hei + 2);
			}
		}

		if (hb->fore != NULL)
			gdk_gc_set_foreground(gc, hb->fore);
		else
			gdk_gc_set_foreground(gc, &widget->style->fg[GTK_STATE_NORMAL]);

		gdk_draw_pixmap(html->html_area, gc, hb->pm, 0, 0, hb->x,
						hb->y - html->yoffset - (hb->height) + 4, -1, -1);
	}
}



gint compare_types(GtkHtmlBit * hb, GtkHtmlBit * hb2)
{
	/*
	 * In this function, it's OK to accidently return a
	 * * 0, but will cause problems on an accidental 1 
	 */

	if (!hb || !hb2)
		return 0;


	if (hb->uline != hb2->uline)
		return 0;
	if (hb->strike != hb2->strike)
		return 0;
	if (hb->font && hb2->font)
	{
		if (!gdk_font_equal(hb->font, hb2->font))
			return 0;
	}
	else if (hb->font && !hb2->font)
	{
		return 0;
	}
	else if (!hb->font && hb2->font)
	{
		return 0;
	}
	if (hb->type != hb2->type)
		return 0;

	if (hb->fore && hb2->fore)
	{
		if (!gdk_color_equal(hb->fore, hb2->fore))
			return 0;
	}
	else if (hb->fore && !hb2->fore)
	{
		return 0;
	}
	else if (!hb->fore && hb2->fore)
	{
		return 0;
	}

	if (hb->back && hb2->back)
	{
		if (!gdk_color_equal(hb->back, hb2->back))
			return 0;
	}
	else if (hb->back && !hb2->back)
	{
		return 0;
	}
	else if (!hb->back && hb2->back)
	{
		return 0;
	}

	if ((hb->url != NULL && hb2->url == NULL) ||
		(hb->url == NULL && hb2->url != NULL))
		return 0;

	if (hb->url != NULL && hb2->url != NULL)
		if (strcasecmp(hb->url, hb2->url))
			return 0;

	return 1;
}

static gint html_bit_is_onscreen(GtkHtml * html, GtkHtmlBit * hb)
{
	gint width,
	  height;

	gdk_window_get_size(html->html_area, &width, &height);

	if (hb->y < html->yoffset)
	{
		return 0;
	}

	if ((hb->y - hb->height) > (html->yoffset + height))
	{
		return 0;
	}
	return 1;
}

static void draw_cursor(GtkHtml * html)
{
	if (html->editable &&
		html->cursor_hb &&
		GTK_WIDGET_DRAWABLE(html) &&
		html_bit_is_onscreen(html, html->cursor_hb))
	{
		gint x,
		  y;
		gint width;

		GdkFont *font = html->cursor_hb->font;

		gdk_text_extents(font, html->cursor_hb->text, html->cursor_pos, NULL,
						 NULL, &width, NULL, NULL);

		gdk_gc_set_foreground(html->gc,
							  &GTK_WIDGET(html)->style->text[GTK_STATE_NORMAL]);

		y = html->cursor_hb->y - html->yoffset;
		x = html->cursor_hb->x + width;


		gdk_draw_line(html->html_area, html->gc, x, y, x, y - font->ascent);

	}
}

static void undraw_cursor(GtkHtml * html)
{
	if (html->editable &&
		html->cursor_hb &&
		GTK_WIDGET_DRAWABLE(html) &&
		html_bit_is_onscreen(html, html->cursor_hb))
	{
		gint x,
		  y;
		gint width;
		GdkRectangle area;

		GdkFont *font = html->cursor_hb->font;

		gdk_text_extents(font, html->cursor_hb->text, html->cursor_pos, NULL,
						 NULL, &width, NULL, NULL);

		y = html->cursor_hb->y - html->yoffset;
		x = html->cursor_hb->x + width;

		area.x = x;
		area.y = y - font->ascent;
		area.height = font->ascent + 1;
		area.width = 1;


		clear_area(html, &area);

		gtk_html_draw_bit(html, html->cursor_hb, 1);


	}
}


static void expose_html(GtkHtml * html, GdkRectangle * area, gboolean cursor)
{
	GList *hbits;
	GtkHtmlBit *hb;
	gint width,
	  height;
	gint realy;


	if (html->frozen > 0)
		return;


	hbits = html->html_bits;

	gdk_window_get_size(html->html_area, &width, &height);

	realy = area->y + html->yoffset;

	/* this is needed since background colors draw across the entire window width
		if anyone knows of a cleaner way to work bg colors, please submit code =) */
	area->x = 0;
	area->width = width;
	
	clear_area(html, area);

	while (hbits)
	{

		hb = (GtkHtmlBit *) hbits->data;

		if (html_bit_is_onscreen(html, hb))
			gtk_html_draw_bit(html, hb, 1);


		hbits = hbits->next;
	}
}

static void resize_html(GtkHtml * html)
{
	GList *hbits = html->html_bits;
	GList *html_bits = html->html_bits;
	GtkHtmlBit *hb,
	 *hb2;
	char *str;
	gint height;

	if (!hbits)
		return;


	html->html_bits = NULL;

	html->current_x = 0;
	html->current_y = 0;

	html->vadj->upper = 0;

	gtk_html_freeze(html);

	while (hbits)
	{
		hb = (GtkHtmlBit *) hbits->data;
		if (hb->type == HTML_BIT_SEP)
		{

			gtk_html_add_seperator(html, hb->font, hb->fore, hb->back);

			g_free(hb);

			hbits = hbits->next;
			continue;
		}
		if (hb->type == HTML_BIT_PIXMAP)
		{

			gtk_html_add_pixmap(html, hb->pm, hb->fit, hb->newline);

			g_free(hb);

			hbits = hbits->next;
			continue;
		}

		if (hb->newline)
		{
			int i;

			if (!hb->text)
			{
				hb->text = g_malloc(1);
				hb->text[0] = 0;
			}
			for (i = 0; i < hb->newline; i++)
			{
				str = hb->text;
				hb->text = g_strconcat(str, "\n", NULL);
				if (str) g_free(str);
			}
		}

		if (hbits->next)
		{
			hb2 = (GtkHtmlBit *) hbits->next->data;
		}
		else
		{
			hb2 = NULL;
		}



		if (!hb->newline && compare_types(hb, hb2))
		{
			str = hb2->text;
			hb2->text = g_strconcat(hb->text, hb2->text, NULL);
			if (str) g_free(str);
			hb2 = NULL;
		}
		else if (hb->text)
		{
			gtk_html_add_text(html, hb->font, hb->fore, hb->back,
							  hb->text, strlen(hb->text), hb->uline, hb->strike,
							  hb->url);
		}



		/*
		 * Font stays, so do colors (segfaults if I free) 
		 */
		if (hb->fore)
			gdk_color_free(hb->fore);
		if (hb->back)
			gdk_color_free(hb->back);
		if (hb->text)
			g_free(hb->text);
		if (hb->url)
			g_free(hb->url);

		g_free(hb);

		hbits = hbits->next;
	}

	g_list_free(html_bits);


	gtk_html_thaw(html);

	gdk_window_get_size(html->html_area, NULL, &height);
	gtk_adjustment_set_value(html->vadj, html->vadj->upper - height);

}

static GdkGC *create_bg_gc(GtkHtml * html)
{
	GdkGCValues values;

	values.tile = GTK_WIDGET(html)->style->bg_pixmap[GTK_STATE_NORMAL];
	values.fill = GDK_TILED;

	return gdk_gc_new_with_values(html->html_area, &values,
								  GDK_GC_FILL | GDK_GC_TILE);
}

static void clear_area(GtkHtml * html, GdkRectangle * area)
{
	GtkWidget *widget = GTK_WIDGET(html);
	gint x,
	  y;


	if (html->transparent)
	{
		if (html->pm == NULL)
			html->pm = get_desktop_pixmap(widget);

		if (html->pm == NULL)
			return;

		if (html->bg_gc == NULL)
		{
			GdkGCValues values;

			values.tile = html->pm;
			values.fill = GDK_TILED;

			html->bg_gc = gdk_gc_new_with_values(html->html_area, &values,
												 GDK_GC_FILL | GDK_GC_TILE);

		}

		gdk_window_get_deskrelative_origin(html->html_area, &x, &y);

		gdk_draw_pixmap(html->html_area, html->bg_gc, html->pm,
						x + area->x, y + area->y, area->x, area->y, area->width,
						area->height);

		return;

	}
	if (html->bg_gc)
	{

		gint width,
		  height;

		gdk_window_get_size(widget->style->bg_pixmap[GTK_STATE_NORMAL], &width,
							&height);

		gdk_gc_set_ts_origin(html->bg_gc,
							 (-html->xoffset) % width,
							 (-html->yoffset) % height);

		gdk_draw_rectangle(html->html_area, html->bg_gc, TRUE,
						   area->x, area->y, area->width, area->height);
	}
	else
		gdk_window_clear_area(html->html_area, area->x, area->y, area->width,
							  area->height);
}




static void gtk_html_destroy(GtkObject * object)
{
	GtkHtml *html;

	g_return_if_fail(object != NULL);
	g_return_if_fail(GTK_IS_HTML(object));

	html = (GtkHtml *) object;


	gtk_signal_disconnect_by_data(GTK_OBJECT(html->hadj), html);
	gtk_signal_disconnect_by_data(GTK_OBJECT(html->vadj), html);

	if (html->timer)
	{
		gtk_timeout_remove(html->timer);
		html->timer = 0;
	}

	if (html->tooltip_timer)
	{
		gtk_timeout_remove(html->tooltip_timer);
		html->tooltip_timer = -1;
	}


	GTK_OBJECT_CLASS(parent_class)->destroy(object);

}

static void gtk_html_finalize(GtkObject * object)
{
	GList *hbits;
	GtkHtml *html;
	GtkHtmlBit *hb;


	g_return_if_fail(object != NULL);
	g_return_if_fail(GTK_IS_HTML(object));

	html = (GtkHtml *) object;

	gtk_object_unref(GTK_OBJECT(html->hadj));
	gtk_object_unref(GTK_OBJECT(html->vadj));

	hbits = html->html_bits;

	while (hbits)
	{
		hb = (GtkHtmlBit *) hbits->data;
		if (hb->fore)
			gdk_color_free(hb->fore);
		if (hb->back)
			gdk_color_free(hb->back);
		if (hb->text)
			g_free(hb->text);
		if (hb->url)
			g_free(hb->url);
		if (hb->pm)
			gdk_pixmap_unref(hb->pm);

		g_free(hb);
		hbits = hbits->next;
	}
	if (html->html_bits)
		g_list_free(html->html_bits);

	if (html->urls)
		g_list_free(html->urls);

	if (html->selected_text)
		g_free(html->selected_text);

	if (html->gc)
		gdk_gc_destroy(html->gc);

	if (html->bg_gc)
		gdk_gc_destroy(html->bg_gc);

	if (html->tooltip_window)
		gtk_widget_destroy(html->tooltip_window);

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

static void gtk_html_realize(GtkWidget * widget)
{
	GtkHtml *html;
	GdkWindowAttr attributes;
	gint attributes_mask;

	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_HTML(widget));

	html = GTK_HTML(widget);
	GTK_WIDGET_SET_FLAGS(html, GTK_REALIZED);

	attributes.window_type = GDK_WINDOW_CHILD;
	attributes.x = widget->allocation.x;
	attributes.y = widget->allocation.y;
	attributes.width = widget->allocation.width;
	attributes.height = widget->allocation.height;
	attributes.wclass = GDK_INPUT_OUTPUT;
	attributes.visual = gtk_widget_get_visual(widget);
	attributes.colormap = gtk_widget_get_colormap(widget);
	attributes.event_mask = gtk_widget_get_events(widget);
	attributes.event_mask |= (GDK_EXPOSURE_MASK |
							  GDK_BUTTON_PRESS_MASK |
							  GDK_BUTTON_RELEASE_MASK |
							  GDK_BUTTON_MOTION_MASK |
							  GDK_ENTER_NOTIFY_MASK |
							  GDK_LEAVE_NOTIFY_MASK |
							  GDK_POINTER_MOTION_MASK |
							  GDK_POINTER_MOTION_HINT_MASK |
							  GDK_VISIBILITY_NOTIFY_MASK | GDK_KEY_PRESS_MASK);

	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

	widget->window =
		gdk_window_new(gtk_widget_get_parent_window(widget), &attributes,
					   attributes_mask);
	gdk_window_set_user_data(widget->window, html);

	attributes.x = (widget->style->klass->xthickness + BORDER_WIDTH);
	attributes.y = (widget->style->klass->ythickness + BORDER_WIDTH);
	attributes.width =
		MAX(1, (gint) widget->allocation.width - (gint) attributes.x * 2);
	attributes.height =
		MAX(1, (gint) widget->allocation.height - (gint) attributes.y * 2);

	html->html_area =
		gdk_window_new(widget->window, &attributes, attributes_mask);
	gdk_window_set_user_data(html->html_area, html);

	widget->style = gtk_style_attach(widget->style, widget->window);

	/*
	 * Can't call gtk_style_set_background here because it's handled specially 
	 */
	gdk_window_set_background(widget->window,
							  &widget->style->base[GTK_STATE_NORMAL]);
	gdk_window_set_background(html->html_area,
							  &widget->style->base[GTK_STATE_NORMAL]);

	if (widget->style->bg_pixmap[GTK_STATE_NORMAL])
		html->bg_gc = create_bg_gc(html);

	html->gc = gdk_gc_new(html->html_area);
	gdk_gc_set_exposures(html->gc, TRUE);
	gdk_gc_set_foreground(html->gc, &widget->style->text[GTK_STATE_NORMAL]);

	gdk_window_show(html->html_area);

}

static void gtk_html_style_set(GtkWidget * widget, GtkStyle * previous_style)
{
	GtkHtml *html;

	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_HTML(widget));

	html = GTK_HTML(widget);
	if (GTK_WIDGET_REALIZED(widget))
	{
		gdk_window_set_background(widget->window,
								  &widget->style->base[GTK_STATE_NORMAL]);
		gdk_window_set_background(html->html_area,
								  &widget->style->base[GTK_STATE_NORMAL]);

		if (html->bg_gc)
		{
			gdk_gc_destroy(html->bg_gc);
			html->bg_gc = NULL;
		}

		if (widget->style->bg_pixmap[GTK_STATE_NORMAL])
		{
			html->bg_gc = create_bg_gc(html);
		}

	}
}

static void gtk_html_unrealize(GtkWidget * widget)
{
	GtkHtml *html;

	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_HTML(widget));

	html = GTK_HTML(widget);

	gdk_window_set_user_data(html->html_area, NULL);
	gdk_window_destroy(html->html_area);
	html->html_area = NULL;

	gdk_gc_destroy(html->gc);
	html->gc = NULL;

	if (html->bg_gc)
	{
		gdk_gc_destroy(html->bg_gc);
		html->bg_gc = NULL;
	}

	if (GTK_WIDGET_CLASS(parent_class)->unrealize)
		(*GTK_WIDGET_CLASS(parent_class)->unrealize) (widget);
}


void gtk_html_add_pixmap(GtkHtml * html, GdkPixmap * pm, int fit, int newline)
{
	GtkHtmlBit *last_hb;
	GtkHtmlBit *hb = g_new0(GtkHtmlBit, 1);
	GdkWindowPrivate *private = (GdkWindowPrivate *) pm;
	int width, height;

	last_hb = (GtkHtmlBit *) g_list_last(html->html_bits)->data;

	/* wrap pixmaps */
	gdk_window_get_size(html->html_area, &width, &height);
	if ((html->current_x + private->width) >= width) {
		html->current_x = 0;
	}
			
	hb->fit = fit;
	html->current_x += 2;
	hb->x = html->current_x;
	hb->y = html->current_y;
	if (fit)
		hb->height = last_hb->height;
	else
		hb->height = private->height;
	hb->type = HTML_BIT_PIXMAP;
	hb->width = private->width;
	hb->text = NULL;
	hb->url = NULL;
	if (last_hb->fore) hb->fore = gdk_color_copy(last_hb->fore);
	else hb->fore = NULL;
	if (last_hb->back) hb->back = gdk_color_copy(last_hb->back);
	else hb->back = NULL;
	hb->font = last_hb->font;
	hb->uline = 0;
	hb->strike = 0;
	hb->was_selected = 0;
	hb->newline = newline;
	hb->pm = pm;

	if (html->current_x == BORDER_WIDTH)
	{
		html->current_y += hb->height + 3;
		hb->y += hb->height + 3;
	}


	html->current_x += hb->width + 1;

	gtk_html_draw_bit(html, hb, 1);

	if (hb->newline)
		html->current_x = 0;

	html->html_bits = g_list_append(html->html_bits, hb);


}

static void gtk_html_add_seperator(GtkHtml * html, GdkFont *font, GdkColor *fore, GdkColor *back)
{
	GtkHtmlBit *hb = g_new0(GtkHtmlBit, 1);
	gint width,
	  height;

	html->current_x = 0;
	html->current_y += 5;

	gdk_window_get_size(html->html_area, &width, &height);

	hb->x = html->current_x;
	hb->y = html->current_y;
	hb->height = 5;
	hb->type = HTML_BIT_SEP;
	hb->width =
		width -
		GTK_SCROLLED_WINDOW(GTK_WIDGET(html)->parent)->vscrollbar->allocation.
		width - 10;
	hb->text = NULL;
	hb->url = NULL;
	if (fore)
		hb->fore = gdk_color_copy(fore);
	else
		hb->fore = NULL;
	
	if (back)
		hb->back = gdk_color_copy(back);
	else
		hb->back = NULL;
	hb->font = font;
	hb->uline = 0;
	hb->strike = 0;
	hb->was_selected = 0;
	hb->newline = 0;
	hb->pm = NULL;

	gtk_html_draw_bit(html, hb, 1);

	html->html_bits = g_list_append(html->html_bits, hb);

}

static void gtk_html_add_text(GtkHtml * html,
							  GdkFont * cfont,
							  GdkColor * fore,
							  GdkColor * back,
							  char *chars,
							  gint length, gint uline, gint strike, char *url)
{
	char *nextline = NULL,
	 *c,
	 *text,
	 *tmp;
	GdkGC *gc;
	int nl = 0,
	  nl2 = 0;
	int maxwidth, ascent, descent;
	gint lb;
	GList *hbits;
	size_t num = 0;
	int i,
	  height;
	GtkHtmlBit *hb;
	gint hwidth,
	  hheight;

	if (length == 1 && chars[0] == '\n')
	{
		GtkHtmlBit *h;
		hbits = g_list_last(html->html_bits);
		if (!hbits)
			return;
		/*
		 * I realize this loses a \n sometimes
		 * * if it's the first thing in the widget.
		 * * so fucking what. 
		 */

		h = (GtkHtmlBit *) hbits->data;
		h->newline++;
		if (html->current_x > 0)
			html->current_x = 0;
		else
			html->current_y += cfont->ascent + cfont->descent + 5;
		return;
	}



	c = text = g_malloc(length + 2);
	strncpy(text, chars, length);
	text[length] = 0;


	gc = html->gc;

	if (gc == NULL)
		gc = html->gc = gdk_gc_new(html->html_area);

	gdk_gc_set_font(gc, cfont);


	while (*c)
	{
		if (*c == '\n')
		{
			if (*(c + 1) == '\0')
			{
				nl = 1;
				length--;
				c[0] = '\0';
				break;
			}
			if (*c)
			{
				gtk_html_add_text(html, cfont, fore, back, text, num + 1, uline,
								  strike, url);
				tmp = text;
				length -= (num + 1);
				text = g_malloc(length + 2);
				strncpy(text, (c + 1), length);
				text[length] = 0;
				c = text;
				num = 0;
				g_free(tmp);
				continue;
			}
		}

		num++;
		c++;
	}

	/*
	 * Note, yG is chosen because G is damn high, and y is damn low, 
	 */
	/*
	 * it should be just fine. :) 
	 */

	gdk_window_get_size(html->html_area, &hwidth, &hheight);

	num = strlen(text);

	while (GTK_WIDGET(html)->allocation.width < 20)
	{
		while (gtk_events_pending())
			gtk_main_iteration();
	}

	maxwidth = (hwidth - html->current_x - 8);
	/*
	 * HTK_SCROLLED_WINDOW(GTK_WIDGET(layout)->parent)->vscrollbar->allocation.width) - 8; 
	 */

	if ((maxwidth == (hwidth - 8) && gdk_text_measure(cfont, text, num) > 2 * maxwidth) ||
	    gdk_text_measure(cfont, text, num) < 0) {
		int pos = num / 2;
		static int count = 0;
		count ++;
		while (pos < num && (!isspace(text[pos]) || text[pos] == '\n')) pos++;
		if (pos == num) {
			pos = num/2;
			while (pos > 0 && (!isspace(text[pos]) || text[pos] == '\n')) pos--;
			if (!pos) pos = num / 2;
		}
		gtk_html_add_text(html, cfont, fore, back, text, pos + 1, uline, strike, url);
		gtk_html_add_text(html, cfont, fore, back, &text[pos+1], 1, uline, strike, url);
		gtk_html_add_text(html, cfont, fore, back, &text[pos+2], num - pos + 1, uline, strike, url);
		g_free(text);
		count--;
		if (!count) {
			/* FIXME : sometimes we need to add newline, sometimes we don't */
			hbits = g_list_last(html->html_bits);
			if (!hbits)
				return; /* does this ever happen? */
			hb = (GtkHtmlBit *)hbits->data;
			hb->newline++;
		}
		return;
	}

	while (gdk_text_measure(cfont, text, num) > maxwidth)
	{
		if (num > 1)
			num--;
		else
		{
			if (html->current_x != 0) {
				html->current_x = 0;
				if (nl) {
					text[length] = '\n';
					length++;
				}
				gtk_html_add_text(html, cfont, fore, back, text, length, uline, strike, url);
				g_free(text);
				return;
			} else {
				num = strlen (text);
				break;
			}
		}

	}

	gdk_text_extents(cfont, "yG", strlen("yG"), NULL, NULL, NULL, &ascent, &descent);
	height = ascent + descent + 2;
	
	if ((int) (html->vadj->upper - html->current_y) < (int) (height * 2))
	{
		int val;
		val = (height * 2) + html->current_y;
		html->vadj->upper = val;
		adjust_adj(html, html->vadj);
	}


	if (html->current_x == 0)
	{
		html->current_y += height + 6;
		gdk_text_extents(cfont, text, 1, &lb, NULL, NULL, NULL, NULL);
		html->current_x += (2 - lb);
	}
	else if ((hbits = g_list_last(html->html_bits)) != NULL)
	{
		int diff,
		  y;
		hb = (GtkHtmlBit *) hbits->data;
		if (height > hb->height)
		{
			diff = height - hb->height;
			y = hb->y;
			html->current_y += diff;
			while (hbits)
			{
				hb = (GtkHtmlBit *) hbits->data;
				if (hb->y != y)
					break;
				if (hb->type != HTML_BIT_PIXMAP)
					hb->height = height;
				hb->y += diff;  ////////////my thing here /////////////////
				gtk_html_draw_bit(html, hb, FALSE);

				hbits = hbits->prev;
			}
		}
	}




	if (num != strlen(text))
	{
		/*
		 * This is kinda cheesy but it may make things
		 * * much better lookin 
		 */

		for (i=2; (num > i); i++) {
			if (text[num - i] == ' ') {
				num = num - (i - 1);
				nl2 = 1;
				break;
			}
		}

		nextline = g_malloc(length - num + 2);
		strncpy(nextline, (char *) (text + num), length - num);
		nextline[length - num] = 0;
		if (nl)
		{
			nextline[length - num] = '\n';
			nextline[length - num + 1] = 0;
			nl = 0;
		}


		text[num] = 0;
	}


	if (url != NULL) {
		fore = get_color(3355647, gdk_window_get_colormap(html->html_area));
	}

	hb = g_new0(GtkHtmlBit, 1);

	hb->text = g_strdup(text);

	if (fore)
		hb->fore = gdk_color_copy(fore);
	else
		hb->fore = NULL;
	
	if (back)
		hb->back = gdk_color_copy(back);
	else
		hb->back = NULL;
	hb->font = cfont;
	hb->uline = uline;
	hb->strike = strike;
	hb->height = height;
	gdk_text_extents(cfont, text, num, &lb, NULL, &hb->width, NULL, NULL);
	hb->x = html->current_x;
	hb->y = html->current_y;
	hb->type = HTML_BIT_TEXT;
	hb->pm = NULL;
	if (url != NULL)
	{
		uline = 1;
		hb->uline = 1;
		hb->url = g_strdup(url);
	}
	else
	{
		hb->url = NULL;
	}
	html->current_x += hb->width;

	html->html_bits = g_list_append(html->html_bits, hb);
	if (url != NULL)
	{
		html->urls = g_list_append(html->urls, hb);
	}



	gtk_html_draw_bit(html, hb, 1);

	if (nl || nl2)
	{
		if (nl)
			hb->newline = 1;
		html->current_x = 0;
	}
	else
		hb->newline = 0;


	if (nextline != NULL)
	{
		gtk_html_add_text(html, cfont, fore, back, nextline, strlen(nextline),
						  uline, strike, url);
		g_free(nextline);
	}

	g_free(text);
	if (url != NULL)
		g_free(fore);
}

static char * html_strtok( char * input, char delim )
{
	static char * end;
	static char * curr_offset;
	int i;
	int num_quotes=0;

	if( input != NULL)
	{
		curr_offset = input;
		end = input+strlen(input);
	}
	else
	{
		if( curr_offset + strlen(curr_offset) < end )
		{
			curr_offset += strlen(curr_offset) + 1;
		}
		else
		{
			return NULL;
		}
	}
	for( i=0; curr_offset+i < end && 
			  (curr_offset[i] != delim || num_quotes != 0)
		  ; i++ )
	{
		if( curr_offset[i] == '\"' )
		{
			num_quotes = (num_quotes+1)%2;
		}
	}
	curr_offset[i] = '\0';
	return curr_offset;
}


void gtk_html_append_text(GtkHtml * html, char *text, gint options)
{
	GdkColormap *map;
	GdkFont *cfont;
	GdkRectangle area;
	char *ws,
	  tag[BUF_LONG],
	 *c,
	 *url = NULL;
	gint intag = 0,
	  wpos = 0,
	  tpos = 0;
	static gint colorv,
	  bold = 0,
	  italic = 0,
	  fixed = 0,
	  uline = 0,
	  strike = 0,
	  title = 0,
	  height;
	static struct font_state *current = NULL,
	 *tmp;
	static struct font_state def_state = { 3, 0, 0, "", NULL, NULL, NULL };

	if (text == NULL) {
		bold = 0;
		italic = 0;
		fixed = 0;
		uline = 0;
		strike = 0;
		title = 0;
		while (current->next)
		{
			if (current->ownbg)
				g_free(current->bgcol);
			if (current->owncolor)
				g_free(current->color);
			tmp = current;
			current = current->next;
			g_free(tmp);
		}
		return;
	}

	if (!current) current = &def_state;
	map = gdk_window_get_colormap(html->html_area);
	cfont = getfont(current->font, bold, italic, fixed, current->size);
	c = text;

	ws = g_malloc(strlen(text) + 2);

	while (*c)
	{
		if (*c == '<')
		{
			if (!intag)
			{
				ws[wpos] = 0;
				if (wpos)
				{
					if (title)
					{
						if (html->title)
							g_free(html->title);
						html->title = g_strdup(ws);
					}
					else
						gtk_html_add_text(html, cfont, current->color,
										  current->bgcol, ws, strlen(ws), uline,
										  strike, url);
				}
				wpos = 0;
				intag = 1;
			}
			else
			{
				/*
				 * Assuming you NEVER have nested tags
				 * * (and I mean <tag <tag>> by this, not
				 * * <tag><tag2></tag2><tag>..
				 */
				tag[tpos] = 0;
				gtk_html_add_text(html, cfont, current->color, current->bgcol,
								  "<", 1, 0, 0, NULL);
				gtk_html_add_text(html, cfont, current->color, current->bgcol,
								  tag, strlen(tag), 0, 0, NULL);
				tpos = 0;

				tag[0] = *c;
			}
		}
		else if (*c == '>')
		{
			if (intag)
			{
				tag[tpos] = 0;
				if (!strcasecmp(tag, "B"))
					bold = 1;
				else if (!strcasecmp(tag, "STRIKE") || !strcasecmp(tag, "S"))
					strike = 1;
				else if (!strcasecmp(tag, "I"))
					italic = 1;
				else if (!strcasecmp(tag, "U"))
					uline = 1;
				else if (!strcasecmp(tag, "PRE"))
					fixed = 1;
				else if (!strcasecmp(tag, "HR"))
					gtk_html_add_seperator(html, cfont, current->color, current->bgcol);
				else if (!strcasecmp(tag, "/B"))
					bold = 0;
				else if (!strcasecmp(tag, "/STRIKE") || !strcasecmp(tag, "/S"))
					strike = 0;
				else if (!strcasecmp(tag, "/I"))
					italic = 0;
				else if (!strcasecmp(tag, "/U"))
					uline = 0;
				else if (!strcasecmp(tag, "/PRE"))
					fixed = 0;
				else if (!strcasecmp(tag, "TITLE"))
					title = 1;
				else if (!strcasecmp(tag, "/TITLE"))
					title = 0;
				else if (!strncasecmp(tag, "IMG", 3))
				{
					GdkPixmap *legend_i;
					GdkBitmap *legend_m;

					if (strstr(tag, "SRC=\"aol_icon.gif\"") != NULL)
					{
						legend_i = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(html)->window, &legend_m, NULL, aol_icon_xpm);
						gtk_html_add_pixmap(html, legend_i, 0, 0);
					}

					if (strstr(tag, "SRC=\"admin_icon.gif\"") != NULL)
					{
						legend_i = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(html)->window, &legend_m, NULL, admin_icon_xpm);
						gtk_html_add_pixmap(html, legend_i, 0, 0);
					}
					if (strstr(tag, "SRC=\"dt_icon.gif\"") != NULL)
					{
						legend_i = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(html)->window, &legend_m, NULL, dt_icon_xpm);
						gtk_html_add_pixmap(html, legend_i, 0, 0);
					}
					if (strstr(tag, "SRC=\"free_icon.gif\"") != NULL)
					{
						legend_i = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(html)->window, &legend_m, NULL, free_icon_xpm);
						gtk_html_add_pixmap(html, legend_i, 0, 0);
					}
				}
				else if (!strcasecmp(tag, "H3"))
				{
					current = push_state(current);
					current->size = 4;
				}
				else if (!strcasecmp(tag, "/H3"))
				{
					gtk_html_add_text(html, cfont, current->color,
									  current->bgcol, "\n", 1, 0, 0, NULL);

					if (current->next)
					{
						if (current->ownbg)
							g_free(current->bgcol);
						if (current->owncolor)
							g_free(current->color);
						tmp = current;
						current = current->next;
						g_free(tmp);
					}
				}
				else if (!strcasecmp(tag, "TABLE"))
				{
				}
				else if (!strcasecmp(tag, "/TABLE"))
				{
				}
				else if (!strcasecmp(tag, "TR"))
				{
				}
				else if (!strcasecmp(tag, "/TR"))
				{
				}
				else if (!strcasecmp(tag, "/TD"))
				{
				}
				else if (!strcasecmp(tag, "TD"))
				{
					gtk_html_add_text(html, cfont, current->color,
									  current->bgcol, "  ", 2, 0, 0, NULL);
				}
				else if (!strncasecmp(tag, "A ", 2))
				{
					char *d;
					char *temp = d = g_strdup(tag);
					int flag = 0;
					strtok(tag, " ");
					while ((d = strtok(NULL, " ")))
					{
						if (strlen(d) < 7)
							break;
						if (!strncasecmp(d, "HREF=\"", strlen("HREF=\"")))
						{
							d += strlen("HREF=\"");
							d[strlen(d) - 1] = 0;
							url = g_malloc(strlen(d) + 1);
							strcpy(url, d);
							flag = 1;
						}
					}
					g_free(temp);
					if (!flag)
					{
						gtk_html_add_text(html, cfont, current->color,
										  current->bgcol, "<", 1, 0, 0, NULL);
						gtk_html_add_text(html, cfont, current->color,
										  current->bgcol, tag, strlen(tag), 0,
										  0, NULL);
						gtk_html_add_text(html, cfont, current->color,
										  current->bgcol, ">", 1, 0, 0, NULL);
					}
				}
				else if (!strcasecmp(tag, "/A"))
				{
					if (url)
					{
						g_free(url);
						url = NULL;
					}
				}
				else if (!strncasecmp(tag, "FONT", strlen("FONT")))
				{
					char *d;
					/*
					 * Push a new state onto the stack, based on the old state 
					 */
					current = push_state(current);
					html_strtok(tag, ' ');
					while ((d = html_strtok(NULL, ' ')))
					{
						if (!strncasecmp(d, "COLOR=", strlen("COLOR=")))
						{
							d += strlen("COLOR=");
							if (*d == '\"')
								d++;
							if (*d == '#')
								d++;
							if (d[strlen(d) - 1] == '\"')
								d[strlen(d) - 1] = 0;
							if (sscanf(d, "%x", &colorv)
								&& !(options & HTML_OPTION_NO_COLOURS))
							{
								current->color = get_color(colorv, map);
								current->owncolor = 1;
							}
						}
						if (!strncasecmp(d, "FACE=", strlen("FACE=")))
						{
							d += strlen("FACE=");
							if (*d == '\"')
								d++;
							if (d[strlen(d) - 1] == '\"')
								d[strlen(d) - 1] = 0;
							strcpy(current->font, d);
						}
						else if (!strncasecmp(d, "BACK=", strlen("BACK=")))
						{
							d += strlen("BACK=");
							if (*d == '\"')
								d++;
							if (*d == '#')
								d++;
							if (d[strlen(d) - 1] == '\"')
								d[strlen(d) - 1] = 0;
							if (sscanf(d, "%x", &colorv)
								&& !(options & HTML_OPTION_NO_COLOURS))
							{
								current->bgcol = get_color(colorv, map);
								current->ownbg = 1;
							}
						}
						else if (!strncasecmp(d, "SIZE=", strlen("SIZE=")))
						{
							d += strlen("SIZE=");
							if (*d == '\"')
								d++;
							if (*d == '+')
								d++;
							if (sscanf(d, "%d", &colorv))
								current->size = colorv;
						}
						else if (strncasecmp(d, "PTSIZE=", strlen("PTSIZE=")))
						{
						}
					}
				}
				else if (!strncasecmp(tag, "BODY", strlen("BODY")))
				{
					char *d;
					current = push_state(current);
					html_strtok(tag, ' ');
					while ((d = html_strtok(NULL, ' ')))
					{
						if (!strncasecmp(d, "BGCOLOR=", strlen("BGCOLOR=")))
						{
							d += strlen("BGCOLOR=");
							if (*d == '\"')
								d++;
							if (*d == '#')
								d++;
							if (d[strlen(d) - 1] == '\"')
								d[strlen(d) - 1] = 0;
							if (sscanf(d, "%x", &colorv)
								&& !(options & HTML_OPTION_NO_COLOURS))
							{
								if (colorv != 0xffffff ||
								    !(display_options & OPT_DISP_IGN_WHITE)) {
									current->bgcol = get_color(colorv, map);
									current->ownbg = 1;
								}
							}
						}
					}
				}
				else if (!strncasecmp(tag, "/FONT", strlen("/FONT")))
				{
					/*
					 * Pop a font state off the list if possible, freeing
					 * any resources it used 
					 */
					if (current->next)
					{
						if (current->ownbg)
							g_free(current->bgcol);
						if (current->owncolor)
							g_free(current->color);
						tmp = current;
						current = current->next;
						g_free(tmp);
					}

				}
				else if (!strcasecmp(tag, "/BODY"))
				{
					if (current->next)
					{
						if (current->ownbg)
							g_free(current->bgcol);
						if (current->owncolor)
							g_free(current->color);
						tmp = current;
						current = current->next;
						g_free(tmp);
					}			/*
								 * tags we ignore below 
								 */
				}
				else if (!strncasecmp(tag, "BR", 2))
				{
					gtk_html_add_text(html, cfont, current->color,
									  current->bgcol, "\n", 1, 0, 0, NULL);
				}
				else if (strncasecmp(tag, "HTML", 4)
						 && strncasecmp(tag, "/HTML", 5)
						 && strncasecmp(tag, "BODY", 4)
						 && strncasecmp(tag, "/BODY", 5)
						 && (strncasecmp(tag, "P", 1) || tag[1] != '>')
						 && (strncasecmp(tag, "/P", 2) || tag[3] != '>')
						 && strncasecmp(tag, "HEAD", 4)
						 && strncasecmp(tag, "/HEAD", 5))
				{
					if (tpos)
					{
						gtk_html_add_text(html, cfont, current->color,
										  current->bgcol, "<", 1, 0, 0, NULL);
						gtk_html_add_text(html, cfont, current->color,
										  current->bgcol, tag, strlen(tag), 0,
										  0, NULL);
						gtk_html_add_text(html, cfont, current->color,
										  current->bgcol, ">", 1, 0, 0, NULL);

					}
				}
				cfont = getfont(current->font, bold, italic, fixed, current->size);
				tpos = 0;
				intag = 0;
			}
			else
			{
				ws[wpos++] = *c;
			}
		}
		else if (!intag && *c == '&')
		{
			if (!strncasecmp(c, "&amp;", 5))
			{
				ws[wpos++] = '&';
				c += 4;
			}
			else if (!strncasecmp(c, "&lt;", 4))
			{
				ws[wpos++] = '<';
				c += 3;
			}
			else if (!strncasecmp(c, "&gt;", 4))
			{
				ws[wpos++] = '>';
				c += 3;
			}
			else if (!strncasecmp(c, "&nbsp;", 6))
			{
				ws[wpos++] = ' ';
				c += 5;
			}
			else if (!strncasecmp(c, "&copy;", 6))
			{
				ws[wpos++] = '©';
				c += 5;
			}
			else if (!strncasecmp(c, "&quot;", 6))
			{
				ws[wpos++] = '\"';
				c += 5;
			}
			else if (!strncasecmp(c, "&reg;", 5))
			{
				ws[wpos++] = 174;
				c += 4;
			}
			else if (*(c + 1) == '#')
			{
				int pound = 0;
				debug_print("got &#;\n");
				if (sscanf(c, "&#%d;", &pound) > 0) {
					ws[wpos++] = (char)pound;
					c += 2;
					while (isdigit(*c)) c++;
					if (*c != ';') c--;
				} else {
					ws[wpos++] = *c;
				}
			}
			else
			{
				ws[wpos++] = *c;
			}
		}
		else
		{
			if (intag)
			{
				tag[tpos++] = *c;
			}
			else
			{
				ws[wpos++] = *c;
			}
		}
		c++;
	}
	ws[wpos] = 0;
	tag[tpos] = 0;
	if (wpos)
	{
		gtk_html_add_text(html, cfont, current->color, current->bgcol, ws,
						  strlen(ws), uline, strike, url);
	}
	if (tpos)
	{
		gtk_html_add_text(html, cfont, current->color, current->bgcol, "<", 1,
						  0, 0, NULL);
		gtk_html_add_text(html, cfont, current->color, current->bgcol, tag,
						  strlen(tag), 0, 0, NULL);
/*		gtk_html_add_text(html, cfont, current->color, current->bgcol, ">", 1,
						  0, 0, NULL);
*/	}


	g_free(ws);

	gdk_window_get_size(html->html_area, NULL, &height);
	area.height = height;
	gtk_adjustment_set_value(html->vadj, html->vadj->upper - area.height);

	return;
}


static void adjust_adj(GtkHtml * html, GtkAdjustment * adj)
{
	gint height;

	gdk_window_get_size(html->html_area, NULL, &height);

	adj->step_increment = MIN(adj->upper, (float) SCROLL_PIXELS);
	adj->page_increment = MIN(adj->upper, height - (float) KEY_SCROLL_PIXELS);
	adj->page_size = MIN(adj->upper, height);
	adj->value = MIN(adj->value, adj->upper - adj->page_size);
	adj->value = MAX(adj->value, 0.0);

	gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
}


static void scroll_down(GtkHtml * html, gint diff0)
{
	GdkRectangle rect;
	gint width,
	  height;

	html->yoffset += diff0;

	gdk_window_get_size(html->html_area, &width, &height);

	if (html->transparent)
	{
		rect.x = 0;
		rect.y = 0;
		rect.width = width;
		rect.height = height;
	}
	else
	{


		if (height > diff0 && !html->transparent)
			gdk_draw_pixmap(html->html_area,
							html->gc,
							html->html_area,
							0, diff0, 0, 0, width, height - diff0);

		rect.x = 0;
		rect.y = MAX(0, height - diff0);
		rect.width = width;
		rect.height = MIN(height, diff0);
	}

	expose_html(html, &rect, FALSE);
	gtk_html_draw_focus((GtkWidget *) html);

}

static void scroll_up(GtkHtml * html, gint diff0)
{
	GdkRectangle rect;
	gint width,
	  height;

	html->yoffset -= diff0;


	gdk_window_get_size(html->html_area, &width, &height);

	if (html->transparent)
	{
		rect.x = 0;
		rect.y = 0;
		rect.width = width;
		rect.height = height;
	}
	else
	{

		if (height > diff0)
			gdk_draw_pixmap(html->html_area,
							html->gc,
							html->html_area,
							0, 0, 0, diff0, width, height - diff0);

		rect.x = 0;
		rect.y = 0;
		rect.width = width;
		rect.height = MIN(height, diff0);
	}

	expose_html(html, &rect, FALSE);
	gtk_html_draw_focus((GtkWidget *) html);

}



static void gtk_html_adjustment(GtkAdjustment * adjustment, GtkHtml * html)
{
	g_return_if_fail(adjustment != NULL);
	g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
	g_return_if_fail(html != NULL);
	g_return_if_fail(GTK_IS_HTML(html));

	/*
	 * Just ignore it if we haven't been size-allocated and realized yet 
	 */
	if (html->html_area == NULL)
		return;

	if (adjustment == html->hadj)
	{
		g_warning("horizontal scrolling not implemented");
	}
	else
	{
		gint diff = ((gint) adjustment->value) - html->last_ver_value;

		if (diff != 0)
		{
			/*
			 * undraw_cursor (text, FALSE);
			 */

			if (diff > 0)
			{
				scroll_down(html, diff);
			}
			else
			{					/*
								 * if (diff < 0) 
								 */
				scroll_up(html, -diff);
			}
			/*
			 * draw_cursor (text, FALSE); 
			 */

			html->last_ver_value = adjustment->value;
		}
	}
}

static gint gtk_html_visibility_notify(GtkWidget * widget,
									   GdkEventVisibility * event)
{
	GtkHtml *html;
	GdkRectangle rect;
	gint width,
	  height;

	g_return_val_if_fail(widget != NULL, FALSE);
	g_return_val_if_fail(GTK_IS_HTML(widget), FALSE);

	html = GTK_HTML(widget);

	if (GTK_WIDGET_REALIZED(widget) && html->transparent)
	{
		gdk_window_get_size(html->html_area, &width, &height);
		rect.x = 0;
		rect.y = 0;
		rect.width = width;
		rect.height = height;
		expose_html(html, &rect, FALSE);
		gtk_html_draw_focus((GtkWidget *) html);
	}
	else
	{
	}


	return FALSE;
}



static void gtk_html_disconnect(GtkAdjustment * adjustment, GtkHtml * html)
{
	g_return_if_fail(adjustment != NULL);
	g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
	g_return_if_fail(html != NULL);
	g_return_if_fail(GTK_IS_HTML(html));

	if (adjustment == html->hadj)
		gtk_html_set_adjustments(html, NULL, html->vadj);
	if (adjustment == html->vadj)
		gtk_html_set_adjustments(html, html->hadj, NULL);
}

static void move_cursor_ver(GtkHtml * html, int count)
{
	GList *hbits = g_list_find(html->html_bits, html->cursor_hb);
	GtkHtmlBit *hb = NULL,
	 *hb2 = NULL;
	gint y;
	size_t len,
	  len2 = 0;

	undraw_cursor(html);

	if (!html->html_bits)
		return;

	if (!html->cursor_hb)
		html->cursor_hb = (GtkHtmlBit *) html->html_bits->data;

	hb = html->cursor_hb;

	len = html->cursor_pos;
	hbits = hbits->prev;
	while (hbits)
	{
		hb2 = (GtkHtmlBit *) hbits->data;

		if (hb2->y != hb->y)
			break;

		len += strlen(hb2->text);

		hbits = hbits->prev;
	}

	hbits = g_list_find(html->html_bits, html->cursor_hb);

	if (count < 0)
	{
		while (hbits)
		{
			hb2 = (GtkHtmlBit *) hbits->data;

			if (hb2->y != hb->y)
				break;

			hbits = hbits->prev;
		}
		if (!hbits)
		{
			draw_cursor(html);
			return;
		}
		y = hb2->y;
		hb = hb2;
		while (hbits)
		{
			hb2 = (GtkHtmlBit *) hbits->data;

			if (hb2->y != y)
				break;

			hb = hb2;

			hbits = hbits->prev;
		}
		hbits = g_list_find(html->html_bits, hb);
		while (hbits)
		{
			hb2 = (GtkHtmlBit *) hbits->data;

			if (hb->y != hb2->y)
			{
				html->cursor_hb = hb;
				html->cursor_pos = strlen(hb->text);
				break;
			}


			if (len < len2 + strlen(hb2->text))
			{
				html->cursor_hb = hb2;
				html->cursor_pos = len - len2;
				break;
			}

			len2 += strlen(hb2->text);

			hb = hb2;

			hbits = hbits->next;
		}
	}
	else
	{
		while (hbits)
		{
			hb2 = (GtkHtmlBit *) hbits->data;

			if (hb2->y != hb->y)
				break;

			hbits = hbits->next;
		}
		if (!hbits)
		{
			draw_cursor(html);
			return;
		}
		hb = hb2;
		while (hbits)
		{
			hb2 = (GtkHtmlBit *) hbits->data;

			if (hb->y != hb2->y)
			{
				html->cursor_hb = hb;
				html->cursor_pos = strlen(hb->text);
				break;
			}


			if (len < len2 + strlen(hb2->text))
			{
				html->cursor_hb = hb2;
				html->cursor_pos = len - len2;
				break;
			}

			len2 += strlen(hb2->text);

			hb = hb2;

			hbits = hbits->next;
		}
	}

	draw_cursor(html);

}

static void move_cursor_hor(GtkHtml * html, int count)
{
	GList *hbits = g_list_find(html->html_bits, html->cursor_hb);
	GtkHtmlBit *hb,
	 *hb2;

	undraw_cursor(html);

	if (!html->html_bits)
		return;

	if (!html->cursor_hb)
		html->cursor_hb = (GtkHtmlBit *) html->html_bits->data;

	html->cursor_pos += count;

	if (html->cursor_pos < 0)
	{
		if (hbits->prev)
		{
			gint diff;
			hb = html->cursor_hb;
			hb2 = (GtkHtmlBit *) hbits->prev->data;
			diff = html->cursor_pos + strlen(hb2->text) + 1;
			if (hb->y == hb2->y)
				--diff;

			html->cursor_pos = diff;

			html->cursor_hb = (GtkHtmlBit *) hbits->prev->data;
		}
		else
		{
			html->cursor_pos = 0;
		}
	}
	else if ((unsigned) html->cursor_pos > strlen(html->cursor_hb->text))
	{
		if (hbits->next)
		{
			gint diff;
			hb = html->cursor_hb;
			hb2 = (GtkHtmlBit *) hbits->next->data;

			diff = html->cursor_pos - strlen(html->cursor_hb->text) - 1;
			if (hb->y == hb2->y)
				++diff;
			html->cursor_pos = diff;
			html->cursor_hb = (GtkHtmlBit *) hbits->next->data;
		}
		else
		{
			html->cursor_pos = strlen(html->cursor_hb->text);
		}

	}

	draw_cursor(html);
}

static void move_beginning_of_line(GtkHtml * html)
{
	GList *hbits = g_list_find(html->html_bits, html->cursor_hb);
	GtkHtmlBit *hb = NULL;
	gint y;

	undraw_cursor(html);

	if (!html->html_bits)
		return;

	if (!html->cursor_hb)
		html->cursor_hb = (GtkHtmlBit *) html->html_bits->data;

	y = html->cursor_hb->y;

	while (hbits)
	{
		hb = (GtkHtmlBit *) hbits->data;

		if (y != hb->y)
		{
			hb = (GtkHtmlBit *) hbits->next->data;
			break;
		}

		hbits = hbits->prev;
	}
	if (!hbits)
		html->cursor_hb = (GtkHtmlBit *) html->html_bits->data;
	else
		html->cursor_hb = hb;

	html->cursor_pos = 0;


	draw_cursor(html);


}

static void move_end_of_line(GtkHtml * html)
{
	GList *hbits = g_list_find(html->html_bits, html->cursor_hb);
	GtkHtmlBit *hb = NULL;
	gint y;

	undraw_cursor(html);

	if (!html->html_bits)
		return;

	if (!html->cursor_hb)
		html->cursor_hb = (GtkHtmlBit *) html->html_bits->data;

	y = html->cursor_hb->y;

	while (hbits)
	{
		hb = (GtkHtmlBit *) hbits->data;

		if (y != hb->y)
		{
			hb = (GtkHtmlBit *) hbits->prev->data;
			break;
		}

		hbits = hbits->next;
	}
	if (!hbits)
		html->cursor_hb = (GtkHtmlBit *) g_list_last(html->html_bits)->data;
	else
		html->cursor_hb = hb;

	html->cursor_pos = strlen(html->cursor_hb->text);


	draw_cursor(html);


}



static gint gtk_html_key_press(GtkWidget * widget, GdkEventKey * event)
{
	GtkHtml *html;
	gchar key;
	gint return_val;

	g_return_val_if_fail(widget != NULL, FALSE);
	g_return_val_if_fail(GTK_IS_HTML(widget), FALSE);
	g_return_val_if_fail(event != NULL, FALSE);

	return_val = FALSE;

	html = GTK_HTML(widget);

	key = event->keyval;
	return_val = TRUE;


	if (html->editable == FALSE)
	{
		/*
		 * switch (event->keyval) {
		 * case GDK_Home:
		 * if (event->state & GDK_CONTROL_MASK)
		 * scroll_int (text, -text->vadj->value);
		 * else
		 * return_val = FALSE;
		 * break;
		 * case GDK_End:
		 * if (event->state & GDK_CONTROL_MASK)
		 * scroll_int (text, +text->vadj->upper);
		 * else
		 * return_val = FALSE;
		 * break;
		 * case GDK_Page_Up:   scroll_int (text, -text->vadj->page_increment); break;
		 * case GDK_Page_Down: scroll_int (text, +text->vadj->page_increment); break;
		 * case GDK_Up:        scroll_int (text, -KEY_SCROLL_PIXELS); break;
		 * case GDK_Down:      scroll_int (text, +KEY_SCROLL_PIXELS); break;
		 * case GDK_Return:
		 * if (event->state & GDK_CONTROL_MASK)
		 * gtk_signal_emit_by_name (GTK_OBJECT (text), "activate");
		 * else
		 * return_val = FALSE;
		 * break;
		 * default:
		 * return_val = FALSE;
		 * break;
		 * }
		 */
	}
	else
	{

		switch (event->keyval)
		{
		case GDK_Home:
			move_beginning_of_line(html);
			break;
		case GDK_End:
			move_end_of_line(html);
			break;
			/*
			 * case GDK_Page_Up:
			 * move_cursor_page_ver (html, -1);
			 * break;
			 * case GDK_Page_Down:
			 * move_cursor_page_ver (html, +1);
			 * break;
			 */
			/*
			 * CUA has Ctrl-Up/Ctrl-Down as paragraph up down 
			 */
		case GDK_Up:
			move_cursor_ver(html, -1);
			break;
		case GDK_Down:
			move_cursor_ver(html, +1);
			break;
		case GDK_Left:
			move_cursor_hor(html, -1);
			break;
		case GDK_Right:
			move_cursor_hor(html, +1);
			break;
#if 0
		case GDK_BackSpace:
			if (event->state & GDK_CONTROL_MASK)
				gtk_text_delete_backward_word(text);
			else
				gtk_text_delete_backward_character(text);
			break;
		case GDK_Clear:
			gtk_text_delete_line(text);
			break;
		case GDK_Insert:
			if (event->state & GDK_SHIFT_MASK)
			{
				extend_selection = FALSE;
				gtk_editable_paste_clipboard(editable);
			}
			else if (event->state & GDK_CONTROL_MASK)
			{
				gtk_editable_copy_clipboard(editable);
			}
			else
			{
				/*
				 * gtk_toggle_insert(text) -- IMPLEMENT 
				 */
			}
			break;
		case GDK_Delete:
			if (event->state & GDK_CONTROL_MASK)
				gtk_text_delete_forward_word(text);
			else if (event->state & GDK_SHIFT_MASK)
			{
				extend_selection = FALSE;
				gtk_editable_cut_clipboard(editable);
			}
			else
				gtk_text_delete_forward_character(text);
			break;
		case GDK_Tab:
			position = text->point.index;
			gtk_editable_insert_text(editable, "\t", 1, &position);
			break;
		case GDK_Return:
			if (event->state & GDK_CONTROL_MASK)
				gtk_signal_emit_by_name(GTK_OBJECT(text), "activate");
			else
			{
				position = text->point.index;
				gtk_editable_insert_text(editable, "\n", 1, &position);
			}
			break;
		case GDK_Escape:
			/*
			 * Don't insert literally 
			 */
			return_val = FALSE;
			break;
#endif
		default:
			return_val = FALSE;

#if 0
			if (event->state & GDK_CONTROL_MASK)
			{
				if ((key >= 'A') && (key <= 'Z'))
					key -= 'A' - 'a';

				if ((key >= 'a') && (key <= 'z')
					&& control_keys[(int) (key - 'a')])
				{
					(*control_keys[(int) (key - 'a')]) (editable, event->time);
					return_val = TRUE;
				}

				break;
			}
			else if (event->state & GDK_MOD1_MASK)
			{
				if ((key >= 'A') && (key <= 'Z'))
					key -= 'A' - 'a';

				if ((key >= 'a') && (key <= 'z') && alt_keys[(int) (key - 'a')])
				{
					(*alt_keys[(int) (key - 'a')]) (editable, event->time);
					return_val = TRUE;
				}
				break;
			}
#endif
			/*
			 * if (event->length > 0) {
			 * html->cursor_pos++;
			 * gtk_editable_insert_text (editable, event->string, event->length, &position);
			 * 
			 * return_val = TRUE;
			 * }
			 * else
			 * return_val = FALSE;
			 */
		}

	}

	return return_val;
}

void gtk_html_freeze(GtkHtml * html)
{
	g_return_if_fail(html != NULL);
	g_return_if_fail(GTK_IS_HTML(html));

	html->frozen++;
}

void gtk_html_thaw(GtkHtml * html)
{
	GdkRectangle area;

	g_return_if_fail(html != NULL);
	g_return_if_fail(GTK_IS_HTML(html));

	gtk_html_append_text(html, NULL, 0);

	html->frozen--;

	if (html->frozen < 0)
		html->frozen = 0;

	if (html->frozen == 0)
	{
		if (html->html_area)
		{
			gint width,
			  height;
			area.x = 0;
			area.y = 0;

			gdk_window_get_size(html->html_area, &width, &height);

			area.width = width;
			area.height = height;

			expose_html(html, &area, TRUE);
		}
	}
}

static int get_line_max_height(GtkHtml *html, GtkHtmlBit *start)
{
	int height, max_height = 0;
	GList *hbits = html->html_bits;
	GtkHtmlBit *hbit = start; /* default this in case hbits is NULL */

	hbits = g_list_find(hbits, start);

	while (hbits)
	{
		hbit = (GtkHtmlBit *)hbits->data;	
		if (hbit->font)
			height = gdk_text_height(hbit->font, "C", 1);	
		else if(hbit->type == HTML_BIT_PIXMAP)
			height = 19;
		if (max_height < height)
			max_height = height;
		if (hbit->newline)
			break;
		hbits = hbits->next;
	}

	if (!max_height)
		max_height = gdk_text_height(hbit->font, "C", 1);	

	return max_height;
}

static void get_line_max_extents(GtkHtml *html, GtkHtmlBit *start, int *max_acsent, int *max_descent)
{
	int acsent, descent;
	GList *hbits = html->html_bits;
	GtkHtmlBit *hbit = start; /* default this in case hbits is NULL */

	*max_acsent = 0;
	*max_descent = 0;
	
	hbits = g_list_find(hbits, start);

	while (hbits)
	{
		hbit = (GtkHtmlBit *)hbits->data;	
		if (hbit->font)
			gdk_text_extents(hbit->font, "yG", strlen("yG"), NULL, NULL, NULL, &acsent, &descent);
		else if (hbit->type == HTML_BIT_PIXMAP)
		{
			acsent =9;
			descent = 8;
		}
		
		if (*max_acsent < acsent)
			*max_acsent = acsent;
		if (*max_descent < descent)
			*max_descent =descent;
		if (hbit->newline)
			break;
		hbits = hbits->next;
	}

	if (!*max_acsent)
		gdk_text_extents(hbit->font, "yG", strlen("yG"), NULL, NULL, NULL, max_acsent, NULL);
	if (!*max_descent)
		gdk_text_extents(hbit->font, "yG", strlen("yG"), NULL, NULL, NULL, NULL, max_descent);

	return;
}