# HG changeset patch
# Node ID 6d9e1ca52ee7
# Parent  da54a3c1e468
diff --git a/pidgin/plugins/meson.build b/pidgin/plugins/meson.build
--- a/pidgin/plugins/meson.build
+++ b/pidgin/plugins/meson.build
@@ -28,14 +28,11 @@ spellchk = library('spellchk', 'spellchk
     build_by_default: false,
     install : false, install_dir : PIDGIN_PLUGINDIR)
 
-if IS_WIN32
-	transparency = library('transparency', 'transparency.c',
-		c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-Transparency"'],
-		dependencies : [libpurple_dep, libpidgin_dep, glib],
-		name_prefix : '',
-		build_by_default: false,
-		install : true, install_dir : PIDGIN_PLUGINDIR)
-endif
+transparency = library('transparency', 'transparency.c',
+    c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-Transparency"'],
+    dependencies : [libpurple_dep, libpidgin_dep, glib],
+    name_prefix : '',
+    install : true, install_dir : PIDGIN_PLUGINDIR)
 
 if enable_unity
 	unity = library('unity', 'unity.c',
diff --git a/pidgin/plugins/transparency.c b/pidgin/plugins/transparency.c
--- a/pidgin/plugins/transparency.c
+++ b/pidgin/plugins/transparency.c
@@ -39,13 +39,11 @@
  * loading for users who were using it. */
 #define WINTRANS_PLUGIN_ID	"gtk-win-trans"
 
-/*
- *  DATA STRUCTS
- */
-typedef struct {
-	GtkWidget *win;
-	GtkWidget *slider;
-} slider_win;
+/* Key used to save GtkEventControllerFocus for this plugin. */
+#define WINTRANS_CONTROLLER_KEY (WINTRANS_PLUGIN_ID "-focus-controller")
+
+/* Key used to save GtkSlider for this plugin. */
+#define WINTRANS_SLIDER_KEY (WINTRANS_PLUGIN_ID "-slider")
 
 /*
  *  LOCALS
@@ -54,12 +52,9 @@ static const char *OPT_WINTRANS_IM_ENABL
 static const char *OPT_WINTRANS_IM_ALPHA  = "/plugins/gtk/transparency/im_alpha";
 static const char *OPT_WINTRANS_IM_SLIDER = "/plugins/gtk/transparency/im_slider";
 static const char *OPT_WINTRANS_IM_ONFOCUS= "/plugins/gtk/transparency/im_solid_onfocus";
-static const char *OPT_WINTRANS_IM_ONTOP  = "/plugins/gtk/transparency/im_always_on_top";
 static const char *OPT_WINTRANS_BL_ENABLED= "/plugins/gtk/transparency/bl_enabled";
 static const char *OPT_WINTRANS_BL_ALPHA  = "/plugins/gtk/transparency/bl_alpha";
 static const char *OPT_WINTRANS_BL_ONFOCUS= "/plugins/gtk/transparency/bl_solid_onfocus";
-static const char *OPT_WINTRANS_BL_ONTOP  = "/plugins/gtk/transparency/bl_always_on_top";
-static GSList *window_list = NULL;
 
 /*
  *  CODE
@@ -80,55 +75,81 @@ get_buddy_list_window(void) {
 }
 
 /* Set window transparency level */
-static void set_wintrans(GtkWidget *window, int alpha, gboolean enabled,
-		gboolean always_on_top) {
-	GdkWindow *gdk_window = gtk_widget_get_window(window);
-
+static void
+set_wintrans(GtkWidget *window, int alpha, gboolean enabled)
+{
 	if (enabled) {
-		gdk_window_set_opacity(gdk_window, alpha / 255.0);
-		gdk_window_set_keep_above(gdk_window, always_on_top);
+		gtk_widget_set_opacity(window, alpha / 255.0);
 	} else {
-		gdk_window_set_opacity(gdk_window, 1);
-		gdk_window_set_keep_above(gdk_window, 0);
+		gtk_widget_set_opacity(window, 1);
 	}
+
+	/* Changing from opaque to partially transparent seems to need some kind of
+	 * structural refresh. Unfortunately, a simple `gtk_widget_queue_draw` is
+	 * not sufficient, so we need to do this instead. */
+	gtk_widget_queue_resize(window);
 }
 
 /* When a conv window is focused, if we're only transparent when unfocused,
  * deal with transparency */
-static gboolean focus_conv_win_cb(GtkWidget *w, GdkEventFocus *e, gpointer d) {
-	if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)
-			&& purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS)) {
-		GtkWidget *window = (GtkWidget *) d;
-		if (e->in) { /* Focused */
-			set_wintrans(window, 0, FALSE,
-				purple_prefs_get_bool(OPT_WINTRANS_IM_ONTOP));
-		} else {
-			set_wintrans(window,
-				purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA),
-				TRUE,
-				purple_prefs_get_bool(OPT_WINTRANS_IM_ONTOP));
-		}
+static void
+focus_conv_win_cb(GtkEventControllerFocus *self, gpointer data) {
+	GtkWidget *window = NULL;
+	gboolean enter = GPOINTER_TO_INT(data);
+
+	if(!purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) {
+		return;
+	}
+	if(!purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS)) {
+		return;
 	}
