# HG changeset patch
# Node ID ab04b8b3b7ea
# Parent  84e92cf6f64a
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',
+	'pidginstatuseditor.c',
 	'pidginstatusmanager.c',
 	'pidginstatusprimitivechooser.c',
 	'pidginstatusprimitivestore.c',
@@ -117,6 +118,7 @@
 	'pidginprotocolchooser.h',
 	'pidginprotocolstore.h',
 	'pidginscrollbook.h',
+	'pidginstatuseditor.h',
 	'pidginstatusmanager.h',
 	'pidginstatusprimitivechooser.h',
 	'pidginstatusprimitivestore.h',
diff --git a/pidgin/pidginstatuseditor.h b/pidgin/pidginstatuseditor.h
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginstatuseditor.h
@@ -0,0 +1,74 @@
+/*
+ * 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_EDITOR_H
+#define PIDGIN_STATUS_EDITOR_H
+
+#include <gtk/gtk.h>
+
+#include <purple.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PidginStatusEditor:
+ *
+ * A dialog for editing statuses.
+ */
+
+#define PIDGIN_TYPE_STATUS_EDITOR (pidgin_status_editor_get_type())
+G_DECLARE_FINAL_TYPE(PidginStatusEditor, pidgin_status_editor, PIDGIN,
+                     STATUS_EDITOR, GtkDialog)
+
+/**
+ * pidgin_status_editor_new:
+ * @status: (nullable): The [type@Purple.SavedStatus] to edit.
+ *
+ * Creates a new instance of the dialog. You can pass %NULL for @status to
+ * create a new status with the dialog.
+ *
+ * Returns: (transfer full): The new #PidginStatusEditor instance.
+ *
+ * Since: 3.0.0
+ */
+GtkWidget *pidgin_status_editor_new(PurpleSavedStatus *status);
+
+/**
+ * pidgin_status_editor_get_status:
+ * @editor: The editor instance.
+ *
+ * Gets the [type@Purple.SavedStatus] that @editor was created for. This can
+ * be %NULL if a new status is being created.
+ *
+ * Returns: (transfer none): The status.
+ *
+ * Since: 3.0.0
+ */
+PurpleSavedStatus *pidgin_status_editor_get_status(PidginStatusEditor *editor);
+
+G_END_DECLS
+
+#endif /* PIDGIN_STATUS_EDITOR_H */
diff --git a/pidgin/pidginstatuseditor.c b/pidgin/pidginstatuseditor.c
new file mode 100644
--- /dev/null
+++ b/pidgin/pidginstatuseditor.c
@@ -0,0 +1,294 @@
+/*
+ * 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 <talkatu/talkatu.h>
+
+#include "pidginstatuseditor.h"
+
+#include "pidginstatusprimitivechooser.h"
+
+enum {
+	PROP_0,
+	PROP_STATUS,
+	N_PROPERTIES
+};
+static GParamSpec *properties[N_PROPERTIES] = {NULL, };
+
+enum {
+	RESPONSE_USE,
+	RESPONSE_SAVE
+};
+
+struct _PidginStatusEditor {
+	GtkDialog parent;
+
+	PurpleSavedStatus *status;
+
+	GtkTextBuffer *buffer;
+
+	GtkWidget *title;
+	GtkWidget *primitive;
+	GtkWidget *message;
+
+	GtkWidget *use;
+	GtkWidget *save;
+};
+
+G_DEFINE_TYPE(PidginStatusEditor, pidgin_status_editor, GTK_TYPE_DIALOG)
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+pidgin_status_editor_set_status(PidginStatusEditor *editor,
+                                PurpleSavedStatus *status)
+{
+	PurpleStatusPrimitive primitive;
+	PidginStatusPrimitiveChooser *chooser = NULL;
+	const gchar *title = NULL, *message = NULL;
+
+	editor->status = status;
+
+	if(editor->status != NULL) {
+		title = purple_savedstatus_get_title(editor->status);
+		primitive = purple_savedstatus_get_primitive_type(editor->status);
+		message = purple_savedstatus_get_message(editor->status);
+	} else {
+		primitive = PURPLE_STATUS_AWAY;
+	}
+
+	if(title == NULL) {
+		title = "";
+	}
+
+	if(message == NULL) {
+		message = "";
+	}
+
+	gtk_entry_set_text(GTK_ENTRY(editor->title), title);
+	chooser = PIDGIN_STATUS_PRIMITIVE_CHOOSER(editor->primitive);
+	pidgin_status_primitive_chooser_set_selected(chooser, primitive);
+	talkatu_markup_set_html(TALKATU_BUFFER(editor->buffer), message, -1);
+
+	g_object_notify_by_pspec(G_OBJECT(editor), properties[PROP_STATUS]);
+}
+
+static void
+pidgin_status_editor_save_status(PidginStatusEditor *editor) {
+	PidginStatusPrimitiveChooser *chooser = NULL;
+	PurpleStatusPrimitive primitive;
+	gchar *message = NULL;
+	const gchar *title = NULL;
+
+	title = gtk_entry_get_text(GTK_ENTRY(editor->title));
+
+	chooser = PIDGIN_STATUS_PRIMITIVE_CHOOSER(editor->primitive);
+	primitive = pidgin_status_primitive_chooser_get_selected(chooser);
+
+	message = talkatu_markup_get_html(editor->buffer, NULL);
+
+	if(editor->status == NULL) {
+		editor->status = purple_savedstatus_new(title, primitive);
+	} else {
+		const gchar *current_title = NULL;
+
+		/* purple_savedstatus_set_title throws a warning if you try to save a
+		 * status with an existing title, which means we can't just save the
+		 * title if it hasn't changed.
+		 */
+		current_title = purple_savedstatus_get_title(editor->status);
+		if(!purple_strequal(title, current_title)) {
+			purple_savedstatus_set_title(editor->status, title);
+		}
+
+		purple_savedstatus_set_primitive_type(editor->status, primitive);
+	}
+
+	purple_savedstatus_set_message(editor->status, message);
+
+	g_free(message);
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_status_editor_response_cb(GtkDialog *dialog, gint response_id,
+                                 gpointer data)
+{
+	PidginStatusEditor *editor = data;
+
+	switch(response_id) {
+		case RESPONSE_USE:
+			pidgin_status_editor_save_status(editor);
+			purple_savedstatus_activate(editor->status);
+			break;
+		case RESPONSE_SAVE:
+			pidgin_status_editor_save_status(editor);
+			break;
+	}
+
+	gtk_widget_destroy(GTK_WIDGET(dialog));
+}
+
+static void
+pidgin_status_editor_title_changed_cb(GtkEditable *editable, gpointer data) {
+	PidginStatusEditor *editor = data;
+	gboolean title_changed = FALSE, sensitive = FALSE;
+	const gchar *title = NULL;
+
+	title = gtk_entry_get_text(GTK_ENTRY(editor->title));
+
+	if(editor->status != NULL) {
+		/* If we're editing a status, check if the title is the same. */
+		title_changed = !purple_strequal(title,
+		                                 purple_savedstatus_get_title(editor->status));
+	} else {
+		/* If this is a new status, check if the title is empty or not. */
+		title_changed = !purple_strequal(title, "");
+	}
+
+	if(title_changed) {
+		gboolean duplicated = purple_savedstatus_find(title) != NULL;
+
+		if(duplicated) {
+			gtk_widget_set_sensitive(editor->use, FALSE);
+			gtk_widget_set_sensitive(editor->save, FALSE);
+
+			return;
+		}
+	}
+
+	sensitive = !purple_strequal(title, "");
+
+	gtk_widget_set_sensitive(editor->use, sensitive);
+	gtk_widget_set_sensitive(editor->save, sensitive);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+pidgin_status_editor_get_property(GObject *obj, guint param_id, GValue *value,
+                                  GParamSpec *pspec)
+{
+	PidginStatusEditor *editor = PIDGIN_STATUS_EDITOR(obj);
+
+	switch(param_id) {
+		case PROP_STATUS:
+			g_value_set_pointer(value,
+			                    pidgin_status_editor_get_status(editor));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+pidgin_status_editor_set_property(GObject *obj, guint param_id,
+                                  const GValue *value, GParamSpec *pspec)
+{
+	PidginStatusEditor *editor = PIDGIN_STATUS_EDITOR(obj);
+
+	switch(param_id) {
+		case PROP_STATUS:
+			pidgin_status_editor_set_status(editor,
+			                                g_value_get_pointer(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+pidgin_status_editor_init(PidginStatusEditor *manager) {
+	gtk_widget_init_template(GTK_WIDGET(manager));
+}
+
+static void
+pidgin_status_editor_class_init(PidginStatusEditorClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	obj_class->get_property = pidgin_status_editor_get_property;
+	obj_class->set_property = pidgin_status_editor_set_property;
+
+	/**
+	 * PidginStatusEditor::status:
+	 *
+	 * The [type@Purple.SavedStatus] that this editor is responsible for.
+	 * This may be %NULL if it is creating a new status.
+	 *
+	 * Since: 3.0.0.
+	 */
+	properties[PROP_STATUS] = g_param_spec_pointer(
+		"status", "status",
+		"The saved status we're editing",
+		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/Status/editor.ui"
+	);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusEditor,
+	                                     buffer);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusEditor,
+	                                     title);
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusEditor,
+	                                     primitive);
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusEditor,
+	                                     message);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusEditor,
+	                                     use);
+	gtk_widget_class_bind_template_child(widget_class, PidginStatusEditor,
+	                                     save);
+
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_status_editor_response_cb);
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_status_editor_title_changed_cb);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+GtkWidget *
+pidgin_status_editor_new(PurpleSavedStatus *status) {
+	return g_object_new(
+		PIDGIN_TYPE_STATUS_EDITOR,
+		"status", status,
+		NULL);
+}
+
+PurpleSavedStatus *
+pidgin_status_editor_get_status(PidginStatusEditor *editor) {
+	g_return_val_if_fail(PIDGIN_IS_STATUS_EDITOR(editor), NULL);
+
+	return editor->status;
+}
diff --git a/pidgin/pidginstatusmanager.c b/pidgin/pidginstatusmanager.c
--- a/pidgin/pidginstatusmanager.c
+++ b/pidgin/pidginstatusmanager.c
@@ -22,8 +22,8 @@
 
 #include "pidginstatusmanager.h"
 
-#include "gtksavedstatuses.h"
 #include "pidginiconname.h"
+#include "pidginstatuseditor.h"
 
 enum {
 	RESPONSE_USE,
@@ -37,7 +37,8 @@
 	COLUMN_ICON_NAME,
 	COLUMN_TYPE,
 	COLUMN_MESSAGE,
-	COLUMN_STATUS
+	COLUMN_STATUS,
+	COLUMN_EDITOR
 };
 
 struct _PidginStatusManager {
@@ -53,9 +54,75 @@
 
 G_DEFINE_TYPE(PidginStatusManager, pidgin_status_manager, GTK_TYPE_DIALOG)
 
+/* Ugh, prototypes :,( */
+static void pidgin_status_editor_destroy_cb(GtkWidget *widget, gpointer data);
+
 /******************************************************************************
  * Helpers
  *****************************************************************************/
+static void
+pidgin_status_manager_show_editor(PidginStatusManager *manager) {
+	PurpleSavedStatus *status = NULL;
+	GtkWidget *editor = NULL;
+	GtkTreeIter iter;
+
+	if(gtk_tree_selection_count_selected_rows(manager->selection) == 0) {
+		return;
+	}
+
+	gtk_tree_selection_get_selected(manager->selection, NULL, &iter);
+	gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
+		COLUMN_STATUS, &status,
+		COLUMN_EDITOR, &editor,
+		-1);
+
+	if(status == NULL) {
+		g_clear_object(&editor);
+
+		return;
+	}
+
+	if(!PIDGIN_IS_STATUS_EDITOR(editor)) {
+		editor = pidgin_status_editor_new(status);
+
+		gtk_window_set_transient_for(GTK_WINDOW(editor), GTK_WINDOW(manager));
+
+		gtk_list_store_set(manager->model, &iter, COLUMN_EDITOR, editor,-1);
+		g_signal_connect_object(editor, "destroy",
+		                        G_CALLBACK(pidgin_status_editor_destroy_cb),
+		                        manager, 0);
+
+		gtk_widget_show(editor);
+	} else {
+		gtk_window_present_with_time(GTK_WINDOW(editor), GDK_CURRENT_TIME);
+	}
+}
+
+static void
+pidgin_status_manager_remove_selected(PidginStatusManager *manager) {
+	PurpleSavedStatus *status = NULL;
+	GtkWidget *editor = NULL;
+	GtkTreeIter iter;
+
+	if(gtk_tree_selection_count_selected_rows(manager->selection) == 0) {
+		return;
+	}
+
+	gtk_tree_selection_get_selected(manager->selection, NULL, &iter);
+	gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
+		COLUMN_STATUS, &status,
+		COLUMN_EDITOR, &editor,
+		-1);
+
+	if(GTK_IS_WIDGET(editor)) {
+		gtk_widget_destroy(editor);
+
+		g_clear_object(&editor);
+	}
+
+	purple_savedstatus_delete_by_status(status);
+}
+
 static PurpleSavedStatus *
 pidgin_status_manager_get_selected_status(PidginStatusManager *manager) {
 	PurpleSavedStatus *status = NULL;
@@ -129,6 +196,7 @@
 {
 	PidginStatusManager *manager = data;
 	PurpleSavedStatus *status = NULL;
+	GtkWidget *editor = NULL;
 
 	switch(response_id) {
 		case RESPONSE_USE:
@@ -138,19 +206,17 @@
 
 			break;
 		case RESPONSE_ADD:
-			pidgin_status_editor_show(FALSE, NULL);
+			editor = pidgin_status_editor_new(NULL);
+			gtk_window_set_transient_for(GTK_WINDOW(editor),
+			                             GTK_WINDOW(manager));
+			gtk_widget_show(editor);
 			break;
 		case RESPONSE_MODIFY:
-			status = pidgin_status_manager_get_selected_status(manager);
-
-			pidgin_status_editor_show(TRUE, status);
+			pidgin_status_manager_show_editor(manager);
 
 			break;
 		case RESPONSE_REMOVE:
-			status = pidgin_status_manager_get_selected_status(manager);
-
-			purple_savedstatus_delete_by_status(status);
-
+			pidgin_status_manager_remove_selected(manager);
 			break;
 		case GTK_RESPONSE_CLOSE:
 			gtk_widget_destroy(GTK_WIDGET(dialog));
@@ -168,13 +234,9 @@
 	GtkTreeIter iter;
 
 	if(gtk_tree_model_get_iter(GTK_TREE_MODEL(manager->model), &iter, path)) {
-		PurpleSavedStatus *status = NULL;
+		gtk_tree_selection_select_iter(manager->selection, &iter);
 
-		gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
-		                   COLUMN_STATUS, &status,
-		                   -1);
-
-		pidgin_status_editor_show(TRUE, status);
+		pidgin_status_manager_show_editor(manager);
 	}
 }
 
@@ -234,6 +296,37 @@
 	pidgin_status_manager_refresh(manager);
 }
 
