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/fe-gtk.c53
-rw-r--r--src/fe-gtk/maingui.c27
-rw-r--r--src/fe-gtk/meson.build10
-rw-r--r--src/fe-gtk/notifications/notification-freedesktop.c148
-rw-r--r--src/fe-gtk/notifications/notification-libnotify.c81
-rw-r--r--src/fe-gtk/plugin-notification.c2
-rw-r--r--src/fe-gtk/servlistgui.c14
-rw-r--r--src/fe-gtk/setup.c2
-rw-r--r--src/fe-gtk/sexy-spell-entry.c20
-rw-r--r--src/fe-gtk/xtext.c17
-rw-r--r--src/fe-gtk/xtext.h22
11 files changed, 278 insertions, 118 deletions
diff --git a/src/fe-gtk/fe-gtk.c b/src/fe-gtk/fe-gtk.c
index ee3e847c..7eca0710 100644
--- a/src/fe-gtk/fe-gtk.c
+++ b/src/fe-gtk/fe-gtk.c
@@ -664,13 +664,13 @@ fe_print_text (struct session *sess, char *text, time_t stamp,
 		return;
 
 	if (sess == current_tab)
-		fe_set_tab_color (sess, 0);
+		fe_set_tab_color (sess, FE_COLOR_NONE);
 	else if (sess->tab_state & TAB_STATE_NEW_HILIGHT)
-		fe_set_tab_color (sess, 3);
+		fe_set_tab_color (sess, FE_COLOR_NEW_HILIGHT);
 	else if (sess->tab_state & TAB_STATE_NEW_MSG)
-		fe_set_tab_color (sess, 2);
+		fe_set_tab_color (sess, FE_COLOR_NEW_MSG);
 	else
-		fe_set_tab_color (sess, 1);
+		fe_set_tab_color (sess, FE_COLOR_NEW_DATA);
 }
 
 void
@@ -1054,6 +1054,46 @@ osx_show_uri (const char *url)
 
 #endif
 
+static inline char *
+escape_uri (const char *uri)
+{
+	return g_uri_escape_string(uri, G_URI_RESERVED_CHARS_GENERIC_DELIMITERS G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, FALSE);
+}
+
+static inline gboolean
+uri_contains_forbidden_characters (const char *uri)
+{
+	while (*uri)
+	{
+		/* This is not an exhaustive list, the full URI has segments that allow characters like "[]:" for example. */
+		if (strchr ("`<> ${}\"+", *uri) != NULL || (*uri & 0x80) /* non-ascii */)
+			return TRUE;
+		uri++;
+	}
+
+	return FALSE;
+}
+
+static char *
+maybe_escape_uri (const char *uri)
+{
+	/* The only way to know if a string has already been escaped or not
+	 * is by fulling parsing each segement but we can try some more simple heuristics. */
+
+	/* If we find characters that should clearly be escaped. */
+	if (uri_contains_forbidden_characters (uri))
+		return escape_uri (uri);
+
+	/* If it fails to be unescaped then it was not escaped. */
+	char *unescaped = g_uri_unescape_string (uri, NULL);
+	if (!unescaped)
+		return escape_uri (uri);
+	g_free (unescaped);
+
+	/* At this point it is probably safe to pass through as-is. */
+	return g_strdup (uri);
+}
+
 static void
 fe_open_url_inner (const char *url)
 {
@@ -1071,7 +1111,10 @@ fe_open_url_inner (const char *url)
 #elif defined(__APPLE__)
     osx_show_uri (url);
 #else
-	gtk_show_uri (NULL, url, GDK_CURRENT_TIME, NULL);
+	char *escaped_url = maybe_escape_uri (url);
+	g_debug ("Opening URL \"%s\" (%s)", escaped_url, url);
+	gtk_show_uri (NULL, escaped_url, GDK_CURRENT_TIME, NULL);
+	g_free (escaped_url);
 #endif
 }
 
diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c
index 4e5baaa0..61f59856 100644
--- a/src/fe-gtk/maingui.c
+++ b/src/fe-gtk/maingui.c
@@ -175,20 +175,26 @@ fe_flash_window (session *sess)
 /* set a tab plain, red, light-red, or blue */
 
 void