-	return FALSE;
+
+	window = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self));
+	set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA), !enter);
+}
+
+static void
+add_focus_controller_to_conv_win(GtkWidget *window) {
+	GtkEventController *focus = NULL;
+
+	focus = gtk_event_controller_focus_new();
+	g_signal_connect(focus, "enter", G_CALLBACK(focus_conv_win_cb),
+	                 GINT_TO_POINTER(TRUE));
+	g_signal_connect(focus, "leave", G_CALLBACK(focus_conv_win_cb),
+	                 GINT_TO_POINTER(FALSE));
+
+	gtk_widget_add_controller(window, focus);
+	g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, focus);
+}
+
+static void
+remove_focus_controller_from_conv_win(GtkWidget *window) {
+	GtkEventController *focus = NULL;
+
+	focus = g_object_get_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY);
+	if(GTK_IS_EVENT_CONTROLLER_FOCUS(focus)) {
+		gtk_widget_remove_controller(window, focus);
+	}
+	g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, NULL);
 }
 
 /* When buddy list window is focused,
  * if we're only transparent when unfocused, deal with transparency */
-static gboolean focus_blist_win_cb(GtkWidget *w, GdkEventFocus *e, gpointer d) {
-	if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)
-			&& purple_prefs_get_bool(OPT_WINTRANS_BL_ONFOCUS)) {
-		GtkWidget *window = (GtkWidget *) d;
-		if (e->in) { /* Focused */
-			set_wintrans(window, 0, FALSE,
-				purple_prefs_get_bool(OPT_WINTRANS_BL_ONTOP));
-		} else {
-			set_wintrans(window,
-				purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA),
-				TRUE,
-				purple_prefs_get_bool(OPT_WINTRANS_BL_ONTOP));
-		}
+static void
+focus_blist_win_cb(GtkEventControllerFocus *self, gpointer data)
+{
+	GtkWidget *window = NULL;
+	gboolean enter = GPOINTER_TO_INT(data);
+
+	if(!purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) {
+		return;
 	}
-	return FALSE;
+	if(!purple_prefs_get_bool(OPT_WINTRANS_BL_ONFOCUS)) {
+		return;
+	}
+
+	window = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self));
+	set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA), !enter);
 }
 
 static void change_alpha(GtkWidget *w, gpointer data) {
@@ -137,84 +158,27 @@ static void change_alpha(GtkWidget *w, g
 
 	/* If we're in no-transparency on focus mode,
 	 * don't take effect immediately */
-	if (!purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS))
-		set_wintrans(GTK_WIDGET(data), alpha, TRUE,
-			purple_prefs_get_bool(OPT_WINTRANS_IM_ONTOP));
-}
-
-
-static GtkWidget *wintrans_slider(GtkWidget *win) {
-	GtkWidget *hbox;
-	GtkWidget *label, *slider;
-	GtkWidget *frame;
-
-	int imalpha = purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA);
-
-	frame = gtk_frame_new(NULL);
-	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
-	gtk_widget_show(frame);
-
-	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
-	gtk_container_add(GTK_CONTAINER(frame), hbox);
-
-	label = gtk_label_new(_("Opacity:"));
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
-	gtk_widget_show(hbox);
-
-	slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1);
-	gtk_range_set_value(GTK_RANGE(slider), imalpha);
-
-	/* On slider val change, update window's transparency level */
-	g_signal_connect(G_OBJECT(slider), "value-changed",
-		G_CALLBACK(change_alpha), win);
-
-	gtk_box_pack_start(GTK_BOX(hbox), slider, FALSE, TRUE, 5);
-
-	/* Set the initial transparency level */
-	set_wintrans(win, imalpha, TRUE,
-		purple_prefs_get_bool(OPT_WINTRANS_IM_ONTOP));
-
-	gtk_widget_show_all(hbox);
-
-	return frame;
-}
-
-static slider_win* find_slidwin(GtkWidget *win) {
-	GSList *tmp = window_list;
-
-	while (tmp) {
-		if (((slider_win*) (tmp->data))->win == win)
-			return (slider_win*) tmp->data;
-		tmp = tmp->next;
+	if (!purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS)) {
+		set_wintrans(GTK_WIDGET(data), alpha, TRUE);
 	}
-	return NULL;
 }
 
 /* Clean up transparency stuff for the conv window */
