# HG changeset patch
# Node ID 4833e6f79878
# Parent  86beaec0fa35
diff --git a/libpurple/account.c b/libpurple/account.c
--- a/libpurple/account.c
+++ b/libpurple/account.c
@@ -541,9 +541,22 @@
 	}
 
 	if(new_err != NULL) {
+		PurpleProtocol *protocol = NULL;
+
 		priv->error_notification =
 			purple_notification_new(PURPLE_NOTIFICATION_TYPE_CONNECTION_ERROR,
 			                        account, new_err, NULL);
+
+		protocol = purple_account_get_protocol(account);
+		if(PURPLE_IS_PROTOCOL(protocol)) {
+			const gchar *icon_name = purple_protocol_get_icon_name(protocol);
+
+			if(icon_name != NULL) {
+				purple_notification_set_icon_name(priv->error_notification,
+				                                  icon_name);
+			}
+		}
+
 		purple_notification_manager_add(manager, priv->error_notification);
 	}
 
diff --git a/libpurple/purplenotificationmanager.h b/libpurple/purplenotificationmanager.h
--- a/libpurple/purplenotificationmanager.h
+++ b/libpurple/purplenotificationmanager.h
@@ -89,6 +89,19 @@
  */
 guint purple_notification_manager_get_unread_count(PurpleNotificationManager *manager);
 
+/**
+ * purple_notification_manager_get_model:
+ * @manager: The instance.
+ *
+ * Gets a [iface@Gio.ListModel] of all the [class@Notification]'s in
+ * @manager.
+ *
+ * Returns: (transfer full): The model.
+ *
+ * Since: 3.0.0
+ */
+GListModel *purple_notification_manager_get_model(PurpleNotificationManager *manager);
+
 G_END_DECLS
 
 #endif /* PURPLE_NOTIFICATION_MANAGER_H */
diff --git a/libpurple/purplenotificationmanager.c b/libpurple/purplenotificationmanager.c
--- a/libpurple/purplenotificationmanager.c
+++ b/libpurple/purplenotificationmanager.c
@@ -363,3 +363,14 @@
 
 	return manager->unread_count;
 }
+
+GListModel *
+purple_notification_manager_get_model(PurpleNotificationManager *manager) {
+	g_return_val_if_fail(PURPLE_IS_NOTIFICATION_MANAGER(manager), NULL);
+
+	if(manager->notifications == NULL) {
+		return NULL;
+	}
+
+	return G_LIST_MODEL(g_object_ref(manager->notifications));
+}
diff --git a/pidgin/glade/pidgin3.xml.in b/pidgin/glade/pidgin3.xml.in
--- a/pidgin/glade/pidgin3.xml.in
+++ b/pidgin/glade/pidgin3.xml.in
@@ -13,6 +13,7 @@
     <glade-widget-class name="PidginCredentialsPage" generic-name="credentials_page" title="CredentialsPage"/>
     <glade-widget-class name="PidginDialog" generic-name="dialog" title="Dialog"/>
     <glade-widget-class name="PidginInviteDialog" generic-name="invite_dialog" title="InviteDialog"/>
+    <glade-widget-class name="PidginNotificationList" generic-name="notification_list" title="NotificationList"/>
     <glade-widget-class name="PidginPresenceIcon" generic-name="presence_icon" title="PresenceIcon"/>
     <glade-widget-class name="PidginProtocolChooser" generic-name="protocol_chooser" title="ProtocolChooser"/>
     <glade-widget-class name="PidginProtocolStore" generic-name="protocol_store" title="ProtocolStore"/>
@@ -34,6 +35,7 @@
     <glade-widget-class-ref name="PidginCredentialsPage"/>
     <glade-widget-class-ref name="PidginDialog"/>
     <glade-widget-class-ref name="PidginInviteDialog"/>
+    <glade-widget-class-ref name="PidginNotificationList"/>
     <glade-widget-class-ref name="PidginPresenceIcon"/>
     <glade-widget-class-ref name="PidginProtocolChooser"/>
     <glade-widget-class-ref name="PidginProtocolStore"/>
