summary refs log tree commit diff stats
path: root/src/fe-gtk
diff options
context:
space:
mode:
Diffstat (limited to 'src/fe-gtk')
-rw-r--r--src/fe-gtk/Makefile.am9
-rw-r--r--src/fe-gtk/about.c55
-rw-r--r--src/fe-gtk/banlist.c10
-rw-r--r--src/fe-gtk/chanlist.c5
-rw-r--r--src/fe-gtk/chanview-tabs.c8
-rw-r--r--src/fe-gtk/editlist.c6
-rw-r--r--src/fe-gtk/fe-gtk.c95
-rw-r--r--src/fe-gtk/fe-gtk.h9
-rw-r--r--src/fe-gtk/fkeys.c7
-rw-r--r--src/fe-gtk/gtkutil.c252
-rw-r--r--src/fe-gtk/joind.c5
-rw-r--r--src/fe-gtk/maingui.c96
-rw-r--r--src/fe-gtk/makefile.mak62
-rw-r--r--src/fe-gtk/menu.c84
-rw-r--r--src/fe-gtk/palette.c8
-rw-r--r--src/fe-gtk/palette.h1
-rw-r--r--src/fe-gtk/plugin-tray.c50
-rw-r--r--src/fe-gtk/plugingui.c9
-rw-r--r--src/fe-gtk/rawlog.c5
-rw-r--r--src/fe-gtk/search.c122
-rw-r--r--src/fe-gtk/servlistgui.c22
-rw-r--r--src/fe-gtk/setup.c134
-rw-r--r--src/fe-gtk/sexy-iso-codes.c305
-rw-r--r--src/fe-gtk/sexy-iso-codes.h37
-rw-r--r--src/fe-gtk/sexy-marshal.c129
-rw-r--r--src/fe-gtk/sexy-marshal.h28
-rw-r--r--src/fe-gtk/sexy-spell-entry.c108
-rw-r--r--src/fe-gtk/typedef.h11
-rw-r--r--src/fe-gtk/xchat.exe.manifest22
-rw-r--r--src/fe-gtk/xchat.rc28
-rw-r--r--src/fe-gtk/xtext.c839
-rw-r--r--src/fe-gtk/xtext.h20
32 files changed, 2221 insertions, 360 deletions
diff --git a/src/fe-gtk/Makefile.am b/src/fe-gtk/Makefile.am
index 7db38096..da36ed57 100644
--- a/src/fe-gtk/Makefile.am
+++ b/src/fe-gtk/Makefile.am
@@ -10,8 +10,8 @@ EXTRA_DIST = \
 	about.h ascii.h banlist.h chanlist.h chanview.h chanview-tabs.c \
 	chanview-tree.c custom-list.h editlist.h fe-gtk.h fkeys.h gtkutil.h joind.h \
 	maingui.h menu.h mmx_cmod.S mmx_cmod.h notifygui.h palette.h pixmaps.h \
-	plugin-tray.h plugingui.c plugingui.h rawlog.h search.h sexy-spell-entry.h \
-   textgui.h urlgrab.h userlistgui.h xtext.h
+	plugin-tray.h plugingui.c plugingui.h rawlog.h search.h sexy-iso-codes.h \
+	sexy-spell-entry.h sexy-marshal.h textgui.h urlgrab.h userlistgui.h xtext.h
 
 if USE_MMX
 mmx_cmod_S = mmx_cmod.S
@@ -22,11 +22,12 @@ plugingui_c = plugingui.c
 endif
 
 if USE_LIBSEXY
-sexy_spell_entry_c = sexy-spell-entry.c
+sexy_spell = \
+	sexy-iso-codes.c sexy-marshal.c sexy-spell-entry.c
 endif
 
 xchat_SOURCES = about.c ascii.c banlist.c chanlist.c chanview.c custom-list.c \
 	dccgui.c editlist.c fe-gtk.c fkeys.c gtkutil.c ignoregui.c joind.c menu.c \
 	maingui.c $(mmx_cmod_S) notifygui.c palette.c pixmaps.c plugin-tray.c $(plugingui_c) \
-	rawlog.c search.c servlistgui.c setup.c $(sexy_spell_entry_c) textgui.c \
+	rawlog.c search.c servlistgui.c setup.c $(sexy_spell) textgui.c \
 	urlgrab.c userlistgui.c xtext.c
diff --git a/src/fe-gtk/about.c b/src/fe-gtk/about.c
index 60700aec..dc1cbf08 100644
--- a/src/fe-gtk/about.c
+++ b/src/fe-gtk/about.c
@@ -39,6 +39,7 @@
 
 #include "../common/xchat.h"
 #include "../common/util.h"
+#include "../common/xchatc.h"
 #include "palette.h"
 #include "pixmaps.h"
 #include "gtkutil.h"
@@ -69,7 +70,7 @@ menu_about (GtkWidget * wid, gpointer sess)
 							"(C) 1998-2005 Peter Zelezny", author, buf, 0));
 }
 
-#else
+#endif
 
 static GtkWidget *about = 0;
 
@@ -95,7 +96,7 @@ menu_about (GtkWidget * wid, gpointer sess)
 	}
 
 	about = gtk_dialog_new ();
-	gtk_window_set_position (GTK_WINDOW (about), GTK_WIN_POS_CENTER);
+	gtk_window_set_position (GTK_WINDOW (about), GTK_WIN_POS_CENTER_ON_PARENT);
 	gtk_window_set_resizable (GTK_WINDOW (about), FALSE);
 	gtk_window_set_title (GTK_WINDOW (about), _("About "DISPLAY_NAME));
 	if (parent_window)
@@ -113,36 +114,47 @@ menu_about (GtkWidget * wid, gpointer sess)
 	gtk_container_add (GTK_CONTAINER (vbox), label);
 	g_get_charset (&locale);
 	(snprintf) (buf, sizeof (buf),
-				"<span size=\"x-large\"><b>"DISPLAY_NAME" "PACKAGE_VERSION"</b></span>\n\n"
-				"%s\n\n"
+				"<span size=\"x-large\"><b>"DISPLAY_NAME" "PACKAGE_VERSION"</b></span>\n"
 #ifdef WIN32
-				/* leave this message to avoid time wasting bug reports! */
-				"This version is unofficial and comes with no support.\n\n"
-#endif
-				"%s\n"
+				"%s%s%s"
+				"\n<b>XChat Release</b>: "XCHAT_RELEASE"\n\n"
+				"<b>OS</b>: %s\n"
 				"<b>Charset</b>: %s "
-#ifdef WIN32 
 				"<b>GTK+</b>: %i.%i.%i\n"
+				"<b>Compiled</b>: "__DATE__"\n"
+				"<b>Portable Mode</b>: %s\n"
+				"<b>Build Type</b>: x%d\n\n"
+				"<small>This version is unofficial and comes with no support.\n"
+				"\302\251 1998-2010 Peter \305\275elezn\303\275 &lt;zed@xchat.org>"
+				/* "\n<a href=\"http://code.google.com/p/xchat-wdk/\">http://code.google.com/p/xchat-wdk/</a>" this is broken in gtk ATM */
+				"</small>",
+				(strcmp (prefs.gui_license, "") ? "<span size=\"x-large\">" : ""),
+				(strcmp (prefs.gui_license, "") ? prefs.gui_license : ""),
+				(strcmp (prefs.gui_license, "") ? "</span>\n" : ""),
+				get_cpu_str (),
+				locale,
+				gtk_major_version,
+				gtk_minor_version,
+				gtk_micro_version,
+				(portable_mode () ? "Yes" : "No"),
+				get_cpu_arch ()
 #else
+				"\n%s\n\n"
+				"%s\n"
+				"<b>Charset</b>: %s "
 				"<b>Renderer</b>: %s\n"
-#endif
 				"<b>Compiled</b>: "__DATE__"\n\n"
 				"<small>\302\251 1998-2010 Peter \305\275elezn\303\275 &lt;zed@xchat.org></small>",
-					_("A multiplatform IRC Client"),
-					get_cpu_str(),
-					locale,
-#ifdef WIN32
-					gtk_major_version,
-					gtk_minor_version,
-					gtk_micro_version
-#else
+				_("A multiplatform IRC Client"),
+				get_cpu_str (),
+				locale,
 #ifdef USE_XFT
-					"Xft"
+				"Xft"
 #else
-					"Pango"
+				"Pango"
 #endif
 #endif
-					);
+				);
 	gtk_label_set_markup (GTK_LABEL (label), buf);
 	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
 
@@ -158,4 +170,3 @@ menu_about (GtkWidget * wid, gpointer sess)
 
 	gtk_widget_show_all (about);
 }
-#endif
diff --git a/src/fe-gtk/banlist.c b/src/fe-gtk/banlist.c
index afaa7eb4..fecb62a5 100644
--- a/src/fe-gtk/banlist.c
+++ b/src/fe-gtk/banlist.c
@@ -19,10 +19,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <fcntl.h>
 #include <time.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "fe-gtk.h"
 
 #include <gtk/gtkhbox.h>
@@ -134,11 +137,8 @@ banlist_do_refresh (struct session *sess)
 		gtk_list_store_clear (store);
 
 		handle_command (sess, "ban", FALSE);
-#ifdef WIN32
-		if (0)
-#else
+
 		if (supports_exempt (sess->server))
-#endif
 		{
 			snprintf (tbuf, sizeof (tbuf), "quote mode %s +e", sess->channel);
 			handle_command (sess, tbuf, FALSE);
diff --git a/src/fe-gtk/chanlist.c b/src/fe-gtk/chanlist.c
index 2f65b518..4dca935b 100644
--- a/src/fe-gtk/chanlist.c
+++ b/src/fe-gtk/chanlist.c
@@ -19,10 +19,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <fcntl.h>
 #include <time.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "fe-gtk.h"
 
 #include <gtk/gtkalignment.h>
diff --git a/src/fe-gtk/chanview-tabs.c b/src/fe-gtk/chanview-tabs.c
index 8e3da8e6..f99e61f1 100644
--- a/src/fe-gtk/chanview-tabs.c
+++ b/src/fe-gtk/chanview-tabs.c
@@ -144,8 +144,8 @@ tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv)
 		for (i = adj->value; ((i > new_value) && (tab_left_is_moving)); i -= 0.1)
 		{
 			gtk_adjustment_set_value (adj, i);
-			while (g_main_pending ())
-				g_main_iteration (TRUE);
+			while (g_main_context_pending (NULL))
+				g_main_context_iteration (NULL, TRUE);
 		}
 
 		gtk_adjustment_set_value (adj, new_value);
@@ -191,8 +191,8 @@ tab_scroll_right_down_clicked (GtkWidget *widget, chanview *cv)
 		for (i = adj->value; ((i < new_value) && (tab_right_is_moving)); i += 0.1)
 		{
 			gtk_adjustment_set_value (adj, i);
-			while (g_main_pending ())
-				g_main_iteration (TRUE);
+			while (g_main_context_pending (NULL))
+				g_main_context_iteration (NULL, TRUE);
 		}
 
 		gtk_adjustment_set_value (adj, new_value);
diff --git a/src/fe-gtk/editlist.c b/src/fe-gtk/editlist.c
index 5af67e32..d374d064 100644
--- a/src/fe-gtk/editlist.c
+++ b/src/fe-gtk/editlist.c
@@ -19,10 +19,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "fe-gtk.h"
 
 #include <gtk/gtkstock.h>
diff --git a/src/fe-gtk/fe-gtk.c b/src/fe-gtk/fe-gtk.c
index 5efcaeec..88a1138c 100644
--- a/src/fe-gtk/fe-gtk.c
+++ b/src/fe-gtk/fe-gtk.c
@@ -19,7 +19,6 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <unistd.h>
 
 #include "fe-gtk.h"
 
@@ -32,6 +31,12 @@
 #include <gtk/gtkmessagedialog.h>
 #include <gtk/gtkversion.h>
 
+#ifdef WIN32
+#include <gdk/gdkwin32.h>
+#else
+#include <unistd.h>
+#endif
+
 #include "../common/xchat.h"
 #include "../common/fe.h"
 #include "../common/util.h"
@@ -39,6 +44,7 @@
 #include "../common/cfgfiles.h"
 #include "../common/xchatc.h"
 #include "../common/plugin.h"
+#include "../common/server.h"
 #include "gtkutil.h"
 #include "maingui.h"
 #include "pixmaps.h"
@@ -136,11 +142,26 @@ static const GOptionEntry gopt_entries[] =
  {NULL}
 };
 