-static void cleanup_conv_window(PidginConvWindow *win) {
-	GtkWidget *window = win->window;
-	slider_win *slidwin = NULL;
-
-	/* Remove window from the window list */
-	purple_debug_info(WINTRANS_PLUGIN_ID,
-		"Conv window destroyed... removing from list\n");
-
-	if ((slidwin = find_slidwin(window))) {
-		window_list = g_slist_remove(window_list, slidwin);
-		g_free(slidwin);
+static void
+conversation_delete_cb(G_GNUC_UNUSED GtkApplication *application,
+                       GtkWindow *window, G_GNUC_UNUSED gpointer data)
+{
+	if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
+		return;
 	}
 
-	/* Remove the focus cbs */
-	g_signal_handlers_disconnect_by_func(G_OBJECT(window),
-		G_CALLBACK(focus_conv_win_cb), window);
-}
+	purple_debug_info(WINTRANS_PLUGIN_ID,
+	                  "Conv window destroyed... removing from list");
 
-static void
-conversation_delete_cb(PurpleConversation *conv) {
-	PidginConvWindow *win = pidgin_conv_get_window(PIDGIN_CONVERSATION(conv));
-	/* If it is the last conversation in the window, cleanup */
-	if (win != NULL && pidgin_conv_window_get_gtkconv_count(win) == 1)
-		cleanup_conv_window(win);
+	g_object_set_data(G_OBJECT(window), WINTRANS_SLIDER_KEY, NULL);
+
+	/* Remove the focus cbs */
+	remove_focus_controller_from_conv_win(GTK_WIDGET(window));
 }
 
 static void set_blist_trans(GtkWidget *w, const char *pref) {
@@ -223,110 +187,120 @@ static void set_blist_trans(GtkWidget *w
 	purple_prefs_set_bool(pref, enabled);
 	if (window != NULL) {
 		set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA),
-			purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED),
-			purple_prefs_get_bool(OPT_WINTRANS_IM_ONTOP));
+			purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED));
 	}
 }
 
+static void
+remove_slider(GtkWidget *slider_frame) {
+	purple_prefs_disconnect_by_handle(slider_frame);
+	gtk_widget_unparent(slider_frame);
+}
+
+static void
+update_slider(G_GNUC_UNUSED const gchar *name,
+              G_GNUC_UNUSED PurplePrefType type,
+              gconstpointer val, gpointer data)
+{
+	GtkWidget *slider = data;
+	gint imalpha = GPOINTER_TO_INT(val);
+
+	g_message("update_slider to %d", imalpha);
+	gtk_range_set_value(GTK_RANGE(slider), imalpha);
+}
+
 static void add_slider(GtkWidget *win) {
-	GList *wl, *wl1;
 	GtkWidget *vbox = NULL;
+	GtkWidget *slider_frame;
+	GtkWidget *hbox;
+	GtkWidget *label, *slider;
+	gint imalpha = 255;
 
 	/* Look up this window to see if it already has a slider */
-	if (!find_slidwin(win)) {
-		GtkWidget *slider_box = NULL;
-		slider_win *slidwin = NULL;
+	if (g_object_get_data(G_OBJECT(win), WINTRANS_SLIDER_KEY) != NULL) {
+		return;
+	}
+
+	vbox = gtk_widget_get_first_child(win);
+	while(vbox != NULL && !GTK_IS_BOX(vbox)) {
+		vbox = gtk_widget_get_next_sibling(vbox);
+	}
 
-		/* Get top vbox */
-		for (wl1 = wl = gtk_container_get_children(
-					GTK_CONTAINER(win));
-				wl != NULL;
-				wl = wl->next) {
-			if (GTK_IS_BOX(G_OBJECT(wl->data)))
-				vbox = GTK_WIDGET(wl->data);
-			else {
-				purple_debug_error(WINTRANS_PLUGIN_ID,
-					"no vbox found\n");
-				return;
-			}
-		}
-		g_list_free(wl1);
+	if(vbox == NULL) {
+		purple_debug_error(WINTRANS_PLUGIN_ID, "no vbox found");
+		return;
+	}
 
-		slider_box = wintrans_slider(win);
-		gtk_box_pack_start(GTK_BOX(vbox), slider_box, FALSE, FALSE, 0);
+	slider_frame = gtk_frame_new(NULL);
+	gtk_widget_set_margin_start(slider_frame, 6);
+	gtk_widget_set_margin_end(slider_frame, 6);
+	gtk_box_prepend(GTK_BOX(vbox), slider_frame);
+
+	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+	gtk_frame_set_child(GTK_FRAME(slider_frame), hbox);
 
-		/* Add window to list, to track that it has a slider */
-		slidwin = g_new0(slider_win, 1);
-		slidwin->win = win;
-		slidwin->slider = slider_box;
-		window_list = g_slist_append(window_list, slidwin);
-	}
-}
+	label = gtk_label_new(_("Opacity:"));
+	gtk_box_append(GTK_BOX(hbox), label);
+
+	slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1);
+	gtk_widget_set_hexpand(slider, TRUE);
+	gtk_box_append(GTK_BOX(hbox), slider);
 
