Wed, 01 Jan 2025 15:32:28 -0600
Prepare for the 2.90.1 release
Testing Done:
Ran `meson dist`
Reviewed at https://reviews.imfreedom.org/r/3716/
/* * 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 "pidginmessage.h" #include "pidgincolor.h" struct _PidginMessage { GtkBox parent; PurpleMessage *message; }; enum { PROP_0, PROP_MESSAGE, N_PROPERTIES, }; static GParamSpec *properties[N_PROPERTIES] = {NULL, }; /****************************************************************************** * Helpers *****************************************************************************/ /** * pidgin_message_set_tooltip_for_timestamp: (skip) * @tooltip: The tooltip to update. * @timestamp: The timestamp to set. * * Updates @tooltip to display @timestamp. This is meant to be called from * a GtkWidget::query-tooltip signal and its return value should be returned * from that handler. * * Returns: %TRUE if a tooltip was set, otherwise %FALSE. * * Since: 3.0 */ static gboolean pidgin_message_set_tooltip_for_timestamp(GtkTooltip *tooltip, GDateTime *timestamp) { GDateTime *local = NULL; char *text = NULL; if(timestamp == NULL) { return FALSE; } local = g_date_time_to_local(timestamp); text = g_date_time_format(local, "%c"); g_clear_pointer(&local, g_date_time_unref); gtk_tooltip_set_text(tooltip, text); g_clear_pointer(&text, g_free); return TRUE; } /****************************************************************************** * Callbacks *****************************************************************************/ static char * pidgin_message_get_author(G_GNUC_UNUSED GObject *self, PurpleMessage *message, G_GNUC_UNUSED gpointer data) { if(PURPLE_IS_MESSAGE(message)) { PurpleConversationMember *author = NULL; author = purple_message_get_author(message); if(PURPLE_IS_CONVERSATION_MEMBER(author)) { return g_strdup(purple_conversation_member_get_name_for_display(author)); } } return g_strdup(""); } static PangoAttrList * pidgin_message_get_author_attributes(G_GNUC_UNUSED GObject *self, PurpleMessage *message, G_GNUC_UNUSED gpointer data) { GdkRGBA rgba; PangoAttrList *attrs = NULL; gboolean color_valid = FALSE; const char *custom_color = NULL; if(!PURPLE_IS_MESSAGE(message)) { return NULL; } custom_color = purple_message_get_author_name_color(message); if(!purple_strempty(custom_color)) { color_valid = gdk_rgba_parse(&rgba, custom_color); } if(!color_valid) { PurpleConversationMember *author = NULL; const char *name_for_display = NULL; author = purple_message_get_author(message); if(PURPLE_IS_CONVERSATION_MEMBER(author)) { name_for_display = purple_conversation_member_get_name_for_display(author); } pidgin_color_calculate_for_text(name_for_display, &rgba); color_valid = TRUE; } attrs = pango_attr_list_new(); if(color_valid) { PangoAttribute *attr = NULL; attr = pango_attr_foreground_new(0xFFFF * rgba.red, 0xFFFF * rgba.green, 0xFFFF * rgba.blue); pango_attr_list_insert(attrs, attr); } return attrs; } static PangoAttrList * pidgin_message_get_message_attributes(G_GNUC_UNUSED GObject *self, PurpleMessage *message, G_GNUC_UNUSED gpointer data) { PangoAttrList *attrs = NULL; if(!PURPLE_IS_MESSAGE(message)) { return NULL; } attrs = pango_attr_list_new(); if(purple_message_get_action(message)) { PangoAttribute *attr = NULL; attr = pango_attr_style_new(PANGO_STYLE_ITALIC); pango_attr_list_insert(attrs, attr); } return attrs; } static char * pidgin_message_get_timestamp_string(G_GNUC_UNUSED GObject *self, PurpleMessage *message, G_GNUC_UNUSED gpointer data) { GDateTime *timestamp = NULL; if(!PURPLE_IS_MESSAGE(message)) { return NULL; } timestamp = purple_message_get_timestamp(message); if(timestamp != NULL) { GDateTime *local = NULL; char *ret = NULL; local = g_date_time_to_local(timestamp); ret = g_date_time_format(local, "%I:%M %p"); g_date_time_unref(local); return ret; } return NULL; } static char * pidgin_message_process_message_contents_cb(G_GNUC_UNUSED GObject *self, const char *contents, G_GNUC_UNUSED gpointer data) { char *escaped = NULL; char *linkified = NULL; escaped = g_markup_escape_text(contents, -1); linkified = purple_markup_linkify(escaped); g_free(escaped); return linkified; } static gboolean pidgin_message_query_tooltip_contents_cb(GtkWidget *self, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED gboolean keyboard_mode, GtkTooltip *tooltip, G_GNUC_UNUSED gpointer data) { const char *uri = NULL; uri = gtk_label_get_current_uri(GTK_LABEL(self)); if(purple_strempty(uri)) { return FALSE; } gtk_tooltip_set_text(tooltip, uri); return TRUE; } static gboolean pidgin_message_query_tooltip_edited_cb(G_GNUC_UNUSED GtkWidget *self, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data) { PidginMessage *pidgin_message = data; PurpleMessage *purple_message = NULL; GDateTime *timestamp = NULL; if(!PIDGIN_IS_MESSAGE(pidgin_message)) { return FALSE; } purple_message = pidgin_message_get_message(pidgin_message); if(!PURPLE_IS_MESSAGE(purple_message)) { return FALSE; } timestamp = purple_message_get_edited_at(purple_message); return pidgin_message_set_tooltip_for_timestamp(tooltip, timestamp); } static gboolean pidgin_message_query_tooltip_timestamp_cb(G_GNUC_UNUSED GtkWidget *self, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data) { PidginMessage *message = data; GDateTime *timestamp = NULL; if(!PURPLE_IS_MESSAGE(message->message)) { return FALSE; } timestamp = purple_message_get_timestamp(message->message); return pidgin_message_set_tooltip_for_timestamp(tooltip, timestamp); } /****************************************************************************** * GObject Implementation *****************************************************************************/ G_DEFINE_FINAL_TYPE(PidginMessage, pidgin_message, GTK_TYPE_BOX) static void pidgin_message_finalize(GObject *obj) { PidginMessage *message = PIDGIN_MESSAGE(obj); g_clear_object(&message->message); G_OBJECT_CLASS(pidgin_message_parent_class)->finalize(obj); } static void pidgin_message_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { PidginMessage *message = PIDGIN_MESSAGE(obj); switch(param_id) { case PROP_MESSAGE: g_value_set_object(value, pidgin_message_get_message(message)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void pidgin_message_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) { PidginMessage *message = PIDGIN_MESSAGE(obj); switch(param_id) { case PROP_MESSAGE: pidgin_message_set_message(message, g_value_get_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void pidgin_message_init(PidginMessage *message) { gtk_widget_init_template(GTK_WIDGET(message)); } static void pidgin_message_class_init(PidginMessageClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); obj_class->finalize = pidgin_message_finalize; obj_class->get_property = pidgin_message_get_property; obj_class->set_property = pidgin_message_set_property; /** * PidginMessage:message: * * The [class@Purple.Message] to be displayed. * * Since: 3.0 */ properties[PROP_MESSAGE] = g_param_spec_object( "message", NULL, NULL, PURPLE_TYPE_MESSAGE, G_PARAM_READWRITE | 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/Conversations/message.ui" ); gtk_widget_class_bind_template_callback(widget_class, pidgin_message_get_author); gtk_widget_class_bind_template_callback(widget_class, pidgin_message_get_author_attributes); gtk_widget_class_bind_template_callback(widget_class, pidgin_message_get_message_attributes); gtk_widget_class_bind_template_callback(widget_class, pidgin_message_get_timestamp_string); gtk_widget_class_bind_template_callback(widget_class, pidgin_message_process_message_contents_cb); gtk_widget_class_bind_template_callback(widget_class, pidgin_message_query_tooltip_contents_cb); gtk_widget_class_bind_template_callback(widget_class, pidgin_message_query_tooltip_edited_cb); gtk_widget_class_bind_template_callback(widget_class, pidgin_message_query_tooltip_timestamp_cb); } /****************************************************************************** * Public API *****************************************************************************/ GtkWidget * pidgin_message_new(PurpleMessage *message) { return g_object_new( PIDGIN_TYPE_MESSAGE, "message", message, NULL); } PurpleMessage * pidgin_message_get_message(PidginMessage *message) { g_return_val_if_fail(PIDGIN_IS_MESSAGE(message), NULL); return message->message; } void pidgin_message_set_message(PidginMessage *pidgin_message, PurpleMessage *purple_message) { g_return_if_fail(PIDGIN_IS_MESSAGE(pidgin_message)); if(g_set_object(&pidgin_message->message, purple_message)) { g_object_notify_by_pspec(G_OBJECT(pidgin_message), properties[PROP_MESSAGE]); } }