pidgin/pidgin

9753153c49fc
Parents cb48a8dd1bfc
Children ff07001f177b
Add a new templated PidginStatusEditor and wire it up.

The substatuses were ignored for now as their future remains unclear. This
greatly simplified this widget.

Testing Done:
Tested everything I could think of in the UI as well as ran the docs without issue.

Reviewed at https://reviews.imfreedom.org/r/1363/
--- a/pidgin/meson.build Thu May 05 22:15:44 2022 -0500
+++ b/pidgin/meson.build Fri May 06 00:37:05 2022 -0500
@@ -53,6 +53,7 @@
'pidginproxyoptions.c',
'pidginscrollbook.c',
'pidginstatusbox.c',
+ 'pidginstatuseditor.c',
'pidginstatusmanager.c',
'pidginstatusprimitivechooser.c',
'pidginstatusprimitivestore.c',
@@ -117,6 +118,7 @@
'pidginproxyoptions.h',
'pidginscrollbook.h',
'pidginstatusbox.h',
+ 'pidginstatuseditor.h',
'pidginstatusmanager.h',
'pidginstatusprimitivechooser.h',
'pidginstatusprimitivestore.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginstatuseditor.c Fri May 06 00:37:05 2022 -0500
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+ /* we don't used boxed here because of the way status are currently
+ * managed.
+ */
+ 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;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginstatuseditor.h Fri May 06 00:37:05 2022 -0500
@@ -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: (transfer none) (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 */
--- a/pidgin/pidginstatusmanager.c Thu May 05 22:15:44 2022 -0500
+++ b/pidgin/pidginstatusmanager.c Fri May 06 00:37:05 2022 -0500
@@ -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
*****************************************************************************/
--- a/pidgin/pidginstatusprimitivechooser.c Thu May 05 22:15:44 2022 -0500
+++ b/pidgin/pidginstatusprimitivechooser.c Fri May 06 00:37:05 2022 -0500
@@ -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);
+}
--- a/pidgin/pidginstatusprimitivechooser.h Thu May 05 22:15:44 2022 -0500
+++ b/pidgin/pidginstatusprimitivechooser.h Fri May 06 00:37:05 2022 -0500
@@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Status/editor.ui Fri May 06 00:37:05 2022 -0500
@@ -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>
--- a/pidgin/resources/Status/manager.ui Thu May 05 22:15:44 2022 -0500
+++ b/pidgin/resources/Status/manager.ui Fri May 06 00:37:05 2022 -0500
@@ -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">
--- a/pidgin/resources/pidgin.gresource.xml Thu May 05 22:15:44 2022 -0500
+++ b/pidgin/resources/pidgin.gresource.xml Fri May 06 00:37:05 2022 -0500
@@ -32,6 +32,7 @@
<file compressed="true">Protocols/chooser.ui</file>
<file compressed="true">Roomlist/roomlist.ui</file>
<file compressed="true">Status/box.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>
--- a/po/POTFILES.in Thu May 05 22:15:44 2022 -0500
+++ b/po/POTFILES.in Fri May 06 00:37:05 2022 -0500
@@ -368,6 +368,8 @@
pidgin/pidginproxyoptions.c
pidgin/pidginscrollbook.c
pidgin/pidginstatusbox.c
+pidgin/pidginstatuseditor.c
+pidgin/pidginstatusmanager.c
pidgin/pidginstatusprimitivechooser.c
pidgin/pidginstatusprimitivestore.c
pidgin/pidginstylecontext.c
@@ -413,6 +415,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