-static void remove_sliders() {
-	if (window_list) {
-		GSList *tmp = window_list;
-		while (tmp) {
-			slider_win *slidwin = (slider_win*) tmp->data;
-			if (slidwin != NULL &&
-					GTK_IS_WINDOW(slidwin->win)) {
-				gtk_widget_destroy(slidwin->slider);
-			}
-			g_free(slidwin);
-			tmp = tmp->next;
-		}
-		g_slist_free(window_list);
-		window_list = NULL;
-	}
+	imalpha = purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA);
+	gtk_range_set_value(GTK_RANGE(slider), imalpha);
+	/* On slider val change, update window's transparency level */
+	g_signal_connect(G_OBJECT(slider), "value-changed",
+	                 G_CALLBACK(change_alpha), win);
+
+	/* Set the initial transparency level */
+	set_wintrans(win, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA), TRUE);
+
+	purple_prefs_connect_callback(slider_frame, OPT_WINTRANS_IM_ALPHA,
+	                              update_slider, slider);
+
+	/* Set window data, to track that it has a slider */
+	g_object_set_data_full(G_OBJECT(win), WINTRANS_SLIDER_KEY, slider_frame,
+	                       (GDestroyNotify)remove_slider);
 }
 
 /* Remove all transparency related aspects from conversation windows */
 static void remove_convs_wintrans(gboolean remove_signal) {
+	GApplication *application = NULL;
 	GList *wins;
 
-	for (wins = pidgin_conv_windows_get_list(); wins; wins = wins->next) {
-		PidginConvWindow *win = wins->data;
-		GtkWidget *window = win->window;
+	application = g_application_get_default();
+	wins = gtk_application_get_windows(GTK_APPLICATION(application));
+
+	for(; wins; wins = wins->next) {
+		GtkWidget *window = wins->data;
 
-		if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED))
-			set_wintrans(window, 0, FALSE, FALSE);
+		if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
+			continue;
+		}
+
+		if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) {
+			set_wintrans(window, 0, FALSE);
+		}
 
 		/* Remove the focus cbs */
-		if (remove_signal)
-			g_signal_handlers_disconnect_by_func(G_OBJECT(window),
-				G_CALLBACK(focus_conv_win_cb), window);
+		if (remove_signal) {
+			remove_focus_controller_from_conv_win(window);
+		}
+
+		g_object_set_data(G_OBJECT(window), WINTRANS_SLIDER_KEY, NULL);
 	}
-
-	remove_sliders();
 }
 
-static void set_conv_window_trans(PidginConvWindow *oldwin, PidginConvWindow *newwin) {
-	GtkWidget *win = newwin->window;
-
+static void
+set_conv_window_trans(GtkWidget *window) {
 	/* check prefs to see if we want trans */
 	if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) {
-		set_wintrans(win, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA),
-			TRUE, purple_prefs_get_bool(OPT_WINTRANS_IM_ONTOP));
+		set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_IM_ALPHA), TRUE);
 
 		if (purple_prefs_get_bool(OPT_WINTRANS_IM_SLIDER)) {
-			add_slider(win);
+			add_slider(window);
 		}
 	}
