--- a/pidgin/pidginconversation.c Tue Mar 11 14:13:42 2025 -0500 +++ b/pidgin/pidginconversation.c Tue Mar 11 16:07:59 2025 -0500 @@ -33,6 +33,7 @@ #include "pidginconversation.h" #include "pidgininfopane.h" #include "pidginmessage.h" +#include "pidginnotifiable.h" #define PIDGIN_CONVERSATION_DATA ("pidgin-conversation") @@ -40,6 +41,9 @@ PROP_0, PROP_CONVERSATION, N_PROPERTIES, + /* Overrides */ + PROP_NEEDS_ATTENTION = N_PROPERTIES, + PROP_NOTIFICATION_COUNT, }; static GParamSpec *properties[N_PROPERTIES] = {NULL, }; @@ -61,21 +65,64 @@ GtkWidget *typing_label; GtkWidget *status_label; + + /* This is a temporary work around to get new message notifications working + * until we implement Purple.History properly. + * -- gk 2025-03-11 + */ + guint notification_count; }; -G_DEFINE_FINAL_TYPE(PidginConversation, pidgin_conversation, GTK_TYPE_BOX) +static void +pidgin_conversation_messages_changed_cb(GListModel *model, guint position, + guint removed, guint added, + gpointer data); /****************************************************************************** * Helpers *****************************************************************************/ static void +pidgin_conversation_set_notification_count(PidginConversation *conversation, + guint notification_count) +{ + if(conversation->notification_count != notification_count) { + GObject *obj = G_OBJECT(conversation); + + conversation->notification_count = notification_count; + + g_object_freeze_notify(obj); + g_object_notify(obj, "needs-attention"); + g_object_notify(obj, "notification-count"); + g_object_thaw_notify(obj); + } +} + +static void pidgin_conversation_set_conversation(PidginConversation *conversation, PurpleConversation *purple_conversation) { + /* Disconnect our old signal. */ + if(PURPLE_IS_CONVERSATION(conversation->conversation)) { + GListModel *model = NULL; + + model = purple_conversation_get_messages(conversation->conversation); + g_signal_handlers_disconnect_by_func(model, + G_CALLBACK(pidgin_conversation_messages_changed_cb), + conversation); + } + if(g_set_object(&conversation->conversation, purple_conversation)) { if(PURPLE_IS_CONVERSATION(purple_conversation)) { + GListModel *model = NULL; + g_object_set_data(G_OBJECT(purple_conversation), PIDGIN_CONVERSATION_DATA, conversation); + + model = purple_conversation_get_messages(conversation->conversation); + + g_signal_connect_object(model, "items-changed", + G_CALLBACK(pidgin_conversation_messages_changed_cb), + conversation, G_CONNECT_DEFAULT); } g_object_notify_by_pspec(G_OBJECT(conversation), @@ -172,9 +219,35 @@ gtk_text_buffer_set_text(buffer, "", -1); } +static gboolean +pidgin_conversation_get_needs_attention(PidginConversation *conversation) +{ + return conversation->notification_count > 0; +} + +static guint +pidgin_conversation_get_notification_count(PidginConversation *conversation) { + return conversation->notification_count; +} + /****************************************************************************** * Callbacks *****************************************************************************/ +static void +pidgin_conversation_messages_changed_cb(G_GNUC_UNUSED GListModel *model, + G_GNUC_UNUSED guint position, + G_GNUC_UNUSED guint removed, + guint added, + gpointer data) +{ + PidginConversation *conversation = data; + guint current = conversation->notification_count; + + current += added; + + pidgin_conversation_set_notification_count(conversation, current); +} + static char * pidgin_conversation_get_status_label(G_GNUC_UNUSED PidginConversation *conversation, GError *error, gboolean online) @@ -358,9 +431,29 @@ pidgin_message_set_message(PIDGIN_MESSAGE(message), NULL); } +static void +pidgin_conversation_map_cb(GtkWidget *self, G_GNUC_UNUSED gpointer data) { + pidgin_conversation_set_notification_count(PIDGIN_CONVERSATION(self), 0); +} + +/****************************************************************************** + * PidginNotifiable Implementation + *****************************************************************************/ +static void +pidgin_conversation_notifiable_init(G_GNUC_UNUSED PidginNotifiableInterface *iface) +{ +} + /****************************************************************************** * GObject Implementation *****************************************************************************/ +G_DEFINE_FINAL_TYPE_WITH_CODE(PidginConversation, + pidgin_conversation, + GTK_TYPE_BOX, + G_IMPLEMENT_INTERFACE(PIDGIN_TYPE_NOTIFIABLE, + pidgin_conversation_notifiable_init)) + + static void pidgin_conversation_dispose(GObject *obj) { PidginConversation *conversation = PIDGIN_CONVERSATION(obj); @@ -383,6 +476,14 @@ g_value_set_object(value, pidgin_conversation_get_conversation(conversation)); break; + case PROP_NEEDS_ATTENTION: + g_value_set_boolean(value, + pidgin_conversation_get_needs_attention(conversation)); + break; + case PROP_NOTIFICATION_COUNT: + g_value_set_uint(value, + pidgin_conversation_get_notification_count(conversation)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; @@ -435,6 +536,10 @@ spelling_text_buffer_adapter_set_enabled(adapter, TRUE); g_clear_object(&adapter); + + /* Connect to our map signal to reset the notification-count property. */ + g_signal_connect(conversation, "map", + G_CALLBACK(pidgin_conversation_map_cb), NULL); } static void @@ -460,6 +565,11 @@ g_object_class_install_properties(obj_class, N_PROPERTIES, properties); + g_object_class_override_property(obj_class, PROP_NEEDS_ATTENTION, + "needs-attention"); + g_object_class_override_property(obj_class, PROP_NOTIFICATION_COUNT, + "notification-count"); + /* Template stuff. */ gtk_widget_class_set_template_from_resource( widget_class,