diff -r 627ee13c5dee -r dff7cdade009 libpurple/purpleattachments.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/purpleattachments.c Thu Jan 16 21:21:59 2025 -0600 @@ -0,0 +1,271 @@ +/* + * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers + * + * Purple 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 library 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 library 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 library; if not, see . + */ + +#include + +#include "purpleattachments.h" + +struct _PurpleAttachments { + GObject parent; + + GPtrArray *attachments; +}; + +enum { + PROP_0, + PROP_ITEM_TYPE, + PROP_N_ITEMS, + N_PROPERTIES, +}; +static GParamSpec *properties[N_PROPERTIES] = {NULL, }; + +/****************************************************************************** + * Helpers + *****************************************************************************/ +static gboolean +purple_attachments_equal(gconstpointer a, gconstpointer b) { + PurpleAttachment *attachment1 = (gpointer)a; + PurpleAttachment *attachment2 = (gpointer)b; + + return purple_attachment_equal(attachment1, attachment2); +} + +/****************************************************************************** + * GListModel Implementation + *****************************************************************************/ +static GType +purple_attachments_get_item_type(G_GNUC_UNUSED GListModel *model) { + return PURPLE_TYPE_ATTACHMENT; +} + +static guint +purple_attachments_get_n_items(GListModel *model) { + PurpleAttachments *attachments = PURPLE_ATTACHMENTS(model); + + return attachments->attachments->len; +} + +static gpointer +purple_attachments_get_item(GListModel *model, guint position) { + PurpleAttachments *attachments = PURPLE_ATTACHMENTS(model); + PurpleAttachment *attachment = NULL; + + if(position < attachments->attachments->len) { + attachment = g_ptr_array_index(attachments->attachments, position); + + if(PURPLE_IS_ATTACHMENT(attachment)) { + g_object_ref(attachment); + } + } + + return attachment; +} + +static void +purple_attachments_list_model_iface_init(GListModelInterface *iface) { + iface->get_item_type = purple_attachments_get_item_type; + iface->get_n_items = purple_attachments_get_n_items; + iface->get_item = purple_attachments_get_item; +} + +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +G_DEFINE_FINAL_TYPE_WITH_CODE(PurpleAttachments, + purple_attachments, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL, + purple_attachments_list_model_iface_init)) + +static void +purple_attachments_finalize(GObject *obj) { + PurpleAttachments *attachments = PURPLE_ATTACHMENTS(obj); + + g_clear_pointer(&attachments->attachments, g_ptr_array_unref); + + G_OBJECT_CLASS(purple_attachments_parent_class)->finalize(obj); +} + +static void +purple_attachments_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *pspec) +{ + GListModel *model = G_LIST_MODEL(obj); + + switch(param_id) { + case PROP_ITEM_TYPE: + g_value_set_gtype(value, g_list_model_get_item_type(model)); + break; + case PROP_N_ITEMS: + g_value_set_uint(value, g_list_model_get_n_items(model)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +static void +purple_attachments_init(PurpleAttachments *attachments) { + attachments->attachments = g_ptr_array_new_full(0, g_object_unref); +} + +static void +purple_attachments_class_init(PurpleAttachmentsClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + obj_class->finalize = purple_attachments_finalize; + obj_class->get_property = purple_attachments_get_property; + + /** + * PurpleAttachments:item-type: + * + * The type of items. See [vfunc@Gio.ListModel.get_item_type]. + * + * Since: 3.0 + */ + properties[PROP_ITEM_TYPE] = g_param_spec_gtype( + "item-type", NULL, NULL, + PURPLE_TYPE_ATTACHMENT, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * PurpleAttachments:n-items: + * + * The number of items. See [vfunc@Gio.ListModel.get_n_items]. + * + * Since: 3.0 + */ + properties[PROP_N_ITEMS] = g_param_spec_uint( + "n-items", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); +} + +/****************************************************************************** + * Public API + *****************************************************************************/ +gboolean +purple_attachments_add_attachment(PurpleAttachments *attachments, + PurpleAttachment *attachment) +{ + GObject *obj = NULL; + gboolean found = FALSE; + guint len = 0; + + g_return_val_if_fail(PURPLE_IS_ATTACHMENTS(attachments), FALSE); + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), FALSE); + + found = g_ptr_array_find_with_equal_func(attachments->attachments, + attachment, + purple_attachments_equal, + NULL); + + if(found) { + return FALSE; + } + + /* Store length before adding to make the math easier to understand. */ + len = attachments->attachments->len; + + g_ptr_array_add(attachments->attachments, g_object_ref(attachment)); + + obj = G_OBJECT(attachments); + g_object_freeze_notify(obj); + g_list_model_items_changed(G_LIST_MODEL(attachments), len, 0, 1); + g_object_notify_by_pspec(obj, properties[PROP_N_ITEMS]); + g_object_thaw_notify(obj); + + return TRUE; +} + +PurpleAttachment * +purple_attachments_find_with_id(PurpleAttachments *attachments, guint64 id) { + g_return_val_if_fail(PURPLE_IS_ATTACHMENTS(attachments), NULL); + + for(guint i = 0; i < attachments->attachments->len; i++) { + PurpleAttachment *attachment = NULL; + + attachment = g_ptr_array_index(attachments->attachments, i); + + if(purple_attachment_get_id(attachment) == id) { + return attachment; + } + } + + return NULL; +} + +PurpleAttachments * +purple_attachments_new(void) { + return g_object_new(PURPLE_TYPE_ATTACHMENTS, NULL); +} + +gboolean +purple_attachments_remove_attachment(PurpleAttachments *attachments, + PurpleAttachment *attachment) +{ + GObject *obj = NULL; + gboolean found = FALSE; + guint index = 0; + + g_return_val_if_fail(PURPLE_IS_ATTACHMENTS(attachments), FALSE); + g_return_val_if_fail(PURPLE_IS_ATTACHMENT(attachment), FALSE); + + found = g_ptr_array_find_with_equal_func(attachments->attachments, + attachment, + purple_attachments_equal, + &index); + if(!found) { + return FALSE; + } + + g_ptr_array_remove_index(attachments->attachments, index); + + obj = G_OBJECT(attachments); + g_object_freeze_notify(obj); + g_list_model_items_changed(G_LIST_MODEL(attachments), index, 1, 0); + g_object_notify_by_pspec(obj, properties[PROP_N_ITEMS]); + g_object_thaw_notify(obj); + + return TRUE; +} + +void +purple_attachments_remove_all(PurpleAttachments *attachments) { + guint removed = 0; + + g_return_if_fail(PURPLE_IS_ATTACHMENTS(attachments)); + + removed = attachments->attachments->len; + g_ptr_array_remove_range(attachments->attachments, 0, removed); + + if(removed > 0) { + GObject *obj = G_OBJECT(attachments); + g_object_freeze_notify(obj); + g_list_model_items_changed(G_LIST_MODEL(attachments), 0, removed, 0); + g_object_notify_by_pspec(obj, properties[PROP_N_ITEMS]); + g_object_thaw_notify(obj); + } +}