Rename pidginconversationwindow.[ch] to pidgindisplaywindow.[ch]

Sun, 18 Sep 2022 22:49:16 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Sun, 18 Sep 2022 22:49:16 -0500
changeset 41702
4289b30044ed
parent 41701
43f994f7f9f2
child 41703
acd605927a16

Rename pidginconversationwindow.[ch] to pidgindisplaywindow.[ch]

Testing Done:
Ran and made sure I could open a conversation.

Bugs closed: PIDGIN-17665

Reviewed at https://reviews.imfreedom.org/r/1792/

pidgin/gtkconv.c file | annotate | diff | comparison | revisions
pidgin/meson.build file | annotate | diff | comparison | revisions
pidgin/pidginapplication.c file | annotate | diff | comparison | revisions
pidgin/pidginconversationwindow.c file | annotate | diff | comparison | revisions
pidgin/pidginconversationwindow.h file | annotate | diff | comparison | revisions
pidgin/pidgindisplaywindow.c file | annotate | diff | comparison | revisions
pidgin/pidgindisplaywindow.h file | annotate | diff | comparison | revisions
pidgin/resources/Conversations/window.ui file | annotate | diff | comparison | revisions
pidgin/resources/Display/window.ui file | annotate | diff | comparison | revisions
pidgin/resources/pidgin.gresource.xml file | annotate | diff | comparison | revisions
po/POTFILES.in file | annotate | diff | comparison | revisions
--- a/pidgin/gtkconv.c	Sun Sep 18 22:36:13 2022 -0500
+++ b/pidgin/gtkconv.c	Sun Sep 18 22:49:16 2022 -0500
@@ -46,8 +46,8 @@
 #include "gtkutils.h"
 #include "pidginavatar.h"
 #include "pidgincolor.h"
-#include "pidginconversationwindow.h"
 #include "pidgincore.h"
+#include "pidgindisplaywindow.h"
 #include "pidgininfopane.h"
 #include "pidgininvitedialog.h"
 #include "pidginmessage.h"
--- a/pidgin/meson.build	Sun Sep 18 22:36:13 2022 -0500
+++ b/pidgin/meson.build	Sun Sep 18 22:49:16 2022 -0500
@@ -33,10 +33,10 @@
 	'pidginavatar.c',
 	'pidgincolor.c',
 	'pidgincommands.c',
-	'pidginconversationwindow.c',
 	'pidgincontactlistwindow.c',
 	'pidgindebug.c',
 	'pidgindialog.c',
+	'pidgindisplaywindow.c',
 	'pidginiconname.c',
 	'pidgininfopane.c',
 	'pidgininvitedialog.c',
@@ -102,10 +102,10 @@
 	'pidginattachment.h',
 	'pidginavatar.h',
 	'pidgincolor.h',
-	'pidginconversationwindow.h',
 	'pidgincontactlistwindow.h',
 	'pidgincore.h',
 	'pidgindialog.h',
+	'pidgindisplaywindow.h',
 	'pidgindebug.h',
 	'pidginiconname.h',
 	'pidgininfopane.h',
--- a/pidgin/pidginapplication.c	Sun Sep 18 22:36:13 2022 -0500
+++ b/pidgin/pidginapplication.c	Sun Sep 18 22:49:16 2022 -0500
@@ -46,9 +46,9 @@
 #include "pidginaccountmanager.h"
 #include "pidginaccountsdisabledmenu.h"
 #include "pidginaccountsenabledmenu.h"
-#include "pidginconversationwindow.h"
 #include "pidgincore.h"
 #include "pidgindebug.h"
+#include "pidgindisplaywindow.h"
 #include "pidginmooddialog.h"
 #include "pidginpluginsdialog.h"
 #include "pidginpluginsmenu.h"