+static void
+pidgin_status_editor_destroy_cb(GtkWidget *widget, gpointer data) {
+	PidginStatusManager *manager = data;
+	GtkTreeIter iter;
+
+	if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(manager->model), &iter)) {
+		return;
+	}
+
+	do {
+		GtkWidget *editor = NULL;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
+			COLUMN_EDITOR, &editor,
+			-1);
+
+		/* Check if editor is the widget being destroyed. */
+		if(editor == widget) {
+			/* It is, so set it back to NULL and unreference the copy we just
+			 * got.
+			 */
+			gtk_list_store_set(manager->model, &iter, COLUMN_EDITOR, NULL, -1);
+			g_clear_object(&editor);
+
+			break;
+		}
+
+		g_clear_object(&editor);
+	} while(gtk_tree_model_iter_next(GTK_TREE_MODEL(manager->model), &iter));
+}
+
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
diff --git a/pidgin/pidginstatusprimitivechooser.h b/pidgin/pidginstatusprimitivechooser.h
--- a/pidgin/pidginstatusprimitivechooser.h
+++ b/pidgin/pidginstatusprimitivechooser.h
@@ -59,6 +59,29 @@
  */
 GtkWidget *pidgin_status_primitive_chooser_new(void);
 
+/**
+ * pidgin_status_primitive_chooser_get_selected:
+ * @chooser: The instance.
+ *
+ * Gets the [enum@Purple.StatusPrimitive] that is selected.
+ *
+ * Returns: The selected primitive status.
+ *
+ * Since: 3.0.0
+ */
+PurpleStatusPrimitive pidgin_status_primitive_chooser_get_selected(PidginStatusPrimitiveChooser *chooser);
+
+/**
+ * pidgin-status_primitive_chooser_set_selected:
+ * @chooser: The instance.
+ * @primitive: The [enum@Purple.StatusPrimitive] to set.
+ *
+ * Sets @primitive as the active item.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_status_primitive_chooser_set_selected(PidginStatusPrimitiveChooser *chooser, PurpleStatusPrimitive primitive);
+
 G_END_DECLS
 
 #endif /* PIDGIN_STATUS_PRIMITIVE_CHOOSER_H */