-fe_set_tab_color (struct session *sess, int col)
+fe_set_tab_color (struct session *sess, tabcolor col)
 {
 	struct session *server_sess = sess->server->server_session;
+	int col_noflags = (col & ~FE_COLOR_ALLFLAGS);
+	int col_shouldoverride = !(col & FE_COLOR_FLAG_NOOVERRIDE);
+
 	if (sess->res->tab && sess->gui->is_tab && (col == 0 || sess != current_tab))
 	{
-		switch (col)
+		switch (col_noflags)
 		{
 		case 0:	/* no particular color (theme default) */
 			sess->tab_state = TAB_STATE_NONE;
 			chan_set_color (sess->res->tab, plain_list);
 			break;
 		case 1:	/* new data has been displayed (dark red) */
-			sess->tab_state = TAB_STATE_NEW_DATA;
-			chan_set_color (sess->res->tab, newdata_list);
+			if (col_shouldoverride || !((sess->tab_state & TAB_STATE_NEW_MSG)
+										|| (sess->tab_state & TAB_STATE_NEW_HILIGHT))) {
+				sess->tab_state = TAB_STATE_NEW_DATA;
+				chan_set_color (sess->res->tab, newdata_list);
+			}
 
 			if (chan_is_collapsed (sess->res->tab)
 				&& !((server_sess->tab_state & TAB_STATE_NEW_MSG)
@@ -201,8 +207,10 @@ fe_set_tab_color (struct session *sess, int col)
 
 			break;
 		case 2:	/* new message arrived in channel (light red) */
-			sess->tab_state = TAB_STATE_NEW_MSG;
-			chan_set_color (sess->res->tab, newmsg_list);
+			if (col_shouldoverride || !(sess->tab_state & TAB_STATE_NEW_HILIGHT)) {
+				sess->tab_state = TAB_STATE_NEW_MSG;
+				chan_set_color (sess->res->tab, newmsg_list);
+			}
 
 			if (chan_is_collapsed (sess->res->tab)
 				&& !(server_sess->tab_state & TAB_STATE_NEW_HILIGHT)
@@ -540,7 +548,7 @@ mg_focus (session *sess)
 	/* when called via mg_changui_new, is_tab might be true, but
 		sess->res->tab is still NULL. */
 	if (sess->res->tab)
-		fe_set_tab_color (sess, 0);
+		fe_set_tab_color (sess, FE_COLOR_NONE);
 }
 
 static int
@@ -1394,6 +1402,8 @@ mg_color_insert (GtkWidget *item, gpointer userdata)
 			text = "\037"; break;
 		case 102:
 			text = "\035"; break;
+		case 103:
+			text = "\036"; break;
 		default:
 			text = "\017"; break;
 		}
@@ -1447,7 +1457,8 @@ mg_create_color_menu (GtkWidget *menu, session *sess)
 	mg_markup_item (submenu, _("<b>Bold</b>"), 100);
 	mg_markup_item (submenu, _("<u>Underline</u>"), 101);
 	mg_markup_item (submenu, _("<i>Italic</i>"), 102);
-	mg_markup_item (submenu, _("Normal"), 103);
+	mg_markup_item (submenu, _("<s>Strikethrough</s>"), 103);
+	mg_markup_item (submenu, _("Normal"), 999);
 
 	subsubmenu = mg_submenu (submenu, _("Colors 0-7"));
 
diff --git a/src/fe-gtk/meson.build b/src/fe-gtk/meson.build
index 3dfc7427..d07514db 100644
--- a/src/fe-gtk/meson.build
+++ b/src/fe-gtk/meson.build
@@ -43,11 +43,7 @@ hexchat_gtk_cflags = []
 
 hexchat_gtk_ldflags = []
 
-if get_option('with-libnotify')
-  hexchat_gtk_sources += 'notifications/notification-libnotify.c'
-  hexchat_gtk_deps += dependency('libnotify')
-elif false # TODO HAVE_GTK_MAC
-elif host_machine.system() == 'windows'
+if host_machine.system() == 'windows'
   hexchat_gtk_sources += 'notifications/notification-windows.c'
 
   # TODO: mingw doesn't have these headers or libs
@@ -57,7 +53,7 @@ elif host_machine.system() == 'windows'
   #)
 
 else
-  hexchat_gtk_sources += 'notifications/notification-dummy.c'
+  hexchat_gtk_sources += 'notifications/notification-freedesktop.c'
 endif
 
 iso_codes = dependency('iso-codes', required: false)
@@ -69,7 +65,7 @@ if iso_codes.found()
                         join_paths(iso_codes_prefix, 'share/locale'))
 endif
 
-if get_option('with-plugin')
+if get_option('plugin')
   hexchat_gtk_sources += 'plugingui.c'
 endif
 
diff --git a/src/fe-gtk/notifications/notification-freedesktop.c b/src/fe-gtk/notifications/notification-freedesktop.c
new file mode 100644
index 00000000..a23284e5
--- /dev/null
+++ b/src/fe-gtk/notifications/notification-freedesktop.c
@@ -0,0 +1,148 @@
+/* HexChat
+ * Copyright (C) 2021 Patrick Griffis.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gio/gio.h>
+
+static GDBusProxy *fdo_notifications;
+static gboolean strip_markup;
+
+static void
+on_notify_ready (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
+{
+    GError *error = NULL;
+    guint32 notification_id;
+    GVariant *response = g_dbus_proxy_call_finish (proxy, res, &error);
+    if (error)
+    {
+        g_info ("Failed to send notification: %s", error->message);
+        g_error_free (error);
+        return;
+    }
+
+    g_variant_get (response, "(u)", &notification_id);
+    g_info ("Notification sent. ID=%u", notification_id);
+
+    g_variant_unref (response);
+}
+
+void
+notification_backend_show (const char *title, const char *text)
+{
+    GVariantBuilder params;
+
+    g_assert (fdo_notifications);
+
+    if (strip_markup)
+        text = g_markup_escape_text (text, -1);
+
+    g_variant_builder_init (&params, G_VARIANT_TYPE ("(susssasa{sv}i)"));
+    g_variant_builder_add (&params, "s", "hexchat"); /* App name */
+    g_variant_builder_add (&params, "u", 0); /* ID, 0 means don't replace */
+    g_variant_builder_add (&params, "s", "io.github.Hexchat"); /* App icon */
+    g_variant_builder_add (&params, "s", title);
+    g_variant_builder_add (&params, "s", text);
+    g_variant_builder_add (&params, "as", NULL); /* Actions */
+
+    /* Hints */
+    g_variant_builder_open (&params, G_VARIANT_TYPE ("a{sv}"));
+    g_variant_builder_open (&params, G_VARIANT_TYPE ("{sv}"));
+    g_variant_builder_add (&params, "s", "desktop-entry");
+    g_variant_builder_add (&params, "v", g_variant_new_string ("io.github.Hexchat"));
+    g_variant_builder_close (&params);
+    g_variant_builder_close (&params);
+
+    g_variant_builder_add (&params, "i", -1); /* Expiration */
+
+    g_dbus_proxy_call (fdo_notifications,
+                       "Notify",
+                       g_variant_builder_end (&params),
+                       G_DBUS_CALL_FLAGS_NONE,
+                       1000,
+                       NULL,
+                       (GAsyncReadyCallback)on_notify_ready,
+                       NULL);
+
+    if (strip_markup)
+        g_free ((char*)text);
+}
+
+int
+notification_backend_init (const char **error)
+{
+    GError *err = NULL;
+    GVariant *response;
+    char **capabilities;
+    guint i;
+
+    fdo_notifications = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+                                                       G_DBUS_PROXY_FLAGS_NONE,
+                                                       NULL,
+                                                       "org.freedesktop.Notifications",
+                                                       "/org/freedesktop/Notifications",
+                                                       "org.freedesktop.Notifications",
+                                                       NULL,
+                                                       &err);
+
+    if (err)
+        goto return_error;
+
+    response = g_dbus_proxy_call_sync (fdo_notifications,
+                                       "GetCapabilities",
+                                       NULL,
+                                       G_DBUS_CALL_FLAGS_NONE,
+                                       30,
+                                       NULL,
+                                       &err);
+
+    if (err)
+    {
+        g_clear_object (&fdo_notifications);
+        goto return_error;
+    }
+
+    g_variant_get (response, "(^a&s)", &capabilities);
+    for (i = 0; capabilities[i]; i++)
+    {
+        if (strcmp (capabilities[i], "body-markup") == 0)
+            strip_markup = TRUE;
+    }
+
+    g_free (capabilities);
+    g_variant_unref (response);
+    return 1;
+
+return_error:
+    *error = g_strdup (err->message);
+    g_error_free (err);
+    return 0;
+}
+
+void
+notification_backend_deinit (void)
+{
+    g_clear_object (&fdo_notifications);
+}
+
+int
+notification_backend_supported (void)
+{
+    return fdo_notifications != NULL;
+}
diff --git a/src/fe-gtk/notifications/notification-libnotify.c b/src/fe-gtk/notifications/notification-libnotify.c
deleted file mode 100644
index ee417396..00000000
--- a/src/fe-gtk/notifications/notification-libnotify.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/* HexChat
- * Copyright (C) 2015 Patrick Griffis.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include "config.h"
-#include <glib.h>
-#include <libnotify/notify.h>
-
-#ifndef NOTIFY_CHECK_VERSION
-#define NOTIFY_CHECK_VERSION(x,y,z) 0
-#endif
-
-static gboolean strip_markup = FALSE;
-
-void
-notification_backend_show (const char *title, const char *text)
-{
-	NotifyNotification *notification;
-
-	if (strip_markup)
-		text = g_markup_escape_text (text, -1);
-
-#if NOTIFY_CHECK_VERSION(0,7,0)
-	notification = notify_notification_new (title, text, "hexchat");
-#else
-	notification = notify_notification_new (title, text, "hexchat", NULL);
-#endif
-#if NOTIFY_CHECK_VERSION(0,6,0)
-	notify_notification_set_hint (notification, "desktop-entry", g_variant_new_string ("io.github.Hexchat"));
-#else
-	notify_notification_set_hint_string (notification, "desktop-entry", "io.github.Hexchat");
-#endif
-
-	notify_notification_show (notification, NULL);
-
-	g_object_unref (notification);
-	if (strip_markup)
-		g_free ((char*)text);
-}
-
-int
-notification_backend_init (const char **error)
-{
-	GList* server_caps;
-
-	if (!notify_init (PACKAGE_NAME))
-		return 0;
-
-	server_caps = notify_get_server_caps ();
-	if (g_list_find_custom (server_caps, "body-markup", (GCompareFunc)g_strcmp0))
-		strip_markup = TRUE;
-	g_list_free_full (server_caps, g_free);
-
-	return 1;
-}
-
-void
-notification_backend_deinit (void)
-{
-	notify_uninit ();
-}
-
-int
-notification_backend_supported (void)
-{
-	return notify_is_initted ();
-}
diff --git a/src/fe-gtk/plugin-notification.c b/src/fe-gtk/plugin-notification.c
index 29478d7a..875b50f4 100644
--- a/src/fe-gtk/plugin-notification.c
+++ b/src/fe-gtk/plugin-notification.c
@@ -218,7 +218,7 @@ notification_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name, cha
 	if (!notification_backend_init (&error))
 	{
 		if (error)
-			hexchat_printf(plugin_handle, "Failed loading notification plugin: %s\n", error);
+			g_debug("Failed loading notification plugin: %s\n", error);
 		return 0;
 	}
 
diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c
index b22330ac..edcd4609 100644
--- a/src/fe-gtk/servlistgui.c
+++ b/src/fe-gtk/servlistgui.c
@@ -39,6 +39,12 @@
 #define SERVLIST_X_PADDING 4			/* horizontal paddig in the network editor */
 #define SERVLIST_Y_PADDING 0			/* vertical padding in the network editor */
 
+#ifdef USE_OPENSSL
+# define DEFAULT_SERVER "newserver/6697"
+#else
+# define DEFAULT_SERVER "newserver/6667"
+#endif
+
 /* servlistgui.c globals */
 static GtkWidget *serverlist_win = NULL;
 static GtkWidget *networks_tree;		/* network TreeView */
@@ -299,7 +305,7 @@ servlist_networks_populate_ (GtkWidget *treeview, GSList *netlist, gboolean favo
 	if (!netlist)
 	{
 		net = servlist_net_add (_("New Network"), "", FALSE);
-		servlist_server_add (net, "newserver/6667");
+		servlist_server_add (net, DEFAULT_SERVER);
 		netlist = network_list;
 	}
 	store = (GtkListStore *)gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
@@ -434,10 +440,10 @@ servlist_addserver (void)
 		return;
 
 	store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (edit_trees[SERVER_TREE])));