--- a/pidgin/pidginconversationwindow.c	Sun Sep 18 22:36:13 2022 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,822 +0,0 @@
-/*
- * Pidgin - Internet Messenger
- * Copyright (C) Pidgin Developers <devel@pidgin.im>
- *
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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, see <https://www.gnu.org/licenses/>.
- */
-
-#include <glib/gi18n-lib.h>
-
-#include <adwaita.h>
-
-#include "pidginconversationwindow.h"
-
-#include "gtkconv.h"
-#include "gtkdialogs.h"
-#include "gtkutils.h"
-#include "pidgininvitedialog.h"
-
-enum {
-	PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT,
-	PIDGIN_DISPLAY_WINDOW_COLUMN_NAME,
-	PIDGIN_DISPLAY_WINDOW_COLUMN_ICON,
-	PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP,
-};
-
-enum {
-	SIG_CONVERSATION_SWITCHED,
-	N_SIGNALS,
-};
-static guint signals[N_SIGNALS] = {0, };
-
-struct _PidginDisplayWindow {
-	GtkApplicationWindow parent;
-
-	GtkWidget *vbox;
-
-	GtkWidget *view;
-	GtkTreeSelection *selection;
-	GtkTreeStore *model;
-
-	GtkWidget *stack;
-
-	GtkWidget *notification_list;
-
-	GtkTreePath *conversation_path;
-};
-
-G_DEFINE_TYPE(PidginDisplayWindow, pidgin_display_window,
-              GTK_TYPE_APPLICATION_WINDOW)
-
-static GtkWidget *default_window = NULL;
-
-/******************************************************************************
- * Helpers
- *****************************************************************************/
-static void
-pidgin_display_window_actions_set_enabled(GActionMap *map,
-                                          const gchar **actions,
-                                          gboolean enabled)
-{
-	gint i = 0;
-
-	for(i = 0; actions[i] != NULL; i++) {
-		GAction *action = NULL;
-		const gchar *name = actions[i];
-
-		action = g_action_map_lookup_action(map, name);
-		if(action != NULL) {
-			g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
-		} else {
-			g_critical("Failed to find action named %s", name);
-		}
-	}
-}
-
-/******************************************************************************
- * Callbacks
- *****************************************************************************/
-static void
-pidgin_display_window_invite_cb(GtkDialog *dialog, gint response_id,
-                                G_GNUC_UNUSED gpointer data)
-{
-	PidginInviteDialog *invite_dialog = PIDGIN_INVITE_DIALOG(dialog);
-	PurpleChatConversation *chat = NULL;
-
-	chat = pidgin_invite_dialog_get_conversation(invite_dialog);
-
-	g_object_set_data(G_OBJECT(chat), "pidgin-invite-dialog", NULL);
-
-	if(response_id == GTK_RESPONSE_ACCEPT) {
-		const gchar *contact = NULL, *message = NULL;
-
-		contact = pidgin_invite_dialog_get_contact(invite_dialog);
-		message = pidgin_invite_dialog_get_message(invite_dialog);
-
-		if(!purple_strequal(contact, "")) {
-			PurpleConnection *connection = NULL;
-
-			connection = purple_conversation_get_connection(PURPLE_CONVERSATION(chat));
-			purple_serv_chat_invite(connection,
-			                        purple_chat_conversation_get_id(chat),
-			                        message, contact);
-		}
-	}
-
-	gtk_window_destroy(GTK_WINDOW(invite_dialog));
-}
-
-/******************************************************************************
- * Actions
- *****************************************************************************/
-static void
-pidgin_display_window_alias(G_GNUC_UNUSED GSimpleAction *simple,
-                            G_GNUC_UNUSED GVariant *parameter,
-                            gpointer data)
-{
-	PidginDisplayWindow *window = data;
-	PurpleConversation *selected = NULL;
-
-	selected = pidgin_display_window_get_selected(window);
-	if(PURPLE_IS_CONVERSATION(selected)) {
-		PurpleAccount *account;
-		const gchar *name;
-
-		account = purple_conversation_get_account(selected);
-		name = purple_conversation_get_name(selected);
-
-		if(PURPLE_IS_IM_CONVERSATION(selected)) {
-			PurpleBuddy *buddy = purple_blist_find_buddy(account, name);
-
-			if(PURPLE_IS_BUDDY(buddy)) {
-				pidgin_dialogs_alias_buddy(buddy);
-			}
-		} else if(PURPLE_IS_CHAT_CONVERSATION(selected)) {
-			PurpleChat *chat = purple_blist_find_chat(account, name);
-
-			if(PURPLE_IS_CHAT(chat)) {
-				pidgin_dialogs_alias_chat(chat);
-			}
-		}
-	}
-}
-
-static void
-pidgin_display_window_close_conversation(G_GNUC_UNUSED GSimpleAction *simple,
-                                         G_GNUC_UNUSED GVariant *parameter,
-                                         gpointer data)
-{
-	PidginDisplayWindow *window = data;
-	PurpleConversation *selected = NULL;
-
-	selected = pidgin_display_window_get_selected(window);
-	if(PURPLE_IS_CONVERSATION(selected)) {
-		pidgin_display_window_remove(window, selected);
-		pidgin_conversation_detach(selected);
-	}
-}
-
-static void
-pidgin_display_window_get_info(G_GNUC_UNUSED GSimpleAction *simple,
-                               G_GNUC_UNUSED GVariant *parameter,
-                               gpointer data)
-{
-	PidginDisplayWindow *window = data;
-	PurpleConversation *selected = NULL;
-
-	selected = pidgin_display_window_get_selected(window);
-	if(PURPLE_IS_CONVERSATION(selected)) {
-		if(PURPLE_IS_IM_CONVERSATION(selected)) {
-			PurpleConnection *connection = NULL;
-
-			connection = purple_conversation_get_connection(selected);
-			pidgin_retrieve_user_info(connection,
-			                          purple_conversation_get_name(selected));
-		}
-	}
-}
-
-static void
-pidgin_display_window_invite(G_GNUC_UNUSED GSimpleAction *simple,
-                             G_GNUC_UNUSED GVariant *parameter,
-                             gpointer data)
-{
-	PidginDisplayWindow *window = data;
-	PurpleConversation *selected = NULL;
-
-	selected = pidgin_display_window_get_selected(window);
-	if(PURPLE_IS_CHAT_CONVERSATION(selected)) {
-		GtkWidget *invite_dialog = NULL;
-
-		invite_dialog = g_object_get_data(G_OBJECT(selected),
-		                                  "pidgin-invite-dialog");
-
-		if(!GTK_IS_WIDGET(invite_dialog)) {
-			invite_dialog = pidgin_invite_dialog_new(PURPLE_CHAT_CONVERSATION(selected));
-			g_object_set_data(G_OBJECT(selected), "pidgin-invite-dialog",
-			                  invite_dialog);
-
-			gtk_window_set_transient_for(GTK_WINDOW(invite_dialog),
-			                             GTK_WINDOW(window));
-			gtk_window_set_destroy_with_parent(GTK_WINDOW(invite_dialog), TRUE);
-
-			g_signal_connect(invite_dialog, "response",
-			                 G_CALLBACK(pidgin_display_window_invite_cb),
-			                 NULL);
-		}
-
-		gtk_widget_show(invite_dialog);
-	}
-}
-
-static void
-pidgin_display_window_send_file(G_GNUC_UNUSED GSimpleAction *simple,
-                                G_GNUC_UNUSED GVariant *parameter,
-                                gpointer data)
-{
-	PidginDisplayWindow *window = data;
-	PurpleConversation *selected = NULL;
-
-	selected = pidgin_display_window_get_selected(window);
-	if(PURPLE_IS_IM_CONVERSATION(selected)) {
-		PurpleConnection *connection = NULL;
-
-		connection = purple_conversation_get_connection(selected);
-		purple_serv_send_file(connection,
-		                      purple_conversation_get_name(selected),
-		                      NULL);
-	}
-}
-
-static GActionEntry win_entries[] = {
-	{
-		.name = "alias",
-		.activate = pidgin_display_window_alias
-	}, {
-		.name = "close",
-		.activate = pidgin_display_window_close_conversation
-	}, {
-		.name = "get-info",
-		.activate = pidgin_display_window_get_info
-	}, {
-		.name = "invite",
-		.activate = pidgin_display_window_invite
-	}, {
-		.name = "send-file",
-		.activate = pidgin_display_window_send_file
-	}
-};
-
-/*<private>
- * pidgin_display_window_conversation_actions:
- *
- * A list of action names that are only valid if a conversation is selected.
- */
-static const gchar *pidgin_display_window_conversation_actions[] = {
-	"alias",
-	"close",
-	"get-info",
-	NULL
-};
-
-static const gchar *pidgin_display_window_im_conversation_actions[] = {
-	"send-file",
-	NULL
-};
-
-static const gchar *pidgin_display_window_chat_conversation_actions[] = {
-	"invite",
-	NULL
-};
-
-/******************************************************************************
- * Callbacks
- *****************************************************************************/
-static void
-pidgin_display_window_selection_changed(GtkTreeSelection *selection,
-                                        gpointer data)
-{
-	PidginDisplayWindow *window = PIDGIN_DISPLAY_WINDOW(data);
-	GtkTreeModel *model = NULL;
-	GtkTreeIter iter;
-	gboolean changed = FALSE;
-
-	if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
-		GObject *obj;
-		gboolean is_conversation = FALSE;
-		gboolean im_selected = FALSE, chat_selected = FALSE;
-		gchar *name = NULL;
-
-		gtk_tree_model_get(model, &iter,
-		                   PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, &name,
-		                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &obj,
-		                   -1);
-
-		adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack),
-		                                      name);
-		g_free(name);
-
-		changed = TRUE;
-
-		/* If a conversation is selected, enable the generic conversation
-		 * actions.
-		 */
-		is_conversation = PURPLE_IS_CONVERSATION(obj);
-		pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window),
-							       pidgin_display_window_conversation_actions,
-							       is_conversation);
-
-		/* If an IM is selected, enable the IM-specific actions otherwise
-		 * disable them.
-		 */
-		im_selected = PURPLE_IS_IM_CONVERSATION(obj);
-		pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window),
-							       pidgin_display_window_im_conversation_actions,
-							       im_selected);
-
-		/* If a chat is selected, enable the chat-specific actions otherwise
-		 * disable them.
-		 */
-		chat_selected = PURPLE_IS_CHAT_CONVERSATION(obj);
-		pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window),
-							       pidgin_display_window_chat_conversation_actions,
-							       chat_selected);
-
-		g_clear_object(&obj);
-	}
-
-	if(!changed) {
-		adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack),
-		                                      "__conversations__");
-	}
-}
-
-static gboolean
-pidgin_display_window_key_pressed_cb(GtkEventControllerKey *controller,
-                                     guint keyval,
-                                     G_GNUC_UNUSED guint keycode,
-                                     GdkModifierType state,
-                                     gpointer data)
-{
-	PidginDisplayWindow *window = data;
-
-	/* If CTRL was held down... */
-	if (state & GDK_CONTROL_MASK) {
-		switch (keyval) {
-			case GDK_KEY_Page_Down:
-			case GDK_KEY_KP_Page_Down:
-			case ']':
-				pidgin_display_window_select_next(window);
-				return TRUE;
-				break;
-
-			case GDK_KEY_Page_Up:
-			case GDK_KEY_KP_Page_Up:
-			case '[':
-				pidgin_display_window_select_previous(window);
-				return TRUE;
-				break;
-		} /* End of switch */
-	}
-
-	/* If ALT (or whatever) was held down... */
-	else if (state & GDK_ALT_MASK) {
-		if ('1' <= keyval && keyval <= '9') {
-			guint switchto = keyval - '1';
-			pidgin_display_window_select_nth(window, switchto);
-
-			return TRUE;
-		}
-	}
-
-	return FALSE;
-}
-
-/******************************************************************************
- * GObjectImplementation
- *****************************************************************************/
-static void
-pidgin_display_window_dispose(GObject *obj) {
-	PidginDisplayWindow *window = PIDGIN_DISPLAY_WINDOW(obj);
-
-	if(GTK_IS_TREE_MODEL(window->model)) {
-		GtkTreeModel *model = GTK_TREE_MODEL(window->model);
-		GtkTreeIter parent, iter;
-
-		gtk_tree_model_get_iter(model, &parent, window->conversation_path);
-		if(gtk_tree_model_iter_children(model, &iter, &parent)) {
-			gboolean valid = FALSE;
-
-			/* gtk_tree_store_remove moves the iter to the next item at the
-			 * same level, so we abuse that to do our iteration.
-			 */
-			do {
-				PurpleConversation *conversation = NULL;
-
-				gtk_tree_model_get(model, &iter,
-				                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &conversation,
-				                   -1);
-
-				if(PURPLE_IS_CONVERSATION(conversation)) {
-					pidgin_conversation_detach(conversation);
-
-					valid = gtk_tree_store_remove(window->model, &iter);
-				}
-			} while(valid);
-		}
-
-		g_clear_pointer(&window->conversation_path, gtk_tree_path_free);
-	}
-
-	G_OBJECT_CLASS(pidgin_display_window_parent_class)->dispose(obj);
-}
-
-static void
-pidgin_display_window_init(PidginDisplayWindow *window) {
-	GtkEventController *key = NULL;
-	GtkTreeIter iter;
-
-	gtk_widget_init_template(GTK_WIDGET(window));
-
-	gtk_window_set_application(GTK_WINDOW(window),
-	                           GTK_APPLICATION(g_application_get_default()));
-
-	g_action_map_add_action_entries(G_ACTION_MAP(window), win_entries,
-	                                G_N_ELEMENTS(win_entries), window);
-
-	key = gtk_event_controller_key_new();
-	gtk_event_controller_set_propagation_phase(key, GTK_PHASE_CAPTURE);
-	g_signal_connect(G_OBJECT(key), "key-pressed",
-	                 G_CALLBACK(pidgin_display_window_key_pressed_cb),
-	                 window);
-	gtk_widget_add_controller(GTK_WIDGET(window), key);
-
-	/* Add our toplevels to the tree store. */
-	gtk_tree_store_append(window->model, &iter, NULL);
-	gtk_tree_store_set(window->model, &iter,
-	                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, window->notification_list,
-	                   PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, "__notifications__",
-	                   PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, _("Notifications"),
-	                   -1);
-
-	gtk_tree_store_append(window->model, &iter, NULL);
-	gtk_tree_store_set(window->model, &iter,
-	                   PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, _("Conversations"),
-	                   PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, "__conversations__",
-	                   -1);
-	gtk_tree_selection_select_iter(window->selection, &iter);
-	window->conversation_path = gtk_tree_model_get_path(GTK_TREE_MODEL(window->model),
-	                                                    &iter);
-}
-
-static void
-pidgin_display_window_class_init(PidginDisplayWindowClass *klass) {
-	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
-	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
-
-	obj_class->dispose = pidgin_display_window_dispose;
-
-	/**
-	 * PidginDisplayWindow::conversation-switched:
-	 * @window: The conversation window.
-	 * @new_conv: The now active conversation.
-	 *
-	 * Emitted when a window switched from one conversation to another.
-	 */
-	signals[SIG_CONVERSATION_SWITCHED] = g_signal_new_class_handler(
-		"conversation-switched",
-		G_OBJECT_CLASS_TYPE(obj_class),
-		G_SIGNAL_RUN_LAST,
-		NULL,
-		NULL,
-		NULL,
-		NULL,
-		G_TYPE_NONE,
-		1,
-		PURPLE_TYPE_CONVERSATION
-	);
-
-	gtk_widget_class_set_template_from_resource(
-	    widget_class,
-	    "/im/pidgin/Pidgin3/Conversations/window.ui"
-	);
-
-	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
-	                                     vbox);
-
-	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
-	                                     model);
-	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
-	                                     view);
-	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
-	                                     selection);
-
-	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
-	                                     stack);
-	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
-	                                     notification_list);
-
-	gtk_widget_class_bind_template_callback(widget_class,
-	                                        pidgin_display_window_selection_changed);
-
-	gtk_widget_class_bind_template_callback(widget_class,
-	                                        pidgin_display_window_key_pressed_cb);
-}
-
-/******************************************************************************
- * API
- *****************************************************************************/
-GtkWidget *
-pidgin_display_window_get_default(void) {
-	if(!GTK_IS_WIDGET(default_window)) {
-		default_window = pidgin_display_window_new();
-		g_object_add_weak_pointer(G_OBJECT(default_window),
-		                          (gpointer)&default_window);
-	}
-
-	return default_window;
-}
-
-GtkWidget *
-pidgin_display_window_new(void) {
-	return g_object_new(
-		PIDGIN_TYPE_DISPLAY_WINDOW,
-		"show-menubar", TRUE,
-		NULL);
-}
-
-void
-pidgin_display_window_add(PidginDisplayWindow *window,
-                          PurpleConversation *conversation)
-{
-	PidginConversation *gtkconv = NULL;
-	GtkTreeIter parent, iter;
-	GtkTreeModel *model = NULL;
-	const gchar *markup = NULL;
-	gboolean expand = FALSE;
-
-	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
-	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
-
-	model = GTK_TREE_MODEL(window->model);
-	if(!gtk_tree_model_get_iter(model, &parent, window->conversation_path)) {
-		/* If we can't find the conversation_path we have to bail. */
-		g_warning("couldn't get an iterator to conversation_path");
-
-		return;
-	}
-
-	if(!gtk_tree_model_iter_has_child(model, &parent)) {
-		expand = TRUE;
-	}
-
-	markup = purple_conversation_get_name(conversation);
-
-	gtkconv = PIDGIN_CONVERSATION(conversation);
-	if(gtkconv != NULL) {
-		GtkWidget *parent = gtk_widget_get_parent(gtkconv->tab_cont);
-
-		if(GTK_IS_WIDGET(parent)) {
-			g_object_ref(gtkconv->tab_cont);
-			gtk_widget_unparent(gtkconv->tab_cont);
-		}
-
-		adw_view_stack_add_named(ADW_VIEW_STACK(window->stack),
-		                         gtkconv->tab_cont, markup);
-		gtk_widget_show(gtkconv->tab_cont);
-
-		if(GTK_IS_WIDGET(parent)) {
-			g_object_unref(gtkconv->tab_cont);
-		}
-	}
-
-	gtk_tree_store_prepend(window->model, &iter, &parent);
-	gtk_tree_store_set(window->model, &iter,
-	                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, conversation,
-	                   PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, markup,
-	                   PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, markup,
-	                   -1);
-
-	/* If we just added the first child, expand the parent. */
-	if(expand) {
-		gtk_tree_view_expand_row(GTK_TREE_VIEW(window->view),
-		                         window->conversation_path, FALSE);
-	}
-
-
-	if(!gtk_widget_is_visible(GTK_WIDGET(window))) {
-		gtk_widget_show(GTK_WIDGET(window));
-	}
-}
-
-void
-pidgin_display_window_remove(PidginDisplayWindow *window,
-                             PurpleConversation *conversation)
-{
-	GtkTreeIter parent, iter;
-	GtkTreeModel *model = NULL;
-	GObject *obj = NULL;
-
-	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
-	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
-
-	model = GTK_TREE_MODEL(window->model);
-
-	if(!gtk_tree_model_get_iter(model, &parent, window->conversation_path)) {
-		/* The path is somehow invalid, so bail... */
-		return;
-	}
-
-	if(!gtk_tree_model_iter_children(model, &iter, &parent)) {
-		/* The conversations iter has no children. */
-		return;
-	}
-
-	do {
-		gtk_tree_model_get(model, &iter,
-		                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &obj,
-		                   -1);
-
-		if(PURPLE_CONVERSATION(obj) == conversation) {
-			GtkWidget *child = NULL;
-			const gchar *name = NULL;
-
-			name = purple_conversation_get_name(conversation);
-			child = adw_view_stack_get_child_by_name(ADW_VIEW_STACK(window->stack),
-			                                         name);
-			if(GTK_IS_WIDGET(child)) {
-				gtk_widget_unparent(child);
-			}
-
-			gtk_tree_store_remove(window->model, &iter);
-
-			g_clear_object(&obj);
-
-			break;
-		}
-
-		g_clear_object(&obj);
-	} while(gtk_tree_model_iter_next(model, &iter));
-}
-
-guint
-pidgin_display_window_get_count(PidginDisplayWindow *window) {
-	GtkSelectionModel *model = NULL;
-	guint count = 0;
-
-	g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), 0);
-
-	model = adw_view_stack_get_pages(ADW_VIEW_STACK(window->stack));
-
-	count = g_list_model_get_n_items(G_LIST_MODEL(model));
-
-	g_object_unref(model);
-
-	return count;
-}
-
-PurpleConversation *
-pidgin_display_window_get_selected(PidginDisplayWindow *window) {
-	PurpleConversation *conversation = NULL;
-	GtkTreeSelection *selection = NULL;
-	GtkTreeIter iter;
-
-	g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), NULL);
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
-	if(gtk_tree_selection_get_selected(selection, NULL, &iter)) {
-
-		gtk_tree_model_get(GTK_TREE_MODEL(window->model), &iter,
-		                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &conversation,
-		                   -1);
-	}
-
-	return conversation;
-}
-
-void
-pidgin_display_window_select(PidginDisplayWindow *window,
-                             PurpleConversation *conversation)
-{
-	const gchar *name = NULL;
-
-	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
-	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
-
-	name = purple_conversation_get_name(conversation);
-	adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack), name);
-}
-
-void
-pidgin_display_window_select_previous(PidginDisplayWindow *window) {
-	GtkTreeIter iter;
-	GtkTreeModel *model = NULL;
-	GtkTreeSelection *selection = NULL;
-	gboolean set = FALSE;
-
-	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
-
-	model = GTK_TREE_MODEL(window->model);
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
-	if(gtk_tree_selection_get_selected(selection, NULL, &iter)) {
-		if(gtk_tree_model_iter_previous(model, &iter)) {
-			gtk_tree_selection_select_iter(selection, &iter);
-			set = TRUE;
-		}
-	}
-
-	if(!set) {
-		pidgin_display_window_select_last(window);
-	}
-}
-
-
-void
-pidgin_display_window_select_next(PidginDisplayWindow *window) {
-	GtkTreeIter iter;
-	GtkTreeModel *model = NULL;
-	GtkTreeSelection *selection = NULL;
-	gboolean set = FALSE;
-
-	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
-
-	model = GTK_TREE_MODEL(window->model);
-
-	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
-	if(gtk_tree_selection_get_selected(selection, NULL, &iter)) {
-		if(gtk_tree_model_iter_next(model, &iter)) {
-			gtk_tree_selection_select_iter(selection, &iter);
-			set = TRUE;
-		}
-	}
-
-	if(!set) {
-		pidgin_display_window_select_first(window);
-	}
-}
-
-void
-pidgin_display_window_select_first(PidginDisplayWindow *window) {
-	GtkTreeIter iter;
-	GtkTreeModel *model = NULL;
-
-	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
-
-	model = GTK_TREE_MODEL(window->model);
-
-	if(gtk_tree_model_get_iter_first(model, &iter)) {
-		GtkTreeSelection *selection = NULL;
-
-		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
-		gtk_tree_selection_select_iter(selection, &iter);
-	}
-}
-
-void
-pidgin_display_window_select_last(PidginDisplayWindow *window) {
-	GtkTreeIter iter;
-	GtkTreeModel *model = NULL;
-	gint count = 0;
-
-	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
-
-	model = GTK_TREE_MODEL(window->model);
-	count = gtk_tree_model_iter_n_children(model, NULL);
-
-	if(gtk_tree_model_iter_nth_child(model, &iter, NULL, count - 1)) {
-		GtkTreeSelection *selection = NULL;
-
-		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
-		gtk_tree_selection_select_iter(selection, &iter);
-	}
-}
-
-void
-pidgin_display_window_select_nth(PidginDisplayWindow *window,
-                                 guint nth)
-{
-	GtkTreeIter iter;
-	GtkTreeModel *model = NULL;
-
-	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
-
-	model = GTK_TREE_MODEL(window->model);
-
-	if(gtk_tree_model_iter_nth_child(model, &iter, NULL, nth)) {
-		GtkTreeSelection *selection = NULL;
-
-		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
-		gtk_tree_selection_select_iter(selection, &iter);
-	}
-}
-
-gboolean
-pidgin_display_window_conversation_is_selected(PidginDisplayWindow *window,
-                                               PurpleConversation *conversation)
-{
-	const gchar *name = NULL, *visible = NULL;
-
-	g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), FALSE);
-	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
-
-	name = purple_conversation_get_name(conversation);
-	visible = adw_view_stack_get_visible_child_name(ADW_VIEW_STACK(window->stack));
-
-	return purple_strequal(name, visible);
-}
--- a/pidgin/pidginconversationwindow.h	Sun Sep 18 22:36:13 2022 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-/*
- * Pidgin - Internet Messenger
- * Copyright (C) Pidgin Developers <devel@pidgin.im>
- *
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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, see <https://www.gnu.org/licenses/>.
- */
-
-#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
-# error "only <pidgin.h> may be included directly"
-#endif
-
-#ifndef PIDGIN_DISPLAY_WINDOW_H
-#define PIDGIN_DISPLAY_WINDOW_H
-
-#include <glib.h>
-
-#include <gtk/gtk.h>
-
-#include <purple.h>
-
-G_BEGIN_DECLS
-
-/**
- * PidginDisplayWindow:
- *
- * #PidginDisplayWindow is a widget that contains #PidginConversations.
- *
- * Since: 3.0.0
- */
-
-#define PIDGIN_TYPE_DISPLAY_WINDOW (pidgin_display_window_get_type())
-G_DECLARE_FINAL_TYPE(PidginDisplayWindow, pidgin_display_window,
-                     PIDGIN, DISPLAY_WINDOW, GtkApplicationWindow)
-
-/**
- * pidgin_display_window_new:
- *
- * Creates a new #PidginDisplayWindow instance.
- *
- * Returns: (transfer full): The new #PidginDisplayWindow instance.
- */
-GtkWidget *pidgin_display_window_new(void);
-
-/**
- * pidgin_display_window_get_default:
- *
- * Gets or creates the default conversation window.
- *
- * Returns: (transfer none): The default #PidginDisplayWindow.
- *
- * Since: 3.0.0
- */
-GtkWidget *pidgin_display_window_get_default(void);
-
-/**
- * pidgin_display_window_add:
- * @window: The #PidginDisplayWindow instance.
- * @conversation: The #PurpleConversation to add to @window.
- *
- * Adds @conversation to @window. If @conversation is already in @window, this
- * does nothing.
- *
- * Since: 3.0.0
- */
-void pidgin_display_window_add(PidginDisplayWindow *window, PurpleConversation *conversation);
-
-/**
- * pidgin_display_window_remove:
- * @window: The #PidginDisplayWindow instance.
- * @conversation: The #PurpleConversation to remove from @window.
- *
- * Removes @conversation from @window. If @conversation is not in @window, this
- * does nothing.
- *
- * Since: 3.0.0
- */
-void pidgin_display_window_remove(PidginDisplayWindow *window, PurpleConversation *conversation);
-
-/**
- * pidgin_display_window_get_count:
- * @window: The conversation window instance.
- *
- * Gets the number of conversations that @window contains.
- *
- * Returns: The number of conversations that @window contains.
- *
- * Since: 3.0.0
- */
-guint pidgin_display_window_get_count(PidginDisplayWindow *window);
-
-/**
- * pidgin_display_window_get_selected:
- * @window: The conversation window instance.
- *
- * Gets the currently selected PurpleConversation or %NULL if there is no
- * selection.
- *
- * Returns: (transfer full): The selected PurpleConversation or %NULL.
- *
- * Since: 3.0.0
- */
-PurpleConversation *pidgin_display_window_get_selected(PidginDisplayWindow *window);
-
-/**
- * pidgin_display_window_select:
- * @window: The conversation window instance.
- * @conversation: The conversation to select.
- *
- * Selects @conversation, making it the active conversation.
- *
- * Since: 3.0.0
- */
-void pidgin_display_window_select(PidginDisplayWindow *window, PurpleConversation *conversation);
-
-/**
- * pidgin_display_window_select_previous:
- * @window: The conversation window instance.
- *
- * Switches to the conversation previous to the currently selected
- * conversation.
- *
- * If no conversation is selected, the last conversation will be selected.
- *
- * Since: 3.0.0
- */
-void pidgin_display_window_select_previous(PidginDisplayWindow *window);
-
-/**
- * pidgin_display_window_select_next:
- * @window: The conversation window instance.
- *
- * Switches to the conversation next of the currently selected conversation.
- *
- * If no conversation is selected, the first conversation will be selected.
- *
- * Since: 3.0.0
- */
-void pidgin_display_window_select_next(PidginDisplayWindow *window);
-
-/**
- * pidgin_display_window_select_first:
- * @window: The conversation window instance.
- *
- * Selects the first conversation in @window. If there are no conversations in
- * @window this does nothing.
- *
- * Since: 3.0.0
- */
-void pidgin_display_window_select_first(PidginDisplayWindow *window);
-
-/**
- * pidgin_display_window_select_last:
- * @window: The conversation window instance.
- *
- * Selects the last conversation in @window. If there are no conversations in
- * @window this does nothing.
- *
- * Since: 3.0.0
- */
-void pidgin_display_window_select_last(PidginDisplayWindow *window);
-
-/**
- * pidgin_display_window_select_nth:
- * @window: The conversation window instance.
- * @nth: The index of the conversation to switch to.
- *
- * Switches to the @nth conversation. @nth is a 0 based index, so the first
- * conversation is at index 0.
- *
- * Since: 3.0.0
- */
-void pidgin_display_window_select_nth(PidginDisplayWindow *window, guint nth);
-
-/**
- * pidgin_display_window_conversation_is_selected:
- * @window: The conversation window instance.
- * @conversation: The conversation instance.
- *
- * Checks whether @conversation is the active conversation in @window.
- *
- * Returns: %TRUE if @conversation is active.
- *
- * Since: 3.0.0
- */
-gboolean pidgin_display_window_conversation_is_selected(PidginDisplayWindow *window, PurpleConversation *conversation);
-
-G_END_DECLS
-
-#endif /* PIDGIN_DISPLAY_WINDOW_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgindisplaywindow.c	Sun Sep 18 22:49:16 2022 -0500
@@ -0,0 +1,822 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include <adwaita.h>
+
+#include "pidgindisplaywindow.h"
+
+#include "gtkconv.h"
+#include "gtkdialogs.h"
+#include "gtkutils.h"
+#include "pidgininvitedialog.h"
+
+enum {
+	PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT,
+	PIDGIN_DISPLAY_WINDOW_COLUMN_NAME,
+	PIDGIN_DISPLAY_WINDOW_COLUMN_ICON,
+	PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP,
+};
+
+enum {
+	SIG_CONVERSATION_SWITCHED,
+	N_SIGNALS,
+};
+static guint signals[N_SIGNALS] = {0, };
+
+struct _PidginDisplayWindow {
+	GtkApplicationWindow parent;
+
+	GtkWidget *vbox;
+
+	GtkWidget *view;
+	GtkTreeSelection *selection;
+	GtkTreeStore *model;
+
+	GtkWidget *stack;
+
+	GtkWidget *notification_list;
+
+	GtkTreePath *conversation_path;
+};
+
+G_DEFINE_TYPE(PidginDisplayWindow, pidgin_display_window,
+              GTK_TYPE_APPLICATION_WINDOW)
+
+static GtkWidget *default_window = NULL;
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+pidgin_display_window_actions_set_enabled(GActionMap *map,
+                                          const gchar **actions,
+                                          gboolean enabled)
+{
+	gint i = 0;
+
+	for(i = 0; actions[i] != NULL; i++) {
+		GAction *action = NULL;
+		const gchar *name = actions[i];
+
+		action = g_action_map_lookup_action(map, name);
+		if(action != NULL) {
+			g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
+		} else {
+			g_critical("Failed to find action named %s", name);
+		}
+	}
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_display_window_invite_cb(GtkDialog *dialog, gint response_id,
+                                G_GNUC_UNUSED gpointer data)
+{
+	PidginInviteDialog *invite_dialog = PIDGIN_INVITE_DIALOG(dialog);
+	PurpleChatConversation *chat = NULL;
+
+	chat = pidgin_invite_dialog_get_conversation(invite_dialog);
+
+	g_object_set_data(G_OBJECT(chat), "pidgin-invite-dialog", NULL);
+
+	if(response_id == GTK_RESPONSE_ACCEPT) {
+		const gchar *contact = NULL, *message = NULL;
+
+		contact = pidgin_invite_dialog_get_contact(invite_dialog);
+		message = pidgin_invite_dialog_get_message(invite_dialog);
+
+		if(!purple_strequal(contact, "")) {
+			PurpleConnection *connection = NULL;
+
+			connection = purple_conversation_get_connection(PURPLE_CONVERSATION(chat));
+			purple_serv_chat_invite(connection,
+			                        purple_chat_conversation_get_id(chat),
+			                        message, contact);
+		}
+	}
+
+	gtk_window_destroy(GTK_WINDOW(invite_dialog));
+}
+
+/******************************************************************************
+ * Actions
+ *****************************************************************************/
+static void
+pidgin_display_window_alias(G_GNUC_UNUSED GSimpleAction *simple,
+                            G_GNUC_UNUSED GVariant *parameter,
+                            gpointer data)
+{
+	PidginDisplayWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_display_window_get_selected(window);
+	if(PURPLE_IS_CONVERSATION(selected)) {
+		PurpleAccount *account;
+		const gchar *name;
+
+		account = purple_conversation_get_account(selected);
+		name = purple_conversation_get_name(selected);
+
+		if(PURPLE_IS_IM_CONVERSATION(selected)) {
+			PurpleBuddy *buddy = purple_blist_find_buddy(account, name);
+
+			if(PURPLE_IS_BUDDY(buddy)) {
+				pidgin_dialogs_alias_buddy(buddy);
+			}
+		} else if(PURPLE_IS_CHAT_CONVERSATION(selected)) {
+			PurpleChat *chat = purple_blist_find_chat(account, name);
+
+			if(PURPLE_IS_CHAT(chat)) {
+				pidgin_dialogs_alias_chat(chat);
+			}
+		}
+	}
+}
+
+static void
+pidgin_display_window_close_conversation(G_GNUC_UNUSED GSimpleAction *simple,
+                                         G_GNUC_UNUSED GVariant *parameter,
+                                         gpointer data)
+{
+	PidginDisplayWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_display_window_get_selected(window);
+	if(PURPLE_IS_CONVERSATION(selected)) {
+		pidgin_display_window_remove(window, selected);
+		pidgin_conversation_detach(selected);
+	}
+}
+
+static void
+pidgin_display_window_get_info(G_GNUC_UNUSED GSimpleAction *simple,
+                               G_GNUC_UNUSED GVariant *parameter,
+                               gpointer data)
+{
+	PidginDisplayWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_display_window_get_selected(window);
+	if(PURPLE_IS_CONVERSATION(selected)) {
+		if(PURPLE_IS_IM_CONVERSATION(selected)) {
+			PurpleConnection *connection = NULL;
+
+			connection = purple_conversation_get_connection(selected);
+			pidgin_retrieve_user_info(connection,
+			                          purple_conversation_get_name(selected));
+		}
+	}
+}
+
+static void
+pidgin_display_window_invite(G_GNUC_UNUSED GSimpleAction *simple,
+                             G_GNUC_UNUSED GVariant *parameter,
+                             gpointer data)
+{
+	PidginDisplayWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_display_window_get_selected(window);
+	if(PURPLE_IS_CHAT_CONVERSATION(selected)) {
+		GtkWidget *invite_dialog = NULL;
+
+		invite_dialog = g_object_get_data(G_OBJECT(selected),
+		                                  "pidgin-invite-dialog");
+
+		if(!GTK_IS_WIDGET(invite_dialog)) {
+			invite_dialog = pidgin_invite_dialog_new(PURPLE_CHAT_CONVERSATION(selected));
+			g_object_set_data(G_OBJECT(selected), "pidgin-invite-dialog",
+			                  invite_dialog);
+
+			gtk_window_set_transient_for(GTK_WINDOW(invite_dialog),
+			                             GTK_WINDOW(window));
+			gtk_window_set_destroy_with_parent(GTK_WINDOW(invite_dialog), TRUE);
+
+			g_signal_connect(invite_dialog, "response",
+			                 G_CALLBACK(pidgin_display_window_invite_cb),
+			                 NULL);
+		}
+
+		gtk_widget_show(invite_dialog);
+	}
+}
+
+static void
+pidgin_display_window_send_file(G_GNUC_UNUSED GSimpleAction *simple,
+                                G_GNUC_UNUSED GVariant *parameter,
+                                gpointer data)
+{
+	PidginDisplayWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_display_window_get_selected(window);
+	if(PURPLE_IS_IM_CONVERSATION(selected)) {
+		PurpleConnection *connection = NULL;
+
+		connection = purple_conversation_get_connection(selected);
+		purple_serv_send_file(connection,
+		                      purple_conversation_get_name(selected),
+		                      NULL);
+	}
+}
+
+static GActionEntry win_entries[] = {
+	{
+		.name = "alias",
+		.activate = pidgin_display_window_alias
+	}, {
+		.name = "close",
+		.activate = pidgin_display_window_close_conversation
+	}, {
+		.name = "get-info",
+		.activate = pidgin_display_window_get_info
+	}, {
+		.name = "invite",
+		.activate = pidgin_display_window_invite
+	}, {
+		.name = "send-file",
+		.activate = pidgin_display_window_send_file
+	}
+};
+
+/*<private>
+ * pidgin_display_window_conversation_actions:
+ *
+ * A list of action names that are only valid if a conversation is selected.
+ */
+static const gchar *pidgin_display_window_conversation_actions[] = {
+	"alias",
+	"close",
+	"get-info",
+	NULL
+};
+
+static const gchar *pidgin_display_window_im_conversation_actions[] = {
+	"send-file",
+	NULL
+};
+
+static const gchar *pidgin_display_window_chat_conversation_actions[] = {
+	"invite",
+	NULL
+};
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_display_window_selection_changed(GtkTreeSelection *selection,
+                                        gpointer data)
+{
+	PidginDisplayWindow *window = PIDGIN_DISPLAY_WINDOW(data);
+	GtkTreeModel *model = NULL;
+	GtkTreeIter iter;
+	gboolean changed = FALSE;
+
+	if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
+		GObject *obj;
+		gboolean is_conversation = FALSE;
+		gboolean im_selected = FALSE, chat_selected = FALSE;
+		gchar *name = NULL;
+
+		gtk_tree_model_get(model, &iter,
+		                   PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, &name,
+		                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &obj,
+		                   -1);
+
+		adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack),
+		                                      name);
+		g_free(name);
+
+		changed = TRUE;
+
+		/* If a conversation is selected, enable the generic conversation
+		 * actions.
+		 */
+		is_conversation = PURPLE_IS_CONVERSATION(obj);
+		pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window),
+							       pidgin_display_window_conversation_actions,
+							       is_conversation);
+
+		/* If an IM is selected, enable the IM-specific actions otherwise
+		 * disable them.
+		 */
+		im_selected = PURPLE_IS_IM_CONVERSATION(obj);
+		pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window),
+							       pidgin_display_window_im_conversation_actions,
+							       im_selected);
+
+		/* If a chat is selected, enable the chat-specific actions otherwise
+		 * disable them.
+		 */
+		chat_selected = PURPLE_IS_CHAT_CONVERSATION(obj);
+		pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window),
+							       pidgin_display_window_chat_conversation_actions,
+							       chat_selected);
+
+		g_clear_object(&obj);
+	}
+
+	if(!changed) {
+		adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack),
+		                                      "__conversations__");
+	}
+}
+
+static gboolean
+pidgin_display_window_key_pressed_cb(GtkEventControllerKey *controller,
+                                     guint keyval,
+                                     G_GNUC_UNUSED guint keycode,
+                                     GdkModifierType state,
+                                     gpointer data)
+{
+	PidginDisplayWindow *window = data;
+
+	/* If CTRL was held down... */
+	if (state & GDK_CONTROL_MASK) {
+		switch (keyval) {
+			case GDK_KEY_Page_Down:
+			case GDK_KEY_KP_Page_Down:
+			case ']':
+				pidgin_display_window_select_next(window);
+				return TRUE;
+				break;
+
+			case GDK_KEY_Page_Up:
+			case GDK_KEY_KP_Page_Up:
+			case '[':
+				pidgin_display_window_select_previous(window);
+				return TRUE;
+				break;
+		} /* End of switch */
+	}
+
+	/* If ALT (or whatever) was held down... */
+	else if (state & GDK_ALT_MASK) {
+		if ('1' <= keyval && keyval <= '9') {
+			guint switchto = keyval - '1';
+			pidgin_display_window_select_nth(window, switchto);
+
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+/******************************************************************************
+ * GObjectImplementation
+ *****************************************************************************/
+static void
+pidgin_display_window_dispose(GObject *obj) {
+	PidginDisplayWindow *window = PIDGIN_DISPLAY_WINDOW(obj);
+
+	if(GTK_IS_TREE_MODEL(window->model)) {
+		GtkTreeModel *model = GTK_TREE_MODEL(window->model);
+		GtkTreeIter parent, iter;
+
+		gtk_tree_model_get_iter(model, &parent, window->conversation_path);
+		if(gtk_tree_model_iter_children(model, &iter, &parent)) {
+			gboolean valid = FALSE;
+
+			/* gtk_tree_store_remove moves the iter to the next item at the
+			 * same level, so we abuse that to do our iteration.
+			 */
+			do {
+				PurpleConversation *conversation = NULL;
+
+				gtk_tree_model_get(model, &iter,
+				                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &conversation,
+				                   -1);
+
+				if(PURPLE_IS_CONVERSATION(conversation)) {
+					pidgin_conversation_detach(conversation);
+
+					valid = gtk_tree_store_remove(window->model, &iter);
+				}
+			} while(valid);
+		}
+
+		g_clear_pointer(&window->conversation_path, gtk_tree_path_free);
+	}
+
+	G_OBJECT_CLASS(pidgin_display_window_parent_class)->dispose(obj);
+}
+
+static void
+pidgin_display_window_init(PidginDisplayWindow *window) {
+	GtkEventController *key = NULL;
+	GtkTreeIter iter;
+
+	gtk_widget_init_template(GTK_WIDGET(window));
+
+	gtk_window_set_application(GTK_WINDOW(window),
+	                           GTK_APPLICATION(g_application_get_default()));
+
+	g_action_map_add_action_entries(G_ACTION_MAP(window), win_entries,
+	                                G_N_ELEMENTS(win_entries), window);
+
+	key = gtk_event_controller_key_new();
+	gtk_event_controller_set_propagation_phase(key, GTK_PHASE_CAPTURE);
+	g_signal_connect(G_OBJECT(key), "key-pressed",
+	                 G_CALLBACK(pidgin_display_window_key_pressed_cb),
+	                 window);
+	gtk_widget_add_controller(GTK_WIDGET(window), key);
+
+	/* Add our toplevels to the tree store. */
+	gtk_tree_store_append(window->model, &iter, NULL);
+	gtk_tree_store_set(window->model, &iter,
+	                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, window->notification_list,
+	                   PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, "__notifications__",
+	                   PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, _("Notifications"),
+	                   -1);
+
+	gtk_tree_store_append(window->model, &iter, NULL);
+	gtk_tree_store_set(window->model, &iter,
+	                   PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, _("Conversations"),
+	                   PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, "__conversations__",
+	                   -1);
+	gtk_tree_selection_select_iter(window->selection, &iter);
+	window->conversation_path = gtk_tree_model_get_path(GTK_TREE_MODEL(window->model),
+	                                                    &iter);
+}
+
+static void
+pidgin_display_window_class_init(PidginDisplayWindowClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	obj_class->dispose = pidgin_display_window_dispose;
+
+	/**
+	 * PidginDisplayWindow::conversation-switched:
+	 * @window: The conversation window.
+	 * @new_conv: The now active conversation.
+	 *
+	 * Emitted when a window switched from one conversation to another.
+	 */
+	signals[SIG_CONVERSATION_SWITCHED] = g_signal_new_class_handler(
+		"conversation-switched",
+		G_OBJECT_CLASS_TYPE(obj_class),
+		G_SIGNAL_RUN_LAST,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		1,
+		PURPLE_TYPE_CONVERSATION
+	);
+
+	gtk_widget_class_set_template_from_resource(
+	    widget_class,
+	    "/im/pidgin/Pidgin3/Display/window.ui"
+	);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
+	                                     vbox);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
+	                                     model);
+	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
+	                                     view);
+	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
+	                                     selection);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
+	                                     stack);
+	gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
+	                                     notification_list);
+
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_display_window_selection_changed);
+
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_display_window_key_pressed_cb);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GtkWidget *
+pidgin_display_window_get_default(void) {
+	if(!GTK_IS_WIDGET(default_window)) {
+		default_window = pidgin_display_window_new();
+		g_object_add_weak_pointer(G_OBJECT(default_window),
+		                          (gpointer)&default_window);
+	}
+
+	return default_window;
+}
+
+GtkWidget *
+pidgin_display_window_new(void) {
+	return g_object_new(
+		PIDGIN_TYPE_DISPLAY_WINDOW,
+		"show-menubar", TRUE,
+		NULL);
+}
+
+void
+pidgin_display_window_add(PidginDisplayWindow *window,
+                          PurpleConversation *conversation)
+{
+	PidginConversation *gtkconv = NULL;
+	GtkTreeIter parent, iter;
+	GtkTreeModel *model = NULL;
+	const gchar *markup = NULL;
+	gboolean expand = FALSE;
+
+	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	model = GTK_TREE_MODEL(window->model);
+	if(!gtk_tree_model_get_iter(model, &parent, window->conversation_path)) {
+		/* If we can't find the conversation_path we have to bail. */
+		g_warning("couldn't get an iterator to conversation_path");
+
+		return;
+	}
+
+	if(!gtk_tree_model_iter_has_child(model, &parent)) {
+		expand = TRUE;
+	}
+
+	markup = purple_conversation_get_name(conversation);
+
+	gtkconv = PIDGIN_CONVERSATION(conversation);
+	if(gtkconv != NULL) {
+		GtkWidget *parent = gtk_widget_get_parent(gtkconv->tab_cont);
+
+		if(GTK_IS_WIDGET(parent)) {
+			g_object_ref(gtkconv->tab_cont);
+			gtk_widget_unparent(gtkconv->tab_cont);
+		}
+
+		adw_view_stack_add_named(ADW_VIEW_STACK(window->stack),
+		                         gtkconv->tab_cont, markup);
+		gtk_widget_show(gtkconv->tab_cont);
+
+		if(GTK_IS_WIDGET(parent)) {
+			g_object_unref(gtkconv->tab_cont);
+		}
+	}
+
+	gtk_tree_store_prepend(window->model, &iter, &parent);
+	gtk_tree_store_set(window->model, &iter,
+	                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, conversation,
+	                   PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, markup,
+	                   PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, markup,
+	                   -1);
+
+	/* If we just added the first child, expand the parent. */
+	if(expand) {
+		gtk_tree_view_expand_row(GTK_TREE_VIEW(window->view),
+		                         window->conversation_path, FALSE);
+	}
+
+
+	if(!gtk_widget_is_visible(GTK_WIDGET(window))) {
+		gtk_widget_show(GTK_WIDGET(window));
+	}
+}
+
+void
+pidgin_display_window_remove(PidginDisplayWindow *window,
+                             PurpleConversation *conversation)
+{
+	GtkTreeIter parent, iter;
+	GtkTreeModel *model = NULL;
+	GObject *obj = NULL;
+
+	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	model = GTK_TREE_MODEL(window->model);
+
+	if(!gtk_tree_model_get_iter(model, &parent, window->conversation_path)) {
+		/* The path is somehow invalid, so bail... */
+		return;
+	}
+
+	if(!gtk_tree_model_iter_children(model, &iter, &parent)) {
+		/* The conversations iter has no children. */
+		return;
+	}
+
+	do {
+		gtk_tree_model_get(model, &iter,
+		                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &obj,
+		                   -1);
+
+		if(PURPLE_CONVERSATION(obj) == conversation) {
+			GtkWidget *child = NULL;
+			const gchar *name = NULL;
+
+			name = purple_conversation_get_name(conversation);
+			child = adw_view_stack_get_child_by_name(ADW_VIEW_STACK(window->stack),
+			                                         name);
+			if(GTK_IS_WIDGET(child)) {
+				gtk_widget_unparent(child);
+			}
+
+			gtk_tree_store_remove(window->model, &iter);
+
+			g_clear_object(&obj);
+
+			break;
+		}
+
+		g_clear_object(&obj);
+	} while(gtk_tree_model_iter_next(model, &iter));
+}
+
+guint
+pidgin_display_window_get_count(PidginDisplayWindow *window) {
+	GtkSelectionModel *model = NULL;
+	guint count = 0;
+
+	g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), 0);
+
+	model = adw_view_stack_get_pages(ADW_VIEW_STACK(window->stack));
+
+	count = g_list_model_get_n_items(G_LIST_MODEL(model));
+
+	g_object_unref(model);
+
+	return count;
+}
+
+PurpleConversation *
+pidgin_display_window_get_selected(PidginDisplayWindow *window) {
+	PurpleConversation *conversation = NULL;
+	GtkTreeSelection *selection = NULL;
+	GtkTreeIter iter;
+
+	g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), NULL);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
+	if(gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+
+		gtk_tree_model_get(GTK_TREE_MODEL(window->model), &iter,
+		                   PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &conversation,
+		                   -1);
+	}
+
+	return conversation;
+}
+
+void
+pidgin_display_window_select(PidginDisplayWindow *window,
+                             PurpleConversation *conversation)
+{
+	const gchar *name = NULL;
+
+	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	name = purple_conversation_get_name(conversation);
+	adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack), name);
+}
+
+void
+pidgin_display_window_select_previous(PidginDisplayWindow *window) {
+	GtkTreeIter iter;
+	GtkTreeModel *model = NULL;
+	GtkTreeSelection *selection = NULL;
+	gboolean set = FALSE;
+
+	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
+
+	model = GTK_TREE_MODEL(window->model);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
+	if(gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+		if(gtk_tree_model_iter_previous(model, &iter)) {
+			gtk_tree_selection_select_iter(selection, &iter);
+			set = TRUE;
+		}
+	}
+
+	if(!set) {
+		pidgin_display_window_select_last(window);
+	}
+}
+
+
+void
+pidgin_display_window_select_next(PidginDisplayWindow *window) {
+	GtkTreeIter iter;
+	GtkTreeModel *model = NULL;
+	GtkTreeSelection *selection = NULL;
+	gboolean set = FALSE;
+
+	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
+
+	model = GTK_TREE_MODEL(window->model);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
+	if(gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+		if(gtk_tree_model_iter_next(model, &iter)) {
+			gtk_tree_selection_select_iter(selection, &iter);
+			set = TRUE;
+		}
+	}
+
+	if(!set) {
+		pidgin_display_window_select_first(window);
+	}
+}
+
+void
+pidgin_display_window_select_first(PidginDisplayWindow *window) {
+	GtkTreeIter iter;
+	GtkTreeModel *model = NULL;
+
+	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
+
+	model = GTK_TREE_MODEL(window->model);
+
+	if(gtk_tree_model_get_iter_first(model, &iter)) {
+		GtkTreeSelection *selection = NULL;
+
+		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
+		gtk_tree_selection_select_iter(selection, &iter);
+	}
+}
+
+void
+pidgin_display_window_select_last(PidginDisplayWindow *window) {
+	GtkTreeIter iter;
+	GtkTreeModel *model = NULL;
+	gint count = 0;
+
+	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
+
+	model = GTK_TREE_MODEL(window->model);
+	count = gtk_tree_model_iter_n_children(model, NULL);
+
+	if(gtk_tree_model_iter_nth_child(model, &iter, NULL, count - 1)) {
+		GtkTreeSelection *selection = NULL;
+
+		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
+		gtk_tree_selection_select_iter(selection, &iter);
+	}
+}
+
+void
+pidgin_display_window_select_nth(PidginDisplayWindow *window,
+                                 guint nth)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model = NULL;
+
+	g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
+
+	model = GTK_TREE_MODEL(window->model);
+
+	if(gtk_tree_model_iter_nth_child(model, &iter, NULL, nth)) {
+		GtkTreeSelection *selection = NULL;
+
+		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
+		gtk_tree_selection_select_iter(selection, &iter);
+	}
+}
+
+gboolean
+pidgin_display_window_conversation_is_selected(PidginDisplayWindow *window,
+                                               PurpleConversation *conversation)
+{
+	const gchar *name = NULL, *visible = NULL;
+
+	g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), FALSE);
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	name = purple_conversation_get_name(conversation);
+	visible = adw_view_stack_get_visible_child_name(ADW_VIEW_STACK(window->stack));
+
+	return purple_strequal(name, visible);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgindisplaywindow.h	Sun Sep 18 22:49:16 2022 -0500
@@ -0,0 +1,204 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
+# error "only <pidgin.h> may be included directly"
+#endif
+
+#ifndef PIDGIN_DISPLAY_WINDOW_H
+#define PIDGIN_DISPLAY_WINDOW_H
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+#include <purple.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PidginDisplayWindow:
+ *
+ * #PidginDisplayWindow is a widget that contains #PidginConversations.
+ *
+ * Since: 3.0.0
+ */
+
+#define PIDGIN_TYPE_DISPLAY_WINDOW (pidgin_display_window_get_type())
+G_DECLARE_FINAL_TYPE(PidginDisplayWindow, pidgin_display_window,
+                     PIDGIN, DISPLAY_WINDOW, GtkApplicationWindow)
+
+/**
+ * pidgin_display_window_new:
+ *
+ * Creates a new #PidginDisplayWindow instance.
+ *
+ * Returns: (transfer full): The new #PidginDisplayWindow instance.
+ */
+GtkWidget *pidgin_display_window_new(void);
+
+/**
+ * pidgin_display_window_get_default:
+ *
+ * Gets or creates the default conversation window.
+ *
+ * Returns: (transfer none): The default #PidginDisplayWindow.
+ *
+ * Since: 3.0.0
+ */
+GtkWidget *pidgin_display_window_get_default(void);
+
+/**
+ * pidgin_display_window_add:
+ * @window: The #PidginDisplayWindow instance.
+ * @conversation: The #PurpleConversation to add to @window.
+ *
+ * Adds @conversation to @window. If @conversation is already in @window, this
+ * does nothing.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_display_window_add(PidginDisplayWindow *window, PurpleConversation *conversation);
+
+/**
+ * pidgin_display_window_remove:
+ * @window: The #PidginDisplayWindow instance.
+ * @conversation: The #PurpleConversation to remove from @window.
+ *
+ * Removes @conversation from @window. If @conversation is not in @window, this
+ * does nothing.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_display_window_remove(PidginDisplayWindow *window, PurpleConversation *conversation);
+
+/**
+ * pidgin_display_window_get_count:
+ * @window: The conversation window instance.
+ *
+ * Gets the number of conversations that @window contains.
+ *
+ * Returns: The number of conversations that @window contains.
+ *
+ * Since: 3.0.0
+ */
+guint pidgin_display_window_get_count(PidginDisplayWindow *window);
+
+/**
+ * pidgin_display_window_get_selected:
+ * @window: The conversation window instance.
+ *
+ * Gets the currently selected PurpleConversation or %NULL if there is no
+ * selection.
+ *
+ * Returns: (transfer full): The selected PurpleConversation or %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversation *pidgin_display_window_get_selected(PidginDisplayWindow *window);
+
+/**
+ * pidgin_display_window_select:
+ * @window: The conversation window instance.
+ * @conversation: The conversation to select.
+ *
+ * Selects @conversation, making it the active conversation.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_display_window_select(PidginDisplayWindow *window, PurpleConversation *conversation);
+
+/**
+ * pidgin_display_window_select_previous:
+ * @window: The conversation window instance.
+ *
+ * Switches to the conversation previous to the currently selected
+ * conversation.
+ *
+ * If no conversation is selected, the last conversation will be selected.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_display_window_select_previous(PidginDisplayWindow *window);
+
+/**
+ * pidgin_display_window_select_next:
+ * @window: The conversation window instance.
+ *
+ * Switches to the conversation next of the currently selected conversation.
+ *
+ * If no conversation is selected, the first conversation will be selected.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_display_window_select_next(PidginDisplayWindow *window);
+
+/**
+ * pidgin_display_window_select_first:
+ * @window: The conversation window instance.
+ *
+ * Selects the first conversation in @window. If there are no conversations in
+ * @window this does nothing.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_display_window_select_first(PidginDisplayWindow *window);
+
+/**
+ * pidgin_display_window_select_last:
+ * @window: The conversation window instance.
+ *
+ * Selects the last conversation in @window. If there are no conversations in
+ * @window this does nothing.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_display_window_select_last(PidginDisplayWindow *window);
+
+/**
+ * pidgin_display_window_select_nth:
+ * @window: The conversation window instance.
+ * @nth: The index of the conversation to switch to.
+ *
+ * Switches to the @nth conversation. @nth is a 0 based index, so the first
+ * conversation is at index 0.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_display_window_select_nth(PidginDisplayWindow *window, guint nth);
+
+/**
+ * pidgin_display_window_conversation_is_selected:
+ * @window: The conversation window instance.
+ * @conversation: The conversation instance.
+ *
+ * Checks whether @conversation is the active conversation in @window.
+ *
+ * Returns: %TRUE if @conversation is active.
+ *
+ * Since: 3.0.0
+ */
+gboolean pidgin_display_window_conversation_is_selected(PidginDisplayWindow *window, PurpleConversation *conversation);
+
+G_END_DECLS
+
+#endif /* PIDGIN_DISPLAY_WINDOW_H */
--- a/pidgin/resources/Conversations/window.ui	Sun Sep 18 22:36:13 2022 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Pidgin - Internet Messenger
-Copyright (C) Pidgin Developers <devel@pidgin.im>
-
-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 library; if not, see <https://www.gnu.org/licenses/>.
--->
-<interface>
-  <requires lib="gtk" version="4.0"/>
-  <requires lib="Adw" version="1.0"/>
-  <requires lib="pidgin" version="3.0"/>
-  <!-- interface-license-type gplv2 -->
-  <!-- interface-name Pidgin -->
-  <!-- interface-description Internet Messenger -->
-  <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
-  <object class="GtkTreeStore" id="model">
-    <columns>
-      <!-- column-name conversation -->
-      <column type="GObject"/>
-      <!-- column-name name -->
-      <column type="gchararray"/>
-      <!-- column-name icon -->
-      <column type="GdkPixbuf"/>
-      <!-- column-name markup -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
-  <template class="PidginDisplayWindow" parent="GtkApplicationWindow">
-    <property name="show-menubar">1</property>
-    <property name="default-height">450</property>
-    <property name="default-width">950</property>
-    <child>
-      <object class="GtkEventControllerKey">
-        <property name="propagation-phase">capture</property>
-        <signal name="key-pressed" handler="pidgin_display_window_key_pressed_cb"/>
-      </object>
-    </child>
-    <child>
-      <object class="GtkBox" id="vbox">
-        <property name="orientation">vertical</property>
-        <child>
-          <object class="GtkPaned" id="paned">
-            <property name="vexpand">1</property>
-            <property name="focusable">1</property>
-            <property name="position">220</property>
-            <child>
-              <object class="GtkBox">
-                <property name="margin-top">6</property>
-                <property name="margin-start">6</property>
-                <property name="margin-end">6</property>
-                <property name="margin-bottom">6</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">6</property>
-                <child>
-                  <object class="PidginStatusBox"/>
-                </child>
-                <child>
-                  <object class="GtkScrolledWindow">
-                    <property name="vexpand">1</property>
-                    <property name="focusable">1</property>
-                    <property name="propagate-natural-width">1</property>
-                    <property name="child">
-                      <object class="GtkTreeView" id="view">
-                        <property name="focusable">1</property>
-                        <property name="model">model</property>
-                        <property name="headers-visible">0</property>
-                        <property name="search-column">3</property>
-                        <child internal-child="selection">
-                          <object class="GtkTreeSelection" id="selection">
-                            <property name="mode">browse</property>
-                            <signal name="changed" handler="pidgin_display_window_selection_changed" object="PidginDisplayWindow" swapped="no"/>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkTreeViewColumn" id="markup">
-                            <child>
-                              <object class="GtkCellRendererPixbuf" id="icon"/>
-                              <attributes>
-                                <attribute name="pixbuf">2</attribute>
-                              </attributes>
-                            </child>
-                            <child>
-                              <object class="GtkCellRendererText" id="name"/>
-                              <attributes>
-                                <attribute name="markup">3</attribute>
-                              </attributes>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="AdwViewStack" id="stack">
-                <child>
-                  <object class="AdwViewStackPage">
-                    <property name="name">__notifications__</property>
-                    <property name="child">
-                      <object class="GtkScrolledWindow">
-                        <child>
-                          <object class="PidginNotificationList" id="notification_list">
-                            <property name="orientation">vertical</property>
-                          </object>
-                        </child>
-                      </object>
-                    </property>
-                  </object>
-                </child>
-                <child>
-                  <object class="AdwViewStackPage">
-                    <property name="name">__conversations__</property>
-                    <property name="child">
-                      <object class="AdwStatusPage">
-                        <property name="icon-name">mail-send-symbolic</property>
-                        <property name="title" translatable="1">Conversations</property>
-                        <property name="description" translatable="1">When you send a message to a friend or join a chat it will show up here!</property>
-                      </object>
-                    </property>
-                  </object>
-                </child>
-              </object>
-            </child>
-          </object>
-        </child>
-      </object>
-    </child>
-  </template>
-</interface>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Display/window.ui	Sun Sep 18 22:49:16 2022 -0500
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Pidgin - Internet Messenger
+Copyright (C) Pidgin Developers <devel@pidgin.im>
+
+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 library; if not, see <https://www.gnu.org/licenses/>.
+-->
+<interface>
+  <requires lib="gtk" version="4.0"/>
+  <requires lib="Adw" version="1.0"/>
+  <requires lib="pidgin" version="3.0"/>
+  <!-- interface-license-type gplv2 -->
+  <!-- interface-name Pidgin -->
+  <!-- interface-description Internet Messenger -->
+  <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+  <object class="GtkTreeStore" id="model">
+    <columns>
+      <!-- column-name conversation -->
+      <column type="GObject"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name icon -->
+      <column type="GdkPixbuf"/>
+      <!-- column-name markup -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <template class="PidginDisplayWindow" parent="GtkApplicationWindow">
+    <property name="show-menubar">1</property>
+    <property name="default-height">450</property>
+    <property name="default-width">950</property>
+    <child>
+      <object class="GtkEventControllerKey">
+        <property name="propagation-phase">capture</property>
+        <signal name="key-pressed" handler="pidgin_display_window_key_pressed_cb"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkBox" id="vbox">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkPaned" id="paned">
+            <property name="vexpand">1</property>
+            <property name="focusable">1</property>
+            <property name="position">220</property>
+            <child>
+              <object class="GtkBox">
+                <property name="margin-top">6</property>
+                <property name="margin-start">6</property>
+                <property name="margin-end">6</property>
+                <property name="margin-bottom">6</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="PidginStatusBox"/>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow">
+                    <property name="vexpand">1</property>
+                    <property name="focusable">1</property>
+                    <property name="propagate-natural-width">1</property>
+                    <property name="child">
+                      <object class="GtkTreeView" id="view">
+                        <property name="focusable">1</property>
+                        <property name="model">model</property>
+                        <property name="headers-visible">0</property>
+                        <property name="search-column">3</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection" id="selection">
+                            <property name="mode">browse</property>
+                            <signal name="changed" handler="pidgin_display_window_selection_changed" object="PidginDisplayWindow" swapped="no"/>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkTreeViewColumn" id="markup">
+                            <child>
+                              <object class="GtkCellRendererPixbuf" id="icon"/>
+                              <attributes>
+                                <attribute name="pixbuf">2</attribute>
+                              </attributes>
+                            </child>
+                            <child>
+                              <object class="GtkCellRendererText" id="name"/>
+                              <attributes>
+                                <attribute name="markup">3</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="AdwViewStack" id="stack">
+                <child>
+                  <object class="AdwViewStackPage">
+                    <property name="name">__notifications__</property>
+                    <property name="child">
+                      <object class="GtkScrolledWindow">
+                        <child>
+                          <object class="PidginNotificationList" id="notification_list">
+                            <property name="orientation">vertical</property>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </child>
+                <child>
+                  <object class="AdwViewStackPage">
+                    <property name="name">__conversations__</property>
+                    <property name="child">
+                      <object class="AdwStatusPage">
+                        <property name="icon-name">mail-send-symbolic</property>
+                        <property name="title" translatable="1">Conversations</property>
+                        <property name="description" translatable="1">When you send a message to a friend or join a chat it will show up here!</property>
+                      </object>
+                    </property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
--- a/pidgin/resources/pidgin.gresource.xml	Sun Sep 18 22:36:13 2022 -0500
+++ b/pidgin/resources/pidgin.gresource.xml	Sun Sep 18 22:49:16 2022 -0500
@@ -15,10 +15,10 @@
     <file compressed="true">Conversations/infopane.ui</file>
     <file compressed="true">Conversations/invite_dialog.ui</file>
     <file compressed="true">Conversations/tab-label.css</file>
