Tue, 07 Jan 2025 04:49:09 -0600
Make sure we notify on the n-items property for all objects that have it
Testing Done:
Called in the turtles.
Reviewed at https://reviews.imfreedom.org/r/3736/
/* * Purple - Internet Messaging Library * Copyright (C) Pidgin Developers <devel@pidgin.im> * * 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 <https://www.gnu.org/licenses/>. */ #include <glib/gi18n-lib.h> #include "purplehistorymanager.h" #include "purplehistoryadapter.h" #include "purplehistoryadapterprivate.h" #include "purplesqlitehistoryadapter.h" #include "debug.h" #include "util.h" enum { PROP_0, PROP_ITEM_TYPE, PROP_N_ITEMS, N_PROPERTIES, }; static GParamSpec *properties[N_PROPERTIES] = {NULL, }; enum { SIG_ACTIVE_CHANGED, SIG_ADDED, SIG_REMOVED, N_SIGNALS, }; static guint signals[N_SIGNALS] = {0, }; struct _PurpleHistoryManager { GObject parent; GPtrArray *adapters; PurpleHistoryAdapter *active_adapter; }; static PurpleHistoryManager *default_manager = NULL; /****************************************************************************** * Helpers *****************************************************************************/ static PurpleHistoryAdapter * purple_history_manager_find_with_id(PurpleHistoryManager *manager, const char *id, guint *position) { g_return_val_if_fail(PURPLE_IS_HISTORY_MANAGER(manager), NULL); for(guint i = 0; i < manager->adapters->len; i++) { PurpleHistoryAdapter *adapter = NULL; const char *candidate_id = NULL; adapter = g_ptr_array_index(manager->adapters, i); candidate_id = purple_history_adapter_get_id(adapter); if(purple_strequal(candidate_id, id)) { if(position != NULL) { *position = i; } return adapter; } } return NULL; } /****************************************************************************** * GListModel Implementation *****************************************************************************/ static GType purple_history_manager_get_item_type(G_GNUC_UNUSED GListModel *list) { return PURPLE_TYPE_HISTORY_ADAPTER; } static guint purple_history_manager_get_n_items(GListModel *list) { PurpleHistoryManager *manager = PURPLE_HISTORY_MANAGER(list); return manager->adapters->len; } static gpointer purple_history_manager_get_item(GListModel *list, guint position) { PurpleHistoryManager *manager = PURPLE_HISTORY_MANAGER(list); PurpleHistoryAdapter *adapter = NULL; if(position < manager->adapters->len) { adapter = g_object_ref(g_ptr_array_index(manager->adapters, position)); } return adapter; } static void pidgin_history_manager_list_model_iface_init(GListModelInterface *iface) { iface->get_item_type = purple_history_manager_get_item_type; iface->get_n_items = purple_history_manager_get_n_items; iface->get_item = purple_history_manager_get_item; } /****************************************************************************** * GObject Implementation *****************************************************************************/ G_DEFINE_FINAL_TYPE_WITH_CODE(PurpleHistoryManager, purple_history_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL, pidgin_history_manager_list_model_iface_init)) static void purple_history_manager_finalize(GObject *obj) { PurpleHistoryManager *manager = NULL; manager = PURPLE_HISTORY_MANAGER(obj); g_clear_pointer(&manager->adapters, g_ptr_array_unref); G_OBJECT_CLASS(purple_history_manager_parent_class)->finalize(obj); } static void purple_history_manager_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, purple_history_manager_get_item_type(model)); break; case PROP_N_ITEMS: g_value_set_uint(value, purple_history_manager_get_n_items(model)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void purple_history_manager_init(PurpleHistoryManager *manager) { manager->adapters = g_ptr_array_new_full(1, g_object_unref); } static void purple_history_manager_class_init(PurpleHistoryManagerClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = purple_history_manager_finalize; obj_class->get_property = purple_history_manager_get_property; /** * PurpleHistoryManager: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, G_TYPE_OBJECT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * PurpleHistoryManager: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); /** * PurpleHistoryManager::active-changed: * @manager: The #PurpleHistoryManager instance. * @old: The old #PurpleHistoryAdapter. * @current: The new activated #PurpleHistoryAdapter. * * Emitted after @adapter has been changed for @manager. * * Since: 3.0 */ signals[SIG_ACTIVE_CHANGED] = g_signal_new_class_handler( "active-changed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 2, PURPLE_TYPE_HISTORY_ADAPTER, PURPLE_TYPE_HISTORY_ADAPTER); /** * PurpleHistoryManager::added: * @manager: The #PurpleHistoryManager instance. * @adapter: The #PurpleHistoryAdapter that was added. * * Emitted after @adapter has been added to @manager. * * Since: 3.0 */ signals[SIG_ADDED] = g_signal_new_class_handler( "added", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 1, PURPLE_TYPE_HISTORY_ADAPTER); /** * PurpleHistoryManager::removed: * @manager: The #PurpleHistoryManager instance. * @adapter: The #PurpleHistoryAdapter that was removed. * * Emitted after @adapter has been removed from @manager. * * Since: 3.0 */ signals[SIG_REMOVED] = g_signal_new_class_handler( "removed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 1, PURPLE_TYPE_HISTORY_ADAPTER); } /****************************************************************************** * Public API *****************************************************************************/ PurpleHistoryManager * purple_history_manager_get_default(void) { return default_manager; } GListModel * purple_history_manager_get_default_as_model(void) { if(PURPLE_IS_HISTORY_MANAGER(default_manager)) { return G_LIST_MODEL(default_manager); } return NULL; } gboolean purple_history_manager_add(PurpleHistoryManager *manager, PurpleHistoryAdapter *adapter, GError **error) { PurpleHistoryAdapter *existing = NULL; const gchar *id = NULL; guint position = 0; g_return_val_if_fail(PURPLE_IS_HISTORY_MANAGER(manager), FALSE); g_return_val_if_fail(PURPLE_IS_HISTORY_ADAPTER(adapter), FALSE); id = purple_history_adapter_get_id(adapter); existing = purple_history_manager_find_with_id(manager, id, NULL); if(PURPLE_IS_HISTORY_ADAPTER(existing)) { g_set_error(error, PURPLE_HISTORY_MANAGER_DOMAIN, 0, _("adapter %s has already been added"), id); return FALSE; } /* Set position to the length before we add the new item to avoid math. */ position = manager->adapters->len; g_ptr_array_add(manager->adapters, g_object_ref(adapter)); g_signal_emit(G_OBJECT(manager), signals[SIG_ADDED], 0, adapter); g_list_model_items_changed(G_LIST_MODEL(manager), position, 0, 1); g_object_notify_by_pspec(G_OBJECT(manager), properties[PROP_N_ITEMS]); return TRUE; } gboolean purple_history_manager_remove(PurpleHistoryManager *manager, PurpleHistoryAdapter *adapter, GError **error) { PurpleHistoryAdapter *existing = NULL; const gchar *id = NULL; gboolean ret = FALSE; guint position = 0; g_return_val_if_fail(PURPLE_IS_HISTORY_MANAGER(manager), FALSE); g_return_val_if_fail(PURPLE_IS_HISTORY_ADAPTER(adapter), FALSE); if(adapter == manager->active_adapter) { g_set_error(error, PURPLE_HISTORY_MANAGER_DOMAIN, 0, _("adapter %s is currently in use"), id); return FALSE; } g_object_ref(adapter); id = purple_history_adapter_get_id(adapter); existing = purple_history_manager_find_with_id(manager, id, &position); if(PURPLE_IS_HISTORY_ADAPTER(existing)) { g_ptr_array_remove_index(manager->adapters, position); g_signal_emit(G_OBJECT(manager), signals[SIG_REMOVED], 0, adapter); g_list_model_items_changed(G_LIST_MODEL(manager), position, 1, 0); g_object_notify_by_pspec(G_OBJECT(manager), properties[PROP_N_ITEMS]); ret = TRUE; } else { g_set_error(error, PURPLE_HISTORY_MANAGER_DOMAIN, 0, _("adapter %s has not been added"), id); ret = FALSE; } g_object_unref(adapter); return ret; } PurpleHistoryAdapter * purple_history_manager_get_active(PurpleHistoryManager *manager) { g_return_val_if_fail(PURPLE_IS_HISTORY_MANAGER(manager), NULL); return manager->active_adapter; } gboolean purple_history_manager_set_active(PurpleHistoryManager *manager, const gchar *id, GError **error) { PurpleHistoryAdapter *old = NULL, *adapter = NULL; g_return_val_if_fail(PURPLE_IS_HISTORY_MANAGER(manager), FALSE); /* First look up the new adapter if we're given one. */ if(id != NULL) { adapter = purple_history_manager_find_with_id(manager, id, NULL); if(!PURPLE_IS_HISTORY_ADAPTER(adapter)) { g_set_error(error, PURPLE_HISTORY_MANAGER_DOMAIN, 0, "no history adapter found with id %s", id); return FALSE; } } if(PURPLE_IS_HISTORY_ADAPTER(manager->active_adapter)) { old = g_object_ref(manager->active_adapter); } if(g_set_object(&manager->active_adapter, adapter)) { if(PURPLE_IS_HISTORY_ADAPTER(old)) { if(!purple_history_adapter_deactivate(old, error)) { g_set_object(&manager->active_adapter, old); g_clear_object(&old); return FALSE; } } if(PURPLE_IS_HISTORY_ADAPTER(adapter)) { if(!purple_history_adapter_activate(adapter, error)) { if(PURPLE_IS_HISTORY_ADAPTER(old)) { purple_history_adapter_activate(old, error); } g_set_object(&manager->active_adapter, old); g_clear_object(&old); return FALSE; } } g_signal_emit(G_OBJECT(manager), signals[SIG_ACTIVE_CHANGED], 0, old, manager->active_adapter); } g_clear_object(&old); purple_debug_info("history-manager", "set active adapter to '%s'", id); return TRUE; } GList * purple_history_manager_query(PurpleHistoryManager *manager, const gchar *query, GError **error) { g_return_val_if_fail(PURPLE_IS_HISTORY_MANAGER(manager), FALSE); if(manager->active_adapter == NULL) { g_set_error_literal(error, PURPLE_HISTORY_MANAGER_DOMAIN, 0, _("no active history adapter")); return FALSE; } return purple_history_adapter_query(manager->active_adapter, query, error); } gboolean purple_history_manager_remove_query(PurpleHistoryManager *manager, const gchar *query, GError **error) { g_return_val_if_fail(PURPLE_IS_HISTORY_MANAGER(manager), FALSE); if(manager->active_adapter == NULL) { g_set_error_literal(error, PURPLE_HISTORY_MANAGER_DOMAIN, 0, _("no active history adapter")); return FALSE; } return purple_history_adapter_remove(manager->active_adapter, query, error); } gboolean purple_history_manager_write(PurpleHistoryManager *manager, PurpleConversation *conversation, PurpleMessage *message, GError **error) { g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE); g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE); g_return_val_if_fail(PURPLE_IS_HISTORY_MANAGER(manager), FALSE); if(manager->active_adapter == NULL) { g_set_error_literal(error, PURPLE_HISTORY_MANAGER_DOMAIN, 0, _("no active history adapter")); return FALSE; } return purple_history_adapter_write(manager->active_adapter, conversation, message, error); } gboolean purple_history_manager_startup(PurpleHistoryAdapter *adapter, GError **error) { const char *id = NULL; if(default_manager == NULL) { default_manager = g_object_new(PURPLE_TYPE_HISTORY_MANAGER, NULL); g_object_add_weak_pointer(G_OBJECT(default_manager), (gpointer)&default_manager); } if(!PURPLE_IS_HISTORY_ADAPTER(adapter)) { adapter = purple_sqlite_history_adapter_new(":memory:"); } if(!purple_history_manager_add(default_manager, adapter, error)) { g_clear_object(&adapter); return FALSE; } id = purple_history_adapter_get_id(adapter); if(!purple_history_manager_set_active(default_manager, id, error)) { g_clear_object(&adapter); return FALSE; } g_clear_object(&adapter); return TRUE; } void purple_history_manager_shutdown(void) { GError **error = NULL; if(default_manager == NULL) { return; } if(PURPLE_IS_HISTORY_ADAPTER(default_manager->active_adapter)) { PurpleHistoryAdapter *adapter = NULL; adapter = g_object_ref(default_manager->active_adapter); purple_history_manager_set_active(default_manager, NULL, NULL); purple_history_manager_remove(default_manager, adapter, error); g_clear_object(&adapter); } g_clear_object(&default_manager); }