-	servlist_server_add (selected_net, "newserver/6667");
+	servlist_server_add (selected_net, DEFAULT_SERVER);
 
 	gtk_list_store_append (store, &iter);
-	gtk_list_store_set (store, &iter, 0, "newserver/6667", 1, TRUE, -1);
+	gtk_list_store_set (store, &iter, 0, DEFAULT_SERVER, 1, TRUE, -1);
 
 	/* select this server */
 	servlist_select_and_show (GTK_TREE_VIEW (edit_trees[SERVER_TREE]), &iter, store);
@@ -498,7 +504,7 @@ servlist_addnet_cb (GtkWidget *item, GtkTreeView *treeview)
 
 	net = servlist_net_add (_("New Network"), "", TRUE);
 	net->encoding = g_strdup (IRC_DEFAULT_CHARSET);
-	servlist_server_add (net, "newserver/6667");
+	servlist_server_add (net, DEFAULT_SERVER);
 
 	store = (GtkListStore *)gtk_tree_view_get_model (treeview);
 	gtk_list_store_prepend (store, &iter);
diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c
index 3d003eef..a7e3a15c 100644
--- a/src/fe-gtk/setup.c
+++ b/src/fe-gtk/setup.c
@@ -614,9 +614,7 @@ static const char *const proxytypes[] =
 	N_("SOCKS4"),
 	N_("SOCKS5"),
 	N_("HTTP"),