+#ifdef WIN32
+static void
+create_msg_dialog (gchar *title, gchar *message)
+{
+	GtkWidget *dialog;
+	dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, message);
+	gtk_window_set_title (GTK_WINDOW (dialog), title);
+	gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (dialog);
+}
+#endif
+
 int
 fe_args (int argc, char *argv[])
 {
 	GError *error = NULL;
 	GOptionContext *context;
+#ifdef WIN32
+	char *buffer[2048];
+#endif
 
 #ifdef ENABLE_NLS
 	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
@@ -149,22 +170,67 @@ fe_args (int argc, char *argv[])
 #endif
 
 	context = g_option_context_new (NULL);
+#ifdef WIN32
+	g_option_context_set_help_enabled (context, FALSE);	/* disable stdout help as stdout is unavailable for subsystem:windows */
+#endif
 	g_option_context_add_main_entries (context, gopt_entries, GETTEXT_PACKAGE);
 	g_option_context_add_group (context, gtk_get_option_group (FALSE));
 	g_option_context_parse (context, &argc, &argv, &error);
 
+#ifdef WIN32
+	if (error)											/* workaround for argv not being available when using subsystem:windows */
+	{
+		if (error->message)								/* the error message contains argv so search for patterns in that */
+		{
+			if (strstr (error->message, "--help-all") != NULL)
+			{
+				if (snprintf (buffer, 2048, g_option_context_get_help (context, FALSE, NULL)))
+				{
+					gtk_init (&argc, &argv);
+					create_msg_dialog ("Long Help", buffer);
+				}
+				return 0;
+			} else if (strstr (error->message, "--help") != NULL || strstr (error->message, "-?") != NULL)
+			{
+				if (snprintf (buffer, 2048, g_option_context_get_help (context, TRUE, NULL)))
+				{
+					gtk_init (&argc, &argv);
+					create_msg_dialog ("Help", buffer);
+				}
+				return 0;
+			} else 
+			{
+				if (snprintf (buffer, 2048, "%s\n", error->message))
+				{
+					gtk_init (&argc, &argv);
+					create_msg_dialog ("Error", buffer);
+				}
+				return 1;
+			}
+		}
+	}
+#else
 	if (error)
 	{
 		if (error->message)
 			printf ("%s\n", error->message);
 		return 1;
 	}
+#endif
 
 	g_option_context_free (context);
 
 	if (arg_show_version)
 	{
+#ifdef WIN32
+		if (snprintf (buffer, 2048, DISPLAY_NAME" "PACKAGE_VERSION"\n"))
+		{
+			gtk_init (&argc, &argv);
+			create_msg_dialog ("Version Information", buffer);
+		}
+#else
 		printf (PACKAGE_TARNAME" "PACKAGE_VERSION"\n");
+#endif
 		return 0;
 	}
 
@@ -177,7 +243,18 @@ fe_args (int argc, char *argv[])
 		if (sl)
 		{
 			*sl = 0;
-			printf ("%s\\plugins\n", exe);
+			if (snprintf (buffer, 2048, "%s\\plugins\n", exe))
+			{
+				gtk_init (&argc, &argv);
+				create_msg_dialog ("Plugin Auto-load Directory", buffer);
+			}
+		} else
+		{
+			if (snprintf (buffer, 2048, ".\\plugins\n"))
+			{
+				gtk_init (&argc, &argv);
+				create_msg_dialog ("Plugin Auto-load Directory", buffer);
+			}
 		}
 #else
 		printf ("%s\n", XCHATLIBDIR"/plugins");
@@ -187,7 +264,15 @@ fe_args (int argc, char *argv[])
 
 	if (arg_show_config)
 	{
+#ifdef WIN32
+		if (snprintf (buffer, 2048, "%s\n", get_xdir_fs ()))
+		{
+			gtk_init (&argc, &argv);
+			create_msg_dialog ("User Config Directory", buffer);
+		}
+#else
 		printf ("%s\n", get_xdir_fs ());
+#endif
 		return 0;
 	}
 
@@ -330,7 +415,7 @@ log_handler (const gchar   *log_domain,
 {
 	session *sess;
 
-	if (getenv ("XCHAT_WARNING_IGNORE"))
+	/* if (getenv ("XCHAT_WARNING_IGNORE")) this gets ignored sometimes, so simply just disable all warnings */
 		return;
 
 	sess = find_dialog (serv_list->data, "(warnings)");
@@ -845,7 +930,11 @@ fe_gui_info_ptr (session *sess, int info_type)
 	{
 	case 0:	/* native window pointer (for plugins) */
 #ifdef WIN32
+#if GTK_CHECK_VERSION(2,24,8)
+		return gdk_win32_window_get_impl_hwnd (sess->gui->window->window);
+#else
 		return GDK_WINDOW_HWND (sess->gui->window->window);
+#endif
 #else
 		return sess->gui->window;
 #endif
diff --git a/src/fe-gtk/fe-gtk.h b/src/fe-gtk/fe-gtk.h
index 12516259..8fffb3cc 100644
--- a/src/fe-gtk/fe-gtk.h
+++ b/src/fe-gtk/fe-gtk.h
@@ -1,13 +1,6 @@
 #include "../../config.h"
 
-#ifdef WIN32
-/* If you're compiling this for Windows, your release is un-official
- * and not condoned. Please don't use the XChat name. Make up your
- * own name! */
-#define DISPLAY_NAME "XChat-Unofficial"
-#else
-#define DISPLAY_NAME "XChat"
-#endif
+#define DISPLAY_NAME "XChat-WDK"
 
 #ifndef WIN32
 #include <sys/types.h>
diff --git a/src/fe-gtk/fkeys.c b/src/fe-gtk/fkeys.c
index 014b5cc3..b9690731 100644
--- a/src/fe-gtk/fkeys.c
+++ b/src/fe-gtk/fkeys.c
@@ -20,11 +20,14 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
 #include <string.h>
 #include <fcntl.h>
 #include <ctype.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "fe-gtk.h"
 
 #include <gtk/gtklabel.h>
@@ -315,7 +318,7 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
 		key_action_tab_clean ();
 		break;
 
-#if defined(USE_GTKSPELL) && !defined(WIN32)
+#if defined(USE_GTKSPELL)/* && !defined(WIN32) */
 	/* gtktextview has no 'activate' event, so we trap ENTER here */
 	case GDK_Return:
 	case GDK_KP_Enter:
diff --git a/src/fe-gtk/gtkutil.c b/src/fe-gtk/gtkutil.c
index 63ab491b..fe4e1737 100644
--- a/src/fe-gtk/gtkutil.c
+++ b/src/fe-gtk/gtkutil.c
@@ -22,8 +22,8 @@
 #include <stdarg.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
 #include <fcntl.h>
+
 #include "fe-gtk.h"
 
 #include <gtk/gtkbutton.h>
@@ -52,6 +52,13 @@
 #include "gtkutil.h"
 #include "pixmaps.h"
 
+#ifdef WIN32
+#include "../common/fe.h"
+#include "../common/thread.h"
+#else
+#include <unistd.h>
+#endif
+
 /* gtkutil.c, just some gtk wrappers */
 
 extern void path_part (char *file, char *path, int pathlen);
@@ -63,6 +70,13 @@ struct file_req
 	void *userdata;
 	filereqcallback callback;
 	int flags;		/* FRF_* flags */
+
+#ifdef WIN32
+	int multiple;
+	thread *th;
+	char *title;	/* native locale */
+	char *filter;
+#endif
 };
 
 static char last_dir[256] = "";
@@ -164,6 +178,190 @@ gtkutil_file_req_response (GtkWidget *dialog, gint res, struct file_req *freq)
 	}
 }
 
+#ifdef WIN32
+static int
+win32_openfile (char *file_buf, int file_buf_len, char *title_text, char *filter,
+			   int multiple)
+{
+	OPENFILENAME o;
+
+	memset (&o, 0, sizeof (o));
+
+	o.lStructSize = sizeof (o);
+	o.lpstrFilter = filter;
+	o.lpstrFile = file_buf;
+	o.nMaxFile = file_buf_len;
+	o.lpstrTitle = title_text;
+	o.Flags = 0x02000000 | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY |
+				OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_LONGNAMES | OFN_NONETWORKBUTTON;
+	if (multiple)
+	{
+		o.Flags |= OFN_ALLOWMULTISELECT;
+	}
+
+	return GetOpenFileName (&o);
+}
+
+static int
+win32_savefile (char *file_buf, int file_buf_len, char *title_text, char *filter,
+               int multiple)
+{
+	/* we need the filter to get the default filename. it is from fe-gtk.c (fe_confirm);
+	 * but that filter is actually the whole filename, so apply an empty filter and all good.
+	 * in win32_thread2 we copy the filter ( = the filename) after the last dir into our
+	 * LPTSTR file buffer to make it actually work. the docs for this amazingly retard api:
+	 *
+	 * http://msdn.microsoft.com/en-us/library/ms646839%28VS.85%29.aspx
+	 */
+
+	OPENFILENAME o;
+
+	memset (&o, 0, sizeof (o));
+
+	o.lStructSize = sizeof (o);
+	o.lpstrFilter = "All files\0*.*\0\0";
+	o.lpstrFile = file_buf;
+	o.nMaxFile = file_buf_len;
+	o.lpstrTitle = title_text;
+	o.Flags = 0x02000000 | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY |
+				OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_LONGNAMES | OFN_NONETWORKBUTTON;
+	if (multiple)
+	{
+		o.Flags |= OFN_ALLOWMULTISELECT;
+	}
+
+	return GetSaveFileName (&o);
+}
+
+static void *
+win32_thread (struct file_req *freq)
+{
+	char buf[1024 + 32];
+	char file[1024];
+
+	memset (file, 0, sizeof (file));
+	safe_strcpy (file, last_dir, sizeof (file));
+
+	if (win32_openfile (file, sizeof (file), freq->title, freq->filter, freq->multiple))
+	{
+		if (freq->multiple)
+		{
+			char *f = file;
+
+			if (f[strlen (f) + 1] == 0)	/* only selected one file */
+			{
+				snprintf (buf, sizeof (buf), "1\n%s\n", file);
+				write (freq->th->pipe_fd[1], buf, strlen (buf));
+			} else
+			{
+				f += strlen (f) + 1; /* skip first, it's only the dir */
+				while (f[0])
+				{
+					snprintf (buf, sizeof (buf), "1\n%s\\%s\n", /*dir!*/file, f);
+					write (freq->th->pipe_fd[1], buf, strlen (buf));
+					f += strlen (f) + 1;
+				}
+			}
+
+		} else
+		{
+			snprintf (buf, sizeof (buf), "1\n%s\n", file);
+			write (freq->th->pipe_fd[1], buf, strlen (buf));
+		}
+	}
+
+	write (freq->th->pipe_fd[1], "0\n", 2);
+	Sleep (2000);
+
+	return NULL;
+}
+
+static void *
+win32_thread2 (struct file_req *freq)
+{
+	char buf[1024 + 32];
+	char file[1024];
+
+	memset (file, 0, sizeof (file));
+	safe_strcpy (file, last_dir, sizeof (file));
+	safe_strcpy (file, freq->filter, sizeof (file));
+
+	if (win32_savefile (file, sizeof (file), freq->title, NULL, freq->multiple))
+	{
+		if (freq->multiple)
+		{
+			char *f = file;
+
+			if (f[strlen (f) + 1] == 0)    /* only selected one file */
+			{
+				snprintf (buf, sizeof (buf), "1\n%s\n", file);
+				write (freq->th->pipe_fd[1], buf, strlen (buf));
+			} else
+			{
+				f += strlen (f) + 1; /* skip first, it's only the dir */
+				while (f[0])
+				{
+					snprintf (buf, sizeof (buf), "1\n%s\\%s\n", /*dir!*/file, f);
+					write (freq->th->pipe_fd[1], buf, strlen (buf));
+					f += strlen (f) + 1;
+				}
+			}
+
+		} else
+		{
+			snprintf (buf, sizeof (buf), "1\n%s\n", file);
+			write (freq->th->pipe_fd[1], buf, strlen (buf));
+		}
+	}
+
+	write (freq->th->pipe_fd[1], "0\n", 2);
+	Sleep (2000);
+
+	return NULL;
+}
+
+static gboolean
+win32_close_pipe (int fd)
+{
+	close (fd);
+	return 0;
+}
+
+static gboolean
+win32_read_thread (GIOChannel *source, GIOCondition cond, struct file_req *freq)
+{
+	char buf[512];
+	char *file;
+
+	waitline2 (source, buf, sizeof buf);
+
+	switch (buf[0])
+	{
+	case '0':	/* filedialog has closed */
+		freq->callback (freq->userdata, NULL);
+		break;
+
+	case '1':	/* got a filename! */
+		waitline2 (source, buf, sizeof buf);
+		file = g_filename_to_utf8 (buf, -1, 0, 0, 0);
+		freq->callback (freq->userdata, file);
+		g_free (file);
+		return TRUE;
+	}
+
+	/* it doesn't work to close them here, because of the weird
+		way giowin32 works. We must _return_ before closing them */
+	g_timeout_add(3000, (GSourceFunc)win32_close_pipe, freq->th->pipe_fd[0]);
+	g_timeout_add(2000, (GSourceFunc)win32_close_pipe, freq->th->pipe_fd[1]);
+
+	g_free (freq->title);
+	free (freq->th);
+	free (freq);
+
+	return FALSE;
+}
+#endif
+
 void
 gtkutil_file_req (const char *title, void *callback, void *userdata, char *filter,
 						int flags)
@@ -172,6 +370,58 @@ gtkutil_file_req (const char *title, void *callback, void *userdata, char *filte
 	GtkWidget *dialog;
 	extern char *get_xdir_fs (void);
 
+#ifdef WIN32
+	if (!(flags & FRF_WRITE))
+	{
+		freq = malloc (sizeof (struct file_req));
+		freq->th = thread_new ();
+		freq->flags = 0;
+		freq->multiple = (flags & FRF_MULTIPLE);
+		freq->callback = callback;
+		freq->userdata = userdata;
+		freq->title = g_locale_from_utf8 (title, -1, 0, 0, 0);
+		if (!filter)
+		{
+			freq->filter =	"All files\0*.*\0"
+							"Executables\0*.exe\0"
+							"ZIP files\0*.zip\0\0";
+		}
+		else
+		{
+			freq->filter = filter;
+		}
+
+		thread_start (freq->th, win32_thread, freq);
+		fe_input_add (freq->th->pipe_fd[0], FIA_FD|FIA_READ, win32_read_thread, freq);
+
+		return;
+
+	}
+	
+	else {
+		freq = malloc (sizeof (struct file_req));
+		freq->th = thread_new ();
+		freq->flags = 0;
+		freq->multiple = (flags & FRF_MULTIPLE);
+		freq->callback = callback;
+		freq->userdata = userdata;
+		freq->title = g_locale_from_utf8 (title, -1, 0, 0, 0);
+		if (!filter)
+		{
+			freq->filter = "All files\0*.*\0\0";
+		}
+		else
+		{
+			freq->filter = filter;
+		}
+
+		thread_start (freq->th, win32_thread2, freq);
+		fe_input_add (freq->th->pipe_fd[0], FIA_FD|FIA_READ, win32_read_thread, freq);
+
+	return;
+	}
+#endif
+
 	if (flags & FRF_WRITE)
 	{
 		dialog = gtk_file_chooser_dialog_new (title, NULL,
diff --git a/src/fe-gtk/joind.c b/src/fe-gtk/joind.c
index ee5c56d1..e645f0bc 100644
--- a/src/fe-gtk/joind.c
+++ b/src/fe-gtk/joind.c
@@ -9,10 +9,13 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
 #include <string.h>
 #include <stdio.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include <gtk/gtkbbox.h>
 #include <gtk/gtkbutton.h>
 #include <gtk/gtkdialog.h>
diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c
index 8e6873a2..0b7f7c43 100644
--- a/src/fe-gtk/maingui.c
+++ b/src/fe-gtk/maingui.c
@@ -53,6 +53,8 @@
 #include "../common/plugin.h"
 #include "../common/modes.h"
 #include "../common/url.h"
+#include "../common/util.h"
+
 #include "fe-gtk.h"
 #include "banlist.h"
 #include "gtkutil.h"
@@ -214,60 +216,10 @@ mg_create_tab_colors (void)
 	away_list = mg_attr_list_create (&colors[COL_AWAY], FALSE);
 }
 
-#ifdef WIN32
-#define WINVER 0x0501	/* needed for vc6? */
-#include <windows.h>
-#include <gdk/gdkwin32.h>
-
-/* Flash the taskbar button on Windows when there's a highlight event. */
-
-static void
-flash_window (GtkWidget *win)
-{
-	FLASHWINFO fi;
-	static HMODULE user = NULL;
-	static BOOL (*flash) (PFLASHWINFO) = NULL;
-
-	if (!user)
-	{
-		user = GetModuleHandleA ("USER32");
-		if (!user)
-			return;	/* this should never fail */
-	}
-
-	if (!flash)
-	{
-		flash = (void *)GetProcAddress (user, "FlashWindowEx");
-		if (!flash)
-			return;	/* this fails on NT4.0 and Win95 */
-	}
-
-	fi.cbSize = sizeof (fi);
-	fi.hwnd = GDK_WINDOW_HWND (win->window);
-	fi.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG;
-	fi.uCount = 0;
-	fi.dwTimeout = 500;
-	flash (&fi);
-	/*FlashWindowEx (&fi);*/
-}
-#else
-
-#ifdef USE_XLIB
-#include <gdk/gdkx.h>
-
 static void
 set_window_urgency (GtkWidget *win, gboolean set)
 {
-	XWMHints *hints;
-
-	hints = XGetWMHints(GDK_WINDOW_XDISPLAY(win->window), GDK_WINDOW_XWINDOW(win->window));
-	if (set)
-		hints->flags |= XUrgencyHint;
-	else
-		hints->flags &= ~XUrgencyHint;
-	XSetWMHints(GDK_WINDOW_XDISPLAY(win->window),
-	            GDK_WINDOW_XWINDOW(win->window), hints);
-	XFree(hints);
+	gtk_window_set_urgency_hint (GTK_WINDOW (win), set);
 }
 
 static void
@@ -281,18 +233,14 @@ unflash_window (GtkWidget *win)
 {
 	set_window_urgency (win, FALSE);
 }
-#endif
-#endif
 
 /* flash the taskbar button */
 
 void
 fe_flash_window (session *sess)
 {
-#if defined(WIN32) || defined(USE_XLIB)
 	if (fe_gui_info (sess, 0) != 1)	/* only do it if not focused */
 		flash_window (sess->gui->window);
-#endif
 }
 
 /* set a tab plain, red, light-red, or blue */
@@ -530,10 +478,22 @@ fe_set_title (session *sess)
 		break;
 	default:
 	def:
-		gtk_window_set_title (GTK_WINDOW (sess->gui->window), DISPLAY_NAME);
+		snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME);
+		if (strcmp (prefs.gui_license, ""))		/* zero means gui_license is empty */
+		{
+			strcat (tbuf, " - ");
+			strcat (tbuf, prefs.gui_license);
+		}
+		gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf);
 		return;
 	}
 
+	if (strcmp (prefs.gui_license, ""))			/* zero means gui_license is empty */
+	{
+		strcat (tbuf, " - ");
+		strcat (tbuf, prefs.gui_license);
+	}
+
 	gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf);
 }
 
@@ -840,6 +800,9 @@ mg_userlist_showhide (session *sess, int show)
 {
 	session_gui *gui = sess->gui;
 	int handle_size;
+	int right_size;
+
+	right_size = MAX (prefs.gui_pane_right_size, prefs.gui_pane_right_size_min);
 
 	if (show)
 	{
@@ -847,7 +810,7 @@ mg_userlist_showhide (session *sess, int show)
 		gui->ul_hidden = 0;
 
 		gtk_widget_style_get (GTK_WIDGET (gui->hpane_right), "handle-size", &handle_size, NULL);
-		gtk_paned_set_position (GTK_PANED (gui->hpane_right), GTK_WIDGET (gui->hpane_right)->allocation.width - (prefs.gui_pane_right_size + handle_size));
+		gtk_paned_set_position (GTK_PANED (gui->hpane_right), GTK_WIDGET (gui->hpane_right)->allocation.width - (right_size + handle_size));
 	}
 	else
 	{
@@ -1335,7 +1298,7 @@ mg_open_quit_dialog (gboolean minimize_button)
 	gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1),
 										GTK_BUTTONBOX_END);
 
-	if (minimize_button)
+	if (minimize_button && !xtray_mode ())
 	{
 		button = gtk_button_new_with_mnemonic (_("_Minimize to Tray"));
 		gtk_widget_show (button);
@@ -1633,7 +1596,12 @@ mg_create_alertmenu (session *sess, GtkWidget *menu)
 	submenu = menu_quick_sub (_("_Extra Alerts"), menu, NULL, XCMENU_MNEMONIC, -1);
 
 	mg_perchan_menu_item (_("Beep on _Message"), submenu, &sess->alert_beep, prefs.input_beep_chans);
-	mg_perchan_menu_item (_("Blink Tray _Icon"), submenu, &sess->alert_tray, prefs.input_tray_chans);
+
+	if (!xtray_mode ())		/*disable this context menu item when xtray is loaded */
+	{
+		mg_perchan_menu_item (_("Blink Tray _Icon"), submenu, &sess->alert_tray, prefs.input_tray_chans);
+	}
+
 	mg_perchan_menu_item (_("Blink Task _Bar"), submenu, &sess->alert_taskbar, prefs.input_flash_chans);
 }
 
@@ -2883,7 +2851,7 @@ mg_create_entry (session *sess, GtkWidget *box)
 #else
 	gui->input_box = entry = gtk_entry_new ();
 #endif
-	gtk_entry_set_max_length (GTK_ENTRY (gui->input_box), 2048);
+	gtk_entry_set_max_length (GTK_ENTRY (gui->input_box), 0);
 	g_signal_connect (G_OBJECT (entry), "activate",
 							G_CALLBACK (mg_inputbox_cb), gui);
 	gtk_container_add (GTK_CONTAINER (hbox), entry);
@@ -2973,11 +2941,7 @@ mg_tabwin_focus_cb (GtkWindow * win, GdkEventFocus *event, gpointer userdata)
 		gtk_xtext_check_marker_visibility (GTK_XTEXT (current_sess->gui->xtext));
 		plugin_emit_dummy_print (current_sess, "Focus Window");
 	}
-#ifndef WIN32
-#ifdef USE_XLIB
 	unflash_window (GTK_WIDGET (win));