diff --git a/pidgin/meson.build b/pidgin/meson.build
--- a/pidgin/meson.build
+++ b/pidgin/meson.build
@@ -44,6 +44,8 @@
 	'pidgininvitedialog.c',
 	'pidginmessage.c',
 	'pidginmooddialog.c',
+	'pidginnotificationconnectionerror.c',
+	'pidginnotificationlist.c',
 	'pidginplugininfo.c',
 	'pidginpluginsdialog.c',
 	'pidginpluginsmenu.c',
@@ -118,6 +120,8 @@
 	'pidgininvitedialog.h',
 	'pidginmessage.h',
 	'pidginmooddialog.h',
+	'pidginnotificationconnectionerror.h',
+	'pidginnotificationlist.h',
 	'pidginplugininfo.h',
 	'pidginpluginsdialog.h',
 	'pidginpluginsmenu.h',
diff --git a/pidgin/pidginapplication.c b/pidgin/pidginapplication.c
--- a/pidgin/pidginapplication.c
+++ b/pidgin/pidginapplication.c
@@ -263,6 +263,24 @@
 }
 
 static void
+pidgin_application_connect_account(GSimpleAction *simple, GVariant *parameter,
+                                   gpointer data)
+{
+	PurpleAccount *account = NULL;
+	PurpleAccountManager *manager = NULL;
+	const gchar *id = NULL;
+
+	id = g_variant_get_string(parameter, NULL);
+
+	manager = purple_account_manager_get_default();
+
+	account = purple_account_manager_find_by_id(manager, id);
+	if(PURPLE_IS_ACCOUNT(account)) {
+		purple_account_connect(account);
+	}
+}
+
+static void
 pidgin_application_debug(GSimpleAction *simple, GVariant *parameter,
                          gpointer data)
 {
@@ -450,6 +468,10 @@
 		.name = "add-group",
 		.activate = pidgin_application_add_group,
 	}, {
+		.name = "connect-account",
+		.activate = pidgin_application_connect_account,
+		.parameter_type = "s",
+	}, {
 		.name = "debug",
 		.activate = pidgin_application_debug,
 	}, {
diff --git a/pidgin/pidginconversationwindow.c b/pidgin/pidginconversationwindow.c
--- a/pidgin/pidginconversationwindow.c
+++ b/pidgin/pidginconversationwindow.c
@@ -28,6 +28,7 @@
 
 enum {
 	PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT,
+	PIDGIN_CONVERSATION_WINDOW_COLUMN_NAME,
 	PIDGIN_CONVERSATION_WINDOW_COLUMN_ICON,
 	PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP,
 };
@@ -48,6 +49,8 @@
 
 	GtkWidget *stack;
 
+	GtkWidget *notification_list;
+
 	GtkTreePath *conversation_path;
 };
 
@@ -69,14 +72,14 @@
 	gboolean changed = FALSE;
 
 	if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
-		gchar *markup = NULL;
+		gchar *name = NULL;
 
 		gtk_tree_model_get(model, &iter,
-		                   PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP, &markup,
+		                   PIDGIN_CONVERSATION_WINDOW_COLUMN_NAME, &name,
 		                   -1);
 
-		gtk_stack_set_visible_child_name(GTK_STACK(window->stack), markup);
-		g_free(markup);
+		gtk_stack_set_visible_child_name(GTK_STACK(window->stack), name);
+		g_free(name);
 
 		changed = TRUE;
 	}
@@ -187,6 +190,13 @@
 	/* Add our toplevels to the tree store. */
 	gtk_tree_store_append(window->model, &iter, NULL);
 	gtk_tree_store_set(window->model, &iter,
+	                   PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, window->notification_list,
+	                   PIDGIN_CONVERSATION_WINDOW_COLUMN_NAME, "__notifications__",
+	                   PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP, _("Notifications"),
+	                   -1);
+
+	gtk_tree_store_append(window->model, &iter, NULL);
+	gtk_tree_store_set(window->model, &iter,
 	                   PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP, _("Conversations"),
 	                   -1);
 	window->conversation_path = gtk_tree_model_get_path(GTK_TREE_MODEL(window->model),
@@ -228,6 +238,8 @@
 
 	gtk_widget_class_bind_template_child(widget_class, PidginConversationWindow,
 	                                     stack);
+	gtk_widget_class_bind_template_child(widget_class, PidginConversationWindow,
+	                                     notification_list);
 
 	gtk_widget_class_bind_template_callback(widget_class,
 	                                        pidgin_conversation_window_selection_changed);
@@ -299,6 +311,7 @@
 	gtk_tree_store_prepend(window->model, &iter, &parent);
 	gtk_tree_store_set(window->model, &iter,
 	                   PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, conversation,
+	                   PIDGIN_CONVERSATION_WINDOW_COLUMN_NAME, markup,
 	                   PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP, markup,
 	                   -1);
 
diff --git a/pidgin/pidginnotificationconnectionerror.h b/pidgin/pidginnotificationconnectionerror.h
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginnotificationconnectionerror.h
@@ -0,0 +1,79 @@
+/*
+ * 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_NOTIFICATION_CONNECTION_ERROR_H
+#define PIDGIN_NOTIFICATION_CONNECTION_ERROR_H
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+#include <handy.h>
+
+#include <purple.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PidginNotificationConnectionError:
+ *
+ * #PidginNotificationConnectionError is a widget that displays notifications from
+ * [class@Purple.NotificationManager].
+ *
+ * Since: 3.0.0
+ */
+
+#define PIDGIN_TYPE_NOTIFICATION_CONNECTION_ERROR (pidgin_notification_connection_error_get_type())
+G_DECLARE_FINAL_TYPE(PidginNotificationConnectionError, pidgin_notification_connection_error,
+                     PIDGIN, NOTIFICATION_CONNECTION_ERROR, HdyActionRow)
+
+/**
+ * pidgin_notification_connection_error_new:
+ * @notification: A [class@Purple.Notification] to display.
+ *
+ * Creates a new #PidginNotificationConnectionError instance that will display
+ * @notification.
+ *
+ * Returns: (transfer full): The new #PidginNotificationConnectionError
+ *          instance.
+ */
+GtkWidget *pidgin_notification_connection_error_new(PurpleNotification *notification);
+
+/**
+ * pidgin_notification_connection_error_get_notification:
+ * @error: The instance.
+ *
+ * Gets the [class@Purple.Notification] that @error is displaying.
+ *
+ * Returns: (transfer none): The notification.
+ *
+ * Since: 3.0.0
+ */
+PurpleNotification *pidgin_notification_connection_error_get_notification(PidginNotificationConnectionError *error);
+
+G_END_DECLS
+
+#endif /* PIDGIN_NOTIFICATION_CONNECTION_ERROR_H */
diff --git a/pidgin/pidginnotificationconnectionerror.c b/pidgin/pidginnotificationconnectionerror.c
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginnotificationconnectionerror.c
@@ -0,0 +1,277 @@
+/*
+ * 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 <purple.h>
+
+#include "pidgin/pidginnotificationconnectionerror.h"
+
+struct _PidginNotificationConnectionError {
+	HdyActionRow parent;
+
+	PurpleNotification *notification;
+
+	GtkWidget *reconnect;
+	GtkWidget *reenable;
+	GtkWidget *modify;
+};
+
+enum {
+	PROP_0,
+	PROP_NOTIFICATION,
+	N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+G_DEFINE_TYPE(PidginNotificationConnectionError,
+              pidgin_notification_connection_error, HDY_TYPE_ACTION_ROW)
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+pidgin_notification_connection_error_update(PidginNotificationConnectionError *error) {
+	PurpleAccount *account = NULL;
+	PurpleConnectionErrorInfo *info = NULL;
+	gchar *title = NULL;
+	const gchar *username = NULL, *icon_name = NULL, *account_id = NULL;
+	gboolean enabled = FALSE;
+
+	g_return_if_fail(PIDGIN_IS_NOTIFICATION_CONNECTION_ERROR(error));
+
+	if(!PURPLE_IS_NOTIFICATION(error->notification)) {
+		hdy_preferences_row_set_title(HDY_PREFERENCES_ROW(error),
+		                              _("Notification missing"));
+
+		hdy_action_row_set_icon_name(HDY_ACTION_ROW(error), NULL);
+		hdy_action_row_set_subtitle(HDY_ACTION_ROW(error), NULL);
+
+		gtk_widget_hide(error->reconnect);
+		gtk_widget_hide(error->reenable);
+		gtk_widget_hide(error->modify);
+
+		return;
+	}
+
+	account = purple_notification_get_account(error->notification);
+	if(!PURPLE_IS_ACCOUNT(account)) {
+		hdy_preferences_row_set_title(HDY_PREFERENCES_ROW(error),
+		                              _("Notification is missing an account"));
+
+		hdy_action_row_set_icon_name(HDY_ACTION_ROW(error), NULL);
+		hdy_action_row_set_subtitle(HDY_ACTION_ROW(error), NULL);
+
+		gtk_widget_hide(error->reconnect);
+		gtk_widget_hide(error->reenable);
+		gtk_widget_hide(error->modify);
+
+		return;
+	}
+
+	/* Set the target for our actions. */
+	account_id = purple_account_get_id(account);
+	gtk_actionable_set_action_target(GTK_ACTIONABLE(error->reconnect), "s",
+	                                 account_id);
+	gtk_actionable_set_action_target(GTK_ACTIONABLE(error->reenable), "s",
+	                                 account_id);
+	gtk_actionable_set_action_target(GTK_ACTIONABLE(error->modify), "s",
+	                                 account_id);
+
+	/* Set the icon name if one was specified. */
+	icon_name = purple_notification_get_icon_name(error->notification);
+	if(icon_name != NULL) {
+		hdy_action_row_set_icon_name(HDY_ACTION_ROW(error), icon_name);
+	}
+
+	username = purple_account_get_username(account);
+
+	enabled = purple_account_get_enabled(account);
+	if(enabled) {
+		title = g_strdup_printf(_("%s disconnected"), username);
+	} else {
+		title = g_strdup_printf(_("%s disabled"), username);
+	}
+	hdy_preferences_row_set_title(HDY_PREFERENCES_ROW(error), title);
+	g_free(title);
+
+	info = purple_notification_get_data(error->notification);
+	if(info != NULL) {
+		hdy_action_row_set_subtitle(HDY_ACTION_ROW(error), info->description);
+	} else {
+		hdy_action_row_set_subtitle(HDY_ACTION_ROW(error), NULL);
+	}
+
+	/* If the account is still enabled, reconnect should be visible, otherwise
+	 * re-enable should be visible.
+	 */
+	gtk_widget_set_visible(error->reconnect, enabled);
+	gtk_widget_set_visible(error->reenable, !enabled);
+}
+
+static void
+pidgin_notification_connection_error_set_notification(PidginNotificationConnectionError *error,
+                                                      PurpleNotification *notification)
+{
+	if(g_set_object(&error->notification, notification)) {
+		pidgin_notification_connection_error_update(error);
+
+		g_object_notify_by_pspec(G_OBJECT(error), properties[PROP_NOTIFICATION]);
+	}
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_notification_connection_error_remove_cb(G_GNUC_UNUSED GtkButton *button,
+                                               gpointer data)
+{
+	PidginNotificationConnectionError *error = data;
+	PurpleNotificationManager *manager = NULL;
+
+	manager = purple_notification_manager_get_default();
+	purple_notification_manager_remove(manager, error->notification);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+pidgin_notification_connection_error_get_property(GObject *obj, guint param_id,
+                                                  GValue *value,
+                                                  GParamSpec *pspec)
+{
+	PidginNotificationConnectionError *error = NULL;
+
+	error = PIDGIN_NOTIFICATION_CONNECTION_ERROR(obj);
+
+	switch(param_id) {
+		case PROP_NOTIFICATION:
+			g_value_set_object(value,
+			                   pidgin_notification_connection_error_get_notification(error));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+pidgin_notification_connection_error_set_property(GObject *obj, guint param_id,
+                                                  const GValue *value,
+                                                  GParamSpec *pspec)
+{
+	PidginNotificationConnectionError *error = NULL;
+
+	error = PIDGIN_NOTIFICATION_CONNECTION_ERROR(obj);
+
+	switch(param_id) {
+		case PROP_NOTIFICATION:
+			pidgin_notification_connection_error_set_notification(error,
+			                                                      g_value_get_object(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+pidgin_notification_connection_error_dispose(GObject *obj) {
+	PidginNotificationConnectionError *error = NULL;
+
+	error = PIDGIN_NOTIFICATION_CONNECTION_ERROR(obj);
+
+	g_clear_object(&error->notification);
+
+	G_OBJECT_CLASS(pidgin_notification_connection_error_parent_class)->dispose(obj);
+}
+
+static void
+pidgin_notification_connection_error_init(PidginNotificationConnectionError *list)
+{
+	gtk_widget_init_template(GTK_WIDGET(list));
+}
+
+static void
+pidgin_notification_connection_error_class_init(PidginNotificationConnectionErrorClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	obj_class->get_property = pidgin_notification_connection_error_get_property;
+	obj_class->set_property = pidgin_notification_connection_error_set_property;
+	obj_class->dispose = pidgin_notification_connection_error_dispose;
+
+	/**
+	 * PidginNotificationConnectionError::info:
+	 *
+	 * The [type@Purple.ConnectionErrorInfo] that this notification is for.
+	 *
+	 * Since: 3.0.0
+	 */
+	properties[PROP_NOTIFICATION] = g_param_spec_object(
+		"notification", "notification",
+		"The notification to display",
+		PURPLE_TYPE_NOTIFICATION,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+
+	gtk_widget_class_set_template_from_resource(
+	    widget_class,
+	    "/im/pidgin/Pidgin3/Notifications/connectionerror.ui"
+	);
+
+	gtk_widget_class_bind_template_child(widget_class,
+	                                     PidginNotificationConnectionError,
+	                                     reconnect);
+	gtk_widget_class_bind_template_child(widget_class,
+	                                     PidginNotificationConnectionError,
+	                                     reenable);
+	gtk_widget_class_bind_template_child(widget_class,
+	                                     PidginNotificationConnectionError,
+	                                     modify);
+
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_notification_connection_error_remove_cb);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GtkWidget *
+pidgin_notification_connection_error_new(PurpleNotification *notification) {
+	return g_object_new(
+		PIDGIN_TYPE_NOTIFICATION_CONNECTION_ERROR,
+		"notification", notification,
+		NULL);
+}
+
+PurpleNotification *
+pidgin_notification_connection_error_get_notification(PidginNotificationConnectionError *error)
+{
+	g_return_val_if_fail(PIDGIN_IS_NOTIFICATION_CONNECTION_ERROR(error), NULL);
+
+	return error->notification;
+}
diff --git a/pidgin/pidginnotificationlist.h b/pidgin/pidginnotificationlist.h
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginnotificationlist.h
@@ -0,0 +1,61 @@
+/*
+ * 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_NOTIFICATION_LIST_H
+#define PIDGIN_NOTIFICATION_LIST_H
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PidginNotificationList:
+ *
+ * #PidginNotificationList is a widget that displays notifications from
+ * [class@Purple.NotificationManager].
+ *
+ * Since: 3.0.0
+ */
+
+#define PIDGIN_TYPE_NOTIFICATION_LIST (pidgin_notification_list_get_type())
+G_DECLARE_FINAL_TYPE(PidginNotificationList, pidgin_notification_list, PIDGIN,
+                     NOTIFICATION_LIST, GtkBox)
+
+/**
+ * pidgin_notification_list_new:
+ *
+ * Creates a new #PidginNotificationList instance that will display
+ * notifications from the default [class@Purple.NotificationManager].
+ *
+ * Returns: (transfer full): The new #PidginNotificationList instance.
+ */
+GtkWidget *pidgin_notification_list_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_NOTIFICATION_LIST_H */
diff --git a/pidgin/pidginnotificationlist.c b/pidgin/pidginnotificationlist.c
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginnotificationlist.c
@@ -0,0 +1,123 @@
+/*
+ * 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 <purple.h>
+
+#include "pidgin/pidginnotificationlist.h"
+
+#include "pidgin/pidginnotificationconnectionerror.h"
+
+struct _PidginNotificationList {
+	GtkBox parent;
+
+	GtkWidget *list_box;
+};
+
+G_DEFINE_TYPE(PidginNotificationList, pidgin_notification_list, GTK_TYPE_BOX)
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static GtkWidget *
+pidgin_notification_list_unknown_notification(PurpleNotification *notification) {
+	GtkWidget *widget = NULL;
+	gchar *label = NULL;
+	const gchar *title = NULL;
+
+	title = purple_notification_get_title(notification);
+	if(title != NULL) {
+		label = g_strdup_printf(_("Unknown notification type %d: %s"),
+		                        purple_notification_get_notification_type(notification),
+		                        title);
+	} else {
+		label = g_strdup_printf(_("Unknown notification type %d"),
+		                        purple_notification_get_notification_type(notification));
+	}
+
+	widget = gtk_label_new(label);
+
+	g_free(label);
+
+	return widget;
+}
+
+static GtkWidget *
+pidgin_notification_list_create_widget_func(gpointer item, gpointer data) {
+	PurpleNotification *notification = item;
+	GtkWidget *widget = NULL;
+
+	switch(purple_notification_get_notification_type(notification)) {
+		case PURPLE_NOTIFICATION_TYPE_CONNECTION_ERROR:
+			widget = pidgin_notification_connection_error_new(notification);
+			break;
+		default:
+			widget = pidgin_notification_list_unknown_notification(notification);
+			break;
+	}
+
+	if(!GTK_IS_WIDGET(widget)) {
+		widget = pidgin_notification_list_unknown_notification(notification);
+	}
+
+	return widget;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+pidgin_notification_list_init(PidginNotificationList *list) {
+	PurpleNotificationManager *manager = NULL;
+	GListModel *model = NULL;
+
+	gtk_widget_init_template(GTK_WIDGET(list));
+
+	manager = purple_notification_manager_get_default();
+	model = purple_notification_manager_get_model(manager);
+
+	gtk_list_box_bind_model(GTK_LIST_BOX(list->list_box), model,
+	                        pidgin_notification_list_create_widget_func,
+	                        list, NULL);
+}
+
+static void
+pidgin_notification_list_class_init(PidginNotificationListClass *klass) {
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	gtk_widget_class_set_template_from_resource(
+	    widget_class,
+	    "/im/pidgin/Pidgin3/Notifications/list.ui"
+	);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginNotificationList,
+	                                     list_box);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GtkWidget *
+pidgin_notification_list_new(void) {
+	return g_object_new(PIDGIN_TYPE_NOTIFICATION_LIST, NULL);
+}
diff --git a/pidgin/resources/Conversations/window.ui b/pidgin/resources/Conversations/window.ui
--- a/pidgin/resources/Conversations/window.ui
+++ b/pidgin/resources/Conversations/window.ui
@@ -30,6 +30,8 @@
     <columns>
       <!-- column-name conversation -->
       <column type="GObject"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
       <!-- column-name icon -->
       <column type="GdkPixbuf"/>
       <!-- column-name markup -->
@@ -79,7 +81,7 @@
                         <property name="can-focus">True</property>
                         <property name="model">model</property>
                         <property name="headers-visible">False</property>
-                        <property name="search-column">2</property>
+                        <property name="search-column">3</property>
                         <child internal-child="selection">
                           <object class="GtkTreeSelection">
                             <signal name="changed" handler="pidgin_conversation_window_selection_changed" object="PidginConversationWindow" swapped="no"/>
@@ -90,13 +92,13 @@
                             <child>
                               <object class="GtkCellRendererPixbuf" id="icon"/>
                               <attributes>
-                                <attribute name="pixbuf">1</attribute>
+                                <attribute name="pixbuf">2</attribute>
                               </attributes>
                             </child>
                             <child>
                               <object class="GtkCellRendererText" id="name"/>
                               <attributes>
-                                <attribute name="markup">2</attribute>
+                                <attribute name="markup">3</attribute>
                               </attributes>
                             </child>
                           </object>
@@ -131,6 +133,21 @@
                     <property name="title" translatable="yes">__empty__</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="PidginNotificationList" id="notification_list">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="name">__notifications__</property>
+                    <property name="title" translatable="yes">notifications</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
               </object>
               <packing>
                 <property name="resize">True</property>
diff --git a/pidgin/resources/Notifications/connectionerror.ui b/pidgin/resources/Notifications/connectionerror.ui
new file mode 100644
--- /dev/null
+++ b/pidgin/resources/Notifications/connectionerror.ui
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 
+
+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 program; if not, see <https://www.gnu.org/licenses/>.
+
+-->
+<interface>
+  <requires lib="gtk+" version="3.24"/>
+  <requires lib="libhandy" version="0.0"/>
+  <!-- interface-license-type gplv2 -->
+  <!-- interface-name Pidgin -->
+  <!-- interface-description Internet Messenger -->
+  <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+  <template class="PidginNotificationConnectionError" parent="HdyActionRow">
+    <property name="visible">True</property>
+    <property name="can-focus">True</property>
+    <property name="activatable">False</property>
+    <property name="subtitle-lines">3</property>
+    <child>
+      <object class="GtkButton" id="reconnect">
+        <property name="label" translatable="yes">Reconnect</property>
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="halign">center</property>
+        <property name="valign">center</property>
+        <property name="action-name">app.connect-account</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkButton" id="reenable">
+        <property name="label" translatable="yes">Re-enable</property>
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="halign">center</property>
+        <property name="valign">center</property>
+        <property name="action-name">app.enable-account</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkButton" id="modify">
+        <property name="label" translatable="yes">Modify Account</property>
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="halign">center</property>
+        <property name="valign">center</property>
+        <property name="action-name">app.edit-account</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkButton" id="remove">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+        <property name="halign">center</property>
+        <property name="valign">center</property>
+        <property name="relief">none</property>
+        <signal name="clicked" handler="pidgin_notification_connection_error_remove_cb" object="PidginNotificationConnectionError" swapped="no"/>
+        <child>
+          <object class="GtkImage">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="icon-name">edit-delete-symbolic</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/pidgin/resources/Notifications/list.ui b/pidgin/resources/Notifications/list.ui
new file mode 100644
--- /dev/null
+++ b/pidgin/resources/Notifications/list.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 
+
+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 program; if not, see <https://www.gnu.org/licenses/>.
+
+-->
+<interface>
+  <requires lib="gtk+" version="3.24"/>
+  <!-- interface-license-type gplv2 -->
+  <!-- interface-name Pidgin -->
+  <!-- interface-description Internet Messenger -->
+  <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+  <template class="PidginNotificationList" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkLabel">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="label" translatable="yes">Notifications</property>
+        <property name="xalign">0</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkListBox" id="list_box">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/pidgin/resources/pidgin.gresource.xml b/pidgin/resources/pidgin.gresource.xml
--- a/pidgin/resources/pidgin.gresource.xml
+++ b/pidgin/resources/pidgin.gresource.xml
@@ -21,6 +21,8 @@
     <file compressed="true">Debug/filter.css</file>
     <file compressed="true">Debug/plugininfo.ui</file>
     <file compressed="true">Log/log-viewer.ui</file>
+    <file compressed="true">Notifications/connectionerror.ui</file>
+    <file compressed="true">Notifications/list.ui</file>
     <file compressed="true">Plugins/dialog.ui</file>
     <file compressed="true">Prefs/away.ui</file>
     <file compressed="true">Prefs/conversation.ui</file>