-#ifdef USE_LIBPROXY
 	N_("Auto"),
-#endif
 	NULL
 };
 
diff --git a/src/fe-gtk/sexy-spell-entry.c b/src/fe-gtk/sexy-spell-entry.c
index dce19b82..04ff0f8a 100644
--- a/src/fe-gtk/sexy-spell-entry.c
+++ b/src/fe-gtk/sexy-spell-entry.c
@@ -390,6 +390,17 @@ insert_italic (SexySpellEntry *entry, guint start, gboolean toggle)
 }
 
 static void
+insert_strikethrough (SexySpellEntry *entry, guint start, gboolean toggle)
+{
+	PangoAttribute *sattr;
+
+	sattr  = pango_attr_strikethrough_new (!toggle);
+	sattr->start_index = start;
+	sattr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
+	pango_attr_list_change (entry->priv->attr_list, sattr);
+}
+
+static void
 insert_color (SexySpellEntry *entry, guint start, int fgcolor, int bgcolor)
 {
 	PangoAttribute *fgattr;
@@ -429,6 +440,7 @@ insert_reset (SexySpellEntry *entry, guint start)
 	insert_bold (entry, start, TRUE);
 	insert_underline (entry, start, TRUE);
 	insert_italic (entry, start, TRUE);
+	insert_strikethrough (entry, start, TRUE);
 	insert_color (entry, start, -1, -1);
 }
 