diff --git a/pidgin/pidginstatusprimitivechooser.c b/pidgin/pidginstatusprimitivechooser.c
--- a/pidgin/pidginstatusprimitivechooser.c
+++ b/pidgin/pidginstatusprimitivechooser.c
@@ -54,3 +54,28 @@
 pidgin_status_primitive_chooser_new(void) {
 	return g_object_new(PIDGIN_TYPE_STATUS_PRIMITIVE_CHOOSER, NULL);
 }
+
+PurpleStatusPrimitive
+pidgin_status_primitive_chooser_get_selected(PidginStatusPrimitiveChooser *chooser) {
+    const gchar *active_id = NULL;
+
+    g_return_val_if_fail(PIDGIN_IS_STATUS_PRIMITIVE_CHOOSER(chooser),
+                         PURPLE_STATUS_UNSET);
+
+    active_id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(chooser));
+
+    return purple_primitive_get_type_from_id(active_id);
+}
+
+void
+pidgin_status_primitive_chooser_set_selected(PidginStatusPrimitiveChooser *chooser,
+                                             PurpleStatusPrimitive primitive)
+{
+    const gchar *active_id = NULL;
+
+    g_return_if_fail(PIDGIN_IS_STATUS_PRIMITIVE_CHOOSER(chooser));
+
+    active_id = purple_primitive_get_id_from_type(primitive);
+
+    gtk_combo_box_set_active_id(GTK_COMBO_BOX(chooser), active_id);
+}
diff --git a/pidgin/resources/Status/editor.ui b/pidgin/resources/Status/editor.ui
new file mode 100644
--- /dev/null
+++ b/pidgin/resources/Status/editor.ui
@@ -0,0 +1,284 @@
+<?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"/>
+  <requires lib="Talkatu" version="0.1"/>
+  <requires lib="pidgin" version="3.0"/>
+  <!-- interface-license-type gplv2 -->
+  <!-- interface-name Pidgin -->
+  <!-- interface-description Internet Messenger -->
+  <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+  <template class="PidginStatusEditor" parent="GtkDialog">
+    <property name="can-focus">False</property>
+    <property name="title" translatable="yes">Status</property>
+    <property name="type-hint">dialog</property>
+    <property name="destroy-with-parent">True</property>
+    <signal name="response" handler="pidgin_status_editor_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="cancel">
+                <property name="label" translatable="yes">_Cancel</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">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="use">
+                <property name="label" translatable="yes">_Use</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="sensitive">False</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="save">
+                <property name="label" translatable="yes">_Save</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="sensitive">False</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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <child>
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes">_Title:</property>
+                    <property name="use-underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="yalign">0</property>
+                    <property name="mnemonic-widget">title</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="title">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <signal name="changed" handler="pidgin_status_editor_title_changed_cb" object="PidginStatusEditor" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <child>
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes">St_atus:</property>
+                    <property name="use-underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="yalign">0</property>
+                    <property name="mnemonic-widget">primitive</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="PidginStatusPrimitiveChooser" id="primitive">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="id-column">0</property>
+                    <property name="model">primitive_store</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes">_Message:</property>
+                    <property name="use-underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="yalign">0</property>
+                    <property name="mnemonic-widget">message</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="TalkatuEditor">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="orientation">vertical</property>
+                    <child internal-child="send_button">
+                      <object class="GtkButton">
+                        <property name="can-focus">False</property>
+                        <property name="receives-default">False</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child internal-child="toolbar">
+                      <object class="TalkatuToolbar">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child internal-child="input">
+                      <object class="TalkatuInput" id="message">
+                        <property name="width-request">450</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="wrap-mode">word</property>
+                        <property name="buffer">buffer</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </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="-6">cancel</action-widget>
+      <action-widget response="0">use</action-widget>
+      <action-widget response="1">save</action-widget>
+    </action-widgets>
+  </template>
+  <object class="GtkSizeGroup">
+    <widgets>
+      <widget name="label1"/>
+      <widget name="label2"/>
+      <widget name="label3"/>
+    </widgets>
+  </object>
+  <object class="TalkatuTagTable" id="tag_table"/>
+  <object class="TalkatuHtmlBuffer" id="buffer">
+    <property name="tag-table">tag_table</property>
+  </object>
+  <object class="PidginStatusPrimitiveStore" id="primitive_store"/>
+</interface>
diff --git a/pidgin/resources/Status/manager.ui b/pidgin/resources/Status/manager.ui
--- a/pidgin/resources/Status/manager.ui
+++ b/pidgin/resources/Status/manager.ui
@@ -36,6 +36,8 @@
       <column type="gchararray"/>
       <!-- column-name status -->
       <column type="gpointer"/>
+      <!-- column-name editor -->
+      <column type="GObject"/>
     </columns>
   </object>
   <template class="PidginStatusManager" parent="GtkDialog">
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/editor.ui</file>
     <file compressed="true">Status/manager.ui</file>
     <file compressed="true">Whiteboard/whiteboard.ui</file>
     <file compressed="true">Xfer/xfer.ui</file>
diff --git a/po/POTFILES.in b/po/POTFILES.in
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -368,6 +368,8 @@
 pidgin/pidginprotocolchooser.c
 pidgin/pidginprotocolstore.c
 pidgin/pidginscrollbook.c
+pidgin/pidginstatuseditor.c
+pidgin/pidginstatusmanager.c
 pidgin/pidginstatusprimitivechooser.c
 pidgin/pidginstatusprimitivestore.c
 pidgin/pidginstylecontext.c
@@ -412,6 +414,7 @@
 pidgin/resources/Privacy/dialog.ui
 pidgin/resources/Protocols/chooser.ui
 pidgin/resources/Roomlist/roomlist.ui
+pidgin/resources/Status/editor.ui
 pidgin/resources/Status/manager.ui
 pidgin/resources/Whiteboard/whiteboard.ui
 pidgin/resources/Xfer/xfer.ui