-
-	/* If we're moving from one window to another,
-	 * add the focus listeners to the new window if not already there */
-	if (oldwin != NULL && oldwin != newwin) {
-		if (pidgin_conv_window_get_gtkconv_count(newwin) == 0) {
-			g_signal_connect(G_OBJECT(win), "focus_in_event",
-				G_CALLBACK(focus_conv_win_cb), win);
-			g_signal_connect(G_OBJECT(win), "focus_out_event",
-				G_CALLBACK(focus_conv_win_cb), win);
-		}
-
-		/* If we've moved the last conversation, cleanup the window */
-		if (pidgin_conv_window_get_gtkconv_count(oldwin) == 1)
-			cleanup_conv_window(oldwin);
-	}
 }
 
 static void update_convs_wintrans(GtkWidget *toggle_btn, const char *pref) {
@@ -334,97 +308,94 @@ static void update_convs_wintrans(GtkWid
 		GTK_TOGGLE_BUTTON(toggle_btn)));
 
 	if (purple_prefs_get_bool(OPT_WINTRANS_IM_ENABLED)) {
+		GApplication *application = NULL;
 		GList *wins;
 
-		for (wins = pidgin_conv_windows_get_list(); wins; wins = wins->next) {
-			PidginConvWindow *win = wins->data;
-			set_conv_window_trans(NULL, win);
-		}
+		application = g_application_get_default();
+		wins = gtk_application_get_windows(GTK_APPLICATION(application));
 
-		if (!purple_prefs_get_bool(OPT_WINTRANS_IM_SLIDER))
-			remove_sliders();
-	}
-	else
-		remove_convs_wintrans(FALSE);
-}
-
-static void
-conv_updated_cb(PurpleConversation *conv, PurpleConversationUpdateType type) {
-	PidginConversation *pconv = PIDGIN_CONVERSATION(conv);
-	PidginConvWindow *win = pidgin_conv_get_window(pconv);
+		for(; wins; wins = wins->next) {
+			GtkWidget *win = wins->data;
 
-	if(type == PURPLE_CONVERSATION_UPDATE_UNSEEN
-			&& pconv->unseen_state == PIDGIN_UNSEEN_NONE
-			&& pidgin_conv_window_get_gtkconv_count(win) == 1) {
-		GtkWidget *window = win->window;
-		gboolean has_focus;
+			if(!PIDGIN_IS_CONVERSATION_WINDOW(win)) {
+				continue;
+			}
 
-		g_object_get(G_OBJECT(window), "has-toplevel-focus", &has_focus, NULL);
+			set_conv_window_trans(win);
 
-		if(!has_focus || !purple_prefs_get_bool(OPT_WINTRANS_IM_ONFOCUS)) {
-			set_conv_window_trans(NULL, win);
+			if (!purple_prefs_get_bool(OPT_WINTRANS_IM_SLIDER)) {
+				g_object_set_data(G_OBJECT(win), WINTRANS_SLIDER_KEY, NULL);
+			}
 		}
-
-		if(g_signal_handler_find(G_OBJECT(window), G_SIGNAL_MATCH_FUNC,
-				0, 0, NULL, G_CALLBACK(focus_conv_win_cb), NULL) == 0) {
-			g_signal_connect(G_OBJECT(window), "focus_in_event",
-				G_CALLBACK(focus_conv_win_cb), window);
-			g_signal_connect(G_OBJECT(window), "focus_out_event",
-				G_CALLBACK(focus_conv_win_cb), window);
-		}
+	} else {
+		remove_convs_wintrans(FALSE);
 	}
 }
 
 static void
-new_conversation_cb(PurpleConversation *conv) {
-	PidginConvWindow *win = pidgin_conv_get_window(PIDGIN_CONVERSATION(conv));
-
-	/* If it is the first conversation in the window,
-	 * add the sliders, and set transparency */
-	if(pidgin_conv_window_get_gtkconv_count(win) == 1) {
-		GtkWidget *window = win->window;
+new_conversation_cb(G_GNUC_UNUSED GtkApplication *application,
+                    GtkWindow *window, G_GNUC_UNUSED gpointer data)
+{
+	if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
+		return;
+	}
 
-		set_conv_window_trans(NULL, win);
-
-		g_signal_connect(G_OBJECT(window), "focus_in_event",
-			G_CALLBACK(focus_conv_win_cb), window);
-		g_signal_connect(G_OBJECT(window), "focus_out_event",
-			G_CALLBACK(focus_conv_win_cb), window);
-	}
+	set_conv_window_trans(GTK_WIDGET(window));
+	add_focus_controller_to_conv_win(GTK_WIDGET(window));
 }
 
 static void
 blist_created_cb(PurpleBuddyList *purple_blist, gpointer data) {
 	GtkWidget *window = get_buddy_list_window();
 	if (window != NULL) {
+		GtkEventController *focus = NULL;
+
 		if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) {
 			set_wintrans(window,
 				purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA),
-				TRUE,
-				purple_prefs_get_bool(OPT_WINTRANS_BL_ONTOP));
+				TRUE);
 		}
 