-#endif
-#endif
 	return FALSE;
 }
 
@@ -2988,11 +2952,7 @@ mg_topwin_focus_cb (GtkWindow * win, GdkEventFocus *event, session *sess)
 	if (!sess->server->server_session)
 		sess->server->server_session = sess;
 	gtk_xtext_check_marker_visibility(GTK_XTEXT (current_sess->gui->xtext));
-#ifndef WIN32
-#ifdef USE_XLIB
 	unflash_window (GTK_WIDGET (win));
-#endif
-#endif
 	plugin_emit_dummy_print (sess, "Focus Window");
 	return FALSE;
 }
diff --git a/src/fe-gtk/makefile.mak b/src/fe-gtk/makefile.mak
new file mode 100644
index 00000000..27922405
--- /dev/null
+++ b/src/fe-gtk/makefile.mak
@@ -0,0 +1,62 @@
+include "..\makeinc.mak"
+
+FEGTK_OBJECTS = \
+about.obj \
+ascii.obj \
+banlist.obj \
+chanlist.obj \
+chanview.obj \
+custom-list.obj \
+dccgui.obj \
+editlist.obj \
+fe-gtk.obj \
+fkeys.obj \
+gtkutil.obj \
+ignoregui.obj \
+joind.obj \
+maingui.obj \
+menu.obj \
+notifygui.obj \
+palette.obj \
+pixmaps.obj \
+plugingui.obj \
+plugin-tray.obj \
+rawlog.obj \
+search.obj \
+servlistgui.obj \
+setup.obj \
+sexy-iso-codes.obj \
+sexy-marshal.obj \
+sexy-spell-entry.obj \
+textgui.obj \
+urlgrab.obj \
+userlistgui.obj \
+xtext.obj
+
+!ifdef X64
+MACHINE_FLAG = /MACHINE:X64
+!else
+MACHINE_FLAG = /MACHINE:X86
+!endif
+
+COMLIB = ..\common\xchatcommon.lib
+PROG = xchat.exe
+
+all: $(PROG)
+
+.c.obj::
+	$(CC) $(CFLAGS) -I..\..\plugins $(GLIB) $(GTK) $<
+
+$(PROG): $(FEGTK_OBJECTS) $(COMLIB) xchat-icon.obj
+	$(LINK) /out:$(PROG) /entry:mainCRTStartup $(LDFLAGS) $(LIBS) $(FEGTK_OBJECTS) $(COMLIB) xchat-icon.obj
+
+xchat.res: xchat.rc ../../xchat.ico
+	rc /nologo /r xchat.rc
+
+xchat-icon.obj: xchat.res
+	cvtres /nologo $(MACHINE_FLAG) /OUT:xchat-icon.obj xchat.res
+
+clean:
+	@del *.obj
+	@del $(PROG)
+	@del xchat.res
diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c
index d04be222..4760ba93 100644
--- a/src/fe-gtk/menu.c
+++ b/src/fe-gtk/menu.c
@@ -20,10 +20,11 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <string.h>
-#include <unistd.h>
 
 #ifdef WIN32
 #include <windows.h>
+#else
+#include <unistd.h>
 #endif
 
 #include "fe-gtk.h"
@@ -1122,6 +1123,23 @@ usermenu_update (void)
 	}
 }
 
+#if 0
+static void
+menu_saveconf (void)
+{
+	session *sess = current_sess;
+
+	if (save_config ())
+	{
+		PrintText (sess, "Settings have been saved successfully.\n");
+	}
+	else
+	{
+		PrintText (sess, "Error saving settings.\n");
+	}
+}
+#endif
+
 static void
 menu_newserver_window (GtkWidget * wid, gpointer none)
 {
@@ -1198,12 +1216,53 @@ menu_search ()
 }
 
 static void
+menu_search_next ()
+{
+	GtkXText *xtext = GTK_XTEXT (current_sess->gui->xtext);
+	xtext_buffer *buf = xtext->buffer;
+
+	if (!gtk_xtext_search (xtext, buf->search_text,
+		(buf->search_flags & (case_match | follow | regexp)), NULL))
+	{
+		fe_message (_("Search hit end, not found."), FE_MSG_ERROR);
+	}
+}
+
+static void
+menu_search_prev ()
+{
+	GtkXText *xtext = GTK_XTEXT (current_sess->gui->xtext);
+	xtext_buffer *buf = xtext->buffer;
+
+	if (!gtk_xtext_search(xtext, buf->search_text,
+		(buf->search_flags & (case_match | follow | regexp) | backward), NULL))
+	{
+		fe_message (_("Search hit end, not found."), FE_MSG_ERROR);
+	}
+}
+
+static void
+menu_search_reset ()
+{
+	GtkXText *xtext = GTK_XTEXT (current_sess->gui->xtext);
+	xtext_buffer *buf = xtext->buffer;
+
+	gtk_xtext_search (xtext, "", 0, NULL);
+}
+
+static void
 menu_resetmarker (GtkWidget * wid, gpointer none)
 {
 	gtk_xtext_reset_marker_pos (GTK_XTEXT (current_sess->gui->xtext));
 }
 
 static void