-    <file compressed="true">Conversations/window.ui</file>
     <file compressed="true">Debug/debug.ui</file>
     <file compressed="true">Dialogs/addbuddy.ui</file>
     <file compressed="true">Dialogs/addchat.ui</file>
+    <file compressed="true">Display/window.ui</file>
     <file compressed="true">Keypad/keypad.ui</file>
     <file compressed="true">Media/window.ui</file>
     <file compressed="true">Notifications/addcontact.ui</file>
--- a/po/POTFILES.in	Sun Sep 18 22:36:13 2022 -0500
+++ b/po/POTFILES.in	Sun Sep 18 22:49:16 2022 -0500
@@ -347,9 +347,9 @@
 pidgin/pidgincolor.c
 pidgin/pidgincommands.c
 pidgin/pidgincontactlistwindow.c
-pidgin/pidginconversationwindow.c
 pidgin/pidgindebug.c
 pidgin/pidgindialog.c
+pidgin/pidgindisplaywindow.c
 pidgin/pidginiconname.c
 pidgin/pidgininfopane.c
 pidgin/pidgininvitedialog.c
@@ -398,10 +398,10 @@
 pidgin/resources/BuddyList/window.ui
 pidgin/resources/Conversations/infopane.ui
 pidgin/resources/Conversations/invite_dialog.ui
-pidgin/resources/Conversations/window.ui
 pidgin/resources/Debug/debug.ui
 pidgin/resources/Dialogs/addbuddy.ui
 pidgin/resources/Dialogs/addchat.ui
+pidgin/resources/Display/window.ui
 pidgin/resources/Keypad/keypad.ui
 pidgin/resources/Media/window.ui
 pidgin/resources/Plugins/dialog.ui

mercurial