-		g_signal_connect(G_OBJECT(window), "focus_in_event",
-			G_CALLBACK(focus_blist_win_cb), window);
-		g_signal_connect(G_OBJECT(window), "focus_out_event",
-			G_CALLBACK(focus_blist_win_cb), window);
+		focus = gtk_event_controller_focus_new();
+
+		g_signal_connect(focus, "enter", G_CALLBACK(focus_blist_win_cb),
+		                 GINT_TO_POINTER(TRUE));
+		g_signal_connect(focus, "leave", G_CALLBACK(focus_blist_win_cb),
+		                 GINT_TO_POINTER(FALSE));
+
+		gtk_widget_add_controller(window, focus);
+		g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, focus);
 	}
 }
 
 static void alpha_change(GtkWidget *w, gpointer data) {
+	GApplication *application = NULL;
 	GList *wins;
 	int imalpha = gtk_range_get_value(GTK_RANGE(w));
 
-	for (wins = pidgin_conv_windows_get_list(); wins; wins = wins->next) {
-		PidginConvWindow *win = wins->data;
-		set_wintrans(win->window, imalpha, TRUE,
-			purple_prefs_get_bool(OPT_WINTRANS_IM_ONTOP));
+	application = g_application_get_default();
+	wins = gtk_application_get_windows(GTK_APPLICATION(application));
+
+	for(; wins; wins = wins->next) {
+		GtkWidget *window = wins->data;
+
+		if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
+			continue;
+		}
+
+		set_wintrans(window, imalpha, TRUE);
 	}
 }
 
-static void alpha_pref_set_int (GtkWidget *w, GdkEventFocus *e, const char *pref)
-{
-	int alpha = gtk_range_get_value(GTK_RANGE(w));
+static void
+alpha_pref_set_int(GtkEventControllerFocus *self, gpointer data) {
+	const char *pref = data;
+	GtkWidget *slider = NULL;
+	int alpha = 255;
+
+	slider = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self));
+	alpha = gtk_range_get_value(GTK_RANGE(slider));
+
 	purple_prefs_set_int(pref, alpha);
 }
 
@@ -435,19 +406,24 @@ static void bl_alpha_change(GtkWidget *w
 	}
 }
 
