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/chanlist.c2
-rw-r--r--src/fe-gtk/dccgui.c2
-rw-r--r--src/fe-gtk/fe-gtk.c56
-rw-r--r--src/fe-gtk/fkeys.c2
-rw-r--r--src/fe-gtk/gtkutil.c12
-rw-r--r--src/fe-gtk/gtkutil.h2
-rw-r--r--src/fe-gtk/maingui.c65
-rw-r--r--src/fe-gtk/menu.c2
-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.c4
-rw-r--r--src/fe-gtk/plugingui.c2
-rw-r--r--src/fe-gtk/rawlog.c2
-rw-r--r--src/fe-gtk/servlistgui.c22
-rw-r--r--src/fe-gtk/setup.c21
-rw-r--r--src/fe-gtk/sexy-spell-entry.c32
-rw-r--r--src/fe-gtk/textgui.c4
-rw-r--r--src/fe-gtk/urlgrab.c2
-rw-r--r--src/fe-gtk/xtext.c22
-rw-r--r--src/fe-gtk/xtext.h22
21 files changed, 346 insertions, 169 deletions
diff --git a/src/fe-gtk/chanlist.c b/src/fe-gtk/chanlist.c
index aeddc417..abf62843 100644
--- a/src/fe-gtk/chanlist.c
+++ b/src/fe-gtk/chanlist.c
@@ -512,7 +512,7 @@ chanlist_save (GtkWidget * wid, server *serv)
 	GtkTreeModel *model = GET_MODEL (serv);
 
 	if (gtk_tree_model_get_iter_first (model, &iter))
-		gtkutil_file_req (_("Select an output filename"), chanlist_filereq_done,
+		gtkutil_file_req (NULL, _("Select an output filename"), chanlist_filereq_done,
 								serv, NULL, NULL, FRF_WRITE);
 }
 
diff --git a/src/fe-gtk/dccgui.c b/src/fe-gtk/dccgui.c
index 5b8dbac9..728698e3 100644
--- a/src/fe-gtk/dccgui.c
+++ b/src/fe-gtk/dccgui.c
@@ -146,7 +146,7 @@ fe_dcc_send_filereq (struct session *sess, char *nick, int maxcps, int passive)
 	mdc->maxcps = maxcps;
 	mdc->passive = passive;
 
-	gtkutil_file_req (tbuf, dcc_send_filereq_file, mdc, prefs.hex_dcc_dir, NULL, FRF_MULTIPLE|FRF_FILTERISINITIAL);
+	gtkutil_file_req (NULL, tbuf, dcc_send_filereq_file, mdc, prefs.hex_dcc_dir, NULL, FRF_MULTIPLE|FRF_FILTERISINITIAL);
 
 	g_free (tbuf);
 }