+menu_copy_selection (GtkWidget * wid, gpointer none)
+{
+	gtk_xtext_copy_selection (GTK_XTEXT (current_sess->gui->xtext));
+}
+
+static void
 menu_flushbuffer (GtkWidget * wid, gpointer none)
 {
 	fe_text_clear (current_sess, 0);
@@ -1580,7 +1639,7 @@ static struct mymenu mymenu[] = {
 #endif
 	{0, 0, 0, M_SEP, 0, 0, 0},	/* 11 */
 #define DETACH_OFFSET (12)
-	{0, menu_detach, GTK_STOCK_REDO, M_MENUSTOCK, 0, 0, 1, GDK_I},	/* 12 */
+	{0, menu_detach, GTK_STOCK_REDO, M_MENUSTOCK, 0, 0, 1, GDK_i},	/* 12 */
 #define CLOSE_OFFSET (13)
 	{0, menu_close, GTK_STOCK_CLOSE, M_MENUSTOCK, 0, 0, 1, GDK_w},
 	{0, 0, 0, M_SEP, 0, 0, 0},
@@ -1633,6 +1692,10 @@ static struct mymenu mymenu[] = {
 		{N_("Userlist Popup..."), menu_ulpopup, 0, M_MENUITEM, 0, 0, 1},
 		{0, 0, 0, M_END, 0, 0, 0},		/* 53 */
 
+#if 0
+	{N_("Save Settings to Disk"), menu_saveconf, GTK_STOCK_SAVE, M_MENUSTOCK, 0, 0, 1}, /* don't use this, a /set auto-save approach will be added instead */
+#endif
+
 	{N_("_Window"), 0, 0, M_NEWMENU, 0, 0, 1},
 	{N_("Ban List..."), menu_banlist, 0, M_MENUITEM, 0, 0, 1},
 	{N_("Character Chart..."), ascii_open, 0, M_MENUITEM, 0, 0, 1},
@@ -1645,12 +1708,19 @@ static struct mymenu mymenu[] = {
 	{N_("URL Grabber..."), url_opengui, 0, M_MENUITEM, 0, 0, 1},
 	{0, 0, 0, M_SEP, 0, 0, 0},
 	{N_("Reset Marker Line"), menu_resetmarker, 0, M_MENUITEM, 0, 0, 1, GDK_m},
+	{N_("_Copy Selection"), menu_copy_selection, 0, M_MENUITEM, 0, 0, 1, GDK_C},
 	{N_("C_lear Text"), menu_flushbuffer, GTK_STOCK_CLEAR, M_MENUSTOCK, 0, 0, 1, GDK_l},
-#define SEARCH_OFFSET 67
-	{N_("Search Text..."), menu_search, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_f},
 	{N_("Save Text..."), menu_savebuffer, GTK_STOCK_SAVE, M_MENUSTOCK, 0, 0, 1},
+#define SEARCH_OFFSET 70
+	{N_("Search"), 0, GTK_STOCK_JUSTIFY_LEFT, M_MENUSUB, 0, 0, 1},
+		{N_("Search Text..."), menu_search, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_f},
+		{N_("Reset Search"), menu_search_reset, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_F},
+		{N_("Search Next"   ), menu_search_next, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_g},
+		{N_("Search Previous"   ), menu_search_prev, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_G},
+		{0, 0, 0, M_END, 0, 0, 0},
+
+	{N_("_Help"), 0, 0, M_NEWMENU, 0, 0, 1},	/* 74 */
 
-	{N_("_Help"), 0, 0, M_NEWMENU, 0, 0, 1},	/* 69 */
 	{N_("_Contents"), menu_docs, GTK_STOCK_HELP, M_MENUSTOCK, 0, 0, 1, GDK_F1},
 #if 0
 	{N_("Check for updates"), menu_update, 0, M_MENUITEM, 0, 1},
@@ -2188,7 +2258,9 @@ normalitem:
 										mymenu[i].key,
 										mymenu[i].key == GDK_F1 ? 0 :
 										mymenu[i].key == GDK_w ? close_mask :
-										GDK_CONTROL_MASK,
+										(g_ascii_isupper (mymenu[i].key)) ?
+											GDK_SHIFT_MASK | GDK_CONTROL_MASK :
+											GDK_CONTROL_MASK,
 										GTK_ACCEL_VISIBLE);
 			if (mymenu[i].callback)
 				g_signal_connect (G_OBJECT (item), "activate",
diff --git a/src/fe-gtk/palette.c b/src/fe-gtk/palette.c
index ebae92ff..104700c2 100644
--- a/src/fe-gtk/palette.c
+++ b/src/fe-gtk/palette.c
@@ -18,11 +18,14 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "fe-gtk.h"
 #include "palette.h"
 
@@ -78,8 +81,9 @@ GdkColor colors[] = {
 	{0, 0x0000, 0x0000, 0xffff}, /* 38 tab Nick Mentioned (blue) */
 	{0, 0xffff, 0x0000, 0x0000}, /* 39 tab New Message (red) */
 	{0, 0x9595, 0x9595, 0x9595}, /* 40 away user (grey) */
+	{0, 0xffff, 0x0000, 0x0000}, /* 41 spell checker color (red) */
 };
-#define MAX_COL 40
+#define MAX_COL 41
 
 
 void
diff --git a/src/fe-gtk/palette.h b/src/fe-gtk/palette.h
index c97693bb..627c6963 100644
--- a/src/fe-gtk/palette.h
+++ b/src/fe-gtk/palette.h
@@ -9,6 +9,7 @@ extern GdkColor colors[];
 #define COL_HILIGHT 38
 #define COL_NEW_MSG 39
 #define COL_AWAY 40
+#define COL_SPELL 41
 
 void palette_alloc (GtkWidget * widget);
 void palette_load (void);
diff --git a/src/fe-gtk/plugin-tray.c b/src/fe-gtk/plugin-tray.c
index e46d9570..b9f60f8d 100644
--- a/src/fe-gtk/plugin-tray.c
+++ b/src/fe-gtk/plugin-tray.c
@@ -1,7 +1,6 @@
 /* Copyright (C) 2006-2007 Peter Zelezny. */
 
 #include <string.h>
-#include <unistd.h>
 #include "../common/xchat-plugin.h"
 #include "../common/xchat.h"
 #include "../common/xchatc.h"
@@ -15,6 +14,10 @@
 #include "menu.h"
 #include <gtk/gtk.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #ifdef USE_LIBNOTIFY
 #include <libnotify/notify.h>
 #ifndef NOTIFY_CHECK_VERSION
@@ -217,10 +220,10 @@ tray_stop_flash (void)
 		nets = tray_count_networks ();
 		chans = tray_count_channels ();
 		if (nets)
-			tray_set_tipf (_("XChat: Connected to %u networks and %u channels"),
+			tray_set_tipf (_("XChat-WDK: Connected to %u networks and %u channels"),
 								nets, chans);
 		else
-			tray_set_tipf ("XChat: %s", _("Not connected."));
+			tray_set_tipf ("XChat-WDK: %s", _("Not connected."));
 	}
 
 	if (custom_icon1)
@@ -370,7 +373,7 @@ tray_toggle_visibility (gboolean force_hide)
 	/* ph may have an invalid context now */
 	xchat_set_context (ph, xchat_find_context (ph, NULL, NULL));
 
-	win = (GtkWindow *)xchat_get_info (ph, "win_ptr");
+	win = xchat_get_info (ph, "gtkwin_ptr");
 
 	tray_stop_flash ();
 	tray_reset_counts ();
@@ -505,11 +508,12 @@ tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
 	/*gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));*/
 
 	if (tray_get_window_status () == WS_HIDDEN)
-		tray_make_item (menu, _("_Restore"), tray_menu_restore_cb, NULL);
+		tray_make_item (menu, _("_Restore Window"), tray_menu_restore_cb, NULL);
 	else
-		tray_make_item (menu, _("_Hide"), tray_menu_restore_cb, NULL);
+		tray_make_item (menu, _("_Hide Window"), tray_menu_restore_cb, NULL);
 	tray_make_item (menu, NULL, tray_menu_quit_cb, NULL);
 
+#ifndef WIN32 /* somehow this is broken on win32 */
 	submenu = mg_submenu (menu, _("_Blink on"));
 	blink_item (&prefs.input_tray_chans, submenu, _("Channel Message"));
 	blink_item (&prefs.input_tray_priv, submenu, _("Private Message"));
@@ -526,6 +530,8 @@ tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
 		gtk_widget_set_sensitive (item, FALSE);
 
 	tray_make_item (menu, NULL, tray_menu_quit_cb, NULL);
+#endif
+
 	mg_create_icon_item (_("_Quit"), GTK_STOCK_QUIT, menu, tray_menu_quit_cb, NULL);
 
 	menu_add_plugin_items (menu, "\x5$TRAY", NULL);
@@ -551,8 +557,12 @@ tray_init (void)
 	sticon = gtk_status_icon_new_from_pixbuf (ICON_NORMAL);
 	if (!sticon)
 		return;
+
+#ifndef WIN32
 	g_signal_connect (G_OBJECT (sticon), "popup-menu",
 							G_CALLBACK (tray_menu_cb), sticon);
+#endif
+
 	g_signal_connect (G_OBJECT (sticon), "activate",
 							G_CALLBACK (tray_menu_restore_cb), NULL);
 }
@@ -570,15 +580,15 @@ tray_hilight_cb (char *word[], void *userdata)
 		/* FIXME: hides any previous private messages */
 		tray_hilight_count++;
 		if (tray_hilight_count == 1)
-			tray_set_tipf (_("XChat: Highlighted message from: %s (%s)"),
+			tray_set_tipf (_("XChat-WDK: Highlighted message from: %s (%s)"),
 								word[1], xchat_get_info (ph, "channel"));
 		else
-			tray_set_tipf (_("XChat: %u highlighted messages, latest from: %s (%s)"),
+			tray_set_tipf (_("XChat-WDK: %u highlighted messages, latest from: %s (%s)"),
 								tray_hilight_count, word[1], xchat_get_info (ph, "channel"));
 	}
 
 	if (prefs.input_balloon_hilight)
-		tray_set_balloonf (word[2], _("XChat: Highlighted message from: %s (%s)"),
+		tray_set_balloonf (word[2], _("XChat-WDK: Highlighted message from: %s (%s)"),
 								 word[1], xchat_get_info (ph, "channel"));
 
 	return XCHAT_EAT_NONE;
@@ -596,14 +606,14 @@ tray_message_cb (char *word[], void *userdata)
 
 		tray_pub_count++;
 		if (tray_pub_count == 1)
-			tray_set_tipf (_("XChat: New public message from: %s (%s)"),
+			tray_set_tipf (_("XChat-WDK: New public message from: %s (%s)"),
 								word[1], xchat_get_info (ph, "channel"));
 		else
-			tray_set_tipf (_("XChat: %u new public messages."), tray_pub_count);
+			tray_set_tipf (_("XChat-WDK: %u new public messages."), tray_pub_count);
 	}
 
 	if (prefs.input_balloon_chans)
-		tray_set_balloonf (word[2], _("XChat: New public message from: %s (%s)"),
+		tray_set_balloonf (word[2], _("XChat-WDK: New public message from: %s (%s)"),
 								 word[1], xchat_get_info (ph, "channel"));
 
 	return XCHAT_EAT_NONE;
@@ -625,14 +635,14 @@ tray_priv (char *from, char *text)
 
 	tray_priv_count++;
 	if (tray_priv_count == 1)
-		tray_set_tipf (_("XChat: Private message from: %s (%s)"),
+		tray_set_tipf (_("XChat-WDK: Private message from: %s (%s)"),
 							from, network);
 	else
-		tray_set_tipf (_("XChat: %u private messages, latest from: %s (%s)"),
+		tray_set_tipf (_("XChat-WDK: %u private messages, latest from: %s (%s)"),
 							tray_priv_count, from, network);
 
 	if (prefs.input_balloon_priv)
-		tray_set_balloonf (text, _("XChat: Private message from: %s (%s)"),
+		tray_set_balloonf (text, _("XChat-WDK: Private message from: %s (%s)"),
 								 from, network);
 }
 
@@ -678,15 +688,15 @@ tray_dcc_cb (char *word[], void *userdata)
 
 		tray_file_count++;
 		if (tray_file_count == 1)
-			tray_set_tipf (_("XChat: File offer from: %s (%s)"),
+			tray_set_tipf (_("XChat-WDK: File offer from: %s (%s)"),
 								word[1], network);
 		else
-			tray_set_tipf (_("XChat: %u file offers, latest from: %s (%s)"),
+			tray_set_tipf (_("XChat-WDK: %u file offers, latest from: %s (%s)"),
 								tray_file_count, word[1], network);
 	}
 
 	if (prefs.input_balloon_priv)
-		tray_set_balloonf ("", _("XChat: File offer from: %s (%s)"),
+		tray_set_balloonf ("", _("XChat-WDK: File offer from: %s (%s)"),
 								word[1], network);
 
 	return XCHAT_EAT_NONE;
@@ -722,7 +732,7 @@ tray_apply_setup (void)
 	}
 	else
 	{
-		if (prefs.gui_tray)
+		if (prefs.gui_tray && !xtray_mode ())
 			tray_init ();
 	}
 }
@@ -754,7 +764,7 @@ tray_plugin_init (xchat_plugin *plugin_handle, char **plugin_name,
 
 	xchat_hook_print (ph, "Focus Window", -1, tray_focus_cb, NULL);
 
-	if (prefs.gui_tray)
+	if (prefs.gui_tray && !xtray_mode ())
 		tray_init ();
 
 	return 1;       /* return 1 for success */
diff --git a/src/fe-gtk/plugingui.c b/src/fe-gtk/plugingui.c
index de59e649..71d2f02e 100644
--- a/src/fe-gtk/plugingui.c
+++ b/src/fe-gtk/plugingui.c
@@ -146,8 +146,13 @@ plugingui_load_cb (session *sess, char *file)
 void
 plugingui_load (void)
 {
-	gtkutil_file_req (_("Select a Plugin or Script to load"), plugingui_load_cb,
-							current_sess, NULL, FRF_ADDFOLDER);
+	gtkutil_file_req (_("Select a Plugin or Script to load"), plugingui_load_cb, current_sess,
+#ifdef WIN32
+							"Plugins and Scripts\0*.dll;*.lua;*.pl;*.py;*.tcl\0"
+							"All files\0*.*\0\0", 0);
+#else
+							NULL, FRF_ADDFOLDER);
+#endif
 }
 
 static void
diff --git a/src/fe-gtk/rawlog.c b/src/fe-gtk/rawlog.c
index 56ca0510..593468c1 100644
--- a/src/fe-gtk/rawlog.c
+++ b/src/fe-gtk/rawlog.c
@@ -19,9 +19,12 @@
 #include <stdio.h>
 #include <string.h>
 #include <fcntl.h>
-#include <unistd.h>
 #include <stdlib.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "fe-gtk.h"
 
 #include <gtk/gtkbutton.h>
diff --git a/src/fe-gtk/search.c b/src/fe-gtk/search.c
index d62e79c7..30ef266f 100644
--- a/src/fe-gtk/search.c
+++ b/src/fe-gtk/search.c
@@ -42,15 +42,20 @@
 #include "xtext.h"
 #include "maingui.h"
 
-
-static textentry *last;	/* our last search pos */
-static int case_match = 0;
-static int search_backward = 0;
-
+GtkWidget *searchwin;
 
 static void
 search_search (session * sess, const gchar *text)
 {
+	gtk_xtext_search_flags flags;
+	textentry *last;
+	GError *err = NULL;
+
+	flags = ((prefs.text_search_case_match == 1? case_match: 0) |
+				(prefs.text_search_backward == 1? backward: 0) |
+				(prefs.text_search_highlight_all == 1? highlight: 0) |
+				(prefs.text_search_follow == 1? follow: 0) |
+				(prefs.text_search_regexp == 1? regexp: 0));
 	if (!is_session (sess))
 	{
 		fe_message (_("The window you opened this Search "
@@ -58,10 +63,20 @@ search_search (session * sess, const gchar *text)
 		return;
 	}
 
-	last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text,
-									 last, case_match, search_backward);
-	if (!last)
+	last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text, flags, &err);
+	if (text == NULL || text[0] == 0)
+	{
+		return;
+	}
+	if (err)
+	{
+		fe_message (_(err->message), FE_MSG_ERROR);
+		g_error_free (err);
+	}
+	else if (!last)
+	{
 		fe_message (_("Search hit end, not found."), FE_MSG_ERROR);
+	}
 }
 
 static void
@@ -79,6 +94,23 @@ static void
 search_close_cb (GtkWidget * button, GtkWidget * win)
 {
 	gtk_widget_destroy (win);
+	searchwin = NULL;
+}
+
+static void
+search_reset_cb (GtkWidget * button, session * sess)
+{
+	search_search (sess, "");
+	if (searchwin)
+	{
+		search_close_cb (button, searchwin);
+	}
+}
+
+static void
+search_cleanup_cb (GtkWidget * button, GtkWidget * win)
+{
+	searchwin = NULL;
 }
 
 static void
@@ -98,13 +130,26 @@ search_key_cb (GtkWidget * window, GdkEventKey * key, gpointer userdata)
 static void
 search_caseign_cb (GtkToggleButton * but, session * sess)
 {
-	case_match = (but->active)? 1: 0;
+	prefs.text_search_case_match = (but->active)? 1: 0;
 }
 
 static void
 search_dirbwd_cb (GtkToggleButton * but, session * sess)
 {
-	search_backward = (but->active)? 1: 0;
+	prefs.text_search_backward = (but->active)? 1: 0;
+}
+
+static void
+search_regexp_cb (GtkToggleButton * but, session * sess)
+{
+	prefs.text_search_regexp = (but->active)? 1: 0;
+}
+
+static void
+search_highlight_cb (GtkToggleButton * but, session * sess)
+{
+	prefs.text_search_highlight_all = (but->active)? 1: 0;
+	search_search (sess, NULL);
 }
 
 void
@@ -112,18 +157,21 @@ search_open (session * sess)
 {
 	GtkWidget *win, *hbox, *vbox, *entry, *wid;
 
-	last = NULL;
+	if (searchwin)
+	{
+		gtk_widget_destroy (searchwin);
+		searchwin = NULL;
+	}
 	win = mg_create_generic_tab ("search", _("XChat: Search"), TRUE, FALSE,
-								 NULL, NULL, 0, 0, &vbox, 0);
+								 search_cleanup_cb, NULL, 0, 0, &vbox, 0);
 	gtk_container_set_border_width (GTK_CONTAINER (win), 12);
 	gtk_box_set_spacing (GTK_BOX (vbox), 4);
 
+	/* First line:  _____________________   _Find */
 	hbox = gtk_hbox_new (0, 10);
 	gtk_container_add (GTK_CONTAINER (vbox), hbox);
 	gtk_widget_show (hbox);
 
-	gtkutil_label_new (_("Find:"), hbox);
-
 	entry = gtk_entry_new ();
 	g_signal_connect (G_OBJECT (entry), "activate",
 							G_CALLBACK (search_entry_cb), sess);
@@ -131,29 +179,61 @@ search_open (session * sess)
 	gtk_widget_show (entry);
 	gtk_widget_grab_focus (entry);
 
+	wid = gtk_hbutton_box_new ();
+	gtk_container_add (GTK_CONTAINER (hbox), wid);
+	gtk_widget_show (wid);
+	wid = gtkutil_button (wid, GTK_STOCK_FIND, 0, search_find_cb, sess,
+								 _("_Find"));
+	g_object_set_data (G_OBJECT (wid), "e", entry);
+
+	/* Second line:  X Match case */
 	wid = gtk_check_button_new_with_mnemonic (_("_Match case"));
-	GTK_TOGGLE_BUTTON (wid)->active = case_match;
+	GTK_TOGGLE_BUTTON (wid)->active = prefs.text_search_case_match;
 	g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_caseign_cb), sess);
 	gtk_container_add (GTK_CONTAINER (vbox), wid);
+	add_tip (wid, "Perform a case-sensitive search.");
 	gtk_widget_show (wid);
 
+	/* Third line:  X Search backwards */
 	wid = gtk_check_button_new_with_mnemonic (_("Search _backwards"));
-	GTK_TOGGLE_BUTTON (wid)->active = search_backward;
+	GTK_TOGGLE_BUTTON (wid)->active = prefs.text_search_backward;
 	g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_dirbwd_cb), sess);
 	gtk_container_add (GTK_CONTAINER (vbox), wid);
+	add_tip (wid, "Search from the newest text line to the oldest.");
+	gtk_widget_show (wid);
+
+	/* Fourth line:  X Highlight all */
+	wid = gtk_check_button_new_with_mnemonic (_("_Highlight all"));
+	GTK_TOGGLE_BUTTON (wid)->active = prefs.text_search_highlight_all;
+	g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_highlight_cb), sess);
+	gtk_container_add (GTK_CONTAINER (vbox), wid);
+	add_tip (wid, "Highlight all occurrences, and underline the current occurrence.");
 	gtk_widget_show (wid);
 
+	/* Fifth line:  X Regular expression */
+	wid = gtk_check_button_new_with_mnemonic (_("R_egular expression"));
+	GTK_TOGGLE_BUTTON (wid)->active = prefs.text_search_regexp;
+	g_signal_connect (G_OBJECT (wid), "toggled", G_CALLBACK (search_regexp_cb), sess);
+	gtk_container_add (GTK_CONTAINER (vbox), wid);
+	add_tip (wid, "Regard search string as a regular expression.");
+	gtk_widget_show (wid);
+
+	/* Sixth line:  _Close    Close and _Reset */
 	hbox = gtk_hbutton_box_new ();
 	gtk_box_pack_start (GTK_BOX (vbox), hbox, 0, 0, 4);
 	gtk_widget_show (hbox);
 
-	gtkutil_button (hbox, GTK_STOCK_CLOSE, 0, search_close_cb, win,
+	wid = gtkutil_button (hbox, GTK_STOCK_CLOSE, 0, search_close_cb, win,
 						_("_Close"));
-	wid = gtkutil_button (hbox, GTK_STOCK_FIND, 0, search_find_cb, sess,
-								_("_Find"));
-	g_object_set_data (G_OBJECT (wid), "e", entry);
+	add_tip (wid, "Close this box, but continue searching new lines.");
+	wid = gtkutil_button (hbox, "gtk-reset", 0, search_reset_cb, sess,
+						_("Close and _Reset"));
+	add_tip (wid, "Close this box, reset highlighted search items, and stop searching new lines.");
 
-	g_signal_connect (G_OBJECT (win), "key-press-event", G_CALLBACK (search_key_cb), win);
+	/* Add recognition of the ESC key to close the box */
+	g_signal_connect (G_OBJECT (win), "key_press_event", G_CALLBACK (search_key_cb), win);
 
+	/* That's all, folks */
+	searchwin = win;
 	gtk_widget_show (win);
 }
diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c
index 2ac0e6c9..96feb5f4 100644
--- a/src/fe-gtk/servlistgui.c
+++ b/src/fe-gtk/servlistgui.c
@@ -61,7 +61,7 @@ static GtkWidget *entry_nick1;
 static GtkWidget *entry_nick2;
 static GtkWidget *entry_nick3;
 static GtkWidget *entry_guser;
-static GtkWidget *entry_greal;
+/* static GtkWidget *entry_greal; */
 
 /* edit area */
 static GtkWidget *edit_win;
@@ -649,8 +649,8 @@ servlist_savegui (void)
 	if (GTK_ENTRY (entry_guser)->text[0] == 0)
 		return 1;
 
-	if (GTK_ENTRY (entry_greal)->text[0] == 0)
-		return 1;
+	/* if (GTK_ENTRY (entry_greal)->text[0] == 0)
+		return 1; */
 
 	strcpy (prefs.nick1, GTK_ENTRY (entry_nick1)->text);
 	strcpy (prefs.nick2, GTK_ENTRY (entry_nick2)->text);
@@ -659,7 +659,7 @@ servlist_savegui (void)
 	sp = strchr (prefs.username, ' ');
 	if (sp)
 		sp[0] = 0;	/* spaces will break the login */
-	strcpy (prefs.realname, GTK_ENTRY (entry_greal)->text);
+	/* strcpy (prefs.realname, GTK_ENTRY (entry_greal)->text); */
 	servlist_save ();
 
 	return 0;
@@ -1624,12 +1624,12 @@ servlist_open_networks (void)
 	GtkWidget *label4;
 	GtkWidget *label5;
 	GtkWidget *label6;
-	GtkWidget *label7;
+	/* GtkWidget *label7; */
 	GtkWidget *entry1;
 	GtkWidget *entry2;
 	GtkWidget *entry3;
 	GtkWidget *entry4;
-	GtkWidget *entry5;
+	/* GtkWidget *entry5; */
 	GtkWidget *vbox2;
 	GtkWidget *label1;
 	GtkWidget *table4;
@@ -1703,12 +1703,12 @@ servlist_open_networks (void)
 							(GtkAttachOptions) (0), 0, 0);
 	gtk_misc_set_alignment (GTK_MISC (label6), 0, 0.5);
 
-	label7 = gtk_label_new_with_mnemonic (_("Rea_l name:"));
+	/* label7 = gtk_label_new_with_mnemonic (_("Rea_l name:"));
 	gtk_widget_show (label7);
 	gtk_table_attach (GTK_TABLE (table1), label7, 0, 1, 4, 5,
 							(GtkAttachOptions) (GTK_FILL),
 							(GtkAttachOptions) (0), 0, 0);
-	gtk_misc_set_alignment (GTK_MISC (label7), 0, 0.5);
+	gtk_misc_set_alignment (GTK_MISC (label7), 0, 0.5);*/
 
 	entry_nick1 = entry1 = gtk_entry_new ();
 	gtk_entry_set_text (GTK_ENTRY (entry1), prefs.nick1);
@@ -1738,12 +1738,12 @@ servlist_open_networks (void)
 							(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
 							(GtkAttachOptions) (0), 0, 0);
 
-	entry_greal = entry5 = gtk_entry_new ();
+	/* entry_greal = entry5 = gtk_entry_new ();
 	gtk_entry_set_text (GTK_ENTRY (entry5), prefs.realname);
 	gtk_widget_show (entry5);
 	gtk_table_attach (GTK_TABLE (table1), entry5, 1, 2, 4, 5,
 							(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
-							(GtkAttachOptions) (0), 0, 0);
+							(GtkAttachOptions) (0), 0, 0); */
 
 	vbox2 = gtk_vbox_new (FALSE, 0);
 	gtk_widget_show (vbox2);
@@ -1882,7 +1882,7 @@ servlist_open_networks (void)
 
 	gtk_label_set_mnemonic_widget (GTK_LABEL (label3), entry1);
 	gtk_label_set_mnemonic_widget (GTK_LABEL (label6), entry4);
-	gtk_label_set_mnemonic_widget (GTK_LABEL (label7), entry5);
+	/* gtk_label_set_mnemonic_widget (GTK_LABEL (label7), entry5); */
 
 	gtk_widget_grab_focus (networks_tree);
 	gtk_widget_grab_default (button_close);
diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c
index 517e0944..cec06b25 100644
--- a/src/fe-gtk/setup.c
+++ b/src/fe-gtk/setup.c
@@ -102,7 +102,12 @@ typedef struct
 static const setting textbox_settings[] =
 {
 	{ST_HEADER,	N_("Text Box Appearance"),0,0,0},
+#ifdef WIN32
+	{ST_EFONT,  N_("Main font:"), P_OFFSETNL(font_main), 0, 0, sizeof prefs.font_main},
+	{ST_ENTRY,  N_("Alternative fonts:"), P_OFFSETNL(font_alternative), "Separate multiple entries with commas without spaces before or after.", 0, sizeof prefs.font_alternative},
+#else
 	{ST_EFONT,  N_("Font:"), P_OFFSETNL(font_normal), 0, 0, sizeof prefs.font_normal},
+#endif
 	{ST_EFILE,  N_("Background image:"), P_OFFSETNL(background), 0, 0, sizeof prefs.background},
 	{ST_NUMBER,	N_("Scrollback lines:"), P_OFFINTNL(max_lines),0,0,100000},
 	{ST_TOGGLE, N_("Colored nick names"), P_OFFINTNL(colorednicks),
@@ -120,7 +125,24 @@ static const setting textbox_settings[] =
 	{ST_HEADER,	N_("Time Stamps"),0,0,0},
 	{ST_TOGGLE, N_("Enable time stamps"), P_OFFINTNL(timestamp),0,0,2},
 	{ST_ENTRY,  N_("Time stamp format:"), P_OFFSETNL(stamp_format),
-					N_("See strftime manpage for details."),0,sizeof prefs.stamp_format},
+#ifdef WIN32
+					N_("See the strftime MSDN article for details."),0,sizeof prefs.stamp_format},
+#else
+					N_("See the strftime manpage for details."),0,sizeof prefs.stamp_format},
+#endif
+
+	{ST_HEADER,	N_("Auto-Copy Behavior"),0,0,0},
+	{ST_TOGGLE, N_("Automatically copy selected text"), P_OFFINTNL(autocopy_text),
+					N_("Copy selected text to clipboard when left mouse button is released. "
+						"Otherwise, CONTROL-SHIFT-C will copy the "
+						"selected text to the clipboard."), 0, 0},
+	{ST_TOGGLE, N_("Automatically include time stamps"), P_OFFINTNL(autocopy_stamp),
+					N_("Automatically include time stamps in copied lines of text. Otherwise, "
+						"include time stamps if the SHIFT key is held down while selecting."), 0, 0},
+	{ST_TOGGLE, N_("Automatically include color information"), P_OFFINTNL(autocopy_color),
+					N_("Automatically include color information in copied lines of text.  "
+						"Otherwise, include color information if the CONTROL key is held down "
+						"while selecting."), 0, 0},
 
 	{ST_END, 0, 0, 0, 0, 0}
 };