-static void update_existing_convs() {
+static void
+update_existing_convs(void) {
+	GApplication *application = NULL;
 	GList *wins;
 
-	for (wins = pidgin_conv_windows_get_list(); wins; wins = wins->next) {
-		PidginConvWindow *win = wins->data;
-		GtkWidget *window = win->window;
+	application = g_application_get_default();
+	wins = gtk_application_get_windows(GTK_APPLICATION(application));
+
+	for(; wins; wins = wins->next) {
+		GtkWidget *window = wins->data;
 
-		set_conv_window_trans(NULL, win);
+		if(!PIDGIN_IS_CONVERSATION_WINDOW(window)) {
+			continue;
+		}
 
-		g_signal_connect(G_OBJECT(window), "focus_in_event",
-			G_CALLBACK(focus_conv_win_cb), window);
-		g_signal_connect(G_OBJECT(window), "focus_out_event",
-			G_CALLBACK(focus_conv_win_cb), window);
+		set_conv_window_trans(window);
+
+		add_focus_controller_to_conv_win(window);
 	}
 }
 
@@ -459,9 +435,13 @@ get_config_frame(PurplePlugin *plugin) {
 	GtkWidget *label, *slider;
 	GtkWidget *button;
 	GtkWidget *trans_box;
+	GtkEventController *focus = NULL;
 
 	ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
-	gtk_container_set_border_width(GTK_CONTAINER (ret), 12);
+	gtk_widget_set_margin_start(ret, 12);
+	gtk_widget_set_margin_end(ret, 12);
+	gtk_widget_set_margin_top(ret, 12);
+	gtk_widget_set_margin_bottom(ret, 12);
 
 	/* IM Convo trans options */
 	imtransbox = pidgin_make_frame(ret, _("IM Conversation Windows"));
@@ -472,7 +452,6 @@ get_config_frame(PurplePlugin *plugin) {
 		(gpointer) OPT_WINTRANS_IM_ENABLED);
 
 	trans_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
-	gtk_widget_show(trans_box);
 	g_object_bind_property(button, "active", trans_box, "sensitive",
 			G_BINDING_SYNC_CREATE);
 
@@ -486,19 +465,13 @@ get_config_frame(PurplePlugin *plugin) {
 		_("Remove IM window transparency on focus"),
 		OPT_WINTRANS_IM_ONFOCUS, trans_box);
 
-	button = pidgin_prefs_checkbox(_("Always on top"), OPT_WINTRANS_IM_ONTOP,
-		trans_box);
-	g_signal_connect(G_OBJECT(button), "clicked",
-		G_CALLBACK(update_convs_wintrans),
-		(gpointer) OPT_WINTRANS_IM_ONTOP);
-
-	gtk_box_pack_start(GTK_BOX(imtransbox), trans_box, FALSE, FALSE, 5);
+	gtk_box_append(GTK_BOX(imtransbox), trans_box);
 
 	/* IM transparency slider */
 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
 
 	label = gtk_label_new(_("Opacity:"));
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+	gtk_box_append(GTK_BOX(hbox), label);
 
 	slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1);
 	gtk_range_set_value(GTK_RANGE(slider),
@@ -506,15 +479,14 @@ get_config_frame(PurplePlugin *plugin) {
 
 	g_signal_connect(G_OBJECT(slider), "value-changed",
 		G_CALLBACK(alpha_change), NULL);
-	g_signal_connect(G_OBJECT(slider), "focus-out-event",
-		G_CALLBACK(alpha_pref_set_int),
-		(gpointer) OPT_WINTRANS_IM_ALPHA);
+	focus = gtk_event_controller_focus_new();
+	g_signal_connect(focus, "leave", G_CALLBACK(alpha_pref_set_int),
+	                 (gpointer)OPT_WINTRANS_IM_ALPHA);
+	gtk_widget_add_controller(slider, focus);
 
-	gtk_box_pack_start(GTK_BOX(hbox), slider, FALSE, TRUE, 5);
+	gtk_box_append(GTK_BOX(hbox), slider);
 
-	gtk_widget_show_all(hbox);
-
-	gtk_box_pack_start(GTK_BOX(trans_box), hbox, FALSE, FALSE, 5);
+	gtk_box_append(GTK_BOX(trans_box), hbox);
 
 	/* Buddy List trans options */
 	bltransbox = pidgin_make_frame (ret, _("Buddy List Window"));
@@ -525,24 +497,18 @@ get_config_frame(PurplePlugin *plugin) {
 		(gpointer) OPT_WINTRANS_BL_ENABLED);
 
 	trans_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
-	gtk_widget_show(trans_box);
 	g_object_bind_property(button, "active", trans_box, "sensitive",
 			G_BINDING_SYNC_CREATE);
 	button = pidgin_prefs_checkbox(
 		_("Remove Buddy List window transparency on focus"),
 		OPT_WINTRANS_BL_ONFOCUS, trans_box);
-	button = pidgin_prefs_checkbox(_("Always on top"), OPT_WINTRANS_BL_ONTOP,
-		trans_box);
-	g_signal_connect(G_OBJECT(button), "clicked",
-		G_CALLBACK(set_blist_trans),
-		(gpointer) OPT_WINTRANS_BL_ONTOP);
-	gtk_box_pack_start(GTK_BOX(bltransbox), trans_box, FALSE, FALSE, 5);
+	gtk_box_append(GTK_BOX(bltransbox), trans_box);
 
 	/* IM transparency slider */
 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
 
 	label = gtk_label_new(_("Opacity:"));
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+	gtk_box_append(GTK_BOX(hbox), label);
 
 	slider = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 50, 255, 1);
 	gtk_range_set_value(GTK_RANGE(slider),
@@ -550,17 +516,15 @@ get_config_frame(PurplePlugin *plugin) {
 
 	g_signal_connect(G_OBJECT(slider), "value-changed",
 		G_CALLBACK(bl_alpha_change), NULL);
-	g_signal_connect(G_OBJECT(slider), "focus-out-event",
-		G_CALLBACK(alpha_pref_set_int),
-		(gpointer) OPT_WINTRANS_BL_ALPHA);
-
-	gtk_box_pack_start(GTK_BOX(hbox), slider, FALSE, TRUE, 5);
+	focus = gtk_event_controller_focus_new();
+	g_signal_connect(focus, "leave", G_CALLBACK(alpha_pref_set_int),
+	                 (gpointer)OPT_WINTRANS_BL_ALPHA);
+	gtk_widget_add_controller(slider, focus);
 
-	gtk_widget_show_all(hbox);
+	gtk_box_append(GTK_BOX(hbox), slider);
 
-	gtk_box_pack_start(GTK_BOX(trans_box), hbox, FALSE, FALSE, 5);
+	gtk_box_append(GTK_BOX(trans_box), hbox);
 
-	gtk_widget_show_all(ret);
 	return ret;
 }
 
@@ -588,8 +552,8 @@ transparency_query(GError **error) {
 
 static gboolean
 transparency_load(GPluginPlugin *plugin, GError **error) {
+	GApplication *application = NULL;
 	GtkWidget *window = NULL;
-	gpointer conv_handle = NULL;
 
 	purple_prefs_add_none("/plugins/gtk");
 	purple_prefs_add_none("/plugins/gtk/transparency");
@@ -597,23 +561,16 @@ transparency_load(GPluginPlugin *plugin,
 	purple_prefs_add_int(OPT_WINTRANS_IM_ALPHA, 255);
 	purple_prefs_add_bool(OPT_WINTRANS_IM_SLIDER, FALSE);
 	purple_prefs_add_bool(OPT_WINTRANS_IM_ONFOCUS, FALSE);
-	purple_prefs_add_bool(OPT_WINTRANS_IM_ONTOP, FALSE);
 	purple_prefs_add_bool(OPT_WINTRANS_BL_ENABLED, FALSE);
 	purple_prefs_add_int(OPT_WINTRANS_BL_ALPHA, 255);
 	purple_prefs_add_bool(OPT_WINTRANS_BL_ONFOCUS, FALSE);
-	purple_prefs_add_bool(OPT_WINTRANS_BL_ONTOP, FALSE);
 	purple_prefs_rename("/plugins/gtk/win32/wintrans", "/plugins/gtk/transparency");
 
-	conv_handle = purple_conversations_get_handle();
-	purple_signal_connect(conv_handle, "conversation-created", plugin,
-	                      G_CALLBACK(new_conversation_cb), NULL);
-
-	/* Set callback to remove window from the list, if the window is destroyed */
-	purple_signal_connect(conv_handle, "deleting-conversation", plugin,
-	                      G_CALLBACK(conversation_delete_cb), NULL);
-
-	purple_signal_connect(conv_handle, "conversation-updated", plugin,
-	                      G_CALLBACK(conv_updated_cb), NULL);
+	application = g_application_get_default();
+	g_signal_connect(application, "window-added",
+	                 G_CALLBACK(new_conversation_cb), NULL);
+	g_signal_connect(application, "window-removed",
+	                 G_CALLBACK(conversation_delete_cb), NULL);
 
 	update_existing_convs();
 
@@ -639,12 +596,17 @@ transparency_unload(GPluginPlugin *plugi
 
 	window = get_buddy_list_window();
 	if (window != NULL) {
-		if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED))
-			set_wintrans(window, 0, FALSE, FALSE);
+		GtkEventController *focus = NULL;
+
+		if (purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)) {
+			set_wintrans(window, 0, FALSE);
+		}
 
 		/* Remove the focus cbs */
-		g_signal_handlers_disconnect_by_func(G_OBJECT(window),
-			G_CALLBACK(focus_blist_win_cb), window);
+		focus = g_object_get_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY);
+		if(GTK_IS_EVENT_CONTROLLER_FOCUS(focus)) {
+			gtk_widget_remove_controller(window, focus);
+		}
 	}
 
 	return TRUE;