@@ -918,6 +930,7 @@ check_attributes (SexySpellEntry *entry, const char *text, int len)
 	gboolean bold = FALSE;
 	gboolean italic = FALSE;
 	gboolean underline = FALSE;
+	gboolean strikethrough = FALSE;
 	int parsing_color = 0;
 	char fg_color[3];
 	char bg_color[3];
@@ -942,6 +955,12 @@ check_attributes (SexySpellEntry *entry, const char *text, int len)
 			italic = !italic;
 			goto check_color;
 
+		case ATTR_STRIKETHROUGH:
+			insert_hiddenchar (entry, i, i + 1);
+			insert_strikethrough (entry, i, strikethrough);
+			strikethrough = !strikethrough;
+			goto check_color;
+
 		case ATTR_UNDERLINE:
 			insert_hiddenchar (entry, i, i + 1);
 			insert_underline (entry, i, underline);
@@ -954,6 +973,7 @@ check_attributes (SexySpellEntry *entry, const char *text, int len)
 			bold = FALSE;
 			italic = FALSE;
 			underline = FALSE;
+			strikethrough = FALSE;
 			goto check_color;
 
 		case ATTR_HIDDEN:
diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c
index 418bb4da..6a0fccba 100644
--- a/src/fe-gtk/xtext.c
+++ b/src/fe-gtk/xtext.c
@@ -433,6 +433,7 @@ gtk_xtext_init (GtkXText * xtext)
 	xtext->nc = 0;
 	xtext->pixel_offset = 0;
 	xtext->underline = FALSE;
