/*
 * 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
 *
 */

#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>

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

#include "gtkhtml.h"

#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
#define BUF_LONG				2048

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

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 };

struct font_state {
        int size;
        int owncolor;
        int ownbg;
        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;
	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        *html);
static void gtk_html_add_pixmap     (GtkHtml        *html,
                                     GdkPixmap *pm,
                                     gint fit);
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 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 (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;
}



GdkFont *font_load(char *fmt, int size)
{
	char buf[256];
	g_snprintf(buf, sizeof(buf), fmt, font_sizes[size]);
	return gdk_font_load(buf);
}


GdkFont *getfont(int bold, int italic, int fixed, int size)
{
	if (size > MAX_SIZE) size = MAX_SIZE;
	if (size < 1) size=1;
	size--;
	if (fixed) {
		if (bold) {
			if (italic) {
				if (!fixed_bold_italic_font[size])
					fixed_bold_italic_font[size] = font_load(FIXED_BOLD_ITALIC_FONT,size);
				return fixed_bold_italic_font[size];
					
			} else {
				if (!fixed_bold_font[size])
					fixed_bold_font[size] = font_load (FIXED_BOLD_FONT,size);
				return fixed_bold_font[size];
			}
		} else if (italic) {
			if (!fixed_italic_font[size])
				fixed_italic_font[size] = font_load(FIXED_ITALIC_FONT,size);
				
			return fixed_italic_font[size];
		} else {
			if (!fixed_font[size]) 
				fixed_font[size] = font_load(FIXED_FONT,size);
			return fixed_font[size];
		}
	} else {
		if (bold) {
			if (italic) {
				if (!prop_bold_italic_font[size])
					prop_bold_italic_font[size] = font_load(PROP_BOLD_ITALIC_FONT,size);
				return prop_bold_italic_font[size];
					
			} else {
				if (!prop_bold_font[size])
					prop_bold_font[size] = font_load (PROP_BOLD_FONT,size);
				return prop_bold_font[size];
			}
		} else if (italic) {
			if (!prop_italic_font[size])
				prop_italic_font[size] = font_load(PROP_ITALIC_FONT,size);
				
			return prop_italic_font[size];
		} else {
			if (!prop_font[size]) 
				prop_font[size] = font_load(PROP_FONT,size);
			return prop_font[size];
		}
	}
}




/* '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 {
			int 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;
		int 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))) {
				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);
				}
				gdk_window_set_cursor(html->html_area, gdk_cursor_new(GDK_HAND2));
				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) {
				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);
					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) {
			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);
		}

                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;

	if (html->frozen > 0)
		return;

	if (hb->type == HTML_BIT_TEXT) {

		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_measure(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_measure(hb->font, hb->text, html->num_start);
					xwidth -= startx;
				}
			} else {
				if (mypos == spos) {
					echar = html->num_start;
					xwidth = gdk_text_measure(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_measure(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->text[selected_state]);
				if (hb->back != NULL)
					gdk_gc_set_background(gc, hb->back);
				else
					gdk_gc_set_background(gc, &widget->style->bg[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 {

			if (hb->fore != NULL)
				gdk_gc_set_foreground(gc, hb->fore);
			else
				gdk_gc_set_foreground(gc, &widget->style->text[selected_state]);
			if (hb->back != NULL)
				gdk_gc_set_background(gc, hb->back);
			else
				gdk_gc_set_background(gc, &widget->style->bg[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) {

		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) {
		gdk_gc_set_background(gc, &widget->style->base[GTK_STATE_NORMAL]);
		gdk_draw_pixmap(html->html_area, gc, hb->pm, 0, 0, hb->x , hb->y - html->yoffset - (hb->height) + 4, hb->width, hb->height - 2);
	}
}



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;

        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);

			g_free(hb);

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

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

			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);
				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);
			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);
}





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

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

	hb->fit = fit;
	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;
	hb->fore = NULL;
	hb->back = NULL;
	hb->font = NULL;
        hb->uline = 0;
        hb->strike = 0;
	hb->was_selected = 0;
        hb->newline = 0;
        hb->pm = pm;

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


        html->current_x += hb->width;
	
	gtk_html_draw_bit(html, hb, 1);

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


}

static void gtk_html_add_seperator(GtkHtml *html)
{
        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;
	hb->fore = NULL;
	hb->back = NULL;
	hb->font = NULL;
        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;
        gint lb;
        GList *hbits;
        int num = 0, 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 += gdk_string_height(cfont, "yG") + 2;
                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; */

        while(gdk_text_measure(cfont, text, num) > maxwidth) {
                if (num > 1)
                        num--;
                else {

                        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;
		}

	}

	height = gdk_string_height(cfont, "yG") + 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;
                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;
                                hb->height = height;
                                hb->y += diff;

                                hbits = hbits->prev;
			}
		}
	}




	if (num != strlen(text)) {
		/* This is kinda cheesy but it may make things
		 * much better lookin */
		for (i=2; i<15; i++) {
			if ((num - i) < 0) {
				html->current_x = 0;
				gtk_html_add_text(html, cfont, fore, back, text, strlen(text), uline, strike, url);
                                return;
			} else 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);


}


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

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


        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"))
					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);
				else if (!strcasecmp(tag, "/B"))
					bold = 0;
				else if (!strcasecmp(tag, "/STRIKE"))
					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)) {
					
				} 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);
					strtok(tag," ");
                                        while((d=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;
							} else {
							}
						} 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 {
							}
						} 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 {
                                                        }
                                                } else if (strncasecmp(d,"PTSIZE=", strlen("PTSIZE="))) {
                                                }
                                        }
                                } else if (!strncasecmp(tag,"BODY BGCOLOR", strlen("BODY BGCOLOR"))) {

                                        /* Ditch trailing \" */
                                        tag[strlen(tag)-1]=0;
                                        if (sscanf(tag + strlen("BODY BGCOLOR=\"#"), "%x", &colorv) && !(options & HTML_OPTION_NO_COLOURS)) {
                                                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, "P", 1) && strncasecmp(tag, "/P", 2) &&
					   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(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 {
				ws[wpos++] = *c;
			}
                } else {
			if (intag) {
				tag[tpos++]=*c;
			} else {
				ws[wpos++]=*c;
			}
		}
		c++;
	}
	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);
	}
	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);
	}


	
        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;
        gint 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 (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));

	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);
		}
	}
}
