summary refs log tree commit diff stats
path: root/src/fe-gtk/xtext.c
diff options
context:
space:
mode:
authorberkeviktor@aol.com <berkeviktor@aol.com>2011-02-24 04:14:30 +0100
committerberkeviktor@aol.com <berkeviktor@aol.com>2011-02-24 04:14:30 +0100
commit4a6ceffb98a0b785494f680d3776c4bfc4052f9e (patch)
tree850703c1c841ccd99f58d0b06084615aaebe782c /src/fe-gtk/xtext.c
parentf16af8be941b596dedac3bf4e371ee2d21f4b598 (diff)
add xchat r1489
Diffstat (limited to 'src/fe-gtk/xtext.c')
-rw-r--r--src/fe-gtk/xtext.c5478
1 files changed, 5478 insertions, 0 deletions
diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c
new file mode 100644
index 00000000..fa9803c7
--- /dev/null
+++ b/src/fe-gtk/xtext.c
@@ -0,0 +1,5478 @@
+/* X-Chat
+ * Copyright (C) 1998 Peter Zelezny.
+ *
+ * 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
+ * =========================================================================
+ *
+ * xtext, the text widget used by X-Chat.
+ * By Peter Zelezny <zed@xchat.org>.
+ *
+ */
+
+#define XCHAT							/* using xchat */
+#define TINT_VALUE 195				/* 195/255 of the brightness. */
+#define MOTION_MONITOR				/* URL hilights. */
+#define SMOOTH_SCROLL				/* line-by-line or pixel scroll? */
+#define SCROLL_HACK					/* use XCopyArea scroll, or full redraw? */
+#undef COLOR_HILIGHT				/* Color instead of underline? */
+/* Italic is buggy because it assumes drawing an italic string will have
+   identical extents to the normal font. This is only true some of the
+   time, so we can't use this hack yet. */
+#undef ITALIC							/* support Italic? */
+#define GDK_MULTIHEAD_SAFE
+#define USE_DB							/* double buffer */
+
+#define MARGIN 2						/* dont touch. */
+#define REFRESH_TIMEOUT 20
+#define WORDWRAP_LIMIT 24
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkselection.h>
+#include <gtk/gtkclipboard.h>
+#include <gtk/gtkversion.h>
+#include <gtk/gtkwindow.h>
+
+#ifdef XCHAT
+#include "../../config.h"			/* can define USE_XLIB here */
+#else
+#define USE_XLIB
+#endif
+
+#ifdef USE_XLIB
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#endif
+
+#ifdef USE_MMX
+#include "mmx_cmod.h"
+#endif
+
+#include "xtext.h"
+
+#define charlen(str) g_utf8_skip[*(guchar *)(str)]
+
+#ifdef WIN32
+#include <windows.h>
+#include <gdk/gdkwin32.h>
+#endif
+
+/* is delimiter */
+#define is_del(c) \
+	(c == ' ' || c == '\n' || c == ')' || c == '(' || \
+	 c == '>' || c == '<' || c == ATTR_RESET || c == ATTR_BOLD || c == 0)
+
+#ifdef SCROLL_HACK
+/* force scrolling off */
+#define dontscroll(buf) (buf)->last_pixel_pos = 0x7fffffff
+#else
+#define dontscroll(buf)
+#endif
+
+static GtkWidgetClass *parent_class = NULL;
+
+struct textentry
+{
+	struct textentry *next;
+	struct textentry *prev;
+	unsigned char *str;
+	time_t stamp;
+	gint16 str_width;
+	gint16 str_len;
+	gint16 mark_start;
+	gint16 mark_end;
+	gint16 indent;
+	gint16 left_len;
+	gint16 lines_taken;
+#define RECORD_WRAPS 4
+	guint16 wrap_offset[RECORD_WRAPS];
+	guchar mb;		/* boolean: is multibyte? */
+	guchar tag;
+	guchar pad1;
+	guchar pad2;	/* 32-bit align : 44 bytes total */
+};
+
+enum
+{
+	WORD_CLICK,
+	LAST_SIGNAL
+};
+
+/* values for selection info */
+enum
+{
+	TARGET_UTF8_STRING,
+	TARGET_STRING,
+	TARGET_TEXT,
+	TARGET_COMPOUND_TEXT
+};
+
+static guint xtext_signals[LAST_SIGNAL];
+
+#ifdef XCHAT
+char *nocasestrstr (const char *text, const char *tofind);	/* util.c */
+int xtext_get_stamp_str (time_t, char **);
+#endif
+static void gtk_xtext_render_page (GtkXText * xtext);
+static void gtk_xtext_calc_lines (xtext_buffer *buf, int);
+#if defined(USE_XLIB) || defined(WIN32)
+static void gtk_xtext_load_trans (GtkXText * xtext);
+static void gtk_xtext_free_trans (GtkXText * xtext);
+#endif
+static char *gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret);
+static textentry *gtk_xtext_nth (GtkXText *xtext, int line, int *subline);
+static void gtk_xtext_adjustment_changed (GtkAdjustment * adj,
+														GtkXText * xtext);
+static int gtk_xtext_render_ents (GtkXText * xtext, textentry *, textentry *);
+static void gtk_xtext_recalc_widths (xtext_buffer *buf, int);
+static void gtk_xtext_fix_indent (xtext_buffer *buf);
+static int gtk_xtext_find_subline (GtkXText *xtext, textentry *ent, int line);
+static char *gtk_xtext_conv_color (unsigned char *text, int len, int *newlen);
+static unsigned char *
+gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
+							  int *newlen, int *mb_ret, int strip_hidden);
+static gboolean gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add);
+static int gtk_xtext_render_page_timeout (GtkXText * xtext);
+
+/* some utility functions first */
+
+#ifndef XCHAT	/* xchat has this in util.c */
+
+static char *
+nocasestrstr (const char *s, const char *tofind)
+{
+   register const size_t len = strlen (tofind);
+
+   if (len == 0)
+     return (char *)s;
+   while (toupper(*s) != toupper(*tofind) || strncasecmp (s, tofind, len))
+     if (*s++ == '\0')
+       return (char *)NULL;
+   return (char *)s;   
+}
+
+#endif
+
+/* gives width of a 8bit string - with no mIRC codes in it */
+
+static int
+gtk_xtext_text_width_8bit (GtkXText *xtext, unsigned char *str, int len)
+{
+	int width = 0;
+
+	while (len)
+	{
+		width += xtext->fontwidth[*str];
+		str++;
+		len--;
+	}
+
+	return width;
+}
+
+#ifdef WIN32
+
+static void
+win32_draw_bg (GtkXText *xtext, int x, int y, int width, int height)
+{
+	HDC hdc;
+	HWND hwnd;
+	HRGN rgn;
+
+	if (xtext->shaded)
+	{
+		/* xtext->pixmap is really a GdkImage, created in win32_tint() */
+		gdk_draw_image (xtext->draw_buf, xtext->bgc, (GdkImage*)xtext->pixmap,
+							 x, y, x, y, width, height);
+	} else
+	{
+		hwnd = GDK_WINDOW_HWND (xtext->draw_buf);
+		hdc = GetDC (hwnd);
+
+		rgn = CreateRectRgn (x, y, x + width, y + height);
+		SelectClipRgn (hdc, rgn);
+
+		PaintDesktop (hdc);
+
+		ReleaseDC (hwnd, hdc);
+		DeleteObject (rgn);
+	}
+}
+
+static void
+xtext_draw_bg (GtkXText *xtext, int x, int y, int width, int height)
+{
+	if (xtext->transparent)
+		win32_draw_bg (xtext, x, y, width, height);
+	else
+		gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1, x, y, width, height);
+}
+
+#else
+
+#define xtext_draw_bg(xt,x,y,w,h) gdk_draw_rectangle(xt->draw_buf, xt->bgc, \
+																	  1,x,y,w,h);
+
+#endif
+
+/* ========================================= */
+/* ========== XFT 1 and 2 BACKEND ========== */
+/* ========================================= */
+
+#ifdef USE_XFT
+
+static void
+backend_font_close (GtkXText *xtext)
+{
+	XftFontClose (GDK_WINDOW_XDISPLAY (xtext->draw_buf), xtext->font);
+#ifdef ITALIC
+	XftFontClose (GDK_WINDOW_XDISPLAY (xtext->draw_buf), xtext->ifont);
+#endif
+}
+
+static void
+backend_init (GtkXText *xtext)
+{
+	if (xtext->xftdraw == NULL)
+	{
+		xtext->xftdraw = XftDrawCreate (
+			GDK_WINDOW_XDISPLAY (xtext->draw_buf),
+			GDK_WINDOW_XWINDOW (xtext->draw_buf),
+			GDK_VISUAL_XVISUAL (gdk_drawable_get_visual (xtext->draw_buf)),
+			GDK_COLORMAP_XCOLORMAP (gdk_drawable_get_colormap (xtext->draw_buf)));
+		XftDrawSetSubwindowMode (xtext->xftdraw, IncludeInferiors);
+	}
+}
+
+static void
+backend_deinit (GtkXText *xtext)
+{
+	if (xtext->xftdraw)
+	{
+		XftDrawDestroy (xtext->xftdraw);
+		xtext->xftdraw = NULL;
+	}
+}
+
+static XftFont *
+backend_font_open_real (Display *xdisplay, char *name, gboolean italics)
+{
+	XftFont *font = NULL;
+	PangoFontDescription *fontd;
+	int weight, slant, screen = DefaultScreen (xdisplay);
+
+	fontd = pango_font_description_from_string (name);
+
+	if (pango_font_description_get_size (fontd) != 0)
+	{
+		weight = pango_font_description_get_weight (fontd);
+		/* from pangoft2-fontmap.c */
+		if (weight < (PANGO_WEIGHT_NORMAL + PANGO_WEIGHT_LIGHT) / 2)
+			weight = XFT_WEIGHT_LIGHT;
+		else if (weight < (PANGO_WEIGHT_NORMAL + 600) / 2)
+			weight = XFT_WEIGHT_MEDIUM;
+		else if (weight < (600 + PANGO_WEIGHT_BOLD) / 2)
+			weight = XFT_WEIGHT_DEMIBOLD;
+		else if (weight < (PANGO_WEIGHT_BOLD + PANGO_WEIGHT_ULTRABOLD) / 2)
+			weight = XFT_WEIGHT_BOLD;
+		else
+			weight = XFT_WEIGHT_BLACK;
+
+		slant = pango_font_description_get_style (fontd);
+		if (slant == PANGO_STYLE_ITALIC)
+			slant = XFT_SLANT_ITALIC;
+		else if (slant == PANGO_STYLE_OBLIQUE)
+			slant = XFT_SLANT_OBLIQUE;
+		else
+			slant = XFT_SLANT_ROMAN;
+
+		font = XftFontOpen (xdisplay, screen,
+						XFT_FAMILY, XftTypeString, pango_font_description_get_family (fontd),
+						XFT_CORE, XftTypeBool, False,
+						XFT_SIZE, XftTypeDouble, (double)pango_font_description_get_size (fontd)/PANGO_SCALE,
+						XFT_WEIGHT, XftTypeInteger, weight,
+						XFT_SLANT, XftTypeInteger, italics ? XFT_SLANT_ITALIC : slant,
+						NULL);
+	}
+	pango_font_description_free (fontd);
+
+	if (font == NULL)
+	{
+		font = XftFontOpenName (xdisplay, screen, name);
+		if (font == NULL)
+			font = XftFontOpenName (xdisplay, screen, "sans-11");
+	}
+
+	return font;
+}
+
+static void
+backend_font_open (GtkXText *xtext, char *name)
+{
+	Display *dis = GDK_WINDOW_XDISPLAY (xtext->draw_buf);
+
+	xtext->font = backend_font_open_real (dis, name, FALSE);
+#ifdef ITALIC
+	xtext->ifont = backend_font_open_real (dis, name, TRUE);
+#endif
+}
+
+inline static int
+backend_get_char_width (GtkXText *xtext, unsigned char *str, int *mbl_ret)
+{
+	XGlyphInfo ext;
+
+	if (*str < 128)
+	{
+		*mbl_ret = 1;
+		return xtext->fontwidth[*str];
+	}
+
+	*mbl_ret = charlen (str);
+	XftTextExtentsUtf8 (GDK_WINDOW_XDISPLAY (xtext->draw_buf), xtext->font, str, *mbl_ret, &ext);
+
+	return ext.xOff;
+}
+
+static int
+backend_get_text_width (GtkXText *xtext, guchar *str, int len, int is_mb)
+{
+	XGlyphInfo ext;
+
+	if (!is_mb)
+		return gtk_xtext_text_width_8bit (xtext, str, len);
+
+	XftTextExtentsUtf8 (GDK_WINDOW_XDISPLAY (xtext->draw_buf), xtext->font, str, len, &ext);
+	return ext.xOff;
+}
+
+static void
+backend_draw_text (GtkXText *xtext, int dofill, GdkGC *gc, int x, int y,
+						 char *str, int len, int str_width, int is_mb)
+{
+	/*Display *xdisplay = GDK_WINDOW_XDISPLAY (xtext->draw_buf);*/
+	void (*draw_func) (XftDraw *, XftColor *, XftFont *, int, int, XftChar8 *, int) = (void *)XftDrawString8;
+	XftFont *font;
+
+	/* if all ascii, use String8 to avoid the conversion penalty */
+	if (is_mb)
+		draw_func = (void *)XftDrawStringUtf8;
+
+	if (dofill)
+	{
+/*		register GC xgc = GDK_GC_XGC (gc);
+		XSetForeground (xdisplay, xgc, xtext->xft_bg->pixel);
+		XFillRectangle (xdisplay, GDK_WINDOW_XWINDOW (xtext->draw_buf), xgc, x,
+							 y - xtext->font->ascent, str_width, xtext->fontsize);*/
+		XftDrawRect (xtext->xftdraw, xtext->xft_bg, x,
+						 y - xtext->font->ascent, str_width, xtext->fontsize);
+	}
+
+	font = xtext->font;
+#ifdef ITALIC
+	if (xtext->italics)
+		font = xtext->ifont;
+#endif
+
+	draw_func (xtext->xftdraw, xtext->xft_fg, font, x, y, str, len);
+
+	if (xtext->overdraw)
+		draw_func (xtext->xftdraw, xtext->xft_fg, font, x, y, str, len);
+
+	if (xtext->bold)
+		draw_func (xtext->xftdraw, xtext->xft_fg, font, x + 1, y, str, len);
+}
+
+/*static void
+backend_set_clip (GtkXText *xtext, GdkRectangle *area)
+{
+	gdk_gc_set_clip_rectangle (xtext->fgc, area);
+	gdk_gc_set_clip_rectangle (xtext->bgc, area);
+}
+
+static void
+backend_clear_clip (GtkXText *xtext)
+{
+	gdk_gc_set_clip_rectangle (xtext->fgc, NULL);
+	gdk_gc_set_clip_rectangle (xtext->bgc, NULL);
+}*/
+
+/*static void
+backend_set_clip (GtkXText *xtext, GdkRectangle *area)
+{
+	Region reg;
+	XRectangle rect;
+
+	rect.x = area->x;
+	rect.y = area->y;
+	rect.width = area->width;
+	rect.height = area->height;
+
+	reg = XCreateRegion ();
+	XUnionRectWithRegion (&rect, reg, reg);
+	XftDrawSetClip (xtext->xftdraw, reg);
+	XDestroyRegion (reg);
+
+	gdk_gc_set_clip_rectangle (xtext->fgc, area);
+}
+
+static void
+backend_clear_clip (GtkXText *xtext)
+{
+	XftDrawSetClip (xtext->xftdraw, NULL);
+	gdk_gc_set_clip_rectangle (xtext->fgc, NULL);
+}
+*/
+#else	/* !USE_XFT */
+
+/* ======================================= */
+/* ============ PANGO BACKEND ============ */
+/* ======================================= */
+
+static void
+backend_font_close (GtkXText *xtext)
+{
+	pango_font_description_free (xtext->font->font);
+#ifdef ITALIC
+	pango_font_description_free (xtext->font->ifont);
+#endif
+}
+
+static void
+backend_init (GtkXText *xtext)
+{
+	if (xtext->layout == NULL)
+	{
+		xtext->layout = gtk_widget_create_pango_layout (GTK_WIDGET (xtext), 0); 
+		if (xtext->font)
+			pango_layout_set_font_description (xtext->layout, xtext->font->font);
+	}
+}
+
+static void
+backend_deinit (GtkXText *xtext)
+{
+	if (xtext->layout)
+	{
+		g_object_unref (xtext->layout);
+		xtext->layout = NULL;
+	}
+}
+
+static PangoFontDescription *
+backend_font_open_real (char *name)
+{
+	PangoFontDescription *font;
+
+	font = pango_font_description_from_string (name);
+	if (font && pango_font_description_get_size (font) == 0)
+	{
+		pango_font_description_free (font);
+		font = pango_font_description_from_string ("sans 11");
+	}
+	if (!font)
+		font = pango_font_description_from_string ("sans 11");
+
+	return font;
+}
+
+static void
+backend_font_open (GtkXText *xtext, char *name)
+{
+	PangoLanguage *lang;
+	PangoContext *context;
+	PangoFontMetrics *metrics;
+
+	xtext->font = &xtext->pango_font;
+	xtext->font->font = backend_font_open_real (name);
+	if (!xtext->font->font)
+	{
+		xtext->font = NULL;
+		return;
+	}
+#ifdef ITALIC
+	xtext->font->ifont = backend_font_open_real (name);
+	pango_font_description_set_style (xtext->font->ifont, PANGO_STYLE_ITALIC);
+#endif
+
+	backend_init (xtext);
+	pango_layout_set_font_description (xtext->layout, xtext->font->font);
+
+	/* vte does it this way */
+	context = gtk_widget_get_pango_context (GTK_WIDGET (xtext));
+	lang = pango_context_get_language (context);
+	metrics = pango_context_get_metrics (context, xtext->font->font, lang);
+	xtext->font->ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
+	xtext->font->descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
+	pango_font_metrics_unref (metrics);
+}
+
+static int
+backend_get_text_width (GtkXText *xtext, guchar *str, int len, int is_mb)
+{
+	int width;
+
+	if (!is_mb)
+		return gtk_xtext_text_width_8bit (xtext, str, len);
+
+	if (*str == 0)
+		return 0;
+
+	pango_layout_set_text (xtext->layout, str, len);
+	pango_layout_get_pixel_size (xtext->layout, &width, NULL);
+
+	return width;
+}
+
+inline static int
+backend_get_char_width (GtkXText *xtext, unsigned char *str, int *mbl_ret)
+{
+	int width;
+
+	if (*str < 128)
+	{
+		*mbl_ret = 1;
+		return xtext->fontwidth[*str];
+	}
+
+	*mbl_ret = charlen (str);
+	pango_layout_set_text (xtext->layout, str, *mbl_ret);
+	pango_layout_get_pixel_size (xtext->layout, &width, NULL);
+
+	return width;
+}
+
+/* simplified version of gdk_draw_layout_line_with_colors() */
+
+static void 
+xtext_draw_layout_line (GdkDrawable      *drawable,
+								GdkGC            *gc,
+								gint              x, 
+								gint              y,
+								PangoLayoutLine  *line)
+{
+	GSList *tmp_list = line->runs;
+	PangoRectangle logical_rect;
+	gint x_off = 0;
+
+	while (tmp_list)
+	{
+		PangoLayoutRun *run = tmp_list->data;
+
+		pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
+											 NULL, &logical_rect);
+
+		gdk_draw_glyphs (drawable, gc, run->item->analysis.font,
+							  x + x_off / PANGO_SCALE, y, run->glyphs);
+
+		x_off += logical_rect.width;
+		tmp_list = tmp_list->next;
+	}
+}
+
+static void
+backend_draw_text (GtkXText *xtext, int dofill, GdkGC *gc, int x, int y,
+						 char *str, int len, int str_width, int is_mb)
+{
+	GdkGCValues val;
+	GdkColor col;
+	PangoLayoutLine *line;
+
+#ifdef ITALIC
+	if (xtext->italics)
+		pango_layout_set_font_description (xtext->layout, xtext->font->ifont);
+#endif
+
+	pango_layout_set_text (xtext->layout, str, len);
+
+	if (dofill)
+	{
+#ifdef WIN32
+		if (xtext->transparent && !xtext->backcolor)
+			win32_draw_bg (xtext, x, y - xtext->font->ascent, str_width,
+								xtext->fontsize);
+		else
+#endif
+		{
+			gdk_gc_get_values (gc, &val);
+			col.pixel = val.background.pixel;
+			gdk_gc_set_foreground (gc, &col);
+			gdk_draw_rectangle (xtext->draw_buf, gc, 1, x, y -
+									  xtext->font->ascent, str_width, xtext->fontsize);
+			col.pixel = val.foreground.pixel;
+			gdk_gc_set_foreground (gc, &col);
+		}
+	}
+
+	line = pango_layout_get_lines (xtext->layout)->data;
+
+	xtext_draw_layout_line (xtext->draw_buf, gc, x, y, line);
+
+	if (xtext->overdraw)
+		xtext_draw_layout_line (xtext->draw_buf, gc, x, y, line);
+
+	if (xtext->bold)
+		xtext_draw_layout_line (xtext->draw_buf, gc, x + 1, y, line);
+
+#ifdef ITALIC
+	if (xtext->italics)
+		pango_layout_set_font_description (xtext->layout, xtext->font->font);
+#endif
+}
+
+/*static void
+backend_set_clip (GtkXText *xtext, GdkRectangle *area)
+{
+	gdk_gc_set_clip_rectangle (xtext->fgc, area);
+	gdk_gc_set_clip_rectangle (xtext->bgc, area);
+}
+
+static void
+backend_clear_clip (GtkXText *xtext)
+{
+	gdk_gc_set_clip_rectangle (xtext->fgc, NULL);
+	gdk_gc_set_clip_rectangle (xtext->bgc, NULL);
+}*/
+
+#endif /* !USE_PANGO */
+
+static void
+xtext_set_fg (GtkXText *xtext, GdkGC *gc, int index)
+{
+	GdkColor col;
+
+	col.pixel = xtext->palette[index];
+	gdk_gc_set_foreground (gc, &col);
+
+#ifdef USE_XFT
+	if (gc == xtext->fgc)
+		xtext->xft_fg = &xtext->color[index];
+	else
+		xtext->xft_bg = &xtext->color[index];
+#endif
+}
+
+#ifdef USE_XFT
+
+#define xtext_set_bg(xt,gc,index) xt->xft_bg = &xt->color[index]
+
+#else
+
+static void
+xtext_set_bg (GtkXText *xtext, GdkGC *gc, int index)
+{
+	GdkColor col;
+
+	col.pixel = xtext->palette[index];
+	gdk_gc_set_background (gc, &col);
+}
+
+#endif
+
+static void
+gtk_xtext_init (GtkXText * xtext)
+{
+	xtext->pixmap = NULL;
+	xtext->io_tag = 0;
+	xtext->add_io_tag = 0;
+	xtext->scroll_tag = 0;
+	xtext->max_lines = 0;
+	xtext->col_back = XTEXT_BG;
+	xtext->col_fore = XTEXT_FG;
+	xtext->nc = 0;
+	xtext->pixel_offset = 0;
+	xtext->bold = FALSE;
+	xtext->underline = FALSE;
+	xtext->italics = FALSE;
+	xtext->hidden = FALSE;
+	xtext->font = NULL;
+#ifdef USE_XFT
+	xtext->xftdraw = NULL;
+#else
+	xtext->layout = NULL;
+#endif
+	xtext->jump_out_offset = 0;
+	xtext->jump_in_offset = 0;
+	xtext->ts_x = 0;
+	xtext->ts_y = 0;
+	xtext->clip_x = 0;
+	xtext->clip_x2 = 1000000;
+	xtext->clip_y = 0;
+	xtext->clip_y2 = 1000000;
+	xtext->error_function = NULL;
+	xtext->urlcheck_function = NULL;
+	xtext->color_paste = FALSE;
+	xtext->skip_border_fills = FALSE;
+	xtext->skip_stamp = FALSE;
+	xtext->render_hilights_only = FALSE;
+	xtext->un_hilight = FALSE;
+	xtext->recycle = FALSE;
+	xtext->dont_render = FALSE;
+	xtext->dont_render2 = FALSE;
+	xtext->overdraw = FALSE;
+	xtext->tint_red = xtext->tint_green = xtext->tint_blue = TINT_VALUE;
+
+	xtext->adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 1, 1, 1, 1);
+	g_object_ref (G_OBJECT (xtext->adj));
+	g_object_ref_sink (G_OBJECT (xtext->adj));
+	g_object_unref (G_OBJECT (xtext->adj));
+
+	xtext->vc_signal_tag = g_signal_connect (G_OBJECT (xtext->adj),
+				"value_changed", G_CALLBACK (gtk_xtext_adjustment_changed), xtext);
+	{
+		static const GtkTargetEntry targets[] = {
+			{ "UTF8_STRING", 0, TARGET_UTF8_STRING },
+			{ "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_selection_add_targets (GTK_WIDGET (xtext), GDK_SELECTION_PRIMARY,
+											targets, n_targets);
+	}
+
+	if (getenv ("XCHAT_OVERDRAW"))
+		xtext->overdraw = TRUE;
+}
+
+static void
+gtk_xtext_adjustment_set (xtext_buffer *buf, int fire_signal)
+{
+	GtkAdjustment *adj = buf->xtext->adj;
+
+	if (buf->xtext->buffer == buf)
+	{
+		adj->lower = 0;
+		adj->upper = buf->num_lines;
+
+		if (adj->upper == 0)
+			adj->upper = 1;
+
+		adj->page_size =
+			(GTK_WIDGET (buf->xtext)->allocation.height -
+			 buf->xtext->font->descent) / buf->xtext->fontsize;
+		adj->page_increment = adj->page_size;
+
+		if (adj->value > adj->upper - adj->page_size)
+			adj->value = adj->upper - adj->page_size;
+
+		if (adj->value < 0)
+			adj->value = 0;
+
+		if (fire_signal)
+			gtk_adjustment_changed (adj);
+	}
+}
+
+static gint
+gtk_xtext_adjustment_timeout (GtkXText * xtext)
+{
+	gtk_xtext_render_page (xtext);
+	xtext->io_tag = 0;
+	return 0;
+}
+
+static void
+gtk_xtext_adjustment_changed (GtkAdjustment * adj, GtkXText * xtext)
+{
+#ifdef SMOOTH_SCROLL
+	if (xtext->buffer->old_value != xtext->adj->value)
+#else
+	if ((int) xtext->buffer->old_value != (int) xtext->adj->value)
+#endif
+	{
+		if (xtext->adj->value >= xtext->adj->upper - xtext->adj->page_size)
+			xtext->buffer->scrollbar_down = TRUE;
+		else
+			xtext->buffer->scrollbar_down = FALSE;
+
+		if (xtext->adj->value + 1 == xtext->buffer->old_value ||
+			 xtext->adj->value - 1 == xtext->buffer->old_value)	/* clicked an arrow? */
+		{
+			if (xtext->io_tag)
+			{
+				g_source_remove (xtext->io_tag);
+				xtext->io_tag = 0;
+			}
+			gtk_xtext_render_page (xtext);
+		} else
+		{
+			if (!xtext->io_tag)
+				xtext->io_tag = g_timeout_add (REFRESH_TIMEOUT,
+															(GSourceFunc)
+															gtk_xtext_adjustment_timeout,
+															xtext);
+		}
+	}
+	xtext->buffer->old_value = adj->value;
+}
+
+GtkWidget *
+gtk_xtext_new (GdkColor palette[], int separator)
+{
+	GtkXText *xtext;
+
+	xtext = g_object_new (gtk_xtext_get_type (), NULL);
+	xtext->separator = separator;
+	xtext->wordwrap = TRUE;
+	xtext->buffer = gtk_xtext_buffer_new (xtext);
+	xtext->orig_buffer = xtext->buffer;
+
+	gtk_widget_set_double_buffered (GTK_WIDGET (xtext), FALSE);
+	gtk_xtext_set_palette (xtext, palette);
+
+	return GTK_WIDGET (xtext);
+}
+
+static void
+gtk_xtext_destroy (GtkObject * object)
+{
+	GtkXText *xtext = GTK_XTEXT (object);
+
+	if (xtext->add_io_tag)
+	{
+		g_source_remove (xtext->add_io_tag);
+		xtext->add_io_tag = 0;
+	}
+
+	if (xtext->scroll_tag)
+	{
+		g_source_remove (xtext->scroll_tag);
+		xtext->scroll_tag = 0;
+	}
+
+	if (xtext->io_tag)
+	{
+		g_source_remove (xtext->io_tag);
+		xtext->io_tag = 0;
+	}
+
+	if (xtext->pixmap)
+	{
+#if defined(USE_XLIB) || defined(WIN32)
+		if (xtext->transparent)
+			gtk_xtext_free_trans (xtext);
+		else
+#endif
+			g_object_unref (xtext->pixmap);
+		xtext->pixmap = NULL;
+	}
+
+	if (xtext->font)
+	{
+		backend_font_close (xtext);
+		xtext->font = NULL;
+	}
+
+	if (xtext->adj)
+	{
+		g_signal_handlers_disconnect_matched (G_OBJECT (xtext->adj),
+					G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xtext);
+	/*	gtk_signal_disconnect_by_data (GTK_OBJECT (xtext->adj), xtext);*/
+		g_object_unref (G_OBJECT (xtext->adj));
+		xtext->adj = NULL;
+	}
+
+	if (xtext->bgc)
+	{
+		g_object_unref (xtext->bgc);
+		xtext->bgc = NULL;
+	}
+
+	if (xtext->fgc)
+	{
+		g_object_unref (xtext->fgc);
+		xtext->fgc = NULL;
+	}
+
+	if (xtext->light_gc)
+	{
+		g_object_unref (xtext->light_gc);
+		xtext->light_gc = NULL;
+	}
+
+	if (xtext->dark_gc)
+	{
+		g_object_unref (xtext->dark_gc);
+		xtext->dark_gc = NULL;
+	}
+
+	if (xtext->thin_gc)
+	{
+		g_object_unref (xtext->thin_gc);
+		xtext->thin_gc = NULL;
+	}
+
+	if (xtext->marker_gc)
+	{
+		g_object_unref (xtext->marker_gc);
+		xtext->marker_gc = NULL;
+	}
+
+	if (xtext->hand_cursor)
+	{
+		gdk_cursor_unref (xtext->hand_cursor);
+		xtext->hand_cursor = NULL;
+	}
+
+	if (xtext->resize_cursor)
+	{
+		gdk_cursor_unref (xtext->resize_cursor);
+		xtext->resize_cursor = NULL;
+	}
+
+	if (xtext->orig_buffer)
+	{
+		gtk_xtext_buffer_free (xtext->orig_buffer);
+		xtext->orig_buffer = NULL;
+	}
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+gtk_xtext_unrealize (GtkWidget * widget)
+{
+	backend_deinit (GTK_XTEXT (widget));
+
+	/* if there are still events in the queue, this'll avoid segfault */
+	gdk_window_set_user_data (widget->window, NULL);
+
+	if (parent_class->unrealize)
+		(* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+gtk_xtext_realize (GtkWidget * widget)
+{
+	GtkXText *xtext;
+	GdkWindowAttr attributes;
+	GdkGCValues val;
+	GdkColor col;
+	GdkColormap *cmap;
+
+	GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+	xtext = GTK_XTEXT (widget);
+
+	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.window_type = GDK_WINDOW_CHILD;
+	attributes.event_mask = gtk_widget_get_events (widget) |
+		GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+#ifdef MOTION_MONITOR
+		| GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK;
+#else
+		| GDK_POINTER_MOTION_MASK;
+#endif
+
+	cmap = gtk_widget_get_colormap (widget);
+	attributes.colormap = cmap;
+	attributes.visual = gtk_widget_get_visual (widget);
+
+	widget->window = gdk_window_new (widget->parent->window, &attributes,
+												GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL |
+												GDK_WA_COLORMAP);
+
+	gdk_window_set_user_data (widget->window, widget);
+
+	xtext->depth = gdk_drawable_get_visual (widget->window)->depth;
+
+	val.subwindow_mode = GDK_INCLUDE_INFERIORS;
+	val.graphics_exposures = 0;
+
+	xtext->bgc = gdk_gc_new_with_values (widget->window, &val,
+													 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+	xtext->fgc = gdk_gc_new_with_values (widget->window, &val,
+													 GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+	xtext->light_gc = gdk_gc_new_with_values (widget->window, &val,
+											GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+	xtext->dark_gc = gdk_gc_new_with_values (widget->window, &val,
+											GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+	xtext->thin_gc = gdk_gc_new_with_values (widget->window, &val,
+											GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+	xtext->marker_gc = gdk_gc_new_with_values (widget->window, &val,
+											GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+
+	/* for the separator bar (light) */
+	col.red = 0xffff; col.green = 0xffff; col.blue = 0xffff;
+	gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE);
+	gdk_gc_set_foreground (xtext->light_gc, &col);
+
+	/* for the separator bar (dark) */
+	col.red = 0x1111; col.green = 0x1111; col.blue = 0x1111;
+	gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE);
+	gdk_gc_set_foreground (xtext->dark_gc, &col);
+
+	/* for the separator bar (thinline) */
+	col.red = 0x8e38; col.green = 0x8e38; col.blue = 0x9f38;
+	gdk_colormap_alloc_color (cmap, &col, FALSE, TRUE);
+	gdk_gc_set_foreground (xtext->thin_gc, &col);
+
+	/* for the marker bar (marker) */
+	col.pixel = xtext->palette[XTEXT_MARKER];
+	gdk_gc_set_foreground (xtext->marker_gc, &col);
+
+	xtext_set_fg (xtext, xtext->fgc, XTEXT_FG);
+	xtext_set_bg (xtext, xtext->fgc, XTEXT_BG);
+	xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
+
+	/* draw directly to window */
+	xtext->draw_buf = widget->window;
+
+#if defined(USE_XLIB) || defined(WIN32)
+	if (xtext->transparent)
+	{
+		gtk_xtext_load_trans (xtext);
+	} else
+#endif
+	if (xtext->pixmap)
+	{
+		gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
+		gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
+		xtext->ts_x = xtext->ts_y = 0;
+		gdk_gc_set_fill (xtext->bgc, GDK_TILED);
+	}
+
+#if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0)
+	xtext->hand_cursor = gdk_cursor_new (GDK_HAND1);
+	xtext->resize_cursor = gdk_cursor_new (GDK_LEFT_SIDE);
+#else
+	xtext->hand_cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (widget->window), GDK_HAND1);
+	xtext->resize_cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (widget->window), GDK_LEFT_SIDE);
+#endif
+
+	gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
+	widget->style = gtk_style_attach (widget->style, widget->window);
+
+	backend_init (xtext);
+}
+
+static void
+gtk_xtext_size_request (GtkWidget * widget, GtkRequisition * requisition)
+{
+	requisition->width = 200;
+	requisition->height = 90;
+}
+
+static void
+gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
+{
+	GtkXText *xtext = GTK_XTEXT (widget);
+	int height_only = FALSE;
+	int do_trans = TRUE;
+
+	if (allocation->width == xtext->buffer->window_width)
+		height_only = TRUE;
+
+	if (allocation->x == widget->allocation.x &&
+		 allocation->y == widget->allocation.y && xtext->avoid_trans)
+		do_trans = FALSE;
+
+	xtext->avoid_trans = FALSE;
+
+	widget->allocation = *allocation;
+	if (GTK_WIDGET_REALIZED (widget))
+	{
+		xtext->buffer->window_width = allocation->width;
+		xtext->buffer->window_height = allocation->height;
+
+		gdk_window_move_resize (widget->window, allocation->x, allocation->y,
+										allocation->width, allocation->height);
+		dontscroll (xtext->buffer);	/* force scrolling off */
+		if (!height_only)
+			gtk_xtext_calc_lines (xtext->buffer, FALSE);
+		else
+		{
+			xtext->buffer->pagetop_ent = NULL;
+			gtk_xtext_adjustment_set (xtext->buffer, FALSE);
+		}
+#if defined(USE_XLIB) || defined(WIN32)
+		if (do_trans && xtext->transparent && xtext->shaded)
+		{
+			gtk_xtext_free_trans (xtext);
+			gtk_xtext_load_trans (xtext);
+		}
+#endif
+		if (xtext->buffer->scrollbar_down)
+			gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
+											  xtext->adj->page_size);
+	}
+}
+
+static void
+gtk_xtext_selection_clear_full (xtext_buffer *buf)
+{
+	textentry *ent = buf->text_first;
+	while (ent)
+	{
+		ent->mark_start = -1;
+		ent->mark_end = -1;
+		ent = ent->next;
+	}
+}
+
+static int
+gtk_xtext_selection_clear (xtext_buffer *buf)
+{
+	textentry *ent;
+	int ret = 0;
+
+	ent = buf->last_ent_start;
+	while (ent)
+	{
+		if (ent->mark_start != -1)
+			ret = 1;
+		ent->mark_start = -1;
+		ent->mark_end = -1;
+		if (ent == buf->last_ent_end)
+			break;
+		ent = ent->next;
+	}
+
+	return ret;
+}
+
+static int
+find_x (GtkXText *xtext, textentry *ent, unsigned char *text, int x, int indent)
+{
+	int xx = indent;
+	int i = 0;
+	int rcol = 0, bgcol = 0;
+	int hidden = FALSE;
+	unsigned char *orig = text;
+	int mbl;
+	int char_width;
+
+	while (*text)
+	{
+		mbl = 1;
+		if (rcol > 0 && (isdigit (*text) || (*text == ',' && isdigit (text[1]) && !bgcol)))
+		{
+			if (text[1] != ',') rcol--;
+			if (*text == ',')
+			{
+				rcol = 2;
+				bgcol = 1;
+			}
+			text++;
+		} else
+		{
+			rcol = bgcol = 0;
+			switch (*text)
+			{
+			case ATTR_COLOR:
+				rcol = 2;
+			case ATTR_BEEP:
+			case ATTR_RESET:
+			case ATTR_REVERSE:
+			case ATTR_BOLD:
+			case ATTR_UNDERLINE:
+			case ATTR_ITALICS:
+				text++;
+				break;
+			case ATTR_HIDDEN:
+				if (xtext->ignore_hidden)
+					goto def;
+				hidden = !hidden;
+				text++;
+				break;
+			default:
+			def:
+				char_width = backend_get_char_width (xtext, text, &mbl);
+				if (!hidden) xx += char_width;
+				text += mbl;
+				if (xx >= x)
+					return i + (orig - ent->str);
+			}
+		}
+
+		i += mbl;
+		if (text - orig >= ent->str_len)
+			return ent->str_len;
+	}
+
+	return ent->str_len;
+}
+
+static int
+gtk_xtext_find_x (GtkXText * xtext, int x, textentry * ent, int subline,
+						int line, int *out_of_bounds)
+{
+	int indent;
+	unsigned char *str;
+
+	if (subline < 1)
+		indent = ent->indent;
+	else
+		indent = xtext->buffer->indent;
+
+	if (line > xtext->adj->page_size || line < 0)
+		return 0;
+
+	if (xtext->buffer->grid_dirty || line > 255)
+	{
+		str = ent->str + gtk_xtext_find_subline (xtext, ent, subline);
+		if (str >= ent->str + ent->str_len)
+			return 0;
+	} else
+	{
+		if (xtext->buffer->grid_offset[line] > ent->str_len)
+			return 0;
+		str = ent->str + xtext->buffer->grid_offset[line];
+	}
+
+	if (x < indent)
+	{
+		*out_of_bounds = 1;
+		return (str - ent->str);
+	}
+
+	*out_of_bounds = 0;
+
+	return find_x (xtext, ent, str, x, indent);
+}
+
+static textentry *
+gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off,
+							int *out_of_bounds)
+{
+	textentry *ent;
+	int line;
+	int subline;
+
+	line = (y + xtext->pixel_offset) / xtext->fontsize;
+	ent = gtk_xtext_nth (xtext, line + (int)xtext->adj->value, &subline);
+	if (!ent)
+		return 0;
+
+	if (off)
+		*off = gtk_xtext_find_x (xtext, x, ent, subline, line, out_of_bounds);
+
+	return ent;
+}
+
+static void
+gtk_xtext_draw_sep (GtkXText * xtext, int y)
+{
+	int x, height;
+	GdkGC *light, *dark;
+
+	if (y == -1)
+	{
+		y = 0;
+		height = GTK_WIDGET (xtext)->allocation.height;
+	} else
+	{
+		height = xtext->fontsize;
+	}
+
+	/* draw the separator line */
+	if (xtext->separator && xtext->buffer->indent)
+	{
+		light = xtext->light_gc;
+		dark = xtext->dark_gc;
+
+		x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
+		if (x < 1)
+			return;
+
+		if (xtext->thinline)
+		{
+			if (xtext->moving_separator)
+				gdk_draw_line (xtext->draw_buf, light, x, y, x, y + height);
+			else
+				gdk_draw_line (xtext->draw_buf, xtext->thin_gc, x, y, x, y + height);
+		} else
+		{
+			if (xtext->moving_separator)
+			{
+				gdk_draw_line (xtext->draw_buf, light, x - 1, y, x - 1, y + height);
+				gdk_draw_line (xtext->draw_buf, dark, x, y, x, y + height);
+			} else
+			{
+				gdk_draw_line (xtext->draw_buf, dark, x - 1, y, x - 1, y + height);
+				gdk_draw_line (xtext->draw_buf, light, x, y, x, y + height);
+			}
+		}
+	}
+}
+
+static void
+gtk_xtext_draw_marker (GtkXText * xtext, textentry * ent, int y)
+{
+	int x, width, render_y;
+
+	if (!xtext->marker) return;
+
+	if (xtext->buffer->marker_pos == ent)
+	{
+		render_y = y + xtext->font->descent;
+	}
+	else if (xtext->buffer->marker_pos == ent->next && ent->next != NULL)
+	{
+		render_y = y + xtext->font->descent + xtext->fontsize * ent->lines_taken;
+	}
+	else return;
+
+	x = 0;
+	width = GTK_WIDGET (xtext)->allocation.width;
+
+	gdk_draw_line (xtext->draw_buf, xtext->marker_gc, x, render_y, x + width, render_y);
+
+#if GTK_CHECK_VERSION(2,4,0)
+	if (gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (xtext)))))
+#else
+	if (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (xtext)))->has_focus)
+#endif
+	{
+		xtext->buffer->marker_seen = TRUE;
+	}
+}
+
+#ifdef USE_SHM
+static int
+have_shm_pixmaps(Display *dpy)
+{
+	int major, minor;
+	static int checked = 0;
+	static int have = FALSE;
+
+	if (!checked)
+	{
+		XShmQueryVersion (dpy, &major, &minor, &have);
+		checked = 1;
+	}
+
+	return have;
+}
+#endif
+
+static void
+gtk_xtext_paint (GtkWidget *widget, GdkRectangle *area)
+{
+	GtkXText *xtext = GTK_XTEXT (widget);
+	textentry *ent_start, *ent_end;
+	int x, y;
+
+#if defined(USE_XLIB) || defined(WIN32)
+	if (xtext->transparent)
+	{
+		gdk_window_get_origin (widget->window, &x, &y);
+		/* update transparency only if it moved */
+		if (xtext->last_win_x != x || xtext->last_win_y != y)
+		{
+			xtext->last_win_x = x;
+			xtext->last_win_y = y;
+#ifndef WIN32
+#ifdef USE_SHM
+			if (xtext->shaded && !have_shm_pixmaps(GDK_WINDOW_XDISPLAY (xtext->draw_buf)))
+#else
+			if (xtext->shaded)
+#endif
+			{
+				xtext->recycle = TRUE;
+				gtk_xtext_load_trans (xtext);
+				xtext->recycle = FALSE;
+			} else
+#endif
+			{
+				gtk_xtext_free_trans (xtext);
+				gtk_xtext_load_trans (xtext);
+			}
+		}
+	}
+#endif
+
+	if (area->x == 0 && area->y == 0 &&
+		 area->height == widget->allocation.height &&
+		 area->width == widget->allocation.width)
+	{
+		dontscroll (xtext->buffer);	/* force scrolling off */
+		gtk_xtext_render_page (xtext);
+		return;
+	}
+
+	ent_start = gtk_xtext_find_char (xtext, area->x, area->y, NULL, NULL);
+	if (!ent_start)
+	{
+		xtext_draw_bg (xtext, area->x, area->y, area->width, area->height);
+		goto xit;
+	}
+	ent_end = gtk_xtext_find_char (xtext, area->x + area->width,
+											 area->y + area->height, NULL, NULL);
+	if (!ent_end)
+		ent_end = xtext->buffer->text_last;
+
+	/* can't set a clip here, because fgc/bgc are used to draw the DB too */
+/*	backend_set_clip (xtext, area);*/
+	xtext->clip_x = area->x;
+	xtext->clip_x2 = area->x + area->width;
+	xtext->clip_y = area->y;
+	xtext->clip_y2 = area->y + area->height;
+
+	/* y is the last pixel y location it rendered text at */
+	y = gtk_xtext_render_ents (xtext, ent_start, ent_end);
+
+	if (y && y < widget->allocation.height && !ent_end->next)
+	{
+		GdkRectangle rect;
+
+		rect.x = 0;
+		rect.y = y;
+		rect.width = widget->allocation.width;
+		rect.height = widget->allocation.height - y;
+
+		/* fill any space below the last line that also intersects with
+			the exposure rectangle */
+		if (gdk_rectangle_intersect (area, &rect, &rect))
+		{
+			xtext_draw_bg (xtext, rect.x, rect.y, rect.width, rect.height);
+		}
+	}
+
+	/*backend_clear_clip (xtext);*/
+	xtext->clip_x = 0;
+	xtext->clip_x2 = 1000000;
+	xtext->clip_y = 0;
+	xtext->clip_y2 = 1000000;
+
+xit:
+	x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
+	if (area->x <= x)
+		gtk_xtext_draw_sep (xtext, -1);
+}
+
+static gboolean
+gtk_xtext_expose (GtkWidget * widget, GdkEventExpose * event)
+{
+	gtk_xtext_paint (widget, &event->area);
+	return FALSE;
+}
+
+/* render a selection that has extended or contracted upward */
+
+static void
+gtk_xtext_selection_up (GtkXText *xtext, textentry *start, textentry *end,
+								int start_offset)
+{
+	/* render all the complete lines */
+	if (start->next == end)
+		gtk_xtext_render_ents (xtext, end, NULL);
+	else
+		gtk_xtext_render_ents (xtext, start->next, end);
+
+	/* now the incomplete upper line */
+	if (start == xtext->buffer->last_ent_start)
+		xtext->jump_in_offset = xtext->buffer->last_offset_start;
+	else
+		xtext->jump_in_offset = start_offset;
+	gtk_xtext_render_ents (xtext, start, NULL);
+	xtext->jump_in_offset = 0;
+}
+
+/* render a selection that has extended or contracted downward */
+
+static void
+gtk_xtext_selection_down (GtkXText *xtext, textentry *start, textentry *end,
+								  int end_offset)
+{
+	/* render all the complete lines */
+	if (end->prev == start)
+		gtk_xtext_render_ents (xtext, start, NULL);
+	else
+		gtk_xtext_render_ents (xtext, start, end->prev);
+
+	/* now the incomplete bottom line */
+	if (end == xtext->buffer->last_ent_end)
+		xtext->jump_out_offset = xtext->buffer->last_offset_end;
+	else
+		xtext->jump_out_offset = end_offset;
+	gtk_xtext_render_ents (xtext, end, NULL);
+	xtext->jump_out_offset = 0;
+}
+
+static void
+gtk_xtext_selection_render (GtkXText *xtext,
+									 textentry *start_ent, int start_offset,
+									 textentry *end_ent, int end_offset)
+{
+	textentry *ent;
+	int start, end;
+
+	xtext->skip_border_fills = TRUE;
+	xtext->skip_stamp = TRUE;
+
+	/* force an optimized render if there was no previous selection */
+	if (xtext->buffer->last_ent_start == NULL && start_ent == end_ent)
+	{
+		xtext->buffer->last_offset_start = start_offset;
+		xtext->buffer->last_offset_end = end_offset;
+		goto lamejump;
+	}
+
+	/* mark changed within 1 ent only? */
+	if (xtext->buffer->last_ent_start == start_ent &&
+		 xtext->buffer->last_ent_end == end_ent)
+	{
+		/* when only 1 end of the selection is changed, we can really
+			save on rendering */
+		if (xtext->buffer->last_offset_start == start_offset ||
+			 xtext->buffer->last_offset_end == end_offset)
+		{
+lamejump:
+			ent = end_ent;
+			/* figure out where to start and end the rendering */
+			if (end_offset > xtext->buffer->last_offset_end)
+			{
+				end = end_offset;
+				start = xtext->buffer->last_offset_end;
+			} else if (end_offset < xtext->buffer->last_offset_end)
+			{
+				end = xtext->buffer->last_offset_end;
+				start = end_offset;
+			} else if (start_offset < xtext->buffer->last_offset_start)
+			{
+				end = xtext->buffer->last_offset_start;
+				start = start_offset;
+				ent = start_ent;
+			} else if (start_offset > xtext->buffer->last_offset_start)
+			{
+				end = start_offset;
+				start = xtext->buffer->last_offset_start;
+				ent = start_ent;
+			} else
+			{	/* WORD selects end up here */
+				end = end_offset;
+				start = start_offset;
+			}
+		} else
+		{
+			/* LINE selects end up here */
+			/* so which ent actually changed? */
+			ent = start_ent;
+			if (xtext->buffer->last_offset_start == start_offset)
+				ent = end_ent;
+
+			end = MAX (xtext->buffer->last_offset_end, end_offset);
+			start = MIN (xtext->buffer->last_offset_start, start_offset);
+		}
+
+		xtext->jump_out_offset = end;
+		xtext->jump_in_offset = start;
+		gtk_xtext_render_ents (xtext, ent, NULL);
+		xtext->jump_out_offset = 0;
+		xtext->jump_in_offset = 0;
+	}
+	/* marking downward? */
+	else if (xtext->buffer->last_ent_start == start_ent &&
+				xtext->buffer->last_offset_start == start_offset)
+	{
+		/* find the range that covers both old and new selection */
+		ent = start_ent;
+		while (ent)
+		{
+			if (ent == xtext->buffer->last_ent_end)
+			{
+				gtk_xtext_selection_down (xtext, ent, end_ent, end_offset);
+				/*gtk_xtext_render_ents (xtext, ent, end_ent);*/
+				break;
+			}
+			if (ent == end_ent)
+			{
+				gtk_xtext_selection_down (xtext, ent, xtext->buffer->last_ent_end, end_offset);
+				/*gtk_xtext_render_ents (xtext, ent, xtext->buffer->last_ent_end);*/
+				break;
+			}
+			ent = ent->next;
+		}
+	}
+	/* marking upward? */
+	else if (xtext->buffer->last_ent_end == end_ent &&
+				xtext->buffer->last_offset_end == end_offset)
+	{
+		ent = end_ent;
+		while (ent)
+		{
+			if (ent == start_ent)
+			{
+				gtk_xtext_selection_up (xtext, xtext->buffer->last_ent_start, ent, start_offset);
+				/*gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, ent);*/
+				break;
+			}
+			if (ent == xtext->buffer->last_ent_start)
+			{
+				gtk_xtext_selection_up (xtext, start_ent, ent, start_offset);
+				/*gtk_xtext_render_ents (xtext, start_ent, ent);*/
+				break;
+			}
+			ent = ent->prev;
+		}
+	}
+	else	/* cross-over mark (stretched or shrunk at both ends) */
+	{
+		/* unrender the old mark */
+		gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, xtext->buffer->last_ent_end);
+		/* now render the new mark, but skip overlaps */
+		if (start_ent == xtext->buffer->last_ent_start)
+		{
+			/* if the new mark is a sub-set of the old, do nothing */
+			if (start_ent != end_ent)
+				gtk_xtext_render_ents (xtext, start_ent->next, end_ent);
+		} else if (end_ent == xtext->buffer->last_ent_end)
+		{
+			/* if the new mark is a sub-set of the old, do nothing */
+			if (start_ent != end_ent)
+				gtk_xtext_render_ents (xtext, start_ent, end_ent->prev);
+		} else
+			gtk_xtext_render_ents (xtext, start_ent, end_ent);
+	}
+
+	xtext->buffer->last_ent_start = start_ent;
+	xtext->buffer->last_ent_end = end_ent;
+	xtext->buffer->last_offset_start = start_offset;
+	xtext->buffer->last_offset_end = end_offset;
+
+	xtext->skip_border_fills = FALSE;
+	xtext->skip_stamp = FALSE;
+}
+
+static void
+gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean render)
+{
+	textentry *ent;
+	textentry *ent_end;
+	textentry *ent_start;
+	int offset_start;
+	int offset_end;
+	int low_x;
+	int low_y;
+	int high_x;
+	int high_y;
+	int tmp;
+
+	if (xtext->select_start_y > xtext->select_end_y)
+	{
+		low_x = xtext->select_end_x;
+		low_y = xtext->select_end_y;
+		high_x = xtext->select_start_x;
+		high_y = xtext->select_start_y;
+	} else
+	{
+		low_x = xtext->select_start_x;
+		low_y = xtext->select_start_y;
+		high_x = xtext->select_end_x;
+		high_y = xtext->select_end_y;
+	}
+
+	ent_start = gtk_xtext_find_char (xtext, low_x, low_y, &offset_start, &tmp);
+	if (!ent_start)
+	{
+		if (xtext->adj->value != xtext->buffer->old_value)
+			gtk_xtext_render_page (xtext);
+		return;
+	}
+
+	ent_end = gtk_xtext_find_char (xtext, high_x, high_y, &offset_end, &tmp);
+	if (!ent_end)
+	{
+		ent_end = xtext->buffer->text_last;
+		if (!ent_end)
+		{
+			if (xtext->adj->value != xtext->buffer->old_value)
+				gtk_xtext_render_page (xtext);
+			return;
+		}
+		offset_end = ent_end->str_len;
+	}
+
+	/* marking less than a complete line? */
+	/* make sure "start" is smaller than "end" (swap them if need be) */
+	if (ent_start == ent_end && offset_start > offset_end)
+	{
+		tmp = offset_start;
+		offset_start = offset_end;
+		offset_end = tmp;
+	}
+
+	/* has the selection changed? Dont render unless necessary */
+	if (xtext->buffer->last_ent_start == ent_start &&
+		 xtext->buffer->last_ent_end == ent_end &&
+		 xtext->buffer->last_offset_start == offset_start &&
+		 xtext->buffer->last_offset_end == offset_end)
+		return;
+
+	/* set all the old mark_ fields to -1 */
+	gtk_xtext_selection_clear (xtext->buffer);
+
+	ent_start->mark_start = offset_start;
+	ent_start->mark_end = offset_end;
+
+	if (ent_start != ent_end)
+	{
+		ent_start->mark_end = ent_start->str_len;
+		if (offset_end != 0)
+		{
+			ent_end->mark_start = 0;
+			ent_end->mark_end = offset_end;
+		}
+
+		/* set all the mark_ fields of the ents within the selection */
+		ent = ent_start->next;
+		while (ent && ent != ent_end)
+		{
+			ent->mark_start = 0;
+			ent->mark_end = ent->str_len;
+			ent = ent->next;
+		}
+	}
+
+	if (render)
+		gtk_xtext_selection_render (xtext, ent_start, offset_start, ent_end, offset_end);
+}
+
+static gint
+gtk_xtext_scrolldown_timeout (GtkXText * xtext)
+{
+	int p_y, win_height;
+
+	gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
+	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, 0, &win_height);
+
+	if (p_y > win_height &&
+		 xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size))
+	{
+		xtext->adj->value++;
+		gtk_adjustment_changed (xtext->adj);
+		gtk_xtext_render_page (xtext);
+		return 1;
+	}
+
+	xtext->scroll_tag = 0;
+	return 0;
+}
+
+static gint
+gtk_xtext_scrollup_timeout (GtkXText * xtext)
+{
+	int p_y;
+
+	gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
+
+	if (p_y < 0 && xtext->adj->value > 0.0)
+	{
+		xtext->adj->value--;
+		gtk_adjustment_changed (xtext->adj);
+		gtk_xtext_render_page (xtext);
+		return 1;
+	}
+
+	xtext->scroll_tag = 0;
+	return 0;
+}
+
+static void
+gtk_xtext_selection_update (GtkXText * xtext, GdkEventMotion * event, int p_y, gboolean render)
+{
+	int win_height;
+	int moved;
+
+	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, 0, &win_height);
+
+	/* selecting past top of window, scroll up! */
+	if (p_y < 0 && xtext->adj->value >= 0)
+	{
+		if (!xtext->scroll_tag)
+			xtext->scroll_tag = g_timeout_add (100,
+															(GSourceFunc)
+														 	gtk_xtext_scrollup_timeout,
+															xtext);
+		return;
+	}
+
+	/* selecting past bottom of window, scroll down! */
+	if (p_y > win_height &&
+		 xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size))
+	{
+		if (!xtext->scroll_tag)
+			xtext->scroll_tag = g_timeout_add (100,
+															(GSourceFunc)
+															gtk_xtext_scrolldown_timeout,
+															xtext);
+		return;
+	}
+
+	moved = (int)xtext->adj->value - xtext->select_start_adj;
+	xtext->select_start_y -= (moved * xtext->fontsize);
+	xtext->select_start_adj = xtext->adj->value;
+	gtk_xtext_selection_draw (xtext, event, render);
+}
+
+static char *
+gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
+						  int *ret_off, int *ret_len)
+{
+	textentry *ent;
+	int offset;
+	unsigned char *str;
+	unsigned char *word;
+	int len;
+	int out_of_bounds = 0;
+
+	ent = gtk_xtext_find_char (xtext, x, y, &offset, &out_of_bounds);
+	if (!ent)
+		return 0;
+
+	if (out_of_bounds)
+		return 0;
+
+	if (offset == ent->str_len)
+		return 0;
+
+	if (offset < 1)
+		return 0;
+
+	/*offset--;*/	/* FIXME: not all chars are 1 byte */
+
+	str = ent->str + offset;
+
+	while (!is_del (*str) && str != ent->str)
+		str--;
+	word = str + 1;
+
+	len = 0;
+	str = word;
+	while (!is_del (*str) && len != ent->str_len)
+	{
+		str++;
+		len++;
+	}
+
+	if (len > 0 && word[len-1]=='.')
+	{
+		len--;
+		str--;
+	}
+
+	if (ret_ent)
+		*ret_ent = ent;
+	if (ret_off)
+		*ret_off = word - ent->str;
+	if (ret_len)
+		*ret_len = str - word;
+
+	return gtk_xtext_strip_color (word, len, xtext->scratch_buffer, NULL, NULL, FALSE);
+}
+
+#ifdef MOTION_MONITOR
+
+static void
+gtk_xtext_unrender_hilight (GtkXText *xtext)
+{
+	xtext->render_hilights_only = TRUE;
+	xtext->skip_border_fills = TRUE;
+	xtext->skip_stamp = TRUE;
+	xtext->un_hilight = TRUE;
+
+	gtk_xtext_render_ents (xtext, xtext->hilight_ent, NULL);
+
+	xtext->render_hilights_only = FALSE;
+	xtext->skip_border_fills = FALSE;
+	xtext->skip_stamp = FALSE;
+	xtext->un_hilight = FALSE;
+}
+
+static gboolean
+gtk_xtext_leave_notify (GtkWidget * widget, GdkEventCrossing * event)
+{
+	GtkXText *xtext = GTK_XTEXT (widget);
+
+	if (xtext->cursor_hand)
+	{
+		gtk_xtext_unrender_hilight (xtext);
+		xtext->hilight_start = -1;
+		xtext->hilight_end = -1;
+		xtext->cursor_hand = FALSE;
+		gdk_window_set_cursor (widget->window, 0);
+		xtext->hilight_ent = NULL;
+	}
+
+	if (xtext->cursor_resize)
+	{
+		gtk_xtext_unrender_hilight (xtext);
+		xtext->hilight_start = -1;
+		xtext->hilight_end = -1;
+		xtext->cursor_resize = FALSE;
+		gdk_window_set_cursor (widget->window, 0);
+		xtext->hilight_ent = NULL;
+	}
+
+	return FALSE;
+}
+
+#endif
+
+/* check if we should mark time stamps, and if a redraw is needed */
+
+static gboolean
+gtk_xtext_check_mark_stamp (GtkXText *xtext, GdkModifierType mask)
+{
+	gboolean redraw = FALSE;
+
+	if ((mask & GDK_SHIFT_MASK))
+	{
+		if (!xtext->mark_stamp)
+		{
+			redraw = TRUE;	/* must redraw all */
+			xtext->mark_stamp = TRUE;
+		}
+	} else
+	{
+		if (xtext->mark_stamp)
+		{
+			redraw = TRUE;	/* must redraw all */
+			xtext->mark_stamp = FALSE;
+		}
+	}
+	return redraw;
+}
+
+static gboolean
+gtk_xtext_motion_notify (GtkWidget * widget, GdkEventMotion * event)
+{
+	GtkXText *xtext = GTK_XTEXT (widget);
+	GdkModifierType mask;
+	int redraw, tmp, x, y, offset, len, line_x;
+	unsigned char *word;
+	textentry *word_ent;
+
+	gdk_window_get_pointer (widget->window, &x, &y, &mask);
+
+	if (xtext->moving_separator)
+	{
+		if (x < (3 * widget->allocation.width) / 5 && x > 15)
+		{
+			tmp = xtext->buffer->indent;
+			xtext->buffer->indent = x;
+			gtk_xtext_fix_indent (xtext->buffer);
+			if (tmp != xtext->buffer->indent)
+			{
+				gtk_xtext_recalc_widths (xtext->buffer, FALSE);
+				if (xtext->buffer->scrollbar_down)
+					gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
+													  xtext->adj->page_size);
+				if (!xtext->io_tag)
+					xtext->io_tag = g_timeout_add (REFRESH_TIMEOUT,
+																(GSourceFunc)
+																gtk_xtext_adjustment_timeout,
+																xtext);
+			}
+		}
+		return FALSE;
+	}
+
+	if (xtext->button_down)
+	{
+		redraw = gtk_xtext_check_mark_stamp (xtext, mask);
+		gtk_grab_add (widget);
+		/*gdk_pointer_grab (widget->window, TRUE,
+									GDK_BUTTON_RELEASE_MASK |
+									GDK_BUTTON_MOTION_MASK, NULL, NULL, 0);*/
+		xtext->select_end_x = x;
+		xtext->select_end_y = y;
+		gtk_xtext_selection_update (xtext, event, y, !redraw);
+		xtext->hilighting = TRUE;
+
+		/* user has pressed or released SHIFT, must redraw entire selection */
+		if (redraw)
+		{
+			xtext->force_stamp = TRUE;
+			gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start,
+										  xtext->buffer->last_ent_end);
+			xtext->force_stamp = FALSE;
+		}
+		return FALSE;
+	}
+#ifdef MOTION_MONITOR
+
+	if (xtext->separator && xtext->buffer->indent)
+	{
+		line_x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
+		if (line_x == x || line_x == x + 1 || line_x == x - 1)
+		{
+			if (!xtext->cursor_resize)
+			{
+				gdk_window_set_cursor (GTK_WIDGET (xtext)->window,
+										  		xtext->resize_cursor);
+				xtext->cursor_resize = TRUE;
+			}
+			return FALSE;
+		}
+	}
+
+	if (xtext->urlcheck_function == NULL)
+		return FALSE;
+
+	word = gtk_xtext_get_word (xtext, x, y, &word_ent, &offset, &len);
+	if (word)
+	{
+		if (xtext->urlcheck_function (GTK_WIDGET (xtext), word, len) > 0)
+		{
+			if (!xtext->cursor_hand ||
+				 xtext->hilight_ent != word_ent ||
+				 xtext->hilight_start != offset ||
+				 xtext->hilight_end != offset + len)
+			{
+				if (!xtext->cursor_hand)
+				{
+					gdk_window_set_cursor (GTK_WIDGET (xtext)->window,
+											  		xtext->hand_cursor);
+					xtext->cursor_hand = TRUE;
+				}
+
+				/* un-render the old hilight */
+				if (xtext->hilight_ent)
+					gtk_xtext_unrender_hilight (xtext);
+
+				xtext->hilight_ent = word_ent;
+				xtext->hilight_start = offset;
+				xtext->hilight_end = offset + len;
+
+				xtext->skip_border_fills = TRUE;
+				xtext->render_hilights_only = TRUE;
+				xtext->skip_stamp = TRUE;
+
+				gtk_xtext_render_ents (xtext, word_ent, NULL);
+
+				xtext->skip_border_fills = FALSE;
+				xtext->render_hilights_only = FALSE;
+				xtext->skip_stamp = FALSE;
+			}
+			return FALSE;
+		}
+	}
+
+	gtk_xtext_leave_notify (widget, NULL);
+
+#endif
+
+	return FALSE;
+}
+
+static void
+gtk_xtext_set_clip_owner (GtkWidget * xtext, GdkEventButton * event)
+{
+	char *str;
+	int len;
+
+	if (GTK_XTEXT (xtext)->selection_buffer &&
+		GTK_XTEXT (xtext)->selection_buffer != GTK_XTEXT (xtext)->buffer)
+		gtk_xtext_selection_clear (GTK_XTEXT (xtext)->selection_buffer);
+
+	GTK_XTEXT (xtext)->selection_buffer = GTK_XTEXT (xtext)->buffer;
+
+	str = gtk_xtext_selection_get_text (GTK_XTEXT (xtext), &len);
+	if (str)
+	{
+#if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0)
+		gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
+										str, len);
+#else
+		gtk_clipboard_set_text (gtk_widget_get_clipboard (xtext, GDK_SELECTION_CLIPBOARD),
+										str, len);
+#endif
+		free (str);
+	}
+
+	gtk_selection_owner_set (xtext, GDK_SELECTION_PRIMARY, event->time);
+}
+
+static void
+gtk_xtext_unselect (GtkXText *xtext)
+{
+	xtext_buffer *buf = xtext->buffer;
+
+	xtext->skip_border_fills = TRUE;
+	xtext->skip_stamp = TRUE;
+
+	xtext->jump_in_offset = buf->last_ent_start->mark_start;
+	/* just a single ent was marked? */
+	if (buf->last_ent_start == buf->last_ent_end)
+	{
+		xtext->jump_out_offset = buf->last_ent_start->mark_end;
+		buf->last_ent_end = NULL;
+	}
+
+	gtk_xtext_selection_clear (xtext->buffer);
+
+	/* FIXME: use jump_out on multi-line selects too! */
+	gtk_xtext_render_ents (xtext, buf->last_ent_start, buf->last_ent_end);
+
+	xtext->jump_in_offset = 0;
+	xtext->jump_out_offset = 0;
+
+	xtext->skip_border_fills = FALSE;
+	xtext->skip_stamp = FALSE;
+
+	xtext->buffer->last_ent_start = NULL;
+	xtext->buffer->last_ent_end = NULL;
+}
+
+static gboolean
+gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
+{
+	GtkXText *xtext = GTK_XTEXT (widget);
+	unsigned char *word;
+	int old;
+
+	if (xtext->moving_separator)
+	{
+		xtext->moving_separator = FALSE;
+		old = xtext->buffer->indent;
+		if (event->x < (4 * widget->allocation.width) / 5 && event->x > 15)
+			xtext->buffer->indent = event->x;
+		gtk_xtext_fix_indent (xtext->buffer);
+		if (xtext->buffer->indent != old)
+		{
+			gtk_xtext_recalc_widths (xtext->buffer, FALSE);
+			gtk_xtext_adjustment_set (xtext->buffer, TRUE);
+			gtk_xtext_render_page (xtext);
+		} else
+			gtk_xtext_draw_sep (xtext, -1);
+		return FALSE;
+	}
+
+	if (xtext->word_or_line_select)
+	{
+		xtext->word_or_line_select = FALSE;
+		xtext->button_down = FALSE;
+		return FALSE;
+	}
+
+	if (event->button == 1)
+	{
+		xtext->button_down = FALSE;
+
+		gtk_grab_remove (widget);
+		/*gdk_pointer_ungrab (0);*/
+
+		/* got a new selection? */
+		if (xtext->buffer->last_ent_start)
+		{
+			xtext->color_paste = FALSE;
+			if (event->state & GDK_CONTROL_MASK)
+				xtext->color_paste = TRUE;
+			gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
+		}
+
+		if (xtext->select_start_x == event->x &&
+			 xtext->select_start_y == event->y &&
+			 xtext->buffer->last_ent_start)
+		{
+			gtk_xtext_unselect (xtext);
+			xtext->mark_stamp = FALSE;
+			return FALSE;
+		}
+
+		if (!xtext->hilighting)
+		{
+			word = gtk_xtext_get_word (xtext, event->x, event->y, 0, 0, 0);
+			g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0, word ? word : NULL, event);
+		} else
+		{
+			xtext->hilighting = FALSE;
+		}
+	}
+
+
+	return FALSE;
+}
+
+static gboolean
+gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
+{
+	GtkXText *xtext = GTK_XTEXT (widget);
+	GdkModifierType mask;
+	textentry *ent;
+	unsigned char *word;
+	int line_x, x, y, offset, len;
+
+	gdk_window_get_pointer (widget->window, &x, &y, &mask);
+
+	if (event->button == 3 || event->button == 2) /* right/middle click */
+	{
+		word = gtk_xtext_get_word (xtext, x, y, 0, 0, 0);
+		if (word)
+		{
+			g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0,
+								word, event);
+		} else
+			g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0,
+								"", event);
+		return FALSE;
+	}
+
+	if (event->button != 1)		  /* we only want left button */
+		return FALSE;
+
+	if (event->type == GDK_2BUTTON_PRESS)	/* WORD select */
+	{
+		gtk_xtext_check_mark_stamp (xtext, mask);
+		if (gtk_xtext_get_word (xtext, x, y, &ent, &offset, &len))
+		{
+			if (len == 0)
+				return FALSE;
+			gtk_xtext_selection_clear (xtext->buffer);
+			ent->mark_start = offset;
+			ent->mark_end = offset + len;
+			gtk_xtext_selection_render (xtext, ent, offset, ent, offset + len);
+			xtext->word_or_line_select = TRUE;
+			gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
+		}
+
+		return FALSE;
+	}
+
+	if (event->type == GDK_3BUTTON_PRESS)	/* LINE select */
+	{
+		gtk_xtext_check_mark_stamp (xtext, mask);
+		if (gtk_xtext_get_word (xtext, x, y, &ent, 0, 0))
+		{
+			gtk_xtext_selection_clear (xtext->buffer);
+			ent->mark_start = 0;
+			ent->mark_end = ent->str_len;
+			gtk_xtext_selection_render (xtext, ent, 0, ent, ent->str_len);
+			xtext->word_or_line_select = TRUE;
+			gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
+		}
+
+		return FALSE;
+	}
+
+	/* check if it was a separator-bar click */
+	if (xtext->separator && xtext->buffer->indent)
+	{
+		line_x = xtext->buffer->indent - ((xtext->space_width + 1) / 2);
+		if (line_x == x || line_x == x + 1 || line_x == x - 1)
+		{
+			xtext->moving_separator = TRUE;
+			/* draw the separator line */
+			gtk_xtext_draw_sep (xtext, -1);
+			return FALSE;
+		}
+	}
+
+	xtext->button_down = TRUE;
+	xtext->select_start_x = x;
+	xtext->select_start_y = y;
+	xtext->select_start_adj = xtext->adj->value;
+
+	return FALSE;
+}
+
+/* another program has claimed the selection */
+
+static gboolean
+gtk_xtext_selection_kill (GtkXText *xtext, GdkEventSelection *event)
+{
+#ifndef WIN32
+	if (xtext->buffer->last_ent_start)
+		gtk_xtext_unselect (xtext);
+#endif
+	return TRUE;
+}
+
+static char *
+gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret)
+{
+	textentry *ent;
+	char *txt;
+	char *pos;
+	char *stripped;
+	int len;
+	int first = TRUE;
+	xtext_buffer *buf;
+
+	buf = xtext->selection_buffer;
+	if (!buf)
+		return NULL;
+
+	/* first find out how much we need to malloc ... */
+	len = 0;
+	ent = buf->last_ent_start;
+	while (ent)
+	{
+		if (ent->mark_start != -1)
+		{
+			/* include timestamp? */
+			if (ent->mark_start == 0 && xtext->mark_stamp)
+			{
+				char *time_str;
+				int stamp_size = xtext_get_stamp_str (ent->stamp, &time_str);
+				g_free (time_str);
+				len += stamp_size;
+			}
+
+			if (ent->mark_end - ent->mark_start > 0)
+				len += (ent->mark_end - ent->mark_start) + 1;
+			else
+				len++;
+		}
+		if (ent == buf->last_ent_end)
+			break;
+		ent = ent->next;
+	}
+
+	if (len < 1)
+		return NULL;
+
+	/* now allocate mem and copy buffer */
+	pos = txt = malloc (len);
+	ent = buf->last_ent_start;
+	while (ent)
+	{
+		if (ent->mark_start != -1)
+		{
+			if (!first)
+			{
+				*pos = '\n';
+				pos++;
+			}
+			first = FALSE;
+			if (ent->mark_end - ent->mark_start > 0)
+			{
+				/* include timestamp? */
+				if (ent->mark_start == 0 && xtext->mark_stamp)
+				{
+					char *time_str;
+					int stamp_size = xtext_get_stamp_str (ent->stamp, &time_str);
+					memcpy (pos, time_str, stamp_size);
+					g_free (time_str);
+					pos += stamp_size;
+				}
+
+				memcpy (pos, ent->str + ent->mark_start,
+						  ent->mark_end - ent->mark_start);
+				pos += ent->mark_end - ent->mark_start;
+			}
+		}
+		if (ent == buf->last_ent_end)
+			break;
+		ent = ent->next;
+	}
+	*pos = 0;
+
+	if (xtext->color_paste)
+	{
+		/*stripped = gtk_xtext_conv_color (txt, strlen (txt), &len);*/
+		stripped = txt;
+		len = strlen (txt);
+	} else
+	{
+		stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, &len, 0, FALSE);
+		free (txt);
+	}
+
+	*len_ret = len;
+	return stripped;
+}
+
+/* another program is asking for our selection */
+
+static void
+gtk_xtext_selection_get (GtkWidget * widget,
+								 GtkSelectionData * selection_data_ptr,
+								 guint info, guint time)
+{
+	GtkXText *xtext = GTK_XTEXT (widget);
+	char *stripped;
+	guchar *new_text;
+	int len;
+	gsize glen;
+
+	stripped = gtk_xtext_selection_get_text (xtext, &len);
+	if (!stripped)
+		return;
+
+	switch (info)
+	{
+	case TARGET_UTF8_STRING:
+		/* it's already in utf8 */
+		gtk_selection_data_set_text (selection_data_ptr, stripped, len);
+		break;
+	case TARGET_TEXT:
+	case TARGET_COMPOUND_TEXT:
+		{
+			GdkAtom encoding;
+			gint format;
+			gint new_length;
+
+#if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0)
+			gdk_string_to_compound_text (
+#else
+			gdk_string_to_compound_text_for_display (
+												gdk_drawable_get_display (widget->window),
+#endif
+												stripped, &encoding, &format, &new_text,
+												&new_length);
+			gtk_selection_data_set (selection_data_ptr, encoding, format,
+											new_text, new_length);
+			gdk_free_compound_text (new_text);
+		}
+		break;
+	default:
+		new_text = g_locale_from_utf8 (stripped, len, NULL, &glen, NULL);
+		gtk_selection_data_set (selection_data_ptr, GDK_SELECTION_TYPE_STRING,
+										8, new_text, glen);
+		g_free (new_text);
+	}
+
+	free (stripped);
+}
+
+static gboolean
+gtk_xtext_scroll (GtkWidget *widget, GdkEventScroll *event)
+{
+	GtkXText *xtext = GTK_XTEXT (widget);
+	gfloat new_value;
+
+	if (event->direction == GDK_SCROLL_UP)		/* mouse wheel pageUp */
+	{
+		new_value = xtext->adj->value - (xtext->adj->page_increment / 10);
+		if (new_value < xtext->adj->lower)
+			new_value = xtext->adj->lower;
+		gtk_adjustment_set_value (xtext->adj, new_value);
+	}
+	else if (event->direction == GDK_SCROLL_DOWN)	/* mouse wheel pageDn */
+	{
+		new_value = xtext->adj->value + (xtext->adj->page_increment / 10);
+		if (new_value > (xtext->adj->upper - xtext->adj->page_size))
+			new_value = xtext->adj->upper - xtext->adj->page_size;
+		gtk_adjustment_set_value (xtext->adj, new_value);
+	}
+
+	return FALSE;
+}
+
+static void
+gtk_xtext_class_init (GtkXTextClass * class)
+{
+	GtkObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+	GtkXTextClass *xtext_class;
+
+	object_class = (GtkObjectClass *) class;
+	widget_class = (GtkWidgetClass *) class;
+	xtext_class = (GtkXTextClass *) class;
+
+	parent_class = gtk_type_class (gtk_widget_get_type ());
+
+	xtext_signals[WORD_CLICK] =
+		g_signal_new ("word_click",
+							G_TYPE_FROM_CLASS (object_class),
+							G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+							G_STRUCT_OFFSET (GtkXTextClass, word_click),
+							NULL, NULL,
+							gtk_marshal_VOID__POINTER_POINTER,
+							G_TYPE_NONE,
+							2, G_TYPE_POINTER, G_TYPE_POINTER);
+	object_class->destroy = gtk_xtext_destroy;
+
+	widget_class->realize = gtk_xtext_realize;
+	widget_class->unrealize = gtk_xtext_unrealize;
+	widget_class->size_request = gtk_xtext_size_request;
+	widget_class->size_allocate = gtk_xtext_size_allocate;
+	widget_class->button_press_event = gtk_xtext_button_press;
+	widget_class->button_release_event = gtk_xtext_button_release;
+	widget_class->motion_notify_event = gtk_xtext_motion_notify;
+	widget_class->selection_clear_event = (void *)gtk_xtext_selection_kill;
+	widget_class->selection_get = gtk_xtext_selection_get;
+	widget_class->expose_event = gtk_xtext_expose;
+	widget_class->scroll_event = gtk_xtext_scroll;
+#ifdef MOTION_MONITOR
+	widget_class->leave_notify_event = gtk_xtext_leave_notify;
+#endif
+
+	xtext_class->word_click = NULL;
+}
+
+GType
+gtk_xtext_get_type (void)
+{
+	static GType xtext_type = 0;
+
+	if (!xtext_type)
+	{
+		static const GTypeInfo xtext_info =
+		{
+			sizeof (GtkXTextClass),
+			NULL,		/* base_init */
+			NULL,		/* base_finalize */
+			(GClassInitFunc) gtk_xtext_class_init,
+			NULL,		/* class_finalize */
+			NULL,		/* class_data */
+			sizeof (GtkXText),
+			0,		/* n_preallocs */
+			(GInstanceInitFunc) gtk_xtext_init,
+		};
+
+		xtext_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkXText",
+														 &xtext_info, 0);
+	}
+
+	return xtext_type;
+}
+
+/* strip MIRC colors and other attribs. */
+
+/* CL: needs to strip hidden when called by gtk_xtext_text_width, but not when copying text */
+
+static unsigned char *
+gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
+							  int *newlen, int *mb_ret, int strip_hidden)
+{
+	int i = 0;
+	int rcol = 0, bgcol = 0;
+	int hidden = FALSE;
+	unsigned char *new_str;
+	int mb = FALSE;
+
+	if (outbuf == NULL)
+		new_str = malloc (len + 2);
+	else
+		new_str = outbuf;
+
+	while (len > 0)
+	{
+		if (*text >= 128)
+			mb = TRUE;
+
+		if (rcol > 0 && (isdigit (*text) || (*text == ',' && isdigit (text[1]) && !bgcol)))
+		{
+			if (text[1] != ',') rcol--;
+			if (*text == ',')
+			{
+				rcol = 2;
+				bgcol = 1;
+			}
+		} else
+		{
+			rcol = bgcol = 0;
+			switch (*text)
+			{
+			case ATTR_COLOR:
+				rcol = 2;
+				break;
+			case ATTR_BEEP:
+			case ATTR_RESET:
+			case ATTR_REVERSE:
+			case ATTR_BOLD:
+			case ATTR_UNDERLINE:
+			case ATTR_ITALICS:
+				break;
+			case ATTR_HIDDEN:
+				hidden = !hidden;
+				break;
+			default:
+				if (!(hidden && strip_hidden))
+					new_str[i++] = *text;
+			}
+		}
+		text++;
+		len--;
+	}
+
+	new_str[i] = 0;
+
+	if (newlen != NULL)
+		*newlen = i;
+
+	if (mb_ret != NULL)
+		*mb_ret = mb;
+
+	return new_str;
+}
+
+/* GeEkMaN: converts mIRC control codes to literal control codes */
+
+static char *
+gtk_xtext_conv_color (unsigned char *text, int len, int *newlen)
+{
+	int i, j = 2;
+	char cchar = 0;
+	char *new_str;
+	int mbl;
+
+	for (i = 0; i < len;)
+	{
+		switch (text[i])
+		{
+		case ATTR_COLOR:
+		case ATTR_RESET:
+		case ATTR_REVERSE:
+		case ATTR_BOLD:
+		case ATTR_UNDERLINE:
+		case ATTR_ITALICS:
+		case ATTR_HIDDEN:
+			j += 3;
+			i++;
+			break;
+		default:
+			mbl = charlen (text + i);
+			j += mbl;
+			i += mbl;
+		}
+	}
+
+	new_str = malloc (j);
+	j = 0;
+
+	for (i = 0; i < len;)
+	{
+		switch (text[i])
+		{
+		case ATTR_COLOR:
+			cchar = 'C';
+			break;
+		case ATTR_RESET:
+			cchar = 'O';
+			break;
+		case ATTR_REVERSE:
+			cchar = 'R';
+			break;
+		case ATTR_BOLD:
+			cchar = 'B';
+			break;
+		case ATTR_UNDERLINE:
+			cchar = 'U';
+			break;
+		case ATTR_ITALICS:
+			cchar = 'I';
+			break;
+		case ATTR_HIDDEN:
+			cchar = 'H';
+			break;
+		case ATTR_BEEP:
+			break;
+		default:
+			mbl = charlen (text + i);
+			if (mbl == 1)
+			{
+				new_str[j] = text[i];
+				j++;
+				i++;
+			} else
+			{
+				/* invalid utf8 safe guard */
+				if (i + mbl > len)
+					mbl = len - i;
+				memcpy (new_str + j, text + i, mbl);
+				j += mbl;
+				i += mbl;
+			}
+		}
+		if (cchar != 0)
+		{
+			new_str[j++] = '%';
+			new_str[j++] = cchar;
+			cchar = 0;
+			i++;
+		}
+	}
+
+	new_str[j] = 0;
+	*newlen = j;
+
+	return new_str;
+}
+
+/* gives width of a string, excluding the mIRC codes */
+
+static int
+gtk_xtext_text_width (GtkXText *xtext, unsigned char *text, int len,
+							 int *mb_ret)
+{
+	unsigned char *new_buf;
+	int new_len, mb;
+
+	new_buf = gtk_xtext_strip_color (text, len, xtext->scratch_buffer,
+												&new_len, &mb, !xtext->ignore_hidden);
+
+	if (mb_ret)
+		*mb_ret = mb;
+
+	return backend_get_text_width (xtext, new_buf, new_len, mb);
+}
+
+/* actually draw text to screen (one run with the same color/attribs) */
+
+static int
+gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
+								int len, GdkGC *gc, int is_mb)
+{
+	int str_width, dofill;
+	GdkDrawable *pix = NULL;
+	int dest_x, dest_y;
+
+	if (xtext->dont_render || len < 1 || xtext->hidden)
+		return 0;
+
+	str_width = backend_get_text_width (xtext, str, len, is_mb);
+
+	if (xtext->dont_render2)
+		return str_width;
+
+	/* roll-your-own clipping (avoiding XftDrawString is always good!) */
+	if (x > xtext->clip_x2 || x + str_width < xtext->clip_x)
+		return str_width;
+	if (y - xtext->font->ascent > xtext->clip_y2 || (y - xtext->font->ascent) + xtext->fontsize < xtext->clip_y)
+		return str_width;
+
+	if (xtext->render_hilights_only)
+	{
+		if (!xtext->in_hilight)	/* is it a hilight prefix? */
+			return str_width;
+#ifndef COLOR_HILIGHT
+		if (!xtext->un_hilight)	/* doing a hilight? no need to draw the text */
+			goto dounder;
+#endif
+	}
+
+#ifdef USE_DB
+#ifdef WIN32
+	if (!xtext->transparent)
+#endif
+	{
+		pix = gdk_pixmap_new (xtext->draw_buf, str_width, xtext->fontsize, xtext->depth);
+		if (pix)
+		{
+#ifdef USE_XFT
+			XftDrawChange (xtext->xftdraw, GDK_WINDOW_XWINDOW (pix));
+#endif
+			dest_x = x;
+			dest_y = y - xtext->font->ascent;
+
+			gdk_gc_set_ts_origin (xtext->bgc, xtext->ts_x - x, xtext->ts_y - dest_y);
+
+			x = 0;
+			y = xtext->font->ascent;
+			xtext->draw_buf = pix;
+		}
+	}
+#endif
+
+	dofill = TRUE;
+
+	/* backcolor is always handled by XDrawImageString */
+	if (!xtext->backcolor && xtext->pixmap)
+	{
+	/* draw the background pixmap behind the text - CAUSES FLICKER HERE!! */
+		xtext_draw_bg (xtext, x, y - xtext->font->ascent, str_width,
+							xtext->fontsize);
+		dofill = FALSE;	/* already drawn the background */
+	}
+
+	backend_draw_text (xtext, dofill, gc, x, y, str, len, str_width, is_mb);
+
+#ifdef USE_DB
+	if (pix)
+	{
+		GdkRectangle clip;
+		GdkRectangle dest;
+
+		gdk_gc_set_ts_origin (xtext->bgc, xtext->ts_x, xtext->ts_y);
+		xtext->draw_buf = GTK_WIDGET (xtext)->window;
+#ifdef USE_XFT
+		XftDrawChange (xtext->xftdraw, GDK_WINDOW_XWINDOW (xtext->draw_buf));
+#endif
+#if 0
+		gdk_draw_drawable (xtext->draw_buf, xtext->bgc, pix, 0, 0, dest_x,
+								 dest_y, str_width, xtext->fontsize);
+#else
+		clip.x = xtext->clip_x;
+		clip.y = xtext->clip_y;
+		clip.width = xtext->clip_x2 - xtext->clip_x;
+		clip.height = xtext->clip_y2 - xtext->clip_y;
+
+		dest.x = dest_x;
+		dest.y = dest_y;
+		dest.width = str_width;
+		dest.height = xtext->fontsize;
+
+		if (gdk_rectangle_intersect (&clip, &dest, &dest))
+			/* dump the DB to window, but only within the clip_x/x2/y/y2 */
+			gdk_draw_drawable (xtext->draw_buf, xtext->bgc, pix,
+									 dest.x - dest_x, dest.y - dest_y,
+									 dest.x, dest.y, dest.width, dest.height);
+#endif
+		g_object_unref (pix);
+	}
+#endif
+
+	if (xtext->underline)
+	{
+#ifdef USE_XFT
+		GdkColor col;
+#endif
+
+#ifndef COLOR_HILIGHT
+dounder:
+#endif
+
+#ifdef USE_XFT
+		col.pixel = xtext->xft_fg->pixel;
+		gdk_gc_set_foreground (gc, &col);
+#endif
+		if (pix)
+			y = dest_y + xtext->font->ascent + 1;
+		else
+		{
+			y++;
+			dest_x = x;
+		}
+		/* draw directly to window, it's out of the range of our DB */
+		gdk_draw_line (xtext->draw_buf, gc, dest_x, y, dest_x + str_width - 1, y);
+	}
+
+	return str_width;
+}
+
+static void
+gtk_xtext_reset (GtkXText * xtext, int mark, int attribs)
+{
+	if (attribs)
+	{
+		xtext->underline = FALSE;
+		xtext->bold = FALSE;
+		xtext->italics = FALSE;
+		xtext->hidden = FALSE;
+	}
+	if (!mark)
+	{
+		xtext->backcolor = FALSE;
+		if (xtext->col_fore != XTEXT_FG)
+			xtext_set_fg (xtext, xtext->fgc, XTEXT_FG);
+		if (xtext->col_back != XTEXT_BG)
+			xtext_set_bg (xtext, xtext->fgc, XTEXT_BG);
+	}
+	xtext->col_fore = XTEXT_FG;
+	xtext->col_back = XTEXT_BG;
+	xtext->parsing_color = FALSE;
+	xtext->parsing_backcolor = FALSE;
+	xtext->nc = 0;
+}
+
+/* render a single line, which WONT wrap, and parse mIRC colors */
+
+static int
+gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
+							 unsigned char *str, int len, int win_width, int indent,
+							 int line, int left_only, int *x_size_ret)
+{
+	GdkGC *gc;
+	int i = 0, x = indent, j = 0;
+	unsigned char *pstr = str;
+	int col_num, tmp;
+	int offset;
+	int mark = FALSE;
+	int ret = 1;
+
+	xtext->in_hilight = FALSE;
+
+	offset = str - ent->str;
+
+	if (line < 255 && line >= 0)
+		xtext->buffer->grid_offset[line] = offset;
+
+	gc = xtext->fgc;				  /* our foreground GC */
+
+	if (ent->mark_start != -1 &&
+		 ent->mark_start <= i + offset && ent->mark_end > i + offset)
+	{
+		xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
+		xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
+		xtext->backcolor = TRUE;
+		mark = TRUE;
+	}
+#ifdef MOTION_MONITOR
+	if (xtext->hilight_ent == ent &&
+		 xtext->hilight_start <= i + offset && xtext->hilight_end > i + offset)
+	{
+		if (!xtext->un_hilight)
+		{
+#ifdef COLOR_HILIGHT
+			xtext_set_bg (xtext, gc, 2);
+#else
+			xtext->underline = TRUE;
+#endif
+		}
+		xtext->in_hilight = TRUE;
+	}
+#endif
+
+	if (!xtext->skip_border_fills && !xtext->dont_render)
+	{
+		/* draw background to the left of the text */
+		if (str == ent->str && indent > MARGIN && xtext->buffer->time_stamp)
+		{
+			/* don't overwrite the timestamp */
+			if (indent > xtext->stamp_width)
+			{
+				xtext_draw_bg (xtext, xtext->stamp_width, y - xtext->font->ascent,
+									indent - xtext->stamp_width, xtext->fontsize);
+			}
+		} else
+		{
+			/* fill the indent area with background gc */
+			if (indent >= xtext->clip_x)
+			{
+				xtext_draw_bg (xtext, 0, y - xtext->font->ascent,
+									MIN (indent, xtext->clip_x2), xtext->fontsize);
+			}
+		}
+	}
+
+	if (xtext->jump_in_offset > 0 && offset < xtext->jump_in_offset)
+		xtext->dont_render2 = TRUE;
+
+	while (i < len)
+	{
+
+#ifdef MOTION_MONITOR
+		if (xtext->hilight_ent == ent && xtext->hilight_start == (i + offset))
+		{
+			x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+			pstr += j;
+			j = 0;
+			if (!xtext->un_hilight)
+			{
+#ifdef COLOR_HILIGHT
+				xtext_set_bg (xtext, gc, 2);
+#else
+				xtext->underline = TRUE;
+#endif
+			}
+
+			xtext->in_hilight = TRUE;
+		}
+#endif
+
+		if ((xtext->parsing_color && isdigit (str[i]) && xtext->nc < 2) ||
+			 (xtext->parsing_color && str[i] == ',' && isdigit (str[i+1]) && xtext->nc < 3 && !xtext->parsing_backcolor))
+		{
+			pstr++;
+			if (str[i] == ',')
+			{
+				xtext->parsing_backcolor = TRUE;
+				if (xtext->nc)
+				{
+					xtext->num[xtext->nc] = 0;
+					xtext->nc = 0;
+					col_num = atoi (xtext->num);
+					if (col_num == 99)	/* mIRC lameness */
+						col_num = XTEXT_FG;
+					else
+						col_num = col_num % XTEXT_MIRC_COLS;
+					xtext->col_fore = col_num;
+					if (!mark)
+						xtext_set_fg (xtext, gc, col_num);
+				}
+			} else
+			{
+				xtext->num[xtext->nc] = str[i];
+				if (xtext->nc < 7)
+					xtext->nc++;
+			}
+		} else
+		{
+			if (xtext->parsing_color)
+			{
+				xtext->parsing_color = FALSE;
+				if (xtext->nc)
+				{
+					xtext->num[xtext->nc] = 0;
+					xtext->nc = 0;
+					col_num = atoi (xtext->num);
+					if (xtext->parsing_backcolor)
+					{
+						if (col_num == 99)	/* mIRC lameness */
+							col_num = XTEXT_BG;
+						else
+							col_num = col_num % XTEXT_MIRC_COLS;
+						if (col_num == XTEXT_BG)
+							xtext->backcolor = FALSE;
+						else
+							xtext->backcolor = TRUE;
+						if (!mark)
+							xtext_set_bg (xtext, gc, col_num);
+						xtext->col_back = col_num;
+					} else
+					{
+						if (col_num == 99)	/* mIRC lameness */
+							col_num = XTEXT_FG;
+						else
+							col_num = col_num % XTEXT_MIRC_COLS;
+						if (!mark)
+							xtext_set_fg (xtext, gc, col_num);
+						xtext->col_fore = col_num;
+					}
+					xtext->parsing_backcolor = FALSE;
+				} else
+				{
+					/* got a \003<non-digit>... i.e. reset colors */
+					x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+					pstr += j;
+					j = 0;
+					gtk_xtext_reset (xtext, mark, FALSE);
+				}
+			}
+
+			switch (str[i])
+			{
+			case '\n':
+			/*case ATTR_BEEP:*/
+				break;
+			case ATTR_REVERSE:
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				pstr += j + 1;
+				j = 0;
+				tmp = xtext->col_fore;
+				xtext->col_fore = xtext->col_back;
+				xtext->col_back = tmp;
+				if (!mark)
+				{
+					xtext_set_fg (xtext, gc, xtext->col_fore);
+					xtext_set_bg (xtext, gc, xtext->col_back);
+				}
+				if (xtext->col_back != XTEXT_BG)
+					xtext->backcolor = TRUE;
+				else
+					xtext->backcolor = FALSE;
+				break;
+			case ATTR_BOLD:
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				xtext->bold = !xtext->bold;
+				pstr += j + 1;
+				j = 0;
+				break;
+			case ATTR_UNDERLINE:
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				xtext->underline = !xtext->underline;
+				pstr += j + 1;
+				j = 0;
+				break;
+			case ATTR_ITALICS:
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				xtext->italics = !xtext->italics;
+				pstr += j + 1;
+				j = 0;
+				break;
+			case ATTR_HIDDEN:
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				xtext->hidden = (!xtext->hidden) & (!xtext->ignore_hidden);
+				pstr += j + 1;
+				j = 0;
+				break;
+			case ATTR_RESET:
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				pstr += j + 1;
+				j = 0;
+				gtk_xtext_reset (xtext, mark, !xtext->in_hilight);
+				break;
+			case ATTR_COLOR:
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				xtext->parsing_color = TRUE;
+				pstr += j + 1;
+				j = 0;
+				break;
+			default:
+				tmp = charlen (str + i);
+				/* invalid utf8 safe guard */
+				if (tmp + i > len)
+					tmp = len - i;
+				j += tmp;	/* move to the next utf8 char */
+			}
+		}
+		i += charlen (str + i);	/* move to the next utf8 char */
+		/* invalid utf8 safe guard */
+		if (i > len)
+			i = len;
+
+		/* Separate the left part, the space and the right part
+		   into separate runs, and reset bidi state inbetween.
+		   Perform this only on the first line of the message.
+                */
+		if (offset == 0)
+		{
+			/* we've reached the end of the left part? */
+			if ((pstr-str)+j == ent->left_len)
+			{
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				pstr += j;
+				j = 0;
+			}
+			else if ((pstr-str)+j == ent->left_len+1)
+			{
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				pstr += j;
+				j = 0;
+			}
+		}
+
+		/* have we been told to stop rendering at this point? */
+		if (xtext->jump_out_offset > 0 && xtext->jump_out_offset <= (i + offset))
+		{
+			gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+			ret = 0;	/* skip the rest of the lines, we're done. */
+			j = 0;
+			break;
+		}
+
+		if (xtext->jump_in_offset > 0 && xtext->jump_in_offset == (i + offset))
+		{
+			x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+			pstr += j;
+			j = 0;
+			xtext->dont_render2 = FALSE;
+		}
+
+#ifdef MOTION_MONITOR
+		if (xtext->hilight_ent == ent && xtext->hilight_end == (i + offset))
+		{
+			x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+			pstr += j;
+			j = 0;
+#ifdef COLOR_HILIGHT
+			if (mark)
+			{
+				xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
+				xtext->backcolor = TRUE;
+			} else
+			{
+				xtext_set_bg (xtext, gc, xtext->col_back);
+				if (xtext->col_back != XTEXT_BG)
+					xtext->backcolor = TRUE;
+				else
+					xtext->backcolor = FALSE;
+			}
+#else
+			xtext->underline = FALSE;
+#endif
+			xtext->in_hilight = FALSE;
+			if (xtext->render_hilights_only)
+			{
+				/* stop drawing this ent */
+				ret = 0;
+				break;
+			}
+		}
+#endif
+
+		if (!mark && ent->mark_start == (i + offset))
+		{
+			x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+			pstr += j;
+			j = 0;
+			xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
+			xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
+			xtext->backcolor = TRUE;
+			mark = TRUE;
+		}
+
+		if (mark && ent->mark_end == (i + offset))
+		{
+			x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+			pstr += j;
+			j = 0;
+			xtext_set_bg (xtext, gc, xtext->col_back);
+			xtext_set_fg (xtext, gc, xtext->col_fore);
+			if (xtext->col_back != XTEXT_BG)
+				xtext->backcolor = TRUE;
+			else
+				xtext->backcolor = FALSE;
+			mark = FALSE;
+		}
+
+	}
+
+	if (j)
+		x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+
+	if (mark)
+	{
+		xtext_set_bg (xtext, gc, xtext->col_back);
+		xtext_set_fg (xtext, gc, xtext->col_fore);
+		if (xtext->col_back != XTEXT_BG)
+			xtext->backcolor = TRUE;
+		else
+			xtext->backcolor = FALSE;
+	}
+
+	/* draw background to the right of the text */
+	if (!left_only && !xtext->dont_render)
+	{
+		/* draw separator now so it doesn't appear to flicker */
+		gtk_xtext_draw_sep (xtext, y - xtext->font->ascent);
+		if (!xtext->skip_border_fills && xtext->clip_x2 >= x)
+		{
+			int xx = MAX (x, xtext->clip_x);
+
+			xtext_draw_bg (xtext,
+								xx,	/* x */
+								y - xtext->font->ascent, /* y */
+				MIN (xtext->clip_x2 - xx, (win_width + MARGIN) - xx), /* width */
+								xtext->fontsize);		/* height */
+		}
+	}
+
+	xtext->dont_render2 = FALSE;
+
+	/* return how much we drew in the x direction */
+	if (x_size_ret)
+		*x_size_ret = x - indent;
+
+	return ret;
+}
+
+#ifdef USE_XLIB
+
+/* get the desktop/root window */
+
+static Window desktop_window = None;
+
+static Window
+get_desktop_window (Display *xdisplay, Window the_window)
+{
+	Atom prop, type;
+	int format;
+	unsigned long length, after;
+	unsigned char *data;
+	unsigned int nchildren;
+	Window w, root, *children, parent;
+
+	prop = XInternAtom (xdisplay, "_XROOTPMAP_ID", True);
+	if (prop == None)
+	{
+		prop = XInternAtom (xdisplay, "_XROOTCOLOR_PIXEL", True);
+		if (prop == None)
+			return None;
+	}
+
+	for (w = the_window; w; w = parent)
+	{
+		if ((XQueryTree (xdisplay, w, &root, &parent, &children,
+				&nchildren)) == False)
+			return None;
+
+		if (nchildren)
+			XFree (children);
+
+		XGetWindowProperty (xdisplay, w, prop, 0L, 1L, False,
+								  AnyPropertyType, &type, &format, &length, &after,
+								  &data);
+		if (data)
+			XFree (data);
+
+		if (type != None)
+			return (desktop_window = w);
+	}
+
+	return (desktop_window = None);
+}
+
+/* find the root window (backdrop) Pixmap */
+
+static Pixmap
+get_pixmap_prop (Display *xdisplay, Window the_window)
+{
+	Atom type;
+	int format;
+	unsigned long length, after;
+	unsigned char *data;
+	Pixmap pix = None;
+	static Atom prop = None;
+
+	if (desktop_window == None)
+		desktop_window = get_desktop_window (xdisplay, the_window);
+	if (desktop_window == None)
+		desktop_window = DefaultRootWindow (xdisplay);
+
+	if (prop == None)
+		prop = XInternAtom (xdisplay, "_XROOTPMAP_ID", True);
+	if (prop == None)
+		return None;
+
+	XGetWindowProperty (xdisplay, desktop_window, prop, 0L, 1L, False,
+							  AnyPropertyType, &type, &format, &length, &after,
+							  &data);
+	if (data)
+	{
+		if (type == XA_PIXMAP)
+			pix = *((Pixmap *) data);
+
+		XFree (data);
+	}
+
+	return pix;
+}
+
+/* slow generic routine, for the depths/bpp we don't know about */
+
+static void
+shade_ximage_generic (GdkVisual *visual, XImage *ximg, int bpl, int w, int h, int rm, int gm, int bm, int bg)
+{
+	int x, y;
+	int bgr = (256 - rm) * (bg & visual->red_mask);
+	int bgg = (256 - gm) * (bg & visual->green_mask);
+	int bgb = (256 - bm) * (bg & visual->blue_mask);
+
+	for (x = 0; x < w; x++)
+	{
+		for (y = 0; y < h; y++)
+		{
+			unsigned long pixel = XGetPixel (ximg, x, y);
+			int r, g, b;
+
+			r = rm * (pixel & visual->red_mask) + bgr;
+			g = gm * (pixel & visual->green_mask) + bgg;
+			b = bm * (pixel & visual->blue_mask) + bgb;
+
+			XPutPixel (ximg, x, y,
+							((r >> 8) & visual->red_mask) |
+							((g >> 8) & visual->green_mask) |
+							((b >> 8) & visual->blue_mask));
+		}
+	}
+}
+
+#endif
+
+/* Fast shading routine. Based on code by Willem Monsuwe <willem@stack.nl> */
+
+#define SHADE_IMAGE(bytes, type, rmask, gmask, bmask) \
+	unsigned char *ptr; \
+	int x, y; \
+	int bgr = (256 - rm) * (bg & rmask); \
+	int bgg = (256 - gm) * (bg & gmask); \
+	int bgb = (256 - bm) * (bg & bmask); \
+	ptr = (unsigned char *) data + (w * bytes); \
+	for (y = h; --y >= 0;) \
+	{ \
+		for (x = -w; x < 0; x++) \
+		{ \
+			int r, g, b; \
+			b = ((type *) ptr)[x]; \
+			r = rm * (b & rmask) + bgr; \
+			g = gm * (b & gmask) + bgg; \
+			b = bm * (b & bmask) + bgb; \
+			((type *) ptr)[x] = ((r >> 8) & rmask) \
+										| ((g >> 8) & gmask) \
+										| ((b >> 8) & bmask); \
+		} \
+		ptr += bpl; \
+    }
+
+/* RGB 15 */
+static void
+shade_ximage_15 (void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg)
+{
+	SHADE_IMAGE (2, guint16, 0x7c00, 0x3e0, 0x1f);
+}
+
+/* RGB 16 */
+static void
+shade_ximage_16 (void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg)
+{
+	SHADE_IMAGE (2, guint16, 0xf800, 0x7e0, 0x1f);
+}
+
+/* RGB 24 */
+static void
+shade_ximage_24 (void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg)
+{
+	/* 24 has to be a special case, there's no guint24, or 24bit MOV :) */
+	unsigned char *ptr;
+	int x, y;
+	int bgr = (256 - rm) * ((bg & 0xff0000) >> 16);
+	int bgg = (256 - gm) * ((bg & 0xff00) >> 8);
+	int bgb = (256 - bm) * (bg & 0xff);
+
+	ptr = (unsigned char *) data + (w * 3);
+	for (y = h; --y >= 0;)
+	{
+		for (x = -(w * 3); x < 0; x += 3)
+		{
+			int r, g, b;
+
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+			r = (ptr[x + 0] * rm + bgr) >> 8;
+			g = (ptr[x + 1] * gm + bgg) >> 8;
+			b = (ptr[x + 2] * bm + bgb) >> 8;
+			ptr[x + 0] = r;
+			ptr[x + 1] = g;
+			ptr[x + 2] = b;
+#else
+			r = (ptr[x + 2] * rm + bgr) >> 8;
+			g = (ptr[x + 1] * gm + bgg) >> 8;
+			b = (ptr[x + 0] * bm + bgb) >> 8;
+			ptr[x + 2] = r;
+			ptr[x + 1] = g;
+			ptr[x + 0] = b;
+#endif
+		}
+		ptr += bpl;
+	}
+}
+
+/* RGB 32 */
+static void
+shade_ximage_32 (void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg)
+{
+	SHADE_IMAGE (4, guint32, 0xff0000, 0xff00, 0xff);
+}
+
+static void
+shade_image (GdkVisual *visual, void *data, int bpl, int bpp, int w, int h,
+				 int rm, int gm, int bm, int bg, int depth)
+{
+	int bg_r, bg_g, bg_b;
+
+	bg_r = bg & visual->red_mask;
+	bg_g = bg & visual->green_mask;
+	bg_b = bg & visual->blue_mask;
+
+#ifdef USE_MMX
+	/* the MMX routines are about 50% faster at 16-bit. */
+	/* only use MMX routines with a pure black background */
+	if (bg_r == 0 && bg_g == 0 && bg_b == 0 && have_mmx ())	/* do a runtime check too! */
+	{
+		switch (depth)
+		{
+		case 15:
+			shade_ximage_15_mmx (data, bpl, w, h, rm, gm, bm);
+			break;
+		case 16:
+			shade_ximage_16_mmx (data, bpl, w, h, rm, gm, bm);
+			break;
+		case 24:
+			if (bpp != 32)
+				goto generic;
+		case 32:
+			shade_ximage_32_mmx (data, bpl, w, h, rm, gm, bm);
+			break;
+		default:
+			goto generic;
+		}
+	} else
+	{
+generic:
+#endif
+		switch (depth)
+		{
+		case 15:
+			shade_ximage_15 (data, bpl, w, h, rm, gm, bm, bg);
+			break;
+		case 16:
+			shade_ximage_16 (data, bpl, w, h, rm, gm, bm, bg);
+			break;
+		case 24:
+			if (bpp != 32)
+			{
+				shade_ximage_24 (data, bpl, w, h, rm, gm, bm, bg);
+				break;
+			}
+		case 32:
+			shade_ximage_32 (data, bpl, w, h, rm, gm, bm, bg);
+		}
+#ifdef USE_MMX
+	}
+#endif
+}
+
+#ifdef USE_XLIB
+
+#ifdef USE_SHM
+
+static XImage *
+get_shm_image (Display *xdisplay, XShmSegmentInfo *shminfo, int x, int y,
+					int w, int h, int depth, Pixmap pix)
+{
+	XImage *ximg;
+
+	shminfo->shmid = -1;
+	shminfo->shmaddr = (char*) -1;
+	ximg = XShmCreateImage (xdisplay, 0, depth, ZPixmap, 0, shminfo, w, h);
+	if (!ximg)
+		return NULL;
+
+	shminfo->shmid = shmget (IPC_PRIVATE, ximg->bytes_per_line * ximg->height,
+									 IPC_CREAT|0600);
+	if (shminfo->shmid == -1)
+	{
+		XDestroyImage (ximg);
+		return NULL;
+	}
+
+	shminfo->readOnly = False;
+	ximg->data = shminfo->shmaddr = (char *)shmat (shminfo->shmid, 0, 0);
+	if (shminfo->shmaddr == ((char *)-1))
+	{
+		shmctl (shminfo->shmid, IPC_RMID, 0);
+		XDestroyImage (ximg);
+		return NULL;
+	}
+
+	XShmAttach (xdisplay, shminfo);
+	XSync (xdisplay, False);
+	shmctl (shminfo->shmid, IPC_RMID, 0);
+	XShmGetImage (xdisplay, pix, ximg, x, y, AllPlanes);
+
+	return ximg;
+}
+
+static XImage *
+get_image (GtkXText *xtext, Display *xdisplay, XShmSegmentInfo *shminfo,
+			  int x, int y, int w, int h, int depth, Pixmap pix)
+{
+	XImage *ximg;
+
+	xtext->shm = 1;
+	ximg = get_shm_image (xdisplay, shminfo, x, y, w, h, depth, pix);
+	if (!ximg)
+	{
+		xtext->shm = 0;
+		ximg = XGetImage (xdisplay, pix, x, y, w, h, -1, ZPixmap);
+	}
+
+	return ximg;
+}
+
+#endif
+
+static GdkPixmap *
+shade_pixmap (GtkXText * xtext, Pixmap p, int x, int y, int w, int h)
+{
+	unsigned int dummy, width, height, depth;
+	GdkPixmap *shaded_pix;
+	Window root;
+	Pixmap tmp;
+	XImage *ximg;
+	XGCValues gcv;
+	GC tgc;
+	Display *xdisplay = GDK_WINDOW_XDISPLAY (xtext->draw_buf);
+
+#ifdef USE_SHM
+	int shm_pixmaps;
+	shm_pixmaps = have_shm_pixmaps(xdisplay);
+#endif
+
+	XGetGeometry (xdisplay, p, &root, &dummy, &dummy, &width, &height,
+					  &dummy, &depth);
+
+	if (width < x + w || height < y + h || x < 0 || y < 0)
+	{
+		gcv.subwindow_mode = IncludeInferiors;
+		gcv.graphics_exposures = False;
+		tgc = XCreateGC (xdisplay, p, GCGraphicsExposures|GCSubwindowMode,
+							  &gcv);
+		tmp = XCreatePixmap (xdisplay, p, w, h, depth);
+		XSetTile (xdisplay, tgc, p);
+		XSetFillStyle (xdisplay, tgc, FillTiled);
+		XSetTSOrigin (xdisplay, tgc, -x, -y);
+		XFillRectangle (xdisplay, tmp, tgc, 0, 0, w, h);
+		XFreeGC (xdisplay, tgc);
+
+#ifdef USE_SHM
+		if (shm_pixmaps)
+			ximg = get_image (xtext, xdisplay, &xtext->shminfo, 0, 0, w, h, depth, tmp);
+		else
+#endif
+			ximg = XGetImage (xdisplay, tmp, 0, 0, w, h, -1, ZPixmap);
+		XFreePixmap (xdisplay, tmp);
+	} else
+	{
+#ifdef USE_SHM
+		if (shm_pixmaps)
+			ximg = get_image (xtext, xdisplay, &xtext->shminfo, x, y, w, h, depth, p);
+		else
+#endif
+			ximg = XGetImage (xdisplay, p, x, y, w, h, -1, ZPixmap);
+	}
+
+	if (!ximg)
+		return NULL;
+
+	if (depth <= 14)
+	{
+		shade_ximage_generic (gdk_drawable_get_visual (GTK_WIDGET (xtext)->window),
+									 ximg, ximg->bytes_per_line, w, h, xtext->tint_red,
+									 xtext->tint_green, xtext->tint_blue,
+									 xtext->palette[XTEXT_BG]);
+	} else
+	{
+		shade_image (gdk_drawable_get_visual (GTK_WIDGET (xtext)->window),
+						 ximg->data, ximg->bytes_per_line, ximg->bits_per_pixel,
+						 w, h, xtext->tint_red, xtext->tint_green, xtext->tint_blue,
+						 xtext->palette[XTEXT_BG], depth);
+	}
+
+	if (xtext->recycle)
+		shaded_pix = xtext->pixmap;
+	else
+	{
+#ifdef USE_SHM
+		if (xtext->shm && shm_pixmaps)
+		{
+#if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0)
+			shaded_pix = gdk_pixmap_foreign_new (
+				XShmCreatePixmap (xdisplay, p, ximg->data, &xtext->shminfo, w, h, depth));
+#else
+			shaded_pix = gdk_pixmap_foreign_new_for_display (
+				gdk_drawable_get_display (xtext->draw_buf),
+				XShmCreatePixmap (xdisplay, p, ximg->data, &xtext->shminfo, w, h, depth));
+#endif
+		} else
+#endif
+		{
+			shaded_pix = gdk_pixmap_new (GTK_WIDGET (xtext)->window, w, h, depth);
+		}
+	}
+
+#ifdef USE_SHM
+	if (!xtext->shm || !shm_pixmaps)
+#endif
+		XPutImage (xdisplay, GDK_WINDOW_XWINDOW (shaded_pix),
+					  GDK_GC_XGC (xtext->fgc), ximg, 0, 0, 0, 0, w, h);
+	XDestroyImage (ximg);
+
+	return shaded_pix;
+}
+
+#endif /* !USE_XLIB */
+
+/* free transparency xtext->pixmap */
+#if defined(USE_XLIB) || defined(WIN32)
+
+static void
+gtk_xtext_free_trans (GtkXText * xtext)
+{
+	if (xtext->pixmap)
+	{
+#ifdef USE_SHM
+		if (xtext->shm && have_shm_pixmaps(GDK_WINDOW_XDISPLAY (xtext->draw_buf)))
+		{
+			XFreePixmap (GDK_WINDOW_XDISPLAY (xtext->pixmap),
+							 GDK_WINDOW_XWINDOW (xtext->pixmap));
+			XShmDetach (GDK_WINDOW_XDISPLAY (xtext->draw_buf), &xtext->shminfo);
+			shmdt (xtext->shminfo.shmaddr);
+		}
+#endif
+		g_object_unref (xtext->pixmap);
+		xtext->pixmap = NULL;
+		xtext->shm = 0;
+	}
+}
+
+#endif
+
+#ifdef WIN32
+
+static GdkPixmap *
+win32_tint (GtkXText *xtext, GdkImage *img, int width, int height)
+{
+	guchar *pixelp;
+	int x, y;
+	GdkPixmap *pix;
+	GdkVisual *visual = gdk_drawable_get_visual (GTK_WIDGET (xtext)->window);
+	guint32 pixel;
+	int r, g, b;
+
+	if (img->depth <= 14)
+	{
+		/* slow generic routine */
+		for (y = 0; y < height; y++)
+		{
+			for (x = 0; x < width; x++)
+			{
+				if (img->depth == 1)
+				{
+					pixel = (((guchar *) img->mem)[y * img->bpl + (x >> 3)] & (1 << (7 - (x & 0x7)))) != 0;
+					goto here;
+				}
+
+				if (img->depth == 4)
+				{
+					pixelp = (guchar *) img->mem + y * img->bpl + (x >> 1);
+					if (x&1)
+					{
+						pixel = (*pixelp) & 0x0F;
+						goto here;
+					}
+
+					pixel = (*pixelp) >> 4;
+					goto here;
+				}
+
+				pixelp = (guchar *) img->mem + y * img->bpl + x * img->bpp;
+
+				switch (img->bpp)
+				{
+				case 1:
+					pixel = *pixelp; break;
+
+				/* Windows is always LSB, no need to check img->byte_order. */
+				case 2:
+					pixel = pixelp[0] | (pixelp[1] << 8); break;
+
+				case 3:
+					pixel = pixelp[0] | (pixelp[1] << 8) | (pixelp[2] << 16); break;
+
+				case 4:
+					pixel = pixelp[0] | (pixelp[1] << 8) | (pixelp[2] << 16); break;
+				}
+
+here:
+				r = (pixel & visual->red_mask) >> visual->red_shift;
+				g = (pixel & visual->green_mask) >> visual->green_shift;
+				b = (pixel & visual->blue_mask) >> visual->blue_shift;
+
+				/* actual tinting is only these 3 lines */
+				pixel = ((r * xtext->tint_red) >> 8) << visual->red_shift |
+							((g * xtext->tint_green) >> 8) << visual->green_shift |
+							((b * xtext->tint_blue) >> 8) << visual->blue_shift;
+
+				if (img->depth == 1)
+					if (pixel & 1)
+						((guchar *) img->mem)[y * img->bpl + (x >> 3)] |= (1 << (7 - (x & 0x7)));
+					else
+						((guchar *) img->mem)[y * img->bpl + (x >> 3)] &= ~(1 << (7 - (x & 0x7)));
+				else if (img->depth == 4)
+				{
+					pixelp = (guchar *) img->mem + y * img->bpl + (x >> 1);
+
+					if (x&1)
+					{
+						*pixelp &= 0xF0;
+						*pixelp |= (pixel & 0x0F);
+					} else
+					{
+						*pixelp &= 0x0F;
+						*pixelp |= (pixel << 4);
+					}
+				} else
+				{
+					pixelp = (guchar *) img->mem + y * img->bpl + x * img->bpp;
+
+					/* Windows is always LSB, no need to check img->byte_order. */
+					switch (img->bpp)
+					{
+					case 4:
+						pixelp[3] = 0;
+					case 3:
+						pixelp[2] = ((pixel >> 16) & 0xFF);
+					case 2:
+						pixelp[1] = ((pixel >> 8) & 0xFF);
+					case 1:
+						pixelp[0] = (pixel & 0xFF);
+					}
+				}
+			}
+		}
+	} else
+	{
+		shade_image (visual, img->mem, img->bpl, img->bpp, width, height,
+						 xtext->tint_red, xtext->tint_green, xtext->tint_blue,
+						 xtext->palette[XTEXT_BG], visual->depth);
+	}
+
+	/* no need to dump it to a Pixmap, it's one and the same on win32 */
+	pix = (GdkPixmap *)img;
+
+	return pix;
+}
+
+#endif /* !WIN32 */
+
+/* grab pixmap from root window and set xtext->pixmap */
+#if defined(USE_XLIB) || defined(WIN32)
+
+static void
+gtk_xtext_load_trans (GtkXText * xtext)
+{
+#ifdef WIN32
+	GdkImage *img;
+	int width, height;
+	HDC hdc;
+	HWND hwnd;
+
+	/* if not shaded, we paint directly with PaintDesktop() */
+	if (!xtext->shaded)
+		return;
+
+	hwnd = GDK_WINDOW_HWND (GTK_WIDGET (xtext)->window);
+	hdc = GetDC (hwnd);
+	PaintDesktop (hdc);
+	ReleaseDC (hwnd, hdc);
+
+	gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+	img = gdk_image_get (GTK_WIDGET (xtext)->window, 0, 0, width+128, height);
+	xtext->pixmap = win32_tint (xtext, img, img->width, img->height);
+
+#else
+
+	Pixmap rootpix;
+	GtkWidget *widget = GTK_WIDGET (xtext);
+	int x, y;
+
+	rootpix = get_pixmap_prop (GDK_WINDOW_XDISPLAY (widget->window), GDK_WINDOW_XWINDOW (widget->window));
+	if (rootpix == None)
+	{
+		if (xtext->error_function)
+			xtext->error_function (0);
+		xtext->transparent = FALSE;
+		return;
+	}
+
+	gdk_window_get_origin (widget->window, &x, &y);
+
+	if (xtext->shaded)
+	{
+		int width, height;
+		gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+		xtext->pixmap = shade_pixmap (xtext, rootpix, x, y, width+105, height);
+		if (xtext->pixmap == NULL)
+		{
+			xtext->shaded = 0;
+			goto noshade;
+		}
+		gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
+		gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
+		xtext->ts_x = xtext->ts_y = 0;
+	} else
+	{
+noshade:
+#if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0)
+		xtext->pixmap = gdk_pixmap_foreign_new (rootpix);
+#else
+		xtext->pixmap = gdk_pixmap_foreign_new_for_display (gdk_drawable_get_display (GTK_WIDGET (xtext)->window), rootpix);
+#endif
+		gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
+		gdk_gc_set_ts_origin (xtext->bgc, -x, -y);
+		xtext->ts_x = -x;
+		xtext->ts_y = -y;
+	}
+	gdk_gc_set_fill (xtext->bgc, GDK_TILED);
+#endif /* !WIN32 */
+}
+
+#endif /* ! XLIB || WIN32 */
+
+/* walk through str until this line doesn't fit anymore */
+
+static int
+find_next_wrap (GtkXText * xtext, textentry * ent, unsigned char *str,
+					 int win_width, int indent)
+{
+	unsigned char *last_space = str;
+	unsigned char *orig_str = str;
+	int str_width = indent;
+	int rcol = 0, bgcol = 0;
+	int hidden = FALSE;
+	int mbl;
+	int char_width;
+	int ret;
+	int limit_offset = 0;
+
+	/* single liners */
+	if (win_width >= ent->str_width + ent->indent)
+		return ent->str_len;
+
+	/* it does happen! */
+	if (win_width < 1)
+	{
+		ret = ent->str_len - (str - ent->str);
+		goto done;
+	}
+
+	while (1)
+	{
+		if (rcol > 0 && (isdigit (*str) || (*str == ',' && isdigit (str[1]) && !bgcol)))
+		{
+			if (str[1] != ',') rcol--;
+			if (*str == ',')
+			{
+				rcol = 2;
+				bgcol = 1;
+			}
+			limit_offset++;
+			str++;
+		} else
+		{
+			rcol = bgcol = 0;
+			switch (*str)
+			{
+			case ATTR_COLOR:
+				rcol = 2;
+			case ATTR_BEEP:
+			case ATTR_RESET:
+			case ATTR_REVERSE:
+			case ATTR_BOLD:
+			case ATTR_UNDERLINE:
+			case ATTR_ITALICS:
+				limit_offset++;
+				str++;
+				break;
+			case ATTR_HIDDEN:
+				if (xtext->ignore_hidden)
+					goto def;
+				hidden = !hidden;
+				limit_offset++;
+				str++;
+				break;
+			default:
+			def:
+				char_width = backend_get_char_width (xtext, str, &mbl);
+				if (!hidden) str_width += char_width;
+				if (str_width > win_width)
+				{
+					if (xtext->wordwrap)
+					{
+						if (str - last_space > WORDWRAP_LIMIT + limit_offset)
+							ret = str - orig_str; /* fall back to character wrap */
+						else
+						{
+							if (*last_space == ' ')
+								last_space++;
+							ret = last_space - orig_str;
+							if (ret == 0) /* fall back to character wrap */
+								ret = str - orig_str;
+						}
+						goto done;
+					}
+					ret = str - orig_str;
+					goto done;
+				}
+
+				/* keep a record of the last space, for wordwrapping */
+				if (is_del (*str))
+				{
+					last_space = str;
+					limit_offset = 0;
+				}
+
+				/* progress to the next char */
+				str += mbl;
+
+			}
+		}
+
+		if (str >= ent->str + ent->str_len)
+		{
+			ret = str - orig_str;
+			goto done;
+		}
+	}
+
+done:
+
+	/* must make progress */
+	if (ret < 1)
+		ret = 1;
+
+	return ret;
+}
+
+/* find the offset, in bytes, that wrap number 'line' starts at */
+
+static int
+gtk_xtext_find_subline (GtkXText *xtext, textentry *ent, int line)
+{
+	int win_width;
+	unsigned char *str;
+	int indent, str_pos, line_pos, len;
+
+	if (ent->lines_taken < 2 || line < 1)
+		return 0;
+
+	/* we record the first 4 lines' wraps, so take a shortcut */
+	if (line <= RECORD_WRAPS)
+		return ent->wrap_offset[line - 1];
+
+	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &win_width, 0);
+	win_width -= MARGIN;
+
+/*	indent = ent->indent;
+	str = ent->str;
+	line_pos = str_pos = 0;*/
+
+	/* start from the last recorded wrap, and move forward */
+	indent = xtext->buffer->indent;
+	str_pos = ent->wrap_offset[RECORD_WRAPS-1];
+	str = str_pos + ent->str;
+	line_pos = RECORD_WRAPS;
+
+	do
+	{
+		len = find_next_wrap (xtext, ent, str, win_width, indent);
+		indent = xtext->buffer->indent;
+		str += len;
+		str_pos += len;
+		line_pos++;
+		if (line_pos >= line)
+			return str_pos;
+	}
+	while (str < ent->str + ent->str_len);
+
+	return 0;
+}
+
+/* horrible hack for drawing time stamps */
+
+static void
+gtk_xtext_render_stamp (GtkXText * xtext, textentry * ent,
+								char *text, int len, int line, int win_width)
+{
+	textentry tmp_ent;
+	int jo, ji, hs;
+	int xsize, y;
+
+	/* trashing ent here, so make a backup first */
+	memcpy (&tmp_ent, ent, sizeof (tmp_ent));
+	ent->mb = TRUE;	/* make non-english days of the week work */
+	jo = xtext->jump_out_offset;	/* back these up */
+	ji = xtext->jump_in_offset;
+	hs = xtext->hilight_start;
+	xtext->jump_out_offset = 0;
+	xtext->jump_in_offset = 0;
+	xtext->hilight_start = 0xffff;	/* temp disable */
+
+	if (xtext->mark_stamp)
+	{
+		/* if this line is marked, mark this stamp too */
+		if (ent->mark_start == 0)	
+		{
+			ent->mark_start = 0;
+			ent->mark_end = len;
+		}
+		else
+		{
+			ent->mark_start = -1;
+			ent->mark_end = -1;
+		}
+		ent->str = text;
+	}
+
+	y = (xtext->fontsize * line) + xtext->font->ascent - xtext->pixel_offset;
+	gtk_xtext_render_str (xtext, y, ent, text, len,
+								 win_width, 2, line, TRUE, &xsize);
+
+	/* restore everything back to how it was */
+	memcpy (ent, &tmp_ent, sizeof (tmp_ent));
+	xtext->jump_out_offset = jo;
+	xtext->jump_in_offset = ji;
+	xtext->hilight_start = hs;
+
+	/* with a non-fixed-width font, sometimes we don't draw enough
+		background i.e. when this stamp is shorter than xtext->stamp_width */
+	xsize += MARGIN;
+	if (xsize < xtext->stamp_width)
+	{
+		y -= xtext->font->ascent;
+		xtext_draw_bg (xtext,
+							xsize,	/* x */
+							y,			/* y */
+							xtext->stamp_width - xsize,	/* width */
+							xtext->fontsize					/* height */);
+	}
+}
+
+/* render a single line, which may wrap to more lines */
+
+static int
+gtk_xtext_render_line (GtkXText * xtext, textentry * ent, int line,
+							  int lines_max, int subline, int win_width)
+{
+	unsigned char *str;
+	int indent, taken, entline, len, y, start_subline;
+
+	entline = taken = 0;
+	str = ent->str;
+	indent = ent->indent;
+	start_subline = subline;
+
+#ifdef XCHAT
+	/* draw the timestamp */
+	if (xtext->auto_indent && xtext->buffer->time_stamp &&
+		 (!xtext->skip_stamp || xtext->mark_stamp || xtext->force_stamp))
+	{
+		char *time_str;
+		int len;
+
+		len = xtext_get_stamp_str (ent->stamp, &time_str);
+		gtk_xtext_render_stamp (xtext, ent, time_str, len, line, win_width);
+		g_free (time_str);
+	}
+#endif
+
+	/* draw each line one by one */
+	do
+	{
+		/* if it's one of the first 4 wraps, we don't need to calculate it, it's
+			recorded in ->wrap_offset. This saves us a loop. */
+		if (entline < RECORD_WRAPS)
+		{
+			if (ent->lines_taken < 2)
+				len = ent->str_len;
+			else
+			{
+				if (entline > 0)
+					len = ent->wrap_offset[entline] - ent->wrap_offset[entline-1];
+				else
+					len = ent->wrap_offset[0];
+			}
+		} else
+			len = find_next_wrap (xtext, ent, str, win_width, indent);
+
+		entline++;
+
+		y = (xtext->fontsize * line) + xtext->font->ascent - xtext->pixel_offset;
+		if (!subline)
+		{
+			if (!gtk_xtext_render_str (xtext, y, ent, str, len, win_width,
+												indent, line, FALSE, NULL))
+			{
+				/* small optimization */
+				gtk_xtext_draw_marker (xtext, ent, y - xtext->fontsize * (taken + start_subline + 1));
+				return ent->lines_taken - subline;
+			}
+		} else
+		{
+			xtext->dont_render = TRUE;
+			gtk_xtext_render_str (xtext, y, ent, str, len, win_width,
+										 indent, line, FALSE, NULL);
+			xtext->dont_render = FALSE;
+			subline--;
+			line--;
+			taken--;
+		}
+
+		indent = xtext->buffer->indent;
+		line++;
+		taken++;
+		str += len;
+
+		if (line >= lines_max)
+			break;
+
+	}
+	while (str < ent->str + ent->str_len);
+
+	gtk_xtext_draw_marker (xtext, ent, y - xtext->fontsize * (taken + start_subline));
+
+	return taken;
+}
+
+void
+gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[])
+{
+	int i;
+	GdkColor col;
+
+	for (i = (XTEXT_COLS-1); i >= 0; i--)
+	{
+#ifdef USE_XFT
+		xtext->color[i].color.red = palette[i].red;
+		xtext->color[i].color.green = palette[i].green;
+		xtext->color[i].color.blue = palette[i].blue;
+		xtext->color[i].color.alpha = 0xffff;
+		xtext->color[i].pixel = palette[i].pixel;
+#endif
+		xtext->palette[i] = palette[i].pixel;
+	}
+
+	if (GTK_WIDGET_REALIZED (xtext))
+	{
+		xtext_set_fg (xtext, xtext->fgc, XTEXT_FG);
+		xtext_set_bg (xtext, xtext->fgc, XTEXT_BG);
+		xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
+
+		col.pixel = xtext->palette[XTEXT_MARKER];
+		gdk_gc_set_foreground (xtext->marker_gc, &col);
+	}
+	xtext->col_fore = XTEXT_FG;
+	xtext->col_back = XTEXT_BG;
+}
+
+static void
+gtk_xtext_fix_indent (xtext_buffer *buf)
+{
+	int j;
+
+	/* make indent a multiple of the space width */
+	if (buf->indent && buf->xtext->space_width)
+	{
+		j = 0;
+		while (j < buf->indent)
+		{
+			j += buf->xtext->space_width;
+		}
+		buf->indent = j;
+	}
+
+	dontscroll (buf);	/* force scrolling off */
+}
+
+static void
+gtk_xtext_recalc_widths (xtext_buffer *buf, int do_str_width)
+{
+	textentry *ent;
+
+	/* since we have a new font, we have to recalc the text widths */
+	ent = buf->text_first;
+	while (ent)
+	{
+		if (do_str_width)
+		{
+			ent->str_width = gtk_xtext_text_width (buf->xtext, ent->str,
+														ent->str_len, NULL);
+		}
+		if (ent->left_len != -1)
+		{
+			ent->indent =
+				(buf->indent -
+				 gtk_xtext_text_width (buf->xtext, ent->str,
+										ent->left_len, NULL)) - buf->xtext->space_width;
+			if (ent->indent < MARGIN)
+				ent->indent = MARGIN;
+		}
+		ent = ent->next;
+	}
+
+	gtk_xtext_calc_lines (buf, FALSE);
+}
+
+int
+gtk_xtext_set_font (GtkXText *xtext, char *name)
+{
+	int i;
+	unsigned char c;
+
+	if (xtext->font)
+		backend_font_close (xtext);
+
+	/* realize now, so that font_open has a XDisplay */
+	gtk_widget_realize (GTK_WIDGET (xtext));
+
+	backend_font_open (xtext, name);
+	if (xtext->font == NULL)
+		return FALSE;
+
+	/* measure the width of every char;  only the ASCII ones for XFT */
+	for (i = 0; i < sizeof(xtext->fontwidth)/sizeof(xtext->fontwidth[0]); i++)
+	{
+		c = i;
+		xtext->fontwidth[i] = backend_get_text_width (xtext, &c, 1, TRUE);
+	}
+	xtext->space_width = xtext->fontwidth[' '];
+	xtext->fontsize = xtext->font->ascent + xtext->font->descent;
+
+#ifdef XCHAT
+	{
+		char *time_str;
+		int stamp_size = xtext_get_stamp_str (time(0), &time_str);
+		xtext->stamp_width =
+			gtk_xtext_text_width (xtext, time_str, stamp_size, NULL) + MARGIN;
+		g_free (time_str);
+	}
+#endif
+
+	gtk_xtext_fix_indent (xtext->buffer);
+
+	if (GTK_WIDGET_REALIZED (xtext))
+		gtk_xtext_recalc_widths (xtext->buffer, TRUE);
+
+	return TRUE;
+}
+
+void
+gtk_xtext_set_background (GtkXText * xtext, GdkPixmap * pixmap, gboolean trans)
+{
+	GdkGCValues val;
+	gboolean shaded = FALSE;
+
+	if (trans && (xtext->tint_red != 255 || xtext->tint_green != 255 || xtext->tint_blue != 255))
+		shaded = TRUE;
+
+#if !defined(USE_XLIB) && !defined(WIN32)
+	shaded = FALSE;
+	trans = FALSE;
+#endif
+
+	if (xtext->pixmap)
+	{
+#if defined(USE_XLIB) || defined(WIN32)
+		if (xtext->transparent)
+			gtk_xtext_free_trans (xtext);
+		else
+#endif
+			g_object_unref (xtext->pixmap);
+		xtext->pixmap = NULL;
+	}
+
+	xtext->transparent = trans;
+
+#if defined(USE_XLIB) || defined(WIN32)
+	if (trans)
+	{
+		xtext->shaded = shaded;
+		if (GTK_WIDGET_REALIZED (xtext))
+			gtk_xtext_load_trans (xtext);
+		return;
+	}
+#endif
+
+	dontscroll (xtext->buffer);
+	xtext->pixmap = pixmap;
+
+	if (pixmap != 0)
+	{
+		g_object_ref (pixmap);
+		if (GTK_WIDGET_REALIZED (xtext))
+		{
+			gdk_gc_set_tile (xtext->bgc, pixmap);
+			gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
+			xtext->ts_x = xtext->ts_y = 0;
+			gdk_gc_set_fill (xtext->bgc, GDK_TILED);
+		}
+	} else if (GTK_WIDGET_REALIZED (xtext))
+	{
+		g_object_unref (xtext->bgc);
+		val.subwindow_mode = GDK_INCLUDE_INFERIORS;
+		val.graphics_exposures = 0;
+		xtext->bgc = gdk_gc_new_with_values (GTK_WIDGET (xtext)->window,
+								&val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+		xtext_set_fg (xtext, xtext->bgc, XTEXT_BG);
+	}
+}
+
+void
+gtk_xtext_save (GtkXText * xtext, int fh)
+{
+	textentry *ent;
+	int newlen;
+	char *buf;
+
+	ent = xtext->buffer->text_first;
+	while (ent)
+	{
+		buf = gtk_xtext_strip_color (ent->str, ent->str_len, NULL,
+											  &newlen, NULL, FALSE);
+		write (fh, buf, newlen);
+		write (fh, "\n", 1);
+		free (buf);
+		ent = ent->next;
+	}
+}
+
+/* count how many lines 'ent' will take (with wraps) */
+
+static int
+gtk_xtext_lines_taken (xtext_buffer *buf, textentry * ent)
+{
+	unsigned char *str;
+	int indent, taken, len;
+	int win_width;
+
+	win_width = buf->window_width - MARGIN;
+
+	if (ent->str_width + ent->indent < win_width)
+		return 1;
+
+	indent = ent->indent;
+	str = ent->str;
+	taken = 0;
+
+	do
+	{
+		len = find_next_wrap (buf->xtext, ent, str, win_width, indent);
+		if (taken < RECORD_WRAPS)
+			ent->wrap_offset[taken] = (str + len) - ent->str;
+		indent = buf->indent;
+		taken++;
+		str += len;
+	}
+	while (str < ent->str + ent->str_len);
+
+	return taken;
+}
+
+/* Calculate number of actual lines (with wraps), to set adj->lower. *
+ * This should only be called when the window resizes.               */
+
+static void
+gtk_xtext_calc_lines (xtext_buffer *buf, int fire_signal)
+{
+	textentry *ent;
+	int width;
+	int height;
+	int lines;
+
+	gdk_drawable_get_size (GTK_WIDGET (buf->xtext)->window, &width, &height);
+	width -= MARGIN;
+
+	if (width < 30 || height < buf->xtext->fontsize || width < buf->indent + 30)
+		return;
+
+	lines = 0;
+	ent = buf->text_first;
+	while (ent)
+	{
+		ent->lines_taken = gtk_xtext_lines_taken (buf, ent);
+		lines += ent->lines_taken;
+		ent = ent->next;
+	}
+
+	buf->pagetop_ent = NULL;
+	buf->num_lines = lines;
+	gtk_xtext_adjustment_set (buf, fire_signal);
+}
+
+/* find the n-th line in the linked list, this includes wrap calculations */
+
+static textentry *
+gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
+{
+	int lines = 0;
+	textentry *ent;
+
+	ent = xtext->buffer->text_first;
+
+	/* -- optimization -- try to make a short-cut using the pagetop ent */
+	if (xtext->buffer->pagetop_ent)
+	{
+		if (line == xtext->buffer->pagetop_line)
+		{
+			*subline = xtext->buffer->pagetop_subline;
+			return xtext->buffer->pagetop_ent;
+		}
+		if (line > xtext->buffer->pagetop_line)
+		{
+			/* lets start from the pagetop instead of the absolute beginning */
+			ent = xtext->buffer->pagetop_ent;
+			lines = xtext->buffer->pagetop_line - xtext->buffer->pagetop_subline;
+		}
+		else if (line > xtext->buffer->pagetop_line - line)
+		{
+			/* move backwards from pagetop */
+			ent = xtext->buffer->pagetop_ent;
+			lines = xtext->buffer->pagetop_line - xtext->buffer->pagetop_subline;
+			while (1)
+			{
+				if (lines <= line)
+				{
+					*subline = line - lines;
+					return ent;
+				}
+				ent = ent->prev;
+				if (!ent)
+					break;
+				lines -= ent->lines_taken;
+			}
+			return 0;
+		}
+	}
+	/* -- end of optimization -- */
+
+	while (ent)
+	{
+		lines += ent->lines_taken;
+		if (lines > line)
+		{
+			*subline = ent->lines_taken - (lines - line);
+			return ent;
+		}
+		ent = ent->next;
+	}
+	return 0;
+}
+
+/* render enta (or an inclusive range enta->entb) */
+
+static int
+gtk_xtext_render_ents (GtkXText * xtext, textentry * enta, textentry * entb)
+{
+	textentry *ent, *orig_ent, *tmp_ent;
+	int line;
+	int lines_max;
+	int width;
+	int height;
+	int subline;
+	int drawing = FALSE;
+
+	if (xtext->buffer->indent < MARGIN)
+		xtext->buffer->indent = MARGIN;	  /* 2 pixels is our left margin */
+
+	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+	width -= MARGIN;
+
+	if (width < 32 || height < xtext->fontsize || width < xtext->buffer->indent + 30)
+		return 0;
+
+	lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1;
+	line = 0;
+	orig_ent = xtext->buffer->pagetop_ent;
+	subline = xtext->buffer->pagetop_subline;
+
+	/* used before a complete page is in buffer */
+	if (orig_ent == NULL)
+		orig_ent = xtext->buffer->text_first;
+
+	/* check if enta is before the start of this page */
+	if (entb)
+	{
+		tmp_ent = orig_ent;
+		while (tmp_ent)
+		{
+			if (tmp_ent == enta)
+				break;
+			if (tmp_ent == entb)
+			{
+				drawing = TRUE;
+				break;
+			}
+			tmp_ent = tmp_ent->next;
+		}
+	}
+
+	ent = orig_ent;
+	while (ent)
+	{
+		if (entb && ent == enta)
+			drawing = TRUE;
+
+		if (drawing || ent == entb || ent == enta)
+		{
+			gtk_xtext_reset (xtext, FALSE, TRUE);
+			line += gtk_xtext_render_line (xtext, ent, line, lines_max,
+													 subline, width);
+			subline = 0;
+			xtext->jump_in_offset = 0;	/* jump_in_offset only for the 1st */
+		} else
+		{
+			if (ent == orig_ent)
+			{
+				line -= subline;
+				subline = 0;
+			}
+			line += ent->lines_taken;
+		}
+
+		if (ent == entb)
+			break;
+
+		if (line >= lines_max)
+			break;
+
+		ent = ent->next;
+	}
+
+	/* space below last line */
+	return (xtext->fontsize * line) - xtext->pixel_offset;
+}
+
+/* render a whole page/window, starting from 'startline' */
+
+static void
+gtk_xtext_render_page (GtkXText * xtext)
+{
+	textentry *ent;
+	int line;
+	int lines_max;
+	int width;
+	int height;
+	int subline;
+	int startline = xtext->adj->value;
+
+	if(!GTK_WIDGET_REALIZED(xtext))
+	  return;
+
+	if (xtext->buffer->indent < MARGIN)
+		xtext->buffer->indent = MARGIN;	  /* 2 pixels is our left margin */
+
+	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+
+	if (width < 34 || height < xtext->fontsize || width < xtext->buffer->indent + 32)
+		return;
+
+#ifdef SMOOTH_SCROLL
+	xtext->pixel_offset = (xtext->adj->value - startline) * xtext->fontsize;
+#else
+	xtext->pixel_offset = 0;
+#endif
+
+	subline = line = 0;
+	ent = xtext->buffer->text_first;
+
+	if (startline > 0)
+		ent = gtk_xtext_nth (xtext, startline, &subline);
+
+	xtext->buffer->pagetop_ent = ent;
+	xtext->buffer->pagetop_subline = subline;
+	xtext->buffer->pagetop_line = startline;
+
+#ifdef SCROLL_HACK
+{
+	int pos, overlap;
+	GdkRectangle area;
+
+	if (xtext->buffer->num_lines <= xtext->adj->page_size)
+		dontscroll (xtext->buffer);
+
+#ifdef SMOOTH_SCROLL
+	pos = xtext->adj->value * xtext->fontsize;
+#else
+	pos = startline * xtext->fontsize;
+#endif
+	overlap = xtext->buffer->last_pixel_pos - pos;
+	xtext->buffer->last_pixel_pos = pos;
+
+#ifdef USE_DB
+#ifdef WIN32
+	if (!xtext->transparent && !xtext->pixmap && abs (overlap) < height)
+#else
+	if (!xtext->pixmap && abs (overlap) < height)
+#endif
+#else
+	/* dont scroll PageUp/Down without a DB, it looks ugly */
+#ifdef WIN32
+	if (!xtext->transparent && !xtext->pixmap && abs (overlap) < height - (3*xtext->fontsize))
+#else
+	if (!xtext->pixmap && abs (overlap) < height - (3*xtext->fontsize))
+#endif
+#endif
+	{
+		/* so the obscured regions are exposed */
+		gdk_gc_set_exposures (xtext->fgc, TRUE);
+		if (overlap < 1)	/* DOWN */
+		{
+			int remainder;
+
+			gdk_draw_drawable (xtext->draw_buf, xtext->fgc, xtext->draw_buf,
+									 0, -overlap, 0, 0, width, height + overlap);
+			remainder = ((height - xtext->font->descent) % xtext->fontsize) +
+							xtext->font->descent;
+			area.y = (height + overlap) - remainder;
+			area.height = remainder - overlap;
+		} else
+		{
+			gdk_draw_drawable (xtext->draw_buf, xtext->fgc, xtext->draw_buf,
+									 0, 0, 0, overlap, width, height - overlap);
+			area.y = 0;
+			area.height = overlap;
+		}
+		gdk_gc_set_exposures (xtext->fgc, FALSE);
+
+		if (area.height > 0)
+		{
+			area.x = 0;
+			area.width = width;
+			gtk_xtext_paint (GTK_WIDGET (xtext), &area);
+		}
+		xtext->buffer->grid_dirty = TRUE;
+
+		return;
+	}
+}
+#endif
+
+	xtext->buffer->grid_dirty = FALSE;
+	width -= MARGIN;
+	lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1;
+
+	while (ent)
+	{
+		gtk_xtext_reset (xtext, FALSE, TRUE);
+		line += gtk_xtext_render_line (xtext, ent, line, lines_max,
+												 subline, width);
+		subline = 0;
+
+		if (line >= lines_max)
+			break;
+
+		ent = ent->next;
+	}
+
+	line = (xtext->fontsize * line) - xtext->pixel_offset;
+	/* fill any space below the last line with our background GC */
+	xtext_draw_bg (xtext, 0, line, width + MARGIN, height - line);
+
+	/* draw the separator line */
+	gtk_xtext_draw_sep (xtext, -1);
+}
+
+void
+gtk_xtext_refresh (GtkXText * xtext, int do_trans)
+{
+	if (GTK_WIDGET_REALIZED (GTK_WIDGET (xtext)))
+	{
+#if defined(USE_XLIB) || defined(WIN32)
+		if (xtext->transparent && do_trans)
+		{
+			gtk_xtext_free_trans (xtext);
+			gtk_xtext_load_trans (xtext);
+		}
+#endif
+		gtk_xtext_render_page (xtext);
+	}
+}
+
+static int
+gtk_xtext_kill_ent (xtext_buffer *buffer, textentry *ent)
+{
+	int visible;
+
+	/* Set visible to TRUE if this is the current buffer */
+	/* and this ent shows up on the screen now */
+	visible = buffer->xtext->buffer == buffer &&
+				 gtk_xtext_check_ent_visibility (buffer->xtext, ent, 0);
+
+	if (ent == buffer->pagetop_ent)
+		buffer->pagetop_ent = NULL;
+
+	if (ent == buffer->last_ent_start)
+	{
+		buffer->last_ent_start = ent->next;
+		buffer->last_offset_start = 0;
+	}
+
+	if (ent == buffer->last_ent_end)
+	{
+		buffer->last_ent_start = NULL;
+		buffer->last_ent_end = NULL;
+	}
+
+	if (buffer->marker_pos == ent) buffer->marker_pos = NULL;
+
+	free (ent);
+	return visible;
+}
+
+/* remove the topline from the list */
+
+static void
+gtk_xtext_remove_top (xtext_buffer *buffer)
+{
+	textentry *ent;
+
+	ent = buffer->text_first;
+	if (!ent)
+		return;
+	buffer->num_lines -= ent->lines_taken;
+	buffer->pagetop_line -= ent->lines_taken;
+	buffer->last_pixel_pos -= (ent->lines_taken * buffer->xtext->fontsize);
+	buffer->text_first = ent->next;
+	if (buffer->text_first)
+		buffer->text_first->prev = NULL;
+	else
+		buffer->text_last = NULL;
+
+	buffer->old_value -= ent->lines_taken;
+	if (buffer->xtext->buffer == buffer)	/* is it the current buffer? */
+	{
+		buffer->xtext->adj->value -= ent->lines_taken;
+		buffer->xtext->select_start_adj -= ent->lines_taken;
+	}
+
+	if (gtk_xtext_kill_ent (buffer, ent))
+	{
+		if (!buffer->xtext->add_io_tag)
+		{
+			/* remove scrolling events */
+			if (buffer->xtext->io_tag)
+			{
+				g_source_remove (buffer->xtext->io_tag);
+				buffer->xtext->io_tag = 0;
+			}
+			buffer->xtext->force_render = TRUE;
+			buffer->xtext->add_io_tag = g_timeout_add (REFRESH_TIMEOUT * 2,
+														(GSourceFunc)
+														gtk_xtext_render_page_timeout,
+														buffer->xtext);
+		}
+	}
+}
+
+static void
+gtk_xtext_remove_bottom (xtext_buffer *buffer)
+{
+	textentry *ent;
+
+	ent = buffer->text_last;
+	if (!ent)
+		return;
+	buffer->num_lines -= ent->lines_taken;
+	buffer->text_last = ent->prev;
+	if (buffer->text_last)
+		buffer->text_last->next = NULL;
+	else
+		buffer->text_first = NULL;
+
+	gtk_xtext_kill_ent (buffer, ent);
+}
+
+/* If lines=0 => clear all */
+
+void
+gtk_xtext_clear (xtext_buffer *buf, int lines)
+{
+	textentry *next;
+
+	if (lines != 0)
+	{
+		if (lines < 0)
+		{
+			/* delete lines from bottom */
+			lines *= -1;
+			while (lines)
+			{
+				gtk_xtext_remove_bottom (buf);
+				lines--;
+			}
+		}
+		else
+		{
+			/* delete lines from top */
+			while (lines)
+			{
+				gtk_xtext_remove_top (buf);
+				lines--;
+			}
+		}
+	}
+	else
+	{
+		/* delete all */
+		if (buf->xtext->auto_indent)
+			buf->indent = MARGIN;
+		buf->scrollbar_down = TRUE;
+		buf->last_ent_start = NULL;
+		buf->last_ent_end = NULL;
+		buf->marker_pos = NULL;
+		dontscroll (buf);
+
+		while (buf->text_first)
+		{
+			next = buf->text_first->next;
+			free (buf->text_first);
+			buf->text_first = next;
+		}
+		buf->text_last = NULL;
+	}
+
+	if (buf->xtext->buffer == buf)
+	{
+		gtk_xtext_calc_lines (buf, TRUE);
+		gtk_xtext_refresh (buf->xtext, 0);
+	} else
+	{
+		gtk_xtext_calc_lines (buf, FALSE);
+	}
+}
+
+static gboolean
+gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add)
+{
+	textentry *ent;
+	int lines_max;
+	int line = 0;
+	int width;
+	int height;
+
+	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+
+	lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + add;
+	ent = xtext->buffer->pagetop_ent;
+
+	while (ent && line < lines_max)
+	{
+		if (find_ent == ent)
+			return TRUE;
+		line += ent->lines_taken;
+		ent = ent->next;
+	}
+
+	return FALSE;
+}
+
+void
+gtk_xtext_check_marker_visibility (GtkXText * xtext)
+{
+	if (gtk_xtext_check_ent_visibility (xtext, xtext->buffer->marker_pos, 1))
+		xtext->buffer->marker_seen = TRUE;
+}
+
+textentry *
+gtk_xtext_search (GtkXText * xtext, const gchar *text, textentry *start, gboolean case_match, gboolean backward)
+{
+	textentry *ent, *fent;
+	int line;
+	gchar *str, *nee, *hay;	/* needle in haystack */
+
+	gtk_xtext_selection_clear_full (xtext->buffer);
+	xtext->buffer->last_ent_start = NULL;
+	xtext->buffer->last_ent_end = NULL;
+
+	/* set up text comparand for Case Match or Ignore */
+	if (case_match)
+		nee = g_strdup (text);
+	else
+		nee = g_utf8_casefold (text, strlen (text));
+
+	/* Validate that start gives a currently valid ent pointer */
+	ent = xtext->buffer->text_first;
+	while (ent)
+	{
+		if (ent == start)
+			break;
+		ent = ent->next;
+	}
+	if (!ent)
+		start = NULL;
+
+	/* Choose first ent to look at */
+	if (start)
+		ent = backward? start->prev: start->next;
+	else
+		ent = backward? xtext->buffer->text_last: xtext->buffer->text_first;
+
+	/* Search from there to one end or the other until found */
+	while (ent)
+	{
+		/* If Case Ignore, fold before & free after calling strstr */
+		if (case_match)
+			hay = g_strdup (ent->str);
+		else
+			hay = g_utf8_casefold (ent->str, strlen (ent->str));
+		/* Try to find the needle in this haystack */
+		str = g_strstr_len (hay, strlen (hay), nee);
+		g_free (hay);
+		if (str)
+			break;
+		ent = backward? ent->prev: ent->next;
+	}
+	fent = ent;
+
+	/* Save distance to start, end of found string */
+	if (ent)
+	{
+		ent->mark_start = str - hay;
+		ent->mark_end = ent->mark_start + strlen (nee);
+
+		/* is the match visible? Might need to scroll */
+		if (!gtk_xtext_check_ent_visibility (xtext, ent, 0))
+		{
+			ent = xtext->buffer->text_first;
+			line = 0;
+			while (ent)
+			{
+				line += ent->lines_taken;
+				ent = ent->next;
+				if (ent == fent)
+					break;
+			}
+			while (line > xtext->adj->upper - xtext->adj->page_size)
+				line--;
+			if (backward)
+				line -= xtext->adj->page_size - ent->lines_taken;
+			xtext->adj->value = line;
+			xtext->buffer->scrollbar_down = FALSE;
+			gtk_adjustment_changed (xtext->adj);
+		}
+	}
+
+	g_free (nee);
+	gtk_widget_queue_draw (GTK_WIDGET (xtext));
+
+	return fent;
+}
+
+static int
+gtk_xtext_render_page_timeout (GtkXText * xtext)
+{
+	GtkAdjustment *adj = xtext->adj;
+
+	xtext->add_io_tag = 0;
+
+	/* less than a complete page? */
+	if (xtext->buffer->num_lines <= adj->page_size)
+	{
+		xtext->buffer->old_value = 0;
+		adj->value = 0;
+		gtk_xtext_render_page (xtext);
+	} else if (xtext->buffer->scrollbar_down)
+	{
+		g_signal_handler_block (xtext->adj, xtext->vc_signal_tag);
+		gtk_xtext_adjustment_set (xtext->buffer, FALSE);
+		gtk_adjustment_set_value (adj, adj->upper - adj->page_size);
+		g_signal_handler_unblock (xtext->adj, xtext->vc_signal_tag);
+		xtext->buffer->old_value = adj->value;
+		gtk_xtext_render_page (xtext);
+	} else
+	{
+		gtk_xtext_adjustment_set (xtext->buffer, TRUE);
+		if (xtext->force_render)
+		{
+			xtext->force_render = FALSE;
+			gtk_xtext_render_page (xtext);
+		}
+	}
+
+	return 0;
+}
+
+/* append a textentry to our linked list */
+
+static void
+gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp)
+{
+	unsigned int mb;
+	int i;
+
+	/* we don't like tabs */
+	i = 0;
+	while (i < ent->str_len)
+	{
+		if (ent->str[i] == '\t')
+			ent->str[i] = ' ';
+		i++;
+	}
+
+	ent->stamp = stamp;
+	if (stamp == 0)
+		ent->stamp = time (0);
+	ent->str_width = gtk_xtext_text_width (buf->xtext, ent->str, ent->str_len, &mb);
+	ent->mb = FALSE;
+	if (mb)
+		ent->mb = TRUE;
+	ent->mark_start = -1;
+	ent->mark_end = -1;
+	ent->next = NULL;
+
+	if (ent->indent < MARGIN)
+		ent->indent = MARGIN;	  /* 2 pixels is the left margin */
+
+	/* append to our linked list */
+	if (buf->text_last)
+		buf->text_last->next = ent;
+	else
+		buf->text_first = ent;
+	ent->prev = buf->text_last;
+	buf->text_last = ent;
+
+	ent->lines_taken = gtk_xtext_lines_taken (buf, ent);
+	buf->num_lines += ent->lines_taken;
+
+	if (buf->reset_marker_pos || 
+		((buf->marker_pos == NULL || buf->marker_seen) && (buf->xtext->buffer != buf || 
+#if GTK_CHECK_VERSION(2,4,0)
+		!gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (buf->xtext)))))))
+#else
+		!(GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (buf->xtext))))->has_focus)))
+#endif
+	{
+		buf->marker_pos = ent;
+		dontscroll (buf); /* force scrolling off */
+		buf->marker_seen = FALSE;
+		buf->reset_marker_pos = FALSE;
+	}
+
+	if (buf->xtext->max_lines > 2 && buf->xtext->max_lines < buf->num_lines)
+	{
+		gtk_xtext_remove_top (buf);
+	}
+
+	if (buf->xtext->buffer == buf)
+	{
+#ifdef SCROLL_HACK
+		/* this could be improved */
+		if ((buf->num_lines - 1) <= buf->xtext->adj->page_size)
+			dontscroll (buf);
+#endif
+
+		if (!buf->xtext->add_io_tag)
+		{
+			/* remove scrolling events */
+			if (buf->xtext->io_tag)
+			{
+				g_source_remove (buf->xtext->io_tag);
+				buf->xtext->io_tag = 0;
+			}
+			buf->xtext->add_io_tag = g_timeout_add (REFRESH_TIMEOUT * 2,
+															(GSourceFunc)
+															gtk_xtext_render_page_timeout,
+															buf->xtext);
+		}
+	} else if (buf->scrollbar_down)
+	{
+		buf->old_value = buf->num_lines - buf->xtext->adj->page_size;
+		if (buf->old_value < 0)
+			buf->old_value = 0;
+	}
+}
+
+/* the main two public functions */
+
+void
+gtk_xtext_append_indent (xtext_buffer *buf,
+								 unsigned char *left_text, int left_len,
+								 unsigned char *right_text, int right_len,
+								 time_t stamp)
+{
+	textentry *ent;
+	unsigned char *str;
+	int space;
+	int tempindent;
+	int left_width;
+
+	if (left_len == -1)
+		left_len = strlen (left_text);
+
+	if (right_len == -1)
+		right_len = strlen (right_text);
+
+	if (right_len >= sizeof (buf->xtext->scratch_buffer))
+		right_len = sizeof (buf->xtext->scratch_buffer) - 1;
+
+	if (right_text[right_len-1] == '\n')
+		right_len--;
+
+	ent = malloc (left_len + right_len + 2 + sizeof (textentry));
+	str = (unsigned char *) ent + sizeof (textentry);
+
+	memcpy (str, left_text, left_len);
+	str[left_len] = ' ';
+	memcpy (str + left_len + 1, right_text, right_len);
+	str[left_len + 1 + right_len] = 0;
+
+	left_width = gtk_xtext_text_width (buf->xtext, left_text, left_len, NULL);
+
+	ent->left_len = left_len;
+	ent->str = str;
+	ent->str_len = left_len + 1 + right_len;
+	ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
+
+	if (buf->time_stamp)
+		space = buf->xtext->stamp_width;
+	else
+		space = 0;
+
+	/* do we need to auto adjust the separator position? */
+	if (buf->xtext->auto_indent && ent->indent < MARGIN + space)
+	{
+		tempindent = MARGIN + space + buf->xtext->space_width + left_width;
+
+		if (tempindent > buf->indent)
+			buf->indent = tempindent;
+
+		if (buf->indent > buf->xtext->max_auto_indent)
+			buf->indent = buf->xtext->max_auto_indent;
+
+		gtk_xtext_fix_indent (buf);
+		gtk_xtext_recalc_widths (buf, FALSE);
+
+		ent->indent = (buf->indent - left_width) - buf->xtext->space_width;
+		buf->xtext->force_render = TRUE;
+	}
+
+	gtk_xtext_append_entry (buf, ent, stamp);
+}
+
+void
+gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len)
+{
+	textentry *ent;
+
+	if (len == -1)
+		len = strlen (text);
+
+	if (text[len-1] == '\n')
+		len--;
+
+	if (len >= sizeof (buf->xtext->scratch_buffer))
+		len = sizeof (buf->xtext->scratch_buffer) - 1;
+
+	ent = malloc (len + 1 + sizeof (textentry));
+	ent->str = (unsigned char *) ent + sizeof (textentry);
+	ent->str_len = len;
+	if (len)
+		memcpy (ent->str, text, len);
+	ent->str[len] = 0;
+	ent->indent = 0;
+	ent->left_len = -1;
+
+	gtk_xtext_append_entry (buf, ent, 0);
+}
+
+gboolean
+gtk_xtext_is_empty (xtext_buffer *buf)
+{
+	return buf->text_first == NULL;
+}
+
+int
+gtk_xtext_lastlog (xtext_buffer *out, xtext_buffer *search_area,
+						 int (*cmp_func) (char *, void *), void *userdata)
+{
+	textentry *ent = search_area->text_first;
+	int matches = 0;
+
+	while (ent)
+	{
+		if (cmp_func (ent->str, userdata))
+		{
+			matches++;
+			/* copy the text over */
+			if (search_area->xtext->auto_indent)
+				gtk_xtext_append_indent (out, ent->str, ent->left_len,
+												 ent->str + ent->left_len + 1,
+												 ent->str_len - ent->left_len - 1, 0);
+			else
+				gtk_xtext_append (out, ent->str, ent->str_len);
+			/* copy the timestamp over */
+			out->text_last->stamp = ent->stamp;
+		}
+		ent = ent->next;
+	}
+
+	return matches;
+}
+
+void
+gtk_xtext_foreach (xtext_buffer *buf, GtkXTextForeach func, void *data)
+{
+	textentry *ent = buf->text_first;
+
+	while (ent)
+	{
+		(*func) (buf->xtext, ent->str, data);
+		ent = ent->next;
+	}
+}
+
+void
+gtk_xtext_set_error_function (GtkXText *xtext, void (*error_function) (int))
+{
+	xtext->error_function = error_function;
+}
+
+void
+gtk_xtext_set_indent (GtkXText *xtext, gboolean indent)
+{
+	xtext->auto_indent = indent;
+}
+
+void
+gtk_xtext_set_max_indent (GtkXText *xtext, int max_auto_indent)
+{
+	xtext->max_auto_indent = max_auto_indent;
+}
+
+void
+gtk_xtext_set_max_lines (GtkXText *xtext, int max_lines)
+{
+	xtext->max_lines = max_lines;
+}
+
+void
+gtk_xtext_set_show_marker (GtkXText *xtext, gboolean show_marker)
+{
+	xtext->marker = show_marker;
+}
+
+void
+gtk_xtext_set_show_separator (GtkXText *xtext, gboolean show_separator)
+{
+	xtext->separator = show_separator;
+}
+
+void
+gtk_xtext_set_thin_separator (GtkXText *xtext, gboolean thin_separator)
+{
+	xtext->thinline = thin_separator;
+}
+
+void
+gtk_xtext_set_time_stamp (xtext_buffer *buf, gboolean time_stamp)
+{
+	buf->time_stamp = time_stamp;
+}
+
+void
+gtk_xtext_set_tint (GtkXText *xtext, int tint_red, int tint_green, int tint_blue)
+{
+	xtext->tint_red = tint_red;
+	xtext->tint_green = tint_green;
+	xtext->tint_blue = tint_blue;
+
+	/*if (xtext->tint_red != 255 || xtext->tint_green != 255 || xtext->tint_blue != 255)
+		shaded = TRUE;*/
+}
+
+void
+gtk_xtext_set_urlcheck_function (GtkXText *xtext, int (*urlcheck_function) (GtkWidget *, char *, int))
+{
+	xtext->urlcheck_function = urlcheck_function;
+}
+
+void
+gtk_xtext_set_wordwrap (GtkXText *xtext, gboolean wordwrap)
+{
+	xtext->wordwrap = wordwrap;
+}
+
+void
+gtk_xtext_reset_marker_pos (GtkXText *xtext)
+{
+	xtext->buffer->marker_pos = NULL;
+	dontscroll (xtext->buffer); /* force scrolling off */
+	gtk_xtext_render_page (xtext);
+	xtext->buffer->reset_marker_pos = TRUE;
+}
+
+void
+gtk_xtext_buffer_show (GtkXText *xtext, xtext_buffer *buf, int render)
+{
+	int w, h;
+
+	buf->xtext = xtext;
+
+	if (xtext->buffer == buf)
+		return;
+
+/*printf("text_buffer_show: xtext=%p buffer=%p\n", xtext, buf);*/
+
+	if (xtext->add_io_tag)
+	{
+		g_source_remove (xtext->add_io_tag);
+		xtext->add_io_tag = 0;
+	}
+
+	if (xtext->io_tag)
+	{
+		g_source_remove (xtext->io_tag);
+		xtext->io_tag = 0;
+	}
+
+	if (!GTK_WIDGET_REALIZED (GTK_WIDGET (xtext)))
+		gtk_widget_realize (GTK_WIDGET (xtext));
+
+	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &w, &h);
+
+	/* after a font change */
+	if (buf->needs_recalc)
+	{
+		buf->needs_recalc = FALSE;
+		gtk_xtext_recalc_widths (buf, TRUE);
+	}
+
+	/* now change to the new buffer */
+	xtext->buffer = buf;
+	dontscroll (buf);	/* force scrolling off */
+	xtext->adj->value = buf->old_value;
+	xtext->adj->upper = buf->num_lines;
+	if (xtext->adj->upper == 0)
+		xtext->adj->upper = 1;
+	/* sanity check */
+	else if (xtext->adj->value > xtext->adj->upper - xtext->adj->page_size)
+	{
+		/*buf->pagetop_ent = NULL;*/
+		xtext->adj->value = xtext->adj->upper - xtext->adj->page_size;
+		if (xtext->adj->value < 0)
+			xtext->adj->value = 0;
+	}
+
+	if (render)
+	{
+		/* did the window change size since this buffer was last shown? */
+		if (buf->window_width != w)
+		{
+			buf->window_width = w;
+			gtk_xtext_calc_lines (buf, FALSE);
+			if (buf->scrollbar_down)
+				gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
+												  xtext->adj->page_size);
+		} else if (buf->window_height != h)
+		{
+			buf->window_height = h;
+			buf->pagetop_ent = NULL;
+			gtk_xtext_adjustment_set (buf, FALSE);
+		}
+
+		gtk_xtext_render_page (xtext);
+		gtk_adjustment_changed (xtext->adj);
+	} else
+	{
+		/* avoid redoing the transparency */
+		xtext->avoid_trans = TRUE;
+	}
+}
+
+xtext_buffer *
+gtk_xtext_buffer_new (GtkXText *xtext)
+{
+	xtext_buffer *buf;
+
+	buf = malloc (sizeof (xtext_buffer));
+	memset (buf, 0, sizeof (xtext_buffer));
+	buf->old_value = -1;
+	buf->xtext = xtext;
+	buf->scrollbar_down = TRUE;
+	buf->indent = xtext->space_width * 2;
+	dontscroll (buf);
+
+	return buf;
+}
+
+void
+gtk_xtext_buffer_free (xtext_buffer *buf)
+{
+	textentry *ent, *next;
+
+	if (buf->xtext->buffer == buf)
+		buf->xtext->buffer = buf->xtext->orig_buffer;
+
+	if (buf->xtext->selection_buffer == buf)
+		buf->xtext->selection_buffer = NULL;
+
+	ent = buf->text_first;
+	while (ent)
+	{
+		next = ent->next;
+		free (ent);
+		ent = next;
+	}
+
+	free (buf);
+}