+	xtext->strikethrough = FALSE;
 	xtext->hidden = FALSE;
 	xtext->font = NULL;
 	xtext->layout = NULL;
@@ -2451,6 +2452,7 @@ gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
 			case ATTR_REVERSE:
 			case ATTR_BOLD:
 			case ATTR_UNDERLINE:
+			case ATTR_STRIKETHROUGH:
 			case ATTR_ITALICS:
 				xtext_do_chunk (&c);
 				if (*text == ATTR_RESET)
@@ -2627,6 +2629,13 @@ gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
 		g_object_unref (pix);
 	}
 
+	if (xtext->strikethrough)
+	{
+		/* pango_attr_strikethrough_new does not render in the custom widget so we need to reinvent the wheel */
+		y = dest_y + (xtext->fontsize / 2);
+		gdk_draw_line (xtext->draw_buf, gc, dest_x, y, dest_x + str_width - 1, y);
+	}
+
 	if (xtext->underline)
 	{
 dounder:
@@ -2651,6 +2660,7 @@ gtk_xtext_reset (GtkXText * xtext, int mark, int attribs)
 	if (attribs)
 	{
 		xtext->underline = FALSE;
+		xtext->strikethrough = FALSE;
 		xtext->hidden = FALSE;
 	}
 	if (!mark)
@@ -2961,6 +2971,12 @@ gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent,
 				pstr += j + 1;
 				j = 0;
 				break;
+			case ATTR_STRIKETHROUGH:
+				RENDER_FLUSH;
+				xtext->strikethrough = !xtext->strikethrough;
+				pstr += j + 1;
+				j = 0;
+				break;
 			case ATTR_ITALICS:
 				RENDER_FLUSH;
 				*emphasis ^= EMPH_ITAL;
@@ -3191,6 +3207,7 @@ find_next_wrap (GtkXText * xtext, textentry * ent, unsigned char *str,
 			case ATTR_REVERSE:
 			case ATTR_BOLD:
 			case ATTR_UNDERLINE:
+			case ATTR_STRIKETHROUGH:
 			case ATTR_ITALICS:
 				if (*str == ATTR_RESET)
 					emphasis = 0;
diff --git a/src/fe-gtk/xtext.h b/src/fe-gtk/xtext.h
index 12d6f563..18d769fb 100644
--- a/src/fe-gtk/xtext.h
+++ b/src/fe-gtk/xtext.h
@@ -29,16 +29,17 @@
 #define GTK_IS_XTEXT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XTEXT))
 #define GTK_XTEXT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_XTEXT, GtkXTextClass))
 
-#define ATTR_BOLD			'\002'
-#define ATTR_COLOR		'\003'
-#define ATTR_BLINK		'\006'
-#define ATTR_BEEP			'\007'
-#define ATTR_HIDDEN		'\010'
-#define ATTR_ITALICS2	'\011'
-#define ATTR_RESET		'\017'
-#define ATTR_REVERSE		'\026'
-#define ATTR_ITALICS		'\035'
-#define ATTR_UNDERLINE	'\037'
+#define ATTR_BOLD				'\002'
+#define ATTR_COLOR			'\003'
+#define ATTR_BLINK			'\006'
+#define ATTR_BEEP				'\007'
+#define ATTR_HIDDEN			'\010'
+#define ATTR_ITALICS2		'\011'
+#define ATTR_RESET			'\017'
+#define ATTR_REVERSE			'\026'
+#define ATTR_ITALICS			'\035'
+#define ATTR_STRIKETHROUGH	'\036'
+#define ATTR_UNDERLINE		'\037'
 
 /* these match palette.h */
 #define XTEXT_MIRC_COLS 32
@@ -207,6 +208,7 @@ struct _GtkXText
 
 	/* current text states */
 	unsigned int underline:1;
+	unsigned int strikethrough:1;
 	unsigned int hidden:1;
 
 	/* text parsing states */