@@ -138,6 +160,12 @@ static const setting inputbox_settings[] =
 	{ST_TOGGLE, N_("Use the Text box font and colors"), P_OFFINTNL(style_inputbox),0,0,0},
 #if defined(USE_GTKSPELL) || defined(USE_LIBSEXY)
 	{ST_TOGGLE, N_("Spell checking"), P_OFFINTNL(gui_input_spell),0,0,0},
+	{ST_ENTRY,	N_("Dictionaries to use:"), P_OFFSETNL(spell_langs),0,0,sizeof prefs.spell_langs},
+#ifdef WIN32
+	{ST_LABEL,	N_("Use language codes (as in \"share\\myspell\\dicts\").\nSeparate multiple entries with commas.")},
+#else
+	{ST_LABEL,	N_("Use language codes. Separate multiple entries with commas.")},
+#endif
 #endif
 
 	{ST_HEADER, N_("Nick Completion"),0,0,0},
@@ -348,6 +376,24 @@ static const setting alert_settings[] =
 	{ST_END, 0, 0, 0, 0, 0}
 };
 
+static const setting alert_settings_xtray[] =
+{
+	{ST_HEADER,	N_("Alerts"),0,0,0},
+
+	{ST_ALERTHEAD},
+	{ST_3OGGLE, N_("Blink task bar on:"), 0, 0, (void *)taskbarlist, 0},
+	{ST_3OGGLE, N_("Make a beep sound on:"), 0, 0, (void *)beeplist, 0},
+
+	{ST_HEADER,	N_("Highlighted Messages"),0,0,0},
+	{ST_LABEL,	N_("Highlighted messages are ones where your nickname is mentioned, but also:"), 0, 0, 0, 1},
+
+	{ST_ENTRY,	N_("Extra words to highlight:"), P_OFFSETNL(irc_extra_hilight), 0, 0, sizeof prefs.irc_extra_hilight},
+	{ST_ENTRY,	N_("Nick names not to highlight:"), P_OFFSETNL(irc_no_hilight), 0, 0, sizeof prefs.irc_no_hilight},
+	{ST_ENTRY,	N_("Nick names to always highlight:"), P_OFFSETNL(irc_nick_hilight), 0, 0, sizeof prefs.irc_nick_hilight},
+	{ST_LABEL,	N_("Separate multiple words with commas.\nWildcards are accepted.")},
+	{ST_END, 0, 0, 0, 0, 0}
+};
+
 static const setting general_settings[] =
 {
 	{ST_HEADER,	N_("Default Messages"),0,0,0},
@@ -363,7 +409,6 @@ static const setting general_settings[] =
 	{ST_END, 0, 0, 0, 0, 0}
 };
 
-#if 0
 static const setting advanced_settings[] =
 {
 	{ST_HEADER,	N_("Advanced Settings"),0,0,0},
@@ -371,6 +416,25 @@ static const setting advanced_settings[] =
 	{ST_TOGGLE,	N_("Display MODEs in raw form"), P_OFFINTNL(raw_modes), 0, 0, 0},
 	{ST_TOGGLE,	N_("Whois on notify"), P_OFFINTNL(whois_on_notifyonline), N_("Sends a /WHOIS when a user comes online in your notify list"), 0, 0},
 	{ST_TOGGLE,	N_("Hide join and part messages"), P_OFFINTNL(confmode), N_("Hide channel join/part messages by default"), 0, 0},
+	{ST_ENTRY,	N_("License Text:"), P_OFFSETNL(gui_license), 0, 0, sizeof prefs.gui_license},
+	{ST_HEADER,	N_("Auto Open DCC Windows"),0,0,0},
+	{ST_TOGGLE, N_("Send window"), P_OFFINTNL(autoopendccsendwindow), 0, 0, 0},
+	{ST_TOGGLE, N_("Receive window"), P_OFFINTNL(autoopendccrecvwindow), 0, 0, 0},
+	{ST_TOGGLE, N_("Chat window"), P_OFFINTNL(autoopendccchatwindow), 0, 0, 0},
+
+	{ST_END, 0, 0, 0, 0, 0}
+};
+
+#ifdef WIN32
+static const setting advanced_settings_oneinstance[] =
+{
+	{ST_HEADER,	N_("Advanced Settings"),0,0,0},
+	{ST_NUMBER,	N_("Auto reconnect delay:"), P_OFFINTNL(recon_delay), 0, 0, 9999},
+	{ST_TOGGLE,	N_("Display MODEs in raw form"), P_OFFINTNL(raw_modes), 0, 0, 0},
+	{ST_TOGGLE,	N_("Whois on notify"), P_OFFINTNL(whois_on_notifyonline), N_("Sends a /WHOIS when a user comes online in your notify list"), 0, 0},
+	{ST_TOGGLE,	N_("Hide join and part messages"), P_OFFINTNL(confmode), N_("Hide channel join/part messages by default"), 0, 0},
+	{ST_TOGGLE,	N_("Allow only one instance of XChat to run"), P_OFFINTNL(gui_one_instance), 0, 0, 0},
+	{ST_ENTRY,	N_("License Text:"), P_OFFSETNL(gui_license), 0, 0, sizeof prefs.gui_license},
 	{ST_HEADER,	N_("Auto Open DCC Windows"),0,0,0},
 	{ST_TOGGLE, N_("Send window"), P_OFFINTNL(autoopendccsendwindow), 0, 0, 0},
 	{ST_TOGGLE, N_("Receive window"), P_OFFINTNL(autoopendccrecvwindow), 0, 0, 0},
@@ -391,7 +455,11 @@ static const setting logging_settings[] =
 	{ST_HEADER,	N_("Time Stamps"),0,0,0},
 	{ST_TOGGLE,	N_("Insert timestamps in logs"), P_OFFINTNL(timestamp_logs), 0, 0, 2},
 	{ST_ENTRY,	N_("Log timestamp format:"), P_OFFSETNL(timestamp_log_format), 0, 0, sizeof prefs.timestamp_log_format},
-	{ST_LABEL,	N_("See strftime manpage for details.")},
+#ifdef WIN32
+	{ST_LABEL,	N_("See the strftime MSDN article for details.")},
+#else
+	{ST_LABEL,	N_("See the strftime manpage for details.")},
+#endif
 
 	{ST_END, 0, 0, 0, 0, 0}
 };
@@ -1328,6 +1396,9 @@ setup_create_color_page (void)
 	setup_create_other_color (_("New message:"), COL_NEW_MSG, 10, tab);
 	setup_create_other_colorR (_("Away user:"), COL_AWAY, 10, tab);
 	setup_create_other_color (_("Highlight:"), COL_HILIGHT, 11, tab);
+#if defined(USE_GTKSPELL) || defined(USE_LIBSEXY)
+	setup_create_other_colorR (_("Spell checker:"), COL_SPELL, 11, tab);
+#endif
 
 	return box;
 }
@@ -1711,7 +1782,7 @@ static const char *const cata[] =
 		N_("General"),
 		N_("Logging"),
 		N_("Sound"),
-/*		N_("Advanced"),*/
+		N_("Advanced"),
 		NULL,
 	N_("Network"),
 		N_("Network setup"),
@@ -1732,10 +1803,33 @@ setup_create_pages (GtkWidget *box)
 	setup_add_page (cata[3], book, setup_create_page (userlist_settings));
 	setup_add_page (cata[4], book, setup_create_page (tabs_settings));
 	setup_add_page (cata[5], book, setup_create_color_page ());
-	setup_add_page (cata[8], book, setup_create_page (alert_settings));
+
+	if (xtray_mode ())
+	{
+		setup_add_page (cata[8], book, setup_create_page (alert_settings_xtray));
+	}
+	else
+	{
+		setup_add_page (cata[8], book, setup_create_page (alert_settings));
+	}
+
 	setup_add_page (cata[9], book, setup_create_page (general_settings));
 	setup_add_page (cata[10], book, setup_create_page (logging_settings));
 	setup_add_page (cata[11], book, setup_create_sound_page ());
+
+#ifdef WIN32
+	if (portable_mode ())
+	{
+		setup_add_page (cata[12], book, setup_create_page (advanced_settings));
+	}
+	else
+	{
+		setup_add_page (cata[12], book, setup_create_page (advanced_settings_oneinstance));
+	}
+#else
+	setup_add_page (cata[12], book, setup_create_page (advanced_settings));
+#endif
+
 	setup_add_page (cata[14], book, setup_create_page (network_settings));
 	setup_add_page (cata[15], book, setup_create_page (filexfer_settings));
 
@@ -1971,6 +2065,12 @@ setup_apply_real (int new_pix, int do_ulist, int do_layout)
 static void
 setup_apply (struct xchatprefs *pr)
 {
+#ifdef WIN32
+	PangoFontDescription *old_desc;
+	PangoFontDescription *new_desc;
+	char buffer[4 * FONTNAMELEN + 1];
+	time_t rawtime;
+#endif
 	int new_pix = FALSE;
 	int noapply = FALSE;
 	int do_ulist = FALSE;
@@ -2014,6 +2114,30 @@ setup_apply (struct xchatprefs *pr)
 
 	memcpy (&prefs, pr, sizeof (prefs));
 
+#ifdef WIN32
+	/* merge font_main and font_alternative into font_normal */
+	old_desc = pango_font_description_from_string (prefs.font_main);
+	sprintf (buffer, "%s,%s", pango_font_description_get_family (old_desc), prefs.font_alternative);
+	new_desc = pango_font_description_from_string (buffer);
+	pango_font_description_set_weight (new_desc, pango_font_description_get_weight (old_desc));
+	pango_font_description_set_style (new_desc, pango_font_description_get_style (old_desc));
+	pango_font_description_set_size (new_desc, pango_font_description_get_size (old_desc));
+	sprintf (prefs.font_normal, "%s", pango_font_description_to_string (new_desc));
+
+	/* FIXME this is not required after pango_font_description_from_string()
+	g_free (old_desc);
+	g_free (new_desc);
+	*/
+
+	/* workaround for strftime differences between POSIX and MSVC */
+	time (&rawtime);
+
+	if (!strftime (buffer, sizeof (buffer), prefs.stamp_format, localtime (&rawtime)) || !strftime (buffer, sizeof (buffer), prefs.timestamp_log_format, localtime (&rawtime)))
+	{
+		fe_message (_("Invalid time stamp format! See the strftime MSDN article for details."), FE_MSG_ERROR);
+	}
+#endif
+
 	setup_apply_real (new_pix, do_ulist, do_layout);
 
 	if (noapply)
diff --git a/src/fe-gtk/sexy-iso-codes.c b/src/fe-gtk/sexy-iso-codes.c
new file mode 100644
index 00000000..4b637c9f
--- /dev/null
+++ b/src/fe-gtk/sexy-iso-codes.c
@@ -0,0 +1,305 @@
+/*
+ *  Copyright (C) 2005 Nathan Fredrickson
+ *  Borrowed from Galeon, renamed, and simplified to only use iso-codes with no
+ *  fallback method.
+ *
+ *  Copyright (C) 2004 Christian Persch
+ *  Copyright (C) 2004 Crispin Flowerday
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "../../config.h"
+
+#include "sexy-iso-codes.h"
+
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+#include <libxml/xmlreader.h>
+
+static GHashTable *iso_639_table = NULL;
+static GHashTable *iso_3166_table = NULL;
+
+#define ISO_639_DOMAIN	"iso_639"
+#define ISO_3166_DOMAIN	"iso_3166"
+
+#ifdef HAVE_ISO_CODES
+
+#define ISOCODESLOCALEDIR "/share/locale"
+
+static void
+read_iso_639_entry (xmlTextReaderPtr reader,
+		    GHashTable *table)
+{
+	xmlChar *code, *name;
+
+	code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_1_code");
+	name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name");
+
+	/* Get iso-639-2 code */
+	if (code == NULL || code[0] == '\0')
+	{
+		xmlFree (code);
+		/* FIXME: use the 2T or 2B code? */
+		code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_2T_code");
+	}
+
+	if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0')
+	{
+		g_hash_table_insert (table, code, name);
+	}
+	else
+	{
+		xmlFree (code);
+		xmlFree (name);
+	}
+}
+
+static void
+read_iso_3166_entry (xmlTextReaderPtr reader,
+		     GHashTable *table)
+{
+	xmlChar *code, *name;
+
+	code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "alpha_2_code");
+	name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name");
+
+	if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0')
+	{
+		char *lcode;
+
+		lcode = g_ascii_strdown ((char *) code, -1);
+		xmlFree (code);
+
+		g_hash_table_insert (table, lcode, name);
+	}
+	else
+	{
+		xmlFree (code);
+		xmlFree (name);
+	}
+
+}
+
+typedef enum
+{
+	STATE_START,
+	STATE_STOP,
+	STATE_ENTRIES,
+} ParserState;
+
+static gboolean
+load_iso_entries (int iso,
+		  GFunc read_entry_func,
+		  gpointer user_data)
+{
+	xmlTextReaderPtr reader;
+	ParserState state = STATE_START;
+	xmlChar iso_entries[32], iso_entry[32];
+	char *filename;
+	int ret = -1;
+
+#ifdef WIN32
+	filename = g_strdup_printf (".\\share\\xml\\iso-codes\\iso_%d.xml", iso);
+#else
+	filename = g_strdup_printf ("/usr/share/xml/iso-codes/iso_%d.xml", iso);
+#endif
+	reader = xmlNewTextReaderFilename (filename);
+	if (reader == NULL) goto out;
+
+	xmlStrPrintf (iso_entries, sizeof (iso_entries),
+				  (xmlChar *)"iso_%d_entries", iso);
+	xmlStrPrintf (iso_entry, sizeof (iso_entry),
+				  (xmlChar *)"iso_%d_entry", iso);
+
+	ret = xmlTextReaderRead (reader);
+
+	while (ret == 1)
+	{
+		const xmlChar *tag;
+		xmlReaderTypes type;
+
+		tag = xmlTextReaderConstName (reader);
+		type = xmlTextReaderNodeType (reader);
+
+		if (state == STATE_ENTRIES &&
+		    type == XML_READER_TYPE_ELEMENT &&
+		    xmlStrEqual (tag, iso_entry))
+		{
+			read_entry_func (reader, user_data);
+		}
+		else if (state == STATE_START &&
+			 type == XML_READER_TYPE_ELEMENT &&
+			 xmlStrEqual (tag, iso_entries))
+		{
+			state = STATE_ENTRIES;
+		}
+		else if (state == STATE_ENTRIES &&
+			 type == XML_READER_TYPE_END_ELEMENT &&
+			 xmlStrEqual (tag, iso_entries))
+		{
+			state = STATE_STOP;
+		}
+		else if (type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE ||
+			 type == XML_READER_TYPE_WHITESPACE ||
+			 type == XML_READER_TYPE_TEXT ||
+			 type == XML_READER_TYPE_COMMENT)
+		{
+			/* eat it */
+		}
+		else
+		{
+			/* ignore it */
+		}
+
+		ret = xmlTextReaderRead (reader);
+	}
+
+	xmlFreeTextReader (reader);
+
+out:
+	if (ret < 0 || state != STATE_STOP)
+	{
+		/* This is not critical, we will fallback to our own code */
+		g_free (filename);
+		return FALSE;
+	}
+
+	g_free (filename);
+
+	return TRUE;
+}
+
+#endif /* HAVE_ISO_CODES */
+
+
+static void
+ensure_iso_codes_initialised (void)
+{
+	static gboolean initialised = FALSE;
+
+	if (initialised == TRUE)
+	{
+		return;
+	}
+	initialised = TRUE;
+
+#if defined (ENABLE_NLS) && defined (HAVE_ISO_CODES)
+	bindtextdomain (ISO_639_DOMAIN, ISOCODESLOCALEDIR);
+	bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8");
+
+	bindtextdomain(ISO_3166_DOMAIN, ISOCODESLOCALEDIR);
+	bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8");
+#endif
+
+	iso_639_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+					       (GDestroyNotify) xmlFree,
+					       (GDestroyNotify) xmlFree);
+
+	iso_3166_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+						(GDestroyNotify) g_free,
+						(GDestroyNotify) xmlFree);
+	
+#ifdef HAVE_ISO_CODES
+	load_iso_entries (639, (GFunc) read_iso_639_entry, iso_639_table);
+	load_iso_entries (3166, (GFunc) read_iso_3166_entry, iso_3166_table);
+#endif
+}
+
+
+static char *
+get_iso_name_for_lang_code (const char *code)
+{
+	char **str;
+	char *name = NULL;
+	const char *langname, *localename;
+	int len;
+
+	str = g_strsplit (code, "_", -1);
+
+	/* count the entries */
+	for (len = 0; str[len]; len++ ) /* empty */;
+
+	g_return_val_if_fail (len != 0, NULL);
+
+	langname = (const char *) g_hash_table_lookup (iso_639_table, str[0]);
+
+	if (len == 1 && langname != NULL)
+	{
+		name = g_strdup (dgettext (ISO_639_DOMAIN, langname));
+	}
+	else if (len == 2 && langname != NULL)
+	{
+		localename = (const char *) g_hash_table_lookup (iso_3166_table, str[1]);
+
+		if (localename != NULL)
+		{
+			/* translators: the first %s is the language name, and the
+			 * second %s is the locale name. Example:
+			 * "French (France)
+			 *
+			 * Also: The text before the "|" is context to help you decide on
+                         * the correct translation. You MUST OMIT it in the translated string.
+			 */
+			name = g_strdup_printf (Q_("language|%s (%s)"),
+						dgettext (ISO_639_DOMAIN, langname),
+						dgettext (ISO_3166_DOMAIN, localename));
+		}
+		else
+		{
+			name = g_strdup_printf (Q_("language|%s (%s)"),
+						dgettext (ISO_639_DOMAIN, langname), str[1]);
+		}
+	}
+
+	g_strfreev (str);
+
+	return name;
+}
+
+/**
+ * gtkspell_iso_codes_lookup_name_for_code:
+ * @code: A language code, e.g. en_CA
+ *
+ * Looks up a name to display to the user for a language code,
+ * this might use the iso-codes package if support was compiled
+ * in, and it is available
+ *
+ * Returns: the UTF-8 string to display to the user, or NULL if 
+ * a name for the code could not be found
+ */
+char *
+gtkspell_iso_codes_lookup_name_for_code (const char *code)
+{
+	char * lcode;
+	char * ret;
+
+	g_return_val_if_fail (code != NULL, NULL);
+
+	ensure_iso_codes_initialised ();
+
+	lcode = g_ascii_strdown (code, -1);
+
+	ret = get_iso_name_for_lang_code (lcode);
+
+	g_free (lcode);
+
+	return ret;
+}
+
diff --git a/src/fe-gtk/sexy-iso-codes.h b/src/fe-gtk/sexy-iso-codes.h
new file mode 100644
index 00000000..e5f37162
--- /dev/null
+++ b/src/fe-gtk/sexy-iso-codes.h
@@ -0,0 +1,37 @@
+/*
+ *  Copyright (C) 2005 Nathan Fredrickson
+ *  Borrowed from Galeon, renamed, and simplified to only use iso-codes with no
+ *  fallback method.
+ *
+ *  Copyright (C) 2004 Christian Persch
+ *  Copyright (C) 2004 Crispin Flowerday
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef GTKSPELL_ISO_CODES_H
+#define GTKSPELL_ISO_CODES_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+char *  gtkspell_iso_codes_lookup_name_for_code (const char *code);
+
+G_END_DECLS
+
+#endif
diff --git a/src/fe-gtk/sexy-marshal.c b/src/fe-gtk/sexy-marshal.c
new file mode 100644
index 00000000..10a629f2
--- /dev/null
+++ b/src/fe-gtk/sexy-marshal.c
@@ -0,0 +1,129 @@
+
+#include	<glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:STRING (./marshal.list:1) */
+void
+sexy_marshal_BOOLEAN__STRING (GClosure     *closure,
+                              GValue       *return_value,
+                              guint         n_param_values,
+                              const GValue *param_values,
+                              gpointer      invocation_hint,
+                              gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__STRING) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gpointer     data2);
+  register GMarshalFunc_BOOLEAN__STRING callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_BOOLEAN__STRING) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_string (param_values + 1),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* OBJECT:OBJECT,OBJECT (./marshal.list:2) */
+void
+sexy_marshal_OBJECT__OBJECT_OBJECT (GClosure     *closure,
+                                    GValue       *return_value,
+                                    guint         n_param_values,
+                                    const GValue *param_values,
+                                    gpointer      invocation_hint,
+                                    gpointer      marshal_data)
+{
+  typedef GObject* (*GMarshalFunc_OBJECT__OBJECT_OBJECT) (gpointer     data1,
+                                                          gpointer     arg_1,
+                                                          gpointer     arg_2,
+                                                          gpointer     data2);
+  register GMarshalFunc_OBJECT__OBJECT_OBJECT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  GObject* v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_OBJECT__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_object (param_values + 1),
+                       g_marshal_value_peek_object (param_values + 2),
+                       data2);
+
+  g_value_take_object (return_value, v_return);
+}
+
diff --git a/src/fe-gtk/sexy-marshal.h b/src/fe-gtk/sexy-marshal.h
new file mode 100644
index 00000000..f41eccbe
--- /dev/null
+++ b/src/fe-gtk/sexy-marshal.h
@@ -0,0 +1,28 @@
+
+#ifndef __sexy_marshal_MARSHAL_H__
+#define __sexy_marshal_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+/* BOOLEAN:STRING (./marshal.list:1) */
+extern void sexy_marshal_BOOLEAN__STRING (GClosure     *closure,
+                                          GValue       *return_value,
+                                          guint         n_param_values,
+                                          const GValue *param_values,
+                                          gpointer      invocation_hint,
+                                          gpointer      marshal_data);
+
+/* OBJECT:OBJECT,OBJECT (./marshal.list:2) */
+extern void sexy_marshal_OBJECT__OBJECT_OBJECT (GClosure     *closure,
+                                                GValue       *return_value,
+                                                guint         n_param_values,
+                                                const GValue *param_values,
+                                                gpointer      invocation_hint,
+                                                gpointer      marshal_data);
+
+G_END_DECLS
+
+#endif /* __sexy_marshal_MARSHAL_H__ */
+
diff --git a/src/fe-gtk/sexy-spell-entry.c b/src/fe-gtk/sexy-spell-entry.c
index d67ffe2d..9483f04b 100644
--- a/src/fe-gtk/sexy-spell-entry.c
+++ b/src/fe-gtk/sexy-spell-entry.c
@@ -26,10 +26,19 @@
 #include <gtk/gtk.h>
 #include "sexy-spell-entry.h"
 #include <string.h>