diff --git a/src/fe-gtk/fe-gtk.c b/src/fe-gtk/fe-gtk.c
index ee3e847c..125ab577 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
@@ -903,7 +903,7 @@ fe_confirm (const char *message, void (*yesproc)(void *), void (*noproc)(void *)
 	if (dcc->file)
 	{
 		char *filepath = g_build_filename (prefs.hex_dcc_dir, dcc->file, NULL);
-		gtkutil_file_req (message, dcc_saveas_cb, ud, filepath, NULL,
+		gtkutil_file_req (NULL, message, dcc_saveas_cb, ud, filepath, NULL,
 								FRF_WRITE|FRF_NOASKOVERWRITE|FRF_FILTERISINITIAL);
 		g_free (filepath);
 	}
@@ -1054,6 +1054,45 @@ 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)
+	{
+		if (!g_ascii_isalnum (*uri) && !strchr ("-._~:/?#[]@!$&'()*+,;=", *uri))
+			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 +1110,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
 }
 
@@ -1173,7 +1215,7 @@ fe_get_file (const char *title, char *initial,
 {
 	/* OK: Call callback once per file, then once more with file=NULL. */
 	/* CANCEL: Call callback once with file=NULL. */
-	gtkutil_file_req (title, callback, userdata, initial, NULL, flags | FRF_FILTERISINITIAL);
+	gtkutil_file_req (NULL, title, callback, userdata, initial, NULL, flags | FRF_FILTERISINITIAL);
 }
 
 void
diff --git a/src/fe-gtk/fkeys.c b/src/fe-gtk/fkeys.c
index dc4b41bc..6dd16e35 100644
--- a/src/fe-gtk/fkeys.c
+++ b/src/fe-gtk/fkeys.c
@@ -894,7 +894,7 @@ key_save_kbs (void)
 #define STRIP_WHITESPACE \
 	while (buf[0] == ' ' || buf[0] == '\t') \
 		buf++; \
-		len = strlen (buf); \
+	len = strlen (buf); \
 	while (buf[len] == ' ' || buf[len] == '\t') \
 	{ \
 		buf[len] = 0; \
diff --git a/src/fe-gtk/gtkutil.c b/src/fe-gtk/gtkutil.c
index 674ad4fc..98a971f9 100644
--- a/src/fe-gtk/gtkutil.c
+++ b/src/fe-gtk/gtkutil.c
@@ -190,7 +190,7 @@ gtkutil_file_req_response (GtkWidget *dialog, gint res, struct file_req *freq)
 }
 
 void
-gtkutil_file_req (const char *title, void *callback, void *userdata, char *filter, char *extensions,
+gtkutil_file_req (GtkWindow *parent, const char *title, void *callback, void *userdata, char *filter, char *extensions,
 						int flags)
 {
 	struct file_req *freq;
@@ -269,6 +269,16 @@ gtkutil_file_req (const char *title, void *callback, void *userdata, char *filte
 							G_CALLBACK (gtkutil_file_req_response), freq);
 	g_signal_connect (G_OBJECT (dialog), "destroy",
 						   G_CALLBACK (gtkutil_file_req_destroy), (gpointer) freq);
+
+	if (parent)
+		gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
+	if (flags & FRF_MODAL)
+	{
+		g_assert (parent);
+		gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+	}
+
 	gtk_widget_show (dialog);
 }
 
diff --git a/src/fe-gtk/gtkutil.h b/src/fe-gtk/gtkutil.h
index 0aa36439..c6e380e9 100644
--- a/src/fe-gtk/gtkutil.h
+++ b/src/fe-gtk/gtkutil.h
@@ -25,7 +25,7 @@
 
 typedef void (*filereqcallback) (void *, char *file);
 
-void gtkutil_file_req (const char *title, void *callback, void *userdata, char *filter, char *extensions, int flags);
+void gtkutil_file_req (GtkWindow *parent, const char *title, void *callback, void *userdata, char *filter, char *extensions, int flags);
 void gtkutil_destroy (GtkWidget * igad, GtkWidget * dgad);
 void gtkutil_destroy_on_esc (GtkWidget *win);
 GtkWidget *gtkutil_button (GtkWidget *box, char *stock, char *tip, void *callback,
diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c
index 4e5baaa0..a3e633bc 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)
@@ -391,27 +399,22 @@ fe_set_title (session *sess)
 					 _(DISPLAY_NAME));
 		break;
 	case SESS_SERVER:
-		g_snprintf (tbuf, sizeof (tbuf), "%s @ %s - %s",
-					 sess->server->nick, server_get_network (sess->server, TRUE),
+		g_snprintf (tbuf, sizeof (tbuf), "%s%s%s - %s",
+					 prefs.hex_gui_win_nick ? sess->server->nick : "",
+					 prefs.hex_gui_win_nick ? " @ " : "", server_get_network (sess->server, TRUE),
 					 _(DISPLAY_NAME));
 		break;
 	case SESS_CHANNEL:
 		/* don't display keys in the titlebar */
-		if (prefs.hex_gui_win_modes)
-		{
-			g_snprintf (tbuf, sizeof (tbuf),
-						 "%s @ %s / %s (%s) - %s",
-						 sess->server->nick, server_get_network (sess->server, TRUE),
-						 sess->channel, sess->current_modes ? sess->current_modes : "",
-						 _(DISPLAY_NAME));
-		}
-		else
-		{
 			g_snprintf (tbuf, sizeof (tbuf),
-						 "%s @ %s / %s - %s",
-						 sess->server->nick, server_get_network (sess->server, TRUE),
-						 sess->channel, _(DISPLAY_NAME));
-		}
+					 "%s%s%s / %s%s%s%s - %s",
+					 prefs.hex_gui_win_nick ? sess->server->nick : "",
+					 prefs.hex_gui_win_nick ? " @ " : "",
+					 server_get_network (sess->server, TRUE), sess->channel,
+					 prefs.hex_gui_win_modes && sess->current_modes ? " (" : "",
+					 prefs.hex_gui_win_modes && sess->current_modes ? sess->current_modes : "",
+					 prefs.hex_gui_win_modes && sess->current_modes ? ")" : "",
+					 _(DISPLAY_NAME));
 		if (prefs.hex_gui_win_ucount)
 		{
 			g_snprintf (tbuf + strlen (tbuf), 9, " (%d)", sess->total);
@@ -419,8 +422,9 @@ fe_set_title (session *sess)
 		break;
 	case SESS_NOTICES:
 	case SESS_SNOTICES:
-		g_snprintf (tbuf, sizeof (tbuf), "%s @ %s (notices) - %s",
-					 sess->server->nick, server_get_network (sess->server, TRUE),
+		g_snprintf (tbuf, sizeof (tbuf), "%s%s%s (notices) - %s",
+					 prefs.hex_gui_win_nick ? sess->server->nick : "",
+					 prefs.hex_gui_win_nick ? " @ " : "", server_get_network (sess->server, TRUE),
 					 _(DISPLAY_NAME));
 		break;
 	default:
@@ -540,7 +544,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
@@ -955,7 +959,7 @@ mg_populate (session *sess)
 
 	mg_set_topic_tip (sess);
 
-	plugin_emit_dummy_print (sess, "Focus Tab");
+	plugin_emit_dummy_print (sess, "Focus Tab", -1);
 }
 
 void
@@ -1394,6 +1398,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 +1453,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"));
 
@@ -3070,7 +3077,7 @@ mg_tabwin_focus_cb (GtkWindow * win, GdkEventFocus *event, gpointer userdata)
 	if (current_sess)
 	{
 		gtk_xtext_check_marker_visibility (GTK_XTEXT (current_sess->gui->xtext));
-		plugin_emit_dummy_print (current_sess, "Focus Window");
+		plugin_emit_dummy_print (current_sess, "Focus Window", -1);
 	}
 	unflash_window (GTK_WIDGET (win));
 	return FALSE;
@@ -3084,7 +3091,7 @@ mg_topwin_focus_cb (GtkWindow * win, GdkEventFocus *event, session *sess)
 		sess->server->server_session = sess;
 	gtk_xtext_check_marker_visibility(GTK_XTEXT (current_sess->gui->xtext));
 	unflash_window (GTK_WIDGET (win));
-	plugin_emit_dummy_print (sess, "Focus Window");
+	plugin_emit_dummy_print (sess, "Focus Window", -1);
 	return FALSE;
 }
 
diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c
index 233715e5..76bc3906 100644
--- a/src/fe-gtk/menu.c
+++ b/src/fe-gtk/menu.c
@@ -1362,7 +1362,7 @@ savebuffer_req_done (session *sess, char *file)
 static void
 menu_savebuffer (GtkWidget * wid, gpointer none)
 {
-	gtkutil_file_req (_("Select an output filename"), savebuffer_req_done,
+	gtkutil_file_req (NULL, _("Select an output filename"), savebuffer_req_done,
 							current_sess, NULL, NULL, FRF_WRITE);
 }
 
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..fc71d22b 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;
 	}
 
@@ -246,7 +246,7 @@ notification_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name, cha
 
 
 int
-notification_plugin_deinit (void)
+notification_plugin_deinit (void *unused_param)
 {
 	notification_backend_deinit ();
 	return 1;
diff --git a/src/fe-gtk/plugingui.c b/src/fe-gtk/plugingui.c
index 83bb745f..c40ac304 100644
--- a/src/fe-gtk/plugingui.c
+++ b/src/fe-gtk/plugingui.c
@@ -161,7 +161,7 @@ plugingui_load (void)
 {
 	char *sub_dir = g_build_filename (get_xdir(), "addons", NULL);
 
-	gtkutil_file_req (_("Select a Plugin or Script to load"), plugingui_load_cb, current_sess,
+	gtkutil_file_req (NULL, _("Select a Plugin or Script to load"), plugingui_load_cb, current_sess,
 							sub_dir, "*."PLUGIN_SUFFIX";*.lua;*.pl;*.py;*.tcl;*.js", FRF_FILTERISINITIAL|FRF_EXTENSIONS);
 
 	g_free (sub_dir);
diff --git a/src/fe-gtk/rawlog.c b/src/fe-gtk/rawlog.c
index 52a77267..666059c6 100644
--- a/src/fe-gtk/rawlog.c
+++ b/src/fe-gtk/rawlog.c
@@ -77,7 +77,7 @@ rawlog_clearbutton (GtkWidget * wid, server *serv)
 static int
 rawlog_savebutton (GtkWidget * wid, server *serv)
 {
-	gtkutil_file_req (_("Save As..."), rawlog_save, serv, NULL, NULL, FRF_WRITE);
+	gtkutil_file_req (NULL, _("Save As..."), rawlog_save, serv, NULL, NULL, FRF_WRITE);
 	return FALSE;
 }
 
diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c
index b22330ac..0e5e108b 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 */
@@ -122,6 +128,9 @@ static int login_types_conf[] =
 	LOGIN_SASL,
 #ifdef USE_OPENSSL
 	LOGIN_SASLEXTERNAL,
+	LOGIN_SASL_SCRAM_SHA_1,
+	LOGIN_SASL_SCRAM_SHA_256,
+	LOGIN_SASL_SCRAM_SHA_512,
 #endif
 	LOGIN_PASS,
 	LOGIN_MSG_NICKSERV,
@@ -140,9 +149,12 @@ static int login_types_conf[] =
 static const char *login_types[]=
 {
 	"Default",
-	"SASL (username + password)",
+	"SASL PLAIN (username + password)",
 #ifdef USE_OPENSSL
 	"SASL EXTERNAL (cert)",
+	"SASL SCRAM-SHA-1",
+	"SASL SCRAM-SHA-256",
+	"SASL SCRAM-SHA-512",
 #endif
 	"Server password (/PASS password)",
 	"NickServ (/MSG NickServ + password)",
@@ -299,7 +311,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 +446,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 +510,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..0e1dfde3 100644
--- a/src/fe-gtk/setup.c
+++ b/src/fe-gtk/setup.c
@@ -48,6 +48,7 @@ GtkStyle *create_input_style (GtkStyle *);
 
 #define LABEL_INDENT 12
 
+static GtkWidget *setup_window = NULL;
 static int last_selected_page = 0;
 static int last_selected_row = 0; /* sound row */
 static gboolean color_change;
@@ -175,6 +176,7 @@ static const setting appearance_settings[] =
 	{ST_HEADER,	N_("Title Bar"),0,0,0},
 	{ST_TOGGLE, N_("Show channel modes"), P_OFFINTNL(hex_gui_win_modes),0,0,0},
 	{ST_TOGGLR, N_("Show number of users"), P_OFFINTNL(hex_gui_win_ucount),0,0,0},
+	{ST_TOGGLE, N_("Show nickname"), P_OFFINTNL(hex_gui_win_nick),0,0,0},
 
 	{ST_END, 0, 0, 0, 0, 0}
 };
@@ -614,9 +616,7 @@ static const char *const proxytypes[] =
 	N_("SOCKS4"),
 	N_("SOCKS5"),
 	N_("HTTP"),
-#ifdef USE_LIBPROXY
 	N_("Auto"),
-#endif
 	NULL
 };
 
@@ -1107,8 +1107,8 @@ setup_browsefile_cb (GtkWidget *button, GtkWidget *entry)
 	filter = "image/*";
 	filter_type = FRF_MIMETYPES;
 #endif
-	gtkutil_file_req (_("Select an Image File"), setup_filereq_cb,
-					entry, NULL, filter, filter_type|FRF_RECENTLYUSED);
+	gtkutil_file_req (GTK_WINDOW (setup_window), _("Select an Image File"), setup_filereq_cb,
+					entry, NULL, filter, filter_type|FRF_RECENTLYUSED|FRF_MODAL);
 }
 
 static void
@@ -1143,7 +1143,7 @@ setup_fontsel_cancel (GtkWidget *button, GtkFontSelectionDialog *dialog)
 static void
 setup_browsefolder_cb (GtkWidget *button, GtkEntry *entry)
 {
-	gtkutil_file_req (_("Select Download Folder"), setup_filereq_cb, entry, (char*)gtk_entry_get_text (entry), NULL, FRF_CHOOSEFOLDER);
+	gtkutil_file_req (GTK_WINDOW (setup_window), _("Select Download Folder"), setup_filereq_cb, entry, (char*)gtk_entry_get_text (entry), NULL, FRF_CHOOSEFOLDER|FRF_MODAL);
 }
 
 static void
@@ -1156,6 +1156,9 @@ setup_browsefont_cb (GtkWidget *button, GtkWidget *entry)
 	dialog = (GtkFontSelectionDialog *) gtk_font_selection_dialog_new (_("Select font"));
 	font_dialog = (GtkWidget *)dialog;	/* global var */
 
+	gtk_window_set_transient_for (GTK_WINDOW (font_dialog), GTK_WINDOW (setup_window));
+	gtk_window_set_modal (GTK_WINDOW (font_dialog), TRUE);
+
 	sel = (GtkFontSelection *) gtk_font_selection_dialog_get_font_selection (dialog);
 
 	if (gtk_entry_get_text (GTK_ENTRY (entry))[0])
@@ -1459,6 +1462,8 @@ setup_color_cb (GtkWidget *button, gpointer userdata)
 	g_object_set_data (G_OBJECT (ok_button), "b", button);
 	gtk_widget_set_sensitive (help_button, FALSE);
 	gtk_color_selection_set_current_color (GTK_COLOR_SELECTION (gtk_color_selection_dialog_get_color_selection (cdialog)), color);
+	gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (setup_window));
+	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
 	gtk_widget_show (dialog);
 
 	g_object_unref (cancel_button);
@@ -1713,8 +1718,8 @@ setup_snd_browse_cb (GtkWidget *button, GtkEntry *entry)
 	filter_type = FRF_MIMETYPES;
 #endif
 
-	gtkutil_file_req (_("Select a sound file"), setup_snd_filereq_cb, entry,
-						sounds_dir, filter, FRF_FILTERISINITIAL|filter_type);
+	gtkutil_file_req (GTK_WINDOW (setup_window), _("Select a sound file"), setup_snd_filereq_cb, entry,
+						sounds_dir, filter, FRF_MODAL|FRF_FILTERISINITIAL|filter_type);
 	g_free (sounds_dir);
 }
 
@@ -2338,8 +2343,6 @@ setup_close_cb (GtkWidget *win, GtkWidget **swin)
 void
 setup_open (void)
 {
-	static GtkWidget *setup_window = NULL;
-
 	if (setup_window)
 	{
 		gtk_window_present (GTK_WINDOW (setup_window));
diff --git a/src/fe-gtk/sexy-spell-entry.c b/src/fe-gtk/sexy-spell-entry.c
index dce19b82..a3042783 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:
@@ -1235,7 +1255,7 @@ void
 sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
 {
 	GSList *enchant_langs;
-	char *lang, *langs;
+	char *lang, **i, **langs;
 
 	if (!have_enchant)
 		return;
@@ -1245,21 +1265,21 @@ sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
 
 	enchant_langs = sexy_spell_entry_get_languages(entry);
 
-	langs = g_strdup (prefs.hex_text_spell_langs);
+	langs = g_strsplit_set (prefs.hex_text_spell_langs, ", \t", 0);
 
-	lang = strtok (langs, ",");
-	while (lang != NULL)
+	for (i = langs; *i; i++)
 	{
+		lang = *i;
+
 		if (enchant_has_lang (lang, enchant_langs))
 		{
 			sexy_spell_entry_activate_language_internal (entry, lang, NULL);
 		}
-		lang = strtok (NULL, ",");
 	}
 
 	g_slist_foreach(enchant_langs, (GFunc) g_free, NULL);
 	g_slist_free(enchant_langs);
-	g_free (langs);
+	g_strfreev (langs);
 
 	/* If we don't have any languages activated, use "en" */
 	if (entry->priv->dict_list == NULL)
diff --git a/src/fe-gtk/textgui.c b/src/fe-gtk/textgui.c
index b0f2f392..b5eaf893 100644
--- a/src/fe-gtk/textgui.c
+++ b/src/fe-gtk/textgui.c
@@ -282,7 +282,7 @@ pevent_save_cb (GtkWidget * wid, void *data)
 {
 	if (data)
 	{
-		gtkutil_file_req (_("Print Texts File"), pevent_save_req_cb, NULL,
+		gtkutil_file_req (NULL, _("Print Texts File"), pevent_save_req_cb, NULL,
 								NULL, NULL, FRF_WRITE);
 		return;
 	}
@@ -304,7 +304,7 @@ pevent_load_req_cb (void *arg1, char *file)
 static void
 pevent_load_cb (GtkWidget * wid, void *data)
 {
-	gtkutil_file_req (_("Print Texts File"), pevent_load_req_cb, NULL, NULL, NULL, 0);
+	gtkutil_file_req (NULL, _("Print Texts File"), pevent_load_req_cb, NULL, NULL, NULL, 0);
 }
 
 static void
diff --git a/src/fe-gtk/urlgrab.c b/src/fe-gtk/urlgrab.c
index fd8d8d91..fc2f4b5a 100644
--- a/src/fe-gtk/urlgrab.c
+++ b/src/fe-gtk/urlgrab.c
@@ -145,7 +145,7 @@ url_save_callback (void *arg1, char *file)
 static void
 url_button_save (void)
 {
-	gtkutil_file_req (_("Select an output filename"),
+	gtkutil_file_req (NULL, _("Select an output filename"),
 							url_save_callback, NULL, NULL, NULL, FRF_WRITE);
 }
 
diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c
index 418bb4da..be978f22 100644
--- a/src/fe-gtk/xtext.c
+++ b/src/fe-gtk/xtext.c
@@ -170,7 +170,8 @@ xtext_pango_attr (PangoAttribute *attr)
 static void
 xtext_pango_init (GtkXText *xtext)
 {
-	int i, j;
+	size_t i;
+	int j;
 	char buf[2] = "\000";
 
 	if (attr_lists[0])
@@ -433,6 +434,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;
@@ -946,7 +948,7 @@ gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off, int *out_of_bound
 	textentry *ent;
 	int line;
 	int subline;
-	int outofbounds;
+	int outofbounds = FALSE;
 
 	/* Adjust y value for negative rounding, double to int */
 	if (y < 0)
@@ -2451,6 +2453,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 +2630,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 +2661,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 +2972,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 +3208,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 */