# HG changeset patch
# Node ID b108bd2f8464
# Parent  92438aae2067
diff --git a/pidgin/meson.build b/pidgin/meson.build
--- a/pidgin/meson.build
+++ b/pidgin/meson.build
@@ -53,6 +53,7 @@
 	'pidginprotocolchooser.c',
 	'pidginprotocolstore.c',
 	'pidginscrollbook.c',
+	'pidginstatusmanager.c',
 	'pidginstatusprimitivechooser.c',
 	'pidginstatusprimitivestore.c',
 	'pidginstylecontext.c',
@@ -116,6 +117,7 @@
 	'pidginprotocolchooser.h',
 	'pidginprotocolstore.h',
 	'pidginscrollbook.h',
+	'pidginstatusmanager.h',
 	'pidginstatusprimitivechooser.h',
 	'pidginstatusprimitivestore.h',
 	'pidginstylecontext.h',
diff --git a/pidgin/pidginapplication.c b/pidgin/pidginapplication.c
--- a/pidgin/pidginapplication.c
+++ b/pidgin/pidginapplication.c
@@ -40,7 +40,6 @@
 #include "gtkdialogs.h"
 #include "gtkprivacy.h"
 #include "gtkroomlist.h"
-#include "gtksavedstatuses.h"
 #include "gtkxfer.h"
 #include "pidginabout.h"
 #include "pidginaccountsdisabledmenu.h"
@@ -50,6 +49,7 @@
 #include "pidgindebug.h"
 #include "pidginmooddialog.h"
 #include "pidgin/pidginpluginsdialog.h"
+#include "pidgin/pidginstatusmanager.h"
 #include "pidginprefs.h"
 
 struct _PidginApplication {
@@ -410,7 +410,14 @@
 pidgin_application_show_status_manager(GSimpleAction *simple,
                                        GVariant *parameter, gpointer data)
 {
-	pidgin_status_window_show();
+	static GtkWidget *manager = NULL;
+
+	if(!GTK_IS_WIDGET(manager)) {
+		manager = pidgin_status_manager_new();
+		g_object_add_weak_pointer(G_OBJECT(manager), (gpointer)&manager);
+	}
+
+	gtk_widget_show_all(manager);
 }
 
 static GActionEntry app_entries[] = {
diff --git a/pidgin/pidginstatusmanager.h b/pidgin/pidginstatusmanager.h
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginstatusmanager.h
@@ -0,0 +1,57 @@
+/*
+ * 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_STATUS_MANAGER_H
+#define PIDGIN_STATUS_MANAGER_H
+
+#include <gtk/gtk.h>
+
+#include <purple.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PidginStatusManager:
+ *
+ * A dialog for managing statuses.
+ */
+
+#define PIDGIN_TYPE_STATUS_MANAGER (pidgin_status_manager_get_type())
+G_DECLARE_FINAL_TYPE(PidginStatusManager, pidgin_status_manager, PIDGIN,
+                     STATUS_MANAGER, GtkDialog)
+
+/**
+ * pidgin_status_manager_new:
+ *
+ * Creates a new instance of the dialog.
+ *
+ * Returns: (transfer full): The new #PidginStatusManager instance.
+ */
+GtkWidget *pidgin_status_manager_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_STATUS_MANAGER_H */
diff --git a/pidgin/pidginstatusmanager.c b/pidgin/pidginstatusmanager.c
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginstatusmanager.c
@@ -0,0 +1,307 @@
+/*
+ * 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 "pidginstatusmanager.h"
+
+#include "gtksavedstatuses.h"
+#include "pidginiconname.h"
+
+enum {
+	RESPONSE_USE,
+	RESPONSE_ADD,
+	RESPONSE_MODIFY,
+	RESPONSE_REMOVE
+};
+
+enum {
+	COLUMN_TITLE,
+	COLUMN_ICON_NAME,
+	COLUMN_TYPE,
+	COLUMN_MESSAGE,
+	COLUMN_STATUS
+};
+
+struct _PidginStatusManager {
+	GtkDialog parent;
+
+	GtkListStore *model;
+	GtkTreeSelection *selection;
+
+	GtkWidget *use_button;
+	GtkWidget *modify_button;
+	GtkWidget *remove_button;
+};
+
+G_DEFINE_TYPE(PidginStatusManager, pidgin_status_manager, GTK_TYPE_DIALOG)
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static PurpleSavedStatus *
+pidgin_status_manager_get_selected_status(PidginStatusManager *manager) {
+	PurpleSavedStatus *status = NULL;
+	GtkTreeIter iter;
+
+	if(gtk_tree_selection_count_selected_rows(manager->selection) == 0) {
+		return NULL;
+	}
+
+	gtk_tree_selection_get_selected(manager->selection, NULL, &iter);
+	gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
+	                   COLUMN_STATUS, &status,
+	                   -1);
+
+	return status;
+}
+
+static void
+pidgin_status_manager_add(PidginStatusManager *manager,
+                          PurpleSavedStatus *status)
+{
+	PurpleStatusPrimitive primitive;
+	GtkTreeIter iter;
+	gchar *message = NULL;
+	const gchar *icon_name = NULL, *type = NULL;
+
+	message = purple_markup_strip_html(purple_savedstatus_get_message(status));
+
+	primitive = purple_savedstatus_get_primitive_type(status);
+	icon_name = pidgin_icon_name_from_status_primitive(primitive, NULL);
+	type = purple_primitive_get_name_from_type(primitive);
+
+	gtk_list_store_append(manager->model, &iter);
+	gtk_list_store_set(manager->model, &iter,
+	                   COLUMN_TITLE, purple_savedstatus_get_title(status),
+	                   COLUMN_ICON_NAME, icon_name,
+	                   COLUMN_TYPE, type,
+	                   COLUMN_MESSAGE, message,
+	                   COLUMN_STATUS, status,
+	                   -1);
+
+	g_free(message);
+}
+
+static void
+pidgin_status_manager_populate_helper(gpointer data, gpointer user_data) {
+	PidginStatusManager *manager = user_data;
+	PurpleSavedStatus *status = data;
+
+	if(!purple_savedstatus_is_transient(status)) {
+		pidgin_status_manager_add(manager, status);
+	}
+}
+
+static void
+pidgin_status_manager_refresh(PidginStatusManager *manager) {
+	GList *statuses = NULL;
+
+	gtk_list_store_clear(manager->model);
+
+	statuses = purple_savedstatuses_get_all();
+	g_list_foreach(statuses, pidgin_status_manager_populate_helper, manager);
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_status_manager_response_cb(GtkDialog *dialog, gint response_id,
+                                  gpointer data)
+{
+	PidginStatusManager *manager = data;
+	PurpleSavedStatus *status = NULL;
+
+	switch(response_id) {
+		case RESPONSE_USE:
+			status = pidgin_status_manager_get_selected_status(manager);
+
+			purple_savedstatus_activate(status);
+
+			break;
+		case RESPONSE_ADD:
+			pidgin_status_editor_show(FALSE, NULL);
+			break;
+		case RESPONSE_MODIFY:
+			status = pidgin_status_manager_get_selected_status(manager);
+
+			pidgin_status_editor_show(TRUE, status);
+
+			break;
+		case RESPONSE_REMOVE:
+			status = pidgin_status_manager_get_selected_status(manager);
+
+			purple_savedstatus_delete_by_status(status);
+
+			break;
+		case GTK_RESPONSE_CLOSE:
+			gtk_widget_destroy(GTK_WIDGET(dialog));
+			break;
+	}
+}
+
+static void
+pidgin_status_manager_row_activated_cb(G_GNUC_UNUSED GtkTreeView *tree_view,
+                                       GtkTreePath *path,
+                                       G_GNUC_UNUSED GtkTreeViewColumn *column,
+                                       gpointer data)
+{
+	PidginStatusManager *manager = data;
+	GtkTreeIter iter;
+
+	if(gtk_tree_model_get_iter(GTK_TREE_MODEL(manager->model), &iter, path)) {
+		PurpleSavedStatus *status = NULL;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
+		                   COLUMN_STATUS, &status,
+		                   -1);
+
+		pidgin_status_editor_show(TRUE, status);
+	}
+}
+
+static void
+pidgin_status_manager_selection_changed_cb(GtkTreeSelection *selection,
+                                           gpointer data)
+{
+	PidginStatusManager *manager = data;
+	gboolean sensitive = TRUE;
+
+	if(gtk_tree_selection_count_selected_rows(selection) == 0) {
+		sensitive = FALSE;
+	}
+
+	gtk_widget_set_sensitive(manager->use_button, sensitive);
+	gtk_widget_set_sensitive(manager->modify_button, sensitive);
+
+	/* Only enable the remove button if the currently selected row is not the
+	 * currently active status.
+	 */
+	if(sensitive) {
+		PurpleSavedStatus *status = NULL;
+
+		status = pidgin_status_manager_get_selected_status(manager);
+
+		sensitive = status != purple_savedstatus_get_current();
+	}
+
+	gtk_widget_set_sensitive(manager->remove_button, sensitive);
+}
+
+static void
+pidgin_status_manager_savedstatus_changed_cb(PurpleSavedStatus *new_status,
+                                             G_GNUC_UNUSED PurpleSavedStatus *old_status,
+                                             gpointer data)
+{
+	PidginStatusManager *manager = data;
+	PurpleSavedStatus *selected = NULL;
+
+	/* Disable the remove button if the selected status is the currently active
+	 * status.
+	 */
+	selected = pidgin_status_manager_get_selected_status(manager);
+	if(selected != NULL) {
+		gboolean sensitive = selected != new_status;
+
+		gtk_widget_set_sensitive(manager->remove_button, sensitive);
+	}
+}
+
+static void
+pidgin_status_manager_savedstatus_updated_cb(G_GNUC_UNUSED PurpleSavedStatus *status,
+                                             gpointer data)
+{
+	PidginStatusManager *manager = data;
+
+	pidgin_status_manager_refresh(manager);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+pidgin_status_manager_finalize(GObject *obj) {
+	purple_signals_disconnect_by_handle(obj);
+
+	G_OBJECT_CLASS(pidgin_status_manager_parent_class)->finalize(obj);
+}
+
+static void
+pidgin_status_manager_init(PidginStatusManager *manager) {
+	gpointer handle = NULL;
+
+	gtk_widget_init_template(GTK_WIDGET(manager));
+
+	pidgin_status_manager_refresh(manager);
+
+	handle = purple_savedstatuses_get_handle();
+	purple_signal_connect(handle, "savedstatus-changed", manager,
+	                      PURPLE_CALLBACK(pidgin_status_manager_savedstatus_changed_cb),
+	                      manager);
+	purple_signal_connect(handle, "savedstatus-added", manager,
+	                      PURPLE_CALLBACK(pidgin_status_manager_savedstatus_updated_cb),
+	                      manager);
+	purple_signal_connect(handle, "savedstatus-deleted", manager,
+	                      PURPLE_CALLBACK(pidgin_status_manager_savedstatus_updated_cb),
+	                      manager);
+	purple_signal_connect(handle, "savedstatus-modified", manager,
+	                      PURPLE_CALLBACK(pidgin_status_manager_savedstatus_updated_cb),
+	                      manager);
+}
+
+static void
+pidgin_status_manager_class_init(PidginStatusManagerClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	obj_class->finalize = pidgin_status_manager_finalize;
+
+	gtk_widget_class_set_template_from_resource(
+		widget_class,
+		"/im/pidgin/Pidgin3/Status/manager.ui"
+	);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusManager,
+	                                     model);
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusManager,
+	                                     selection);
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusManager,
+	                                     use_button);
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusManager,
+	                                     modify_button);
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusManager,
+	                                     remove_button);
+
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_status_manager_response_cb);
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_status_manager_row_activated_cb);
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_status_manager_selection_changed_cb);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+GtkWidget *
+pidgin_status_manager_new(void) {
+	return g_object_new(PIDGIN_TYPE_STATUS_MANAGER, NULL);
+}
diff --git a/pidgin/resources/Status/manager.ui b/pidgin/resources/Status/manager.ui
new file mode 100644
--- /dev/null
+++ b/pidgin/resources/Status/manager.ui
@@ -0,0 +1,226 @@
+<?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 Lesser General Public
+License along with this library; 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> -->
+  <object class="GtkListStore" id="model">
+    <columns>
+      <!-- column-name title -->
+      <column type="gchararray"/>
+      <!-- column-name icon-name -->
+      <column type="gchararray"/>
+      <!-- column-name type -->
+      <column type="gchararray"/>
+      <!-- column-name message -->
+      <column type="gchararray"/>
+      <!-- column-name status -->
+      <column type="gpointer"/>
+    </columns>
+  </object>
+  <template class="PidginStatusManager" parent="GtkDialog">
+    <property name="can-focus">False</property>
+    <property name="title" translatable="yes">Saved Statuses</property>
+    <property name="default-width">550</property>
+    <property name="default-height">250</property>
+    <property name="type-hint">dialog</property>
+    <signal name="response" handler="pidgin_status_manager_response_cb" swapped="no"/>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can-focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can-focus">False</property>
+            <property name="layout-style">end</property>
+            <child>
+              <object class="GtkButton" id="use_button">
+                <property name="label" translatable="yes">_Use</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button1">
+                <property name="label" translatable="yes">_Add</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="modify_button">
+                <property name="label" translatable="yes">_Modify</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="remove_button">
+                <property name="label" translatable="yes">_Remove</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button2">
+                <property name="label" translatable="yes">_Close</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="visible">True</property>
+            <property name="can-focus">True</property>
+            <property name="shadow-type">in</property>
+            <child>
+              <object class="GtkTreeView">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="model">model</property>
+                <property name="search-column">0</property>
+                <signal name="row-activated" handler="pidgin_status_manager_row_activated_cb" object="PidginStatusManager" swapped="no"/>
+                <child internal-child="selection">
+                  <object class="GtkTreeSelection" id="selection">
+                    <signal name="changed" handler="pidgin_status_manager_selection_changed_cb" object="PidginStatusManager" swapped="no"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkTreeViewColumn">
+                    <property name="resizable">True</property>
+                    <property name="min-width">100</property>
+                    <property name="title" translatable="yes">Title</property>
+                    <property name="clickable">True</property>
+                    <property name="sort-column-id">0</property>
+                    <child>
+                      <object class="GtkCellRendererText">
+                        <property name="ellipsize">end</property>
+                      </object>
+                      <attributes>
+                        <attribute name="markup">0</attribute>
+                      </attributes>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkTreeViewColumn">
+                    <property name="resizable">True</property>
+                    <property name="title" translatable="yes">Type</property>
+                    <property name="clickable">True</property>
+                    <property name="sort-column-id">2</property>
+                    <child>
+                      <object class="GtkCellRendererPixbuf"/>
+                      <attributes>
+                        <attribute name="icon-name">1</attribute>
+                      </attributes>
+                    </child>
+                    <child>
+                      <object class="GtkCellRendererText"/>
+                      <attributes>
+                        <attribute name="markup">2</attribute>
+                      </attributes>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkTreeViewColumn">
+                    <property name="resizable">True</property>
+                    <property name="title" translatable="yes">Message</property>
+                    <property name="clickable">True</property>
+                    <property name="sort-column-id">4</property>
+                    <child>
+                      <object class="GtkCellRendererText">
+                        <property name="ellipsize">end</property>
+                      </object>
+                      <attributes>
+                        <attribute name="markup">3</attribute>
+                      </attributes>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">use_button</action-widget>
+      <action-widget response="1">button1</action-widget>
+      <action-widget response="2">modify_button</action-widget>
+      <action-widget response="3">remove_button</action-widget>
+      <action-widget response="-7">button2</action-widget>
+    </action-widgets>
+  </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
@@ -30,6 +30,7 @@
     <file compressed="true">Privacy/dialog.ui</file>
     <file compressed="true">Protocols/chooser.ui</file>
     <file compressed="true">Roomlist/roomlist.ui</file>
+    <file compressed="true">Status/manager.ui</file>
     <file compressed="true">Whiteboard/whiteboard.ui</file>
     <file compressed="true">Xfer/xfer.ui</file>
     <file compressed="true">closebutton.ui</file>
diff --git a/po/POTFILES.in b/po/POTFILES.in
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -412,6 +412,7 @@
 pidgin/resources/Privacy/dialog.ui
 pidgin/resources/Protocols/chooser.ui
 pidgin/resources/Roomlist/roomlist.ui
+pidgin/resources/Status/manager.ui
 pidgin/resources/Whiteboard/whiteboard.ui
 pidgin/resources/Xfer/xfer.ui
 pidgin/resources/gtk/menus.ui