+#include <fcntl.h>
 #include <glib/gi18n.h>
 #include <sys/types.h>
-/*#include "gtkspell-iso-codes.h"
-#include "sexy-marshal.h"*/
+#include <sys/stat.h>
+#include "sexy-iso-codes.h"
+#include "sexy-marshal.h"
+
+#ifdef WIN32
+#include "typedef.h"
+#endif
+
+#include "../common/cfgfiles.h"
+#include "../common/xchatc.h"
 
 /*
  * Bunch of poop to make enchant into a runtime dependency rather than a
@@ -134,12 +143,19 @@ initialize_enchant ()
 	GModule *enchant;
 	gpointer funcptr;
 
+#ifdef WIN32
+	enchant = g_module_open("libenchant.dll", 0);
+#else
 	enchant = g_module_open("libenchant", 0);
+#endif
 	if (enchant == NULL)
 	{
+#ifndef WIN32
 		enchant = g_module_open("libenchant.so.1", 0);
-		if (enchant == NULL)
-			return;
+				if (enchant == NULL)
+					return;
+#endif
+		return;
 	}
 
 	have_enchant = TRUE;
@@ -207,14 +223,14 @@ sexy_spell_entry_class_init(SexySpellEntryClass *klass)
 	 * Returns: %FALSE to indicate that the word should be marked as
 	 * correct.
 	 */
-/*	signals[WORD_CHECK] = g_signal_new("word_check",
+	signals[WORD_CHECK] = g_signal_new("word_check",
 					   G_TYPE_FROM_CLASS(object_class),
 					   G_SIGNAL_RUN_LAST,
 					   G_STRUCT_OFFSET(SexySpellEntryClass, word_check),
 					   (GSignalAccumulator) spell_accumulator, NULL,
 					   sexy_marshal_BOOLEAN__STRING,
 					   G_TYPE_BOOLEAN,
-					   1, G_TYPE_STRING);*/
+					   1, G_TYPE_STRING);
 }
 
 static void
@@ -260,8 +276,42 @@ gtk_entry_find_position (GtkEntry *entry, gint x)
 static void
 insert_underline(SexySpellEntry *entry, guint start, guint end)
 {
-	PangoAttribute *ucolor = pango_attr_underline_color_new (65535, 0, 0);
-	PangoAttribute *unline = pango_attr_underline_new (PANGO_UNDERLINE_ERROR);
+	int fh, l;
+	int red, green, blue;
+	struct stat st;
+	char *cfg;
+	PangoAttribute *ucolor;
+	PangoAttribute *unline;
+
+	fh = xchat_open_file ("colors.conf", O_RDONLY, 0, 0);
+
+	if (fh != -1)
+	{
+		fstat (fh, &st);
+		cfg = malloc (st.st_size + 1);
+
+		if (cfg)
+		{
+			cfg[0] = '\0';
+			l = read (fh, cfg, st.st_size);
+			if (l >= 0)
+			{
+				cfg[l] = '\0';
+			}
+
+			cfg_get_color (cfg, "color_265", &red, &green, &blue);
+			free (cfg);
+		}
+
+		close (fh);
+	} else
+	{
+		red = 65535;
+		green = blue = 0;
+	}
+
+	ucolor = pango_attr_underline_color_new (red, green, blue);
+	unline = pango_attr_underline_new (PANGO_UNDERLINE_ERROR);
 
 	ucolor->start_index = start;
 	unline->start_index = start;
@@ -457,10 +507,6 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word)
 	if (entry->priv->dict_list == NULL)
 		return topmenu;
 
-#if 1
-	dict = (struct EnchantDict *) entry->priv->dict_list->data;
-	build_suggestion_menu(entry, topmenu, dict, word);
-#else
 	/* Suggestions */
 	if (g_slist_length(entry->priv->dict_list) == 1) {
 		dict = (struct EnchantDict *) entry->priv->dict_list->data;
@@ -489,7 +535,6 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word)
 			build_suggestion_menu(entry, menu, dict, word);
 		}
 	}
-#endif
 
 	/* Separator */
 	mi = gtk_separator_menu_item_new ();
@@ -503,11 +548,6 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word)
 
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
 
-#if 1
-	dict = (struct EnchantDict *) entry->priv->dict_list->data;
-	g_object_set_data(G_OBJECT(mi), "enchant-dict", dict);
-	g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(add_to_dictionary), entry);
-#else
 	if (g_slist_length(entry->priv->dict_list) == 1) {
 		dict = (struct EnchantDict *) entry->priv->dict_list->data;
 		g_object_set_data(G_OBJECT(mi), "enchant-dict", dict);
@@ -539,7 +579,6 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word)
 			gtk_menu_shell_append(GTK_MENU_SHELL(menu), submi);
 		}
 	}
-#endif
 
 	gtk_widget_show_all(mi);
 	gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi);
@@ -721,11 +760,7 @@ word_misspelled(SexySpellEntry *entry, int start, int end)
 
 	g_strlcpy(word, text + start, end - start + 1);
 
-#if 0
 	g_signal_emit(entry, signals[WORD_CHECK], 0, word, &ret);
-#else
-	ret = default_word_check (entry, word);
-#endif
 
 	g_free(word);
 	return ret;
@@ -921,10 +956,10 @@ void
 sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
 {
 #if GLIB_CHECK_VERSION (2, 6, 0)
-	const gchar* const *langs;
+	/*const gchar* const *langs;
 	int i;
-	gchar *lastprefix = NULL;
-	GSList *enchant_langs;
+	gchar *lastprefix = NULL;*/
+	GSList *enchant_langs, *i;
 
 	if (!have_enchant)
 		return;
@@ -933,14 +968,14 @@ sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
 		entry->priv->broker = enchant_broker_init();
 
 
-	langs = g_get_language_names ();
+	/*langs = g_get_language_names ();
 
 	if (langs == NULL)
-		return;
+		return;*/
 
 	enchant_langs = sexy_spell_entry_get_languages(entry);
 
-	for (i = 0; langs[i]; i++) {
+	/*for (i = 0; langs[i]; i++) {
 		if ((g_strncasecmp(langs[i], "C", 1) != 0) &&
 		    (strlen(langs[i]) >= 2) &&
 		    enchant_has_lang(langs[i], enchant_langs)) {
@@ -952,10 +987,19 @@ sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
 		}
 	}
 	if (lastprefix != NULL)
-		g_free(lastprefix);
+		g_free(lastprefix);*/
+
+	for (i = enchant_langs; i; i = g_slist_next (i))
+	{
+		if (strstr (prefs.spell_langs, i->data) != NULL)
+		{
+			sexy_spell_entry_activate_language_internal (entry, i->data, NULL);
+		}
+	}
 
 	g_slist_foreach(enchant_langs, (GFunc) g_free, NULL);
 	g_slist_free(enchant_langs);
+	g_slist_free (i);
 
 	/* If we don't have any languages activated, use "en" */
 	if (entry->priv->dict_list == NULL)
@@ -1083,8 +1127,8 @@ gchar *
 sexy_spell_entry_get_language_name(const SexySpellEntry *entry,
 								   const gchar *lang)
 {
-	/*if (have_enchant)
-		return gtkspell_iso_codes_lookup_name_for_code(lang);*/
+	if (have_enchant)
+		return gtkspell_iso_codes_lookup_name_for_code(lang);
 	return NULL;
 }
 
diff --git a/src/fe-gtk/typedef.h b/src/fe-gtk/typedef.h
new file mode 100644
index 00000000..b20612ea
--- /dev/null
+++ b/src/fe-gtk/typedef.h
@@ -0,0 +1,11 @@
+#ifndef SSIZE_T_DEFINED
+#ifdef ssize_t
+#undef ssize_t
+#endif
+#ifdef _WIN64
+typedef __int64          ssize_t;
+#else
+typedef _W64 int         ssize_t;
+#endif
+#define SSIZE_T_DEFINED
+#endif
diff --git a/src/fe-gtk/xchat.exe.manifest b/src/fe-gtk/xchat.exe.manifest
new file mode 100644
index 00000000..13685d98
--- /dev/null
+++ b/src/fe-gtk/xchat.exe.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity
+    name="XChat"
+    processorArchitecture="*"
+    version="1.0.0.0"
+    type="win32"
+  />
+  <description>XChat IRC client</description>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity
+        type="win32"
+        name="Microsoft.Windows.Common-Controls"
+        version="6.0.0.0"
+        processorArchitecture="*"
+        publicKeyToken="6595b64144ccf1df"
+        language="*"
+      />
+    </dependentAssembly>
+  </dependency>
+</assembly>
diff --git a/src/fe-gtk/xchat.rc b/src/fe-gtk/xchat.rc
new file mode 100644
index 00000000..28676b83
--- /dev/null
+++ b/src/fe-gtk/xchat.rc
@@ -0,0 +1,28 @@
+#include <winver.h>

+#include "../../config.h"

+#include "../../resource.h"

+

+1 24 "xchat.exe.manifest"

+

+XC_ICON ICON "../../xchat.ico" 

+

+VS_VERSION_INFO VERSIONINFO

+	FILEVERSION    COMMA_VERSION

+	PRODUCTVERSION COMMA_VERSION

+	BEGIN

+		BLOCK "StringFileInfo"

+		BEGIN

+			BLOCK "040904B0"

+			BEGIN

+				

+				VALUE "FileDescription", "XChat-WDK IRC Client"

+				VALUE "ProductName", "XChat-WDK" 

+				VALUE "ProductVersion", PACKAGE_VERSION

+				VALUE "FileVersion", PACKAGE_VERSION

+			END

+		END

+		BLOCK "VarFileInfo"

+		BEGIN

+			VALUE "Translation", 0x0409, 0x04B0

+		END

+	END

diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c
index fa9803c7..d474f148 100644
--- a/src/fe-gtk/xtext.c
+++ b/src/fe-gtk/xtext.c
@@ -42,7 +42,6 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <time.h>
-#include <unistd.h>
 #include <gtk/gtkmain.h>
 #include <gtk/gtksignal.h>
 #include <gtk/gtkselection.h>
@@ -66,6 +65,11 @@
 #include "mmx_cmod.h"
 #endif
 
+#include "../common/xchat.h"
+#include "../common/fe.h"
+#include "../common/util.h"
+#include "../common/xchatc.h"
+#include "fe-gtk.h"
 #include "xtext.h"
 
 #define charlen(str) g_utf8_skip[*(guchar *)(str)]
@@ -73,6 +77,8 @@
 #ifdef WIN32
 #include <windows.h>
 #include <gdk/gdkwin32.h>
+#else
+#include <unistd.h>
 #endif
 
 /* is delimiter */
@@ -89,6 +95,20 @@
 
 static GtkWidgetClass *parent_class = NULL;
 
+/*
+ * offsets_t is used for retaining search information.
+ * It is stored in the 'data' member of a GList,
+ * as chained from ent->marks.  It saves starting and
+ * ending+1 offset of a found occurrence.
+ */
+typedef union offsets_u {
+	struct offsets_s {
+		guint16	start;
+		guint16	end;
+	} o;
+	guint32 u;
+} offsets_t;
+
 struct textentry
 {
 	struct textentry *next;
@@ -108,6 +128,7 @@ struct textentry
 	guchar tag;
 	guchar pad1;
 	guchar pad2;	/* 32-bit align : 44 bytes total */
+	GList *marks;	/* List of found strings */
 };
 
 enum
@@ -146,11 +167,25 @@ 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);
+/* For use by gtk_xtext_strip_color() and its callers -- */
+typedef union offlen_u {
+   struct offlen_s {
+      guint16  off;
+      guint16  len;
+   } o;
+   guint32 u;
+} offlen_t;
 static unsigned char *
 gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
-							  int *newlen, int *mb_ret, int strip_hidden);
+							  int *newlen, int *mb_ret, GSList **slp, 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);
+static int gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off);
+static void gtk_xtext_search_textentry (xtext_buffer *, textentry *, int);
+static void gtk_xtext_search_textentry_del (xtext_buffer *, textentry *);
+static void gtk_xtext_search_textentry_fini (gpointer, gpointer);
+static void gtk_xtext_search_fini (xtext_buffer *);
+static gboolean gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gtk_xtext_search_flags flags, GError **perr);
 
 /* some utility functions first */
 
@@ -767,9 +802,8 @@ gtk_xtext_adjustment_set (xtext_buffer *buf, int fire_signal)
 		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_size = GTK_WIDGET (buf->xtext)->allocation.height /
+							  buf->xtext->fontsize;
 		adj->page_increment = adj->page_size;
 
 		if (adj->value > adj->upper - adj->page_size)
@@ -998,7 +1032,11 @@ gtk_xtext_realize (GtkWidget * widget)
 
 	gdk_window_set_user_data (widget->window, widget);
 
+#if GTK_CHECK_VERSION(2,24,0)
+	xtext->depth = gdk_window_get_visual (widget->window)->depth;
+#else
 	xtext->depth = gdk_drawable_get_visual (widget->window)->depth;
+#endif
 
 	val.subwindow_mode = GDK_INCLUDE_INFERIORS;
 	val.graphics_exposures = 0;
@@ -1093,6 +1131,7 @@ gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
 
 	xtext->avoid_trans = FALSE;
 
+	allocation->height = allocation->height / xtext->fontsize * xtext->fontsize;
 	widget->allocation = *allocation;
 	if (GTK_WIDGET_REALIZED (widget))
 	{
@@ -1122,18 +1161,6 @@ gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
 	}
 }
 
-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)
 {
@@ -1717,13 +1744,6 @@ gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean ren
 		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);
 
@@ -1753,10 +1773,24 @@ gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean ren
 		gtk_xtext_selection_render (xtext, ent_start, offset_start, ent_end, offset_end);
 }
 
+static int
+gtk_xtext_timeout_ms (GtkXText *xtext, int pixes)
+{
+	int apixes = abs(pixes);
+
+	if (apixes < 6) return 100;
+	if (apixes < 12) return 50;
+	if (apixes < 20) return 20;
+	return 10;
+}
+
 static gint
 gtk_xtext_scrolldown_timeout (GtkXText * xtext)
 {
 	int p_y, win_height;
+	xtext_buffer *buf = xtext->buffer;
+	GtkAdjustment *adj = xtext->adj;
+	textentry *ent;
 
 	gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
 	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, 0, &win_height);
@@ -1764,13 +1798,30 @@ gtk_xtext_scrolldown_timeout (GtkXText * xtext)
 	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->adj->value += buf->pagetop_ent->lines_taken;
+		ent = buf->last_ent_end->next;
+		if (ent)
+		{
+			gtk_adjustment_value_changed (xtext->adj);
+		}
+		else
+		{
+			buf->scrollbar_down = TRUE;
+		}
+		xtext->scroll_tag = g_timeout_add (gtk_xtext_timeout_ms (xtext, p_y - win_height),
+														(GSourceFunc)
+														gtk_xtext_scrolldown_timeout,
+														xtext);
+		xtext->select_start_y -= (adj->value - xtext->select_start_adj) * xtext->fontsize;
+		xtext->select_start_adj = adj->value;
+		gtk_xtext_selection_draw (xtext, NULL, TRUE);
+		gtk_xtext_render_ents (xtext, ent, buf->last_ent_end);
+	}
+	else
+	{
+		xtext->scroll_tag = 0;
 	}
 
-	xtext->scroll_tag = 0;
 	return 0;
 }
 
@@ -1778,18 +1829,35 @@ static gint
 gtk_xtext_scrollup_timeout (GtkXText * xtext)
 {
 	int p_y;
+	xtext_buffer *buf = xtext->buffer;
+	GtkAdjustment *adj = xtext->adj;
+	textentry *ent;
 
 	gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
 
-	if (p_y < 0 && xtext->adj->value > 0.0)
+	if (p_y < 0 && adj->value >= 0)
 	{
-		xtext->adj->value--;
-		gtk_adjustment_changed (xtext->adj);
-		gtk_xtext_render_page (xtext);
-		return 1;
+		buf->scrollbar_down = FALSE;
+		ent = buf->last_ent_start->prev;
+		if (ent)
+		{
+			adj->value -= ent->lines_taken;
+			gtk_adjustment_value_changed (adj);
+		}
+		xtext->select_start_y -= (adj->value - xtext->select_start_adj) * xtext->fontsize;
+		xtext->select_start_adj = adj->value;
+		gtk_xtext_selection_draw (xtext, NULL, TRUE);
+		gtk_xtext_render_ents (xtext, ent, buf->last_ent_end);
+		xtext->scroll_tag = g_timeout_add (gtk_xtext_timeout_ms (xtext, p_y),
+														(GSourceFunc)
+														gtk_xtext_scrollup_timeout,
+														xtext);
+	}
+	else
+	{
+		xtext->scroll_tag = 0;
 	}
 
-	xtext->scroll_tag = 0;
 	return 0;
 }
 
@@ -1799,35 +1867,32 @@ gtk_xtext_selection_update (GtkXText * xtext, GdkEventMotion * event, int p_y, g
 	int win_height;
 	int moved;
 
+	if (xtext->scroll_tag)
+	{
+		return;
+	}
+
 	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;
+		gtk_xtext_scrollup_timeout (xtext);
 	}
 
 	/* selecting past bottom of window, scroll down! */
-	if (p_y > win_height &&
+	else 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;
+		gtk_xtext_scrolldown_timeout (xtext);
+	}
+	else
+	{
+		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);
 	}
-
-	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 *
@@ -1883,7 +1948,7 @@ gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
 	if (ret_len)
 		*ret_len = str - word;
 
-	return gtk_xtext_strip_color (word, len, xtext->scratch_buffer, NULL, NULL, FALSE);
+	return gtk_xtext_strip_color (word, len, xtext->scratch_buffer, NULL, NULL, NULL, FALSE);
 }
 
 #ifdef MOTION_MONITOR
@@ -1941,7 +2006,7 @@ gtk_xtext_check_mark_stamp (GtkXText *xtext, GdkModifierType mask)
 {
 	gboolean redraw = FALSE;
 
-	if ((mask & GDK_SHIFT_MASK))
+	if (mask & GDK_SHIFT_MASK || prefs.autocopy_stamp)
 	{
 		if (!xtext->mark_stamp)
 		{
@@ -2106,7 +2171,16 @@ gtk_xtext_set_clip_owner (GtkWidget * xtext, GdkEventButton * event)
 		free (str);
 	}
 
-	gtk_selection_owner_set (xtext, GDK_SELECTION_PRIMARY, event->time);
+	if (event)
+	{
+		gtk_selection_owner_set (xtext, GDK_SELECTION_PRIMARY, event->time);
+	}
+}
+
+void
+gtk_xtext_copy_selection (GtkXText *xtext)
+{
+	gtk_xtext_set_clip_owner (xtext, NULL);
 }
 
 static void
@@ -2174,6 +2248,11 @@ gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
 	if (event->button == 1)
 	{
 		xtext->button_down = FALSE;
+		if (xtext->scroll_tag)
+		{
+			g_source_remove (xtext->scroll_tag);
+			xtext->scroll_tag = 0;
+		}
 
 		gtk_grab_remove (widget);
 		/*gdk_pointer_ungrab (0);*/
@@ -2182,9 +2261,12 @@ gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
 		if (xtext->buffer->last_ent_start)
 		{
 			xtext->color_paste = FALSE;
-			if (event->state & GDK_CONTROL_MASK)
+			if (event->state & GDK_CONTROL_MASK || prefs.autocopy_color)
 				xtext->color_paste = TRUE;
-			gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
+			if (prefs.autocopy_text)
+			{
+				gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
+			}
 		}
 
 		if (xtext->select_start_x == event->x &&
@@ -2249,7 +2331,10 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
 			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);
+			if (prefs.autocopy_text)
+			{
+				gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
+			}
 		}
 
 		return FALSE;
@@ -2265,7 +2350,10 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
 			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);
+			if (prefs.autocopy_text)
+			{
+				gtk_xtext_set_clip_owner (GTK_WIDGET (xtext), event);
+			}
 		}
 
 		return FALSE;
@@ -2391,7 +2479,7 @@ gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret)
 		len = strlen (txt);
 	} else
 	{
-		stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, &len, 0, FALSE);
+		stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, &len, NULL, NULL, FALSE);
 		free (txt);
 	}
 
@@ -2431,12 +2519,14 @@ gtk_xtext_selection_get (GtkWidget * widget,
 
 #if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0)
 			gdk_string_to_compound_text (
+												stripped, &encoding, &format, &new_text,
+												&new_length);
 #else
 			gdk_string_to_compound_text_for_display (
 												gdk_drawable_get_display (widget->window),
-#endif
 												stripped, &encoding, &format, &new_text,
 												&new_length);
+#endif
 			gtk_selection_data_set (selection_data_ptr, encoding, format,
 											new_text, new_length);
 			gdk_free_compound_text (new_text);
@@ -2551,19 +2641,26 @@ gtk_xtext_get_type (void)
 
 static unsigned char *
 gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
-							  int *newlen, int *mb_ret, int strip_hidden)
+							  int *newlen, int *mb_ret, GSList **slp, int strip_hidden)
 {
 	int i = 0;
 	int rcol = 0, bgcol = 0;
 	int hidden = FALSE;
 	unsigned char *new_str;
 	int mb = FALSE;
+	GSList *sl = NULL;
+	unsigned char *text0 = text;
+	int off1, len1;
+	offlen_t data;
 
 	if (outbuf == NULL)
 		new_str = malloc (len + 2);
 	else
 		new_str = outbuf;
 
+	off1 = 0;
+	len1 = 0;
+	data.u = 0;
 	while (len > 0)
 	{
 		if (*text >= 128)
@@ -2597,12 +2694,32 @@ gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
 				break;
 			default:
 				if (!(hidden && strip_hidden))
+				{
+					if (text - text0 - off1 != len1)
+					{
+						if (len1)
+						{
+							data.o.off = off1;
+							data.o.len = len1;
+							sl = g_slist_append (sl, GUINT_TO_POINTER (data.u));
+							len1 = 0;
+						}
+						off1 = text - text0;
+					}
+					len1++;
 					new_str[i++] = *text;
+				}
 			}
 		}
 		text++;
 		len--;
 	}
+	if (len1)
+	{
+		data.o.off = off1;
+		data.o.len = len1;
+		sl = g_slist_append (sl, GUINT_TO_POINTER (data.u));
+	}
 
 	new_str[i] = 0;
 
@@ -2612,9 +2729,15 @@ gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
 	if (mb_ret != NULL)
 		*mb_ret = mb;
 
+	if (slp)
+		*slp = sl;
+	else
+		g_slist_free (sl);
+
 	return new_str;
 }
 
+
 /* GeEkMaN: converts mIRC control codes to literal control codes */
 
 static char *
@@ -2718,7 +2841,7 @@ gtk_xtext_text_width (GtkXText *xtext, unsigned char *text, int len,
 	int new_len, mb;
 
 	new_buf = gtk_xtext_strip_color (text, len, xtext->scratch_buffer,
-												&new_len, &mb, !xtext->ignore_hidden);
+												&new_len, &mb, NULL, !xtext->ignore_hidden);
 
 	if (mb_ret)
 		*mb_ret = mb;
@@ -2884,6 +3007,63 @@ gtk_xtext_reset (GtkXText * xtext, int mark, int attribs)
 	xtext->nc = 0;
 }
 
+/*
+ * gtk_xtext_search_offset (buf, ent, off) --
+ * Look for arg offset in arg textentry
+ * Return one or more flags:
+ * 	GTK_MATCH_MID if we are in a match
+ * 	GTK_MATCH_START if we're at the first byte of it
+ * 	GTK_MATCH_END if we're at the first byte past it
+ * 	GTK_MATCH_CUR if it is the current match
+ */
+#define GTK_MATCH_START	1
+#define GTK_MATCH_MID	2
+#define GTK_MATCH_END	4
+#define GTK_MATCH_CUR	8
+static int
+gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off)
+{
+	GList *gl;
+	offsets_t o;
+	int flags = 0;
+
+	for (gl = g_list_first (ent->marks); gl; gl = g_list_next (gl))
+	{
+		o.u = GPOINTER_TO_UINT (gl->data);
+		if (off < o.o.start || off > o.o.end)
+			continue;
+		flags = GTK_MATCH_MID;
+		if (off == o.o.start)
+			flags |= GTK_MATCH_START;
+		if (off == o.o.end)
+		{
+			gl = g_list_next (gl);
+			if (gl)
+			{
+				o.u = GPOINTER_TO_UINT (gl->data);
+				if (off ==  o.o.start)	/* If subseq match is adjacent */
+				{
+					flags |= (gl == buf->curmark)? GTK_MATCH_CUR: 0;
+				}
+				else		/* If subseq match is not adjacent */
+				{
+					flags |= GTK_MATCH_END;
+				}
+			}
+			else		/* If there is no subseq match */
+			{
+				flags |= GTK_MATCH_END;
+			}
+		}
+		else if (gl == buf->curmark)	/* If not yet at the end of this match */
+		{
+			flags |= GTK_MATCH_CUR;
+		}
+		break;
+	}
+	return flags;
+}
+
 /* render a single line, which WONT wrap, and parse mIRC colors */
 
 static int
@@ -2898,6 +3078,9 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
 	int offset;
 	int mark = FALSE;
 	int ret = 1;
+	int k;
+	int srch_underline = FALSE;
+	int srch_mark = FALSE;
 
 	xtext->in_hilight = FALSE;
 
@@ -3049,6 +3232,50 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
 				}
 			}
 
+			if (!left_only && !mark &&
+				 (k = gtk_xtext_search_offset (xtext->buffer, ent, offset + i)))
+			{
+				x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+				pstr += j;
+				j = 0;
+				if (!(xtext->buffer->search_flags & highlight))
+				{
+					if (k & GTK_MATCH_CUR)
+					{
+						xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
+						xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
+						xtext->backcolor = TRUE;
+						srch_mark = TRUE;
+					} else
+					{
+						xtext_set_bg (xtext, gc, xtext->col_back);
+						xtext_set_fg (xtext, gc, xtext->col_fore);
+						xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE;
+						srch_mark = FALSE;
+					}
+				}
+				else
+				{
+					xtext->underline = (k & GTK_MATCH_CUR)? TRUE: FALSE;
+					if (k & (GTK_MATCH_START | GTK_MATCH_MID))
+					{
+						xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
+						xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
+						xtext->backcolor = TRUE;
+						srch_mark = TRUE;
+					}
+					if (k & GTK_MATCH_END)
+					{
+						xtext_set_bg (xtext, gc, xtext->col_back);
+						xtext_set_fg (xtext, gc, xtext->col_fore);
+						xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE;
+						srch_mark = FALSE;
+						xtext->underline = FALSE;
+					}
+					srch_underline = xtext->underline;
+				}
+			}
+
 			switch (str[i])
 			{
 			case '\n':
@@ -3198,6 +3425,11 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
 			xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
 			xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
 			xtext->backcolor = TRUE;
+			if (srch_underline)
+			{
+				xtext->underline = FALSE;
+				srch_underline = FALSE;
+			}
 			mark = TRUE;
 		}
 
@@ -3220,7 +3452,7 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
 	if (j)
 		x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
 
-	if (mark)
+	if (mark || srch_mark)
 	{
 		xtext_set_bg (xtext, gc, xtext->col_back);
 		xtext_set_fg (xtext, gc, xtext->col_fore);
@@ -3836,7 +4068,7 @@ gtk_xtext_load_trans (GtkXText * xtext)
 	PaintDesktop (hdc);
 	ReleaseDC (hwnd, hdc);
 
-	gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+	gdk_drawable_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);
 
@@ -4388,7 +4620,7 @@ gtk_xtext_save (GtkXText * xtext, int fh)
 	while (ent)
 	{
 		buf = gtk_xtext_strip_color (ent->str, ent->str_len, NULL,
-											  &newlen, NULL, FALSE);
+											  &newlen, NULL, NULL, FALSE);
 		write (fh, buf, newlen);
 		write (fh, "\n", 1);
 		free (buf);
@@ -4775,6 +5007,11 @@ gtk_xtext_kill_ent (xtext_buffer *buffer, textentry *ent)
 
 	if (buffer->marker_pos == ent) buffer->marker_pos = NULL;
 
+	if (ent->marks)
+	{
+		gtk_xtext_search_textentry_del (buffer, ent);
+	}
+
 	free (ent);
 	return visible;
 }
@@ -4839,7 +5076,23 @@ gtk_xtext_remove_bottom (xtext_buffer *buffer)
 	else
 		buffer->text_first = NULL;
 
-	gtk_xtext_kill_ent (buffer, ent);
+	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);
+		}
+	}
 }
 
 /* If lines=0 => clear all */
@@ -4874,6 +5127,8 @@ gtk_xtext_clear (xtext_buffer *buf, int lines)
 	else
 	{
 		/* delete all */
+		if (buf->search_found)
+			gtk_xtext_search_fini (buf);
 		if (buf->xtext->auto_indent)
 			buf->indent = MARGIN;
 		buf->scrollbar_down = TRUE;
@@ -4905,21 +5160,37 @@ static gboolean
 gtk_xtext_check_ent_visibility (GtkXText * xtext, textentry *find_ent, int add)
 {
 	textentry *ent;
-	int lines_max;
-	int line = 0;
+	int lines;
+	xtext_buffer *buf = xtext->buffer;
 	int width;
 	int height;
 
-	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+	if (find_ent == NULL)
+	{
+		return FALSE;
+	}
 
-	lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + add;
-	ent = xtext->buffer->pagetop_ent;
+	gdk_drawable_get_size (GTK_WIDGET (xtext)->window, &width, &height);
 
-	while (ent && line < lines_max)
+	ent = buf->pagetop_ent;
+	/* If top line not completely displayed return FALSE */
+	if (ent == find_ent && buf->pagetop_subline > 0)
 	{
-		if (find_ent == ent)
+		return FALSE;
+	}
+	/* Loop through line positions looking for find_ent */
+	lines = ((height + xtext->pixel_offset) / xtext->fontsize) + buf->pagetop_subline + add;
+	while (ent)	
+	{
+		lines -= ent->lines_taken;
+		if (lines <= 0)
+		{
+			return FALSE;
+		}
+		if (ent == find_ent)
+		{
 			return TRUE;
-		line += ent->lines_taken;
+		}
 		ent = ent->next;
 	}
 
@@ -4933,90 +5204,369 @@ gtk_xtext_check_marker_visibility (GtkXText * xtext)
 		xtext->buffer->marker_seen = TRUE;
 }
 
-textentry *
-gtk_xtext_search (GtkXText * xtext, const gchar *text, textentry *start, gboolean case_match, gboolean backward)
+static void
+gtk_xtext_unstrip_color (gint start, gint end, GSList *slp, GList **gl, gint maxo)
 {
-	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;
+	gint off1, off2, curlen;
+	GSList *cursl;
+	offsets_t marks;
 
-	/* set up text comparand for Case Match or Ignore */
-	if (case_match)
-		nee = g_strdup (text);
-	else
-		nee = g_utf8_casefold (text, strlen (text));
+	off1 = 0;
+	curlen = 0;
+	cursl = slp;
+	while (cursl)
+	{
+		offlen_t ol;
+		ol.u = GPOINTER_TO_UINT(cursl->data);
+		if (start < ol.o.len)
+		{
+			off1 = ol.o.off + start;
+			break;
+		}
+		curlen += ol.o.len;
+		start -= ol.o.len;
+		end -= ol.o.len;
+		cursl = g_slist_next (cursl);
+	}
 
-	/* Validate that start gives a currently valid ent pointer */
-	ent = xtext->buffer->text_first;
-	while (ent)
+	off2 = off1;
+	while (cursl)
 	{
-		if (ent == start)
+		offlen_t ol;
+		ol.u = GPOINTER_TO_UINT(cursl->data);
+		if (end < ol.o.len)
+		{
+			off2 = ol.o.off + end;
 			break;
-		ent = ent->next;
+		}
+		curlen += ol.o.len;
+		end -= ol.o.len;
+		cursl = g_slist_next (cursl);
+	}
+	if (!cursl)
+	{
+		off2 = maxo;
 	}
-	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;
+	marks.o.start = off1;
+	marks.o.end = off2;
+	*gl = g_list_append (*gl, GUINT_TO_POINTER (marks.u));
+}
 
-	/* Search from there to one end or the other until found */
-	while (ent)
+/* Search a single textentry for occurrence(s) of search arg string */
+static void
+gtk_xtext_search_textentry (xtext_buffer *buf, textentry *ent, int pre)
+{
+	gchar *str;								/* text string to be searched */
+	GList *gl = NULL;
+	GSList *slp;
+	gint lstr;
+
+	if (buf->search_text == NULL)
 	{
-		/* 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);
+		return;
+	}
+
+	str = gtk_xtext_strip_color (ent->str, ent->str_len, buf->xtext->scratch_buffer,
+										  &lstr, NULL, &slp, !buf->xtext->ignore_hidden);
+
+	/* Regular-expression matching --- */
+	if (buf->search_flags & regexp)
+	{
+		GMatchInfo *gmi;
+		gint start, end;
+
+		if (buf->search_re == NULL)
+		{
+			return;
+		}
+		g_regex_match (buf->search_re, str, 0, &gmi);
+		while (g_match_info_matches (gmi))
+		{
+			g_match_info_fetch_pos (gmi, 0,  &start, &end);
+			gtk_xtext_unstrip_color (start, end, slp, &gl, ent->str_len);
+			g_match_info_next (gmi, NULL);
+		}
+		g_match_info_free (gmi);
+
+	/* Non-regular-expression matching --- */
+	} else {
+		gchar *hay, *pos;
+		gint lhay, off, len;
+		gint match = buf->search_flags & case_match;
+
+		hay = match? g_strdup (str): g_utf8_casefold (str, lstr);
+		lhay = strlen (hay);
+		off = 0;
+
+		for (pos = hay, len = lhay; len;
+			  off += buf->search_lnee, pos = hay + off, len = lhay - off)
+		{
+			str = g_strstr_len (pos, len, buf->search_nee);
+			if (str == NULL)
+			{
+				break;
+			}
+			off = str - hay;
+			gtk_xtext_unstrip_color (off, off + buf->search_lnee,
+											 slp, &gl, ent->str_len);
+		}
+
 		g_free (hay);
-		if (str)
-			break;
-		ent = backward? ent->prev: ent->next;
 	}
-	fent = ent;
 
-	/* Save distance to start, end of found string */
-	if (ent)
+	/* Common processing --- */
+	g_slist_free (slp);
+	ent->marks = gl;
+	if (gl)
+	{
+		buf->search_found = (pre? g_list_prepend: g_list_append) (buf->search_found, ent);
+		if (pre == FALSE && buf->hintsearch == NULL)
+		{
+			buf->hintsearch = ent;
+		}
+	}
+	return;
+}
+
+/* Free all search information for a textentry */
+static void
+gtk_xtext_search_textentry_del (xtext_buffer *buf, textentry *ent)
+{
+	g_list_free (ent->marks);
+	ent->marks = NULL;
+	if (buf->cursearch && buf->cursearch->data == ent)
+	{
+		buf->cursearch = NULL;
+		buf->curmark = NULL;
+	}
+	if (buf->pagetop_ent == ent)
+	{
+		buf->pagetop_ent = NULL;
+	}
+	if (buf->hintsearch == ent)
+	{
+		buf->hintsearch = NULL;
+	}
+	buf->search_found = g_list_remove (buf->search_found, ent);
+}
+
+/* Used only by glist_foreach */
+static void
+gtk_xtext_search_textentry_fini (gpointer entp, gpointer dummy)
+{
+	textentry *ent = entp;
+
+	g_list_free (ent->marks);
+	ent->marks = NULL;
+}
+
+/* Free all search information for all textentrys and the xtext_buffer */
+static void
+gtk_xtext_search_fini (xtext_buffer *buf)
+{
+	g_list_foreach (buf->search_found, gtk_xtext_search_textentry_fini, 0);
+	g_list_free (buf->search_found);
+	buf->search_found = NULL;
+	g_free (buf->search_text);
+	buf->search_text = NULL;
+	g_free (buf->search_nee);
+	buf->search_nee = NULL;
+	buf->search_flags = 0;
+	buf->cursearch = NULL;
+	buf->curmark = NULL;
+	if (buf->search_re)
+	{
+		g_regex_unref (buf->search_re);
+		buf->search_re = NULL;
+	}
+}
+
+/* Returns TRUE if the base search information exists and is still okay to use */
+static gboolean
+gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gtk_xtext_search_flags flags, GError **perr)
+{
+	/* Of the five flags, backward and highlight_all do not need a new search */
+	if (buf->search_found &&
+		 strcmp (buf->search_text, text) == 0 &&
+		 (buf->search_flags & case_match) == (flags & case_match) &&
+		 (buf->search_flags & follow) == (flags & follow) &&
+		 (buf->search_flags & regexp) == (flags & regexp))
+	{
+		return TRUE;
+	}
+	buf->hintsearch = buf->cursearch? buf->cursearch->data: NULL;
+	gtk_xtext_search_fini (buf);
+	buf->search_text = g_strdup (text);
+	if (flags & regexp)
+	{
+		buf->search_re = g_regex_new (text, (flags & case_match)? 0: G_REGEX_CASELESS, 0, perr);
+		if (perr && *perr)
+		{
+			return FALSE;
+		}
+	}
+	else
+	{
+		if (flags & case_match)
+		{
+			buf->search_nee = g_strdup (text);
+		}
+		else
+		{
+			buf->search_nee = g_utf8_casefold (text, strlen (text));
+		}
+		buf->search_lnee = strlen (buf->search_nee);
+	}
+	buf->search_flags = flags;
+	buf->cursearch = NULL;
+	buf->curmark = NULL;
+	return FALSE;
+}
+
+#define BACKWARD (flags & backward)
+#define FIRSTLAST(lp)  (BACKWARD? g_list_last(lp): g_list_first(lp))
+#define NEXTPREVIOUS(lp) (BACKWARD? g_list_previous(lp): g_list_next(lp))
+textentry *
+gtk_xtext_search (GtkXText * xtext, const gchar *text, gtk_xtext_search_flags flags, GError **perr)
+{
+	textentry *ent = NULL;
+	xtext_buffer *buf = xtext->buffer;
+	GList *gl;
+
+	if (buf->text_first == NULL)
 	{
-		ent->mark_start = str - hay;
-		ent->mark_end = ent->mark_start + strlen (nee);
+		return NULL;
+	}
 
-		/* is the match visible? Might need to scroll */
-		if (!gtk_xtext_check_ent_visibility (xtext, ent, 0))
+	/* If the text arg is NULL, one of these has been toggled: highlight follow */
+	if (text == NULL)		/* Here on highlight or follow toggle */
+	{
+		gint oldfollow = buf->search_flags & follow;
+		gint newfollow = flags & follow;
+
+		/* If "Follow" has just been checked, search possible new textentries --- */
+		if (newfollow && (newfollow != oldfollow))
 		{
-			ent = xtext->buffer->text_first;
-			line = 0;
-			while (ent)
+			gl = g_list_last (buf->search_found);
+			ent = gl? gl->data: buf->text_first;
+			for (; ent; ent = ent->next)
 			{
-				line += ent->lines_taken;
-				ent = ent->next;
-				if (ent == fent)
-					break;
+				gtk_xtext_search_textentry (buf, ent, FALSE);
+			}
+		}
+		buf->search_flags = flags;
+		ent = buf->pagetop_ent;
+	}
+
+	/* if the text arg is "", the reset button has been clicked or Control-Shift-F has been hit */
+	else if (text[0] == 0)		/* Let a null string do a reset. */
+	{
+		gtk_xtext_search_fini (buf);
+	}
+
+	/* If the text arg is neither NULL nor "", it's the search string */
+	else
+	{
+		if (gtk_xtext_search_init (buf, text, flags, perr) == FALSE)	/* If a new search: */
+		{
+			if (perr && *perr)
+			{
+				return NULL;
+			}
+			for (ent = buf->text_first; ent; ent = ent->next)
+			{
+				gtk_xtext_search_textentry (buf, ent, TRUE);
+			}
+			buf->search_found = g_list_reverse (buf->search_found);
+		}
+
+		/* Now base search results are in place. */
+
+		if (buf->search_found)
+		{
+			/* If we're in the midst of moving among found items */
+			if (buf->cursearch)
+			{
+				ent = buf->cursearch->data;
+				buf->curmark = NEXTPREVIOUS (buf->curmark);
+				if (buf->curmark == NULL)
+				{
+					/* We've returned all the matches for this textentry. */
+					buf->cursearch = NEXTPREVIOUS (buf->cursearch);
+					if (buf->cursearch)
+					{
+						ent = buf->cursearch->data;
+						buf->curmark = FIRSTLAST (ent->marks);
+					}
+					else	/* We've returned all the matches for all textentries */
+					{
+						ent = NULL;
+					}
+				}
+			}
+#if 0
+			/* If user changed the search, let's look starting where he was */
+			else if (buf->hintsearch)
+			{
+				for (ent = buf->hintsearch; ent; ent = BACKWARD? ent->prev: ent->next)
+					if (ent->marks)
+						break;
+				if (ent == NULL)
+					for (ent = buf->hintsearch; ent; ent = BACKWARD? ent->next: ent->prev)
+						if (ent->marks)
+							break;
+				if (ent)
+				{
+					buf->cursearch = g_list_find (buf->search_found, ent);
+					buf->curmark = FIRSTLAST (ent->marks);
+				}
+			}
+#endif
+			/* This is a fresh search */
+			else
+			{
+				buf->cursearch = FIRSTLAST (buf->search_found);
+				ent = buf->cursearch->data;
+				buf->curmark = FIRSTLAST (ent->marks);
 			}
-			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);
 		}
 	}
+	buf->hintsearch = ent;
+
+	if (!gtk_xtext_check_ent_visibility (xtext, ent, 1))
+	{
+		GtkAdjustment *adj = xtext->adj;
+		float value;
+
+		buf->pagetop_ent = NULL;
+		for (value = 0, ent = buf->text_first;
+			  ent && ent != buf->hintsearch; ent = ent->next)
+		{
+			value += ent->lines_taken;
+		}
+		if (value > adj->upper - adj->page_size)
+		{
+			value = adj->upper - adj->page_size;
+		}
+		else if ((flags & backward)  && ent)
+		{
+			value -= adj->page_size - ent->lines_taken;
+			if (value < 0)
+			{
+				value = 0;
+			}
+		}
+		gtk_adjustment_set_value (adj, value);
+	}
 
-	g_free (nee);
 	gtk_widget_queue_draw (GTK_WIDGET (xtext));
 
-	return fent;
+	return buf->hintsearch;
 }
+#undef BACKWARD
+#undef FIRSTLAST
+#undef NEXTPREVIOUS
 
 static int
 gtk_xtext_render_page_timeout (GtkXText * xtext)
@@ -5079,6 +5629,7 @@ gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp)
 	ent->mark_start = -1;
 	ent->mark_end = -1;
 	ent->next = NULL;
+	ent->marks = NULL;
 
 	if (ent->indent < MARGIN)
 		ent->indent = MARGIN;	  /* 2 pixels is the left margin */
@@ -5097,10 +5648,11 @@ gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp)
 	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)))))))
+		!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)))
+		!(GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (buf->xtext))))->has_focus
 #endif
+	   )))
 	{
 		buf->marker_pos = ent;
 		dontscroll (buf); /* force scrolling off */
@@ -5140,6 +5692,10 @@ gtk_xtext_append_entry (xtext_buffer *buf, textentry * ent, time_t stamp)
 		if (buf->old_value < 0)
 			buf->old_value = 0;
 	}
+	if (buf->search_flags & follow)
+	{
+		gtk_xtext_search_textentry (buf, ent, FALSE);
+	}
 }
 
 /* the main two public functions */
@@ -5466,6 +6022,11 @@ gtk_xtext_buffer_free (xtext_buffer *buf)
 	if (buf->xtext->selection_buffer == buf)
 		buf->xtext->selection_buffer = NULL;
 
+	if (buf->search_found)
+	{
+		gtk_xtext_search_fini (buf);
+	}
+
 	ent = buf->text_first;
 	while (ent)
 	{
diff --git a/src/fe-gtk/xtext.h b/src/fe-gtk/xtext.h
index a37ddc32..6c126346 100644
--- a/src/fe-gtk/xtext.h
+++ b/src/fe-gtk/xtext.h
@@ -43,6 +43,13 @@
 typedef struct _GtkXText GtkXText;
 typedef struct _GtkXTextClass GtkXTextClass;
 typedef struct textentry textentry;
+typedef enum gtk_xtext_search_flags_e {
+	case_match = 1,
+	backward = 2,
+	highlight = 4,
+	follow = 8,
+	regexp = 16
+} gtk_xtext_search_flags;
 
 typedef struct {
 	GtkXText *xtext;					/* attached to this widget */
@@ -77,6 +84,16 @@ typedef struct {
 	unsigned int grid_dirty:1;
 	unsigned int marker_seen:1;
 	unsigned int reset_marker_pos:1;
+
+	GList *search_found;		/* list of textentries where search found strings */
+	gchar *search_text;		/* desired text to search for */
+	gchar *search_nee;		/* prepared needle to look in haystack for */
+	gint search_lnee;		/* its length */
+	gtk_xtext_search_flags search_flags;	/* match, bwd, highlight */
+	GList *cursearch;			/* GList whose 'data' pts to current textentry */
+	GList *curmark;			/* current item in ent->marks */
+	GRegex *search_re;		/* Compiled regular expression */
+	textentry *hintsearch;	/* textentry found for last search */
 } xtext_buffer;
 
 struct _GtkXText
@@ -247,7 +264,7 @@ void gtk_xtext_clear (xtext_buffer *buf, int lines);
 void gtk_xtext_save (GtkXText * xtext, int fh);
 void gtk_xtext_refresh (GtkXText * xtext, int do_trans);
 int gtk_xtext_lastlog (xtext_buffer *out, xtext_buffer *search_area, int (*cmp_func) (char *, void *userdata), void *userdata);
-textentry *gtk_xtext_search (GtkXText * xtext, const gchar *text, textentry *start, gboolean case_match, gboolean backward);
+textentry *gtk_xtext_search (GtkXText * xtext, const gchar *text, gtk_xtext_search_flags flags, GError **err);
 void gtk_xtext_reset_marker_pos (GtkXText *xtext);
 void gtk_xtext_check_marker_visibility(GtkXText *xtext);
 
@@ -270,6 +287,7 @@ void gtk_xtext_set_wordwrap (GtkXText *xtext, gboolean word_wrap);
 xtext_buffer *gtk_xtext_buffer_new (GtkXText *xtext);
 void gtk_xtext_buffer_free (xtext_buffer *buf);
 void gtk_xtext_buffer_show (GtkXText *xtext, xtext_buffer *buf, int render);
+void gtk_xtext_copy_selection (GtkXText *xtext);
 GType gtk_xtext_get_type (void);
 
 #endif