--- a/finch/finchnotifications.c Wed Nov 23 23:03:30 2022 -0600
+++ b/finch/finchnotifications.c Thu Nov 24 00:23:08 2022 -0600
@@ -330,7 +330,6 @@
******************************************************************************/
finch_notifications_window_show(void) {
- PurpleNotificationManager *manager = NULL;
GListModel *model = NULL;
@@ -358,8 +357,7 @@
G_CALLBACK(finch_notifications_activate_cb), NULL);
/* Get the notification manager to get the model and populate the list. */
- manager = purple_notification_manager_get_default();
- model = purple_notification_manager_get_model(manager);
+ model = purple_notification_manager_get_default_as_model(); finch_notifications_update(GNT_TREE(notifications.list), model);
g_signal_connect_object(model, "items-changed",
G_CALLBACK(finch_notifications_changed_cb),
--- a/libpurple/purplenotificationmanager.c Wed Nov 23 23:03:30 2022 -0600
+++ b/libpurple/purplenotificationmanager.c Thu Nov 24 00:23:08 2022 -0600
@@ -41,14 +41,11 @@
struct _PurpleNotificationManager {
- GListStore *notifications;
+ GPtrArray *notifications; -G_DEFINE_TYPE(PurpleNotificationManager, purple_notification_manager,
static PurpleNotificationManager *default_manager = NULL;
/******************************************************************************
@@ -114,8 +111,48 @@
/******************************************************************************
+ * GListModel Implementation + *****************************************************************************/ +purple_notification_manager_get_item_type(G_GNUC_UNUSED GListModel *list) { + return PURPLE_TYPE_NOTIFICATION; +purple_notification_manager_get_n_items(GListModel *list) { + PurpleNotificationManager *manager = PURPLE_NOTIFICATION_MANAGER(list); + return manager->notifications->len; +purple_notification_manager_get_item(GListModel *list, guint position) { + PurpleNotificationManager *manager = PURPLE_NOTIFICATION_MANAGER(list); + PurpleNotification *notification = NULL; + if(position < manager->notifications->len) { + notification = g_ptr_array_index(manager->notifications, position); + g_object_ref(notification); +purple_notification_manager_list_model_init(GListModelInterface *iface) { + iface->get_item_type = purple_notification_manager_get_item_type; + iface->get_n_items = purple_notification_manager_get_n_items; + iface->get_item = purple_notification_manager_get_item; +/****************************************************************************** *****************************************************************************/
+G_DEFINE_TYPE_EXTENDED(PurpleNotificationManager, purple_notification_manager, + G_TYPE_OBJECT, G_TYPE_FLAG_FINAL, + G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL, purple_notification_manager_list_model_init)); purple_notification_manager_get_property(GObject *obj, guint param_id,
GValue *value, GParamSpec *pspec)
@@ -139,14 +176,16 @@
manager = PURPLE_NOTIFICATION_MANAGER(obj);
- g_clear_object(&manager->notifications);
+ g_ptr_array_free(manager->notifications, TRUE); + manager->notifications = NULL; G_OBJECT_CLASS(purple_notification_manager_parent_class)->finalize(obj);
purple_notification_manager_init(PurpleNotificationManager *manager) {
- manager->notifications = g_list_store_new(PURPLE_TYPE_NOTIFICATION);
+ manager->notifications = g_ptr_array_new_full(0, + (GDestroyNotify)g_object_unref); @@ -267,6 +306,8 @@
purple_notification_manager_startup(void) {
if(default_manager == NULL) {
default_manager = g_object_new(PURPLE_TYPE_NOTIFICATION_MANAGER, NULL);
+ g_object_add_weak_pointer(G_OBJECT(default_manager), + (gpointer *)&default_manager); @@ -283,6 +324,15 @@
+purple_notification_manager_get_default_as_model(void) { + if(PURPLE_IS_NOTIFICATION_MANAGER(default_manager)) { + return G_LIST_MODEL(default_manager); purple_notification_manager_add(PurpleNotificationManager *manager,
PurpleNotification *notification)
@@ -290,7 +340,7 @@
g_return_if_fail(PURPLE_IS_NOTIFICATION_MANAGER(manager));
g_return_if_fail(PURPLE_IS_NOTIFICATION(notification));
- if(g_list_store_find(manager->notifications, notification, NULL)) {
+ if(g_ptr_array_find(manager->notifications, notification, NULL)) { const gchar *id = purple_notification_get_id(notification);
g_warning("double add detected for notification %s", id);
@@ -298,9 +348,7 @@
- g_list_store_insert_sorted(manager->notifications, notification,
- (GCompareDataFunc)purple_notification_compare,
+ g_ptr_array_insert(manager->notifications, 0, g_object_ref(notification)); /* Connect to the notify signal for the read property only so we can
* propagate out changes for any notification.
@@ -316,23 +364,25 @@
g_signal_emit(G_OBJECT(manager), signals[SIG_ADDED], 0, notification);
+ g_list_model_items_changed(G_LIST_MODEL(manager), 0, 0, 1); purple_notification_manager_remove(PurpleNotificationManager *manager,
PurpleNotification *notification)
g_return_if_fail(PURPLE_IS_NOTIFICATION_MANAGER(manager));
g_return_if_fail(PURPLE_IS_NOTIFICATION(notification));
- if(g_list_store_find(manager->notifications, notification, &position)) {
+ if(g_ptr_array_find(manager->notifications, notification, &index)) { /* Reference the notification so we can emit the signal after it's been
* removed from the hash table.
g_object_ref(notification);
+ g_ptr_array_remove_index(manager->notifications, index); /* Remove the notify signal handler for the read state incase someone
* else added a reference to the notification which would then mess
* with our unread count accounting.
@@ -348,18 +398,17 @@
purple_notification_manager_decrement_unread_count(manager);
- g_list_store_remove(manager->notifications, position);
g_signal_emit(G_OBJECT(manager), signals[SIG_REMOVED], 0, notification);
+ g_list_model_items_changed(G_LIST_MODEL(manager), index, 1, 0); g_object_unref(notification);
This function uses the following algorithm to optimally remove items from the
-GListStore. See the psuedo code below for an easier to follow version.
+g_ptr_array to minimize the number of calls to g_list_model_items_changed. See +the pseudo code below for an easier to follow version. @@ -397,6 +446,7 @@
+ GListModel *model = NULL; guint start = 0, count = 0;
gboolean have_same = FALSE;
@@ -404,15 +454,15 @@
g_return_if_fail(PURPLE_IS_NOTIFICATION_MANAGER(manager));
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
- len = g_list_model_get_n_items(G_LIST_MODEL(manager->notifications));
- for(pos = 0; pos < len; pos++) {
+ model = G_LIST_MODEL(manager); + for(pos = 0; pos < manager->notifications->len; pos++) { PurpleAccount *account2 = NULL;
PurpleNotification *notification = NULL;
PurpleNotificationType type;
gboolean can_remove = TRUE;
- notification = g_list_model_get_item(G_LIST_MODEL(manager->notifications),
+ notification = g_ptr_array_index(manager->notifications, pos); /* If the notification's type is connection error, set can_remove to
* the value of the all parameter.
@@ -438,8 +488,8 @@
/* Remove the run of items from the list. */
- g_list_store_splice(manager->notifications, start, count, NULL,
+ g_ptr_array_remove_range(manager->notifications, start, count); + g_list_model_items_changed(model, start, count, 0); /* Adjust pos and len for the items that we removed. */
@@ -448,13 +498,12 @@
- g_clear_object(¬ification);
/* Clean up the last bit if the last item needs to be removed. */
- g_list_store_splice(manager->notifications, start, count, NULL, 0);
+ g_ptr_array_remove_range(manager->notifications, start, count); + g_list_model_items_changed(model, start, count, 0); @@ -465,13 +514,23 @@
return manager->unread_count;
-purple_notification_manager_get_model(PurpleNotificationManager *manager) {
- g_return_val_if_fail(PURPLE_IS_NOTIFICATION_MANAGER(manager), NULL);
+purple_notification_manager_clear(PurpleNotificationManager *manager) { + g_return_if_fail(PURPLE_IS_NOTIFICATION_MANAGER(manager)); - if(manager->notifications == NULL) {
+ count = manager->notifications->len; + for(guint pos = 0; pos < count; pos++) { + PurpleNotification *notification = NULL; + notification = g_ptr_array_index(manager->notifications, pos); + g_signal_emit(manager, signals[SIG_REMOVED], 0, notification); - return G_LIST_MODEL(g_object_ref(manager->notifications));
+ g_ptr_array_remove_range(manager->notifications, 0, count); + g_list_model_items_changed(G_LIST_MODEL(manager), 0, count, 0); --- a/libpurple/purplenotificationmanager.h Wed Nov 23 23:03:30 2022 -0600
+++ b/libpurple/purplenotificationmanager.h Thu Nov 24 00:23:08 2022 -0600
@@ -56,6 +56,17 @@
PurpleNotificationManager *purple_notification_manager_get_default(void);
+ * purple_notification_manager_get_default_as_model: + * Gets the default manager instance type casted to [iface@Gio.ListModel]. + * Returns: (transfer none): The model. +GListModel *purple_notification_manager_get_default_as_model(void); * purple_notification_manager_add:
* @manager: The instance.
* @notification: (transfer full): The [class@Notification] to add.
@@ -107,17 +118,14 @@
guint purple_notification_manager_get_unread_count(PurpleNotificationManager *manager);
- * purple_notification_manager_get_model:
+ * purple_notification_manager_clear: * @manager: The instance.
- * Gets a [iface@Gio.ListModel] of all the [class@Notification]'s in
- * Returns: (transfer full): The model.
+ * Removes all notifications from @manager. -GListModel *purple_notification_manager_get_model(PurpleNotificationManager *manager);
+void purple_notification_manager_clear(PurpleNotificationManager *manager); --- a/libpurple/tests/test_notification_manager.c Wed Nov 23 23:03:30 2022 -0600
+++ b/libpurple/tests/test_notification_manager.c Thu Nov 24 00:23:08 2022 -0600
@@ -169,7 +169,7 @@
GListModel *model = NULL;
manager = g_object_new(PURPLE_TYPE_NOTIFICATION_MANAGER, NULL);
- model = purple_notification_manager_get_model(manager);
+ model = G_LIST_MODEL(manager); account = purple_account_new("test", "test");
/* Make sure that nothing happens on an empty list. */
@@ -182,7 +182,7 @@
purple_notification_manager_add(manager, notification);
purple_notification_manager_remove_with_account(manager, account, TRUE);
g_assert_cmpuint(1, ==, g_list_model_get_n_items(model));
- g_list_store_remove_all(G_LIST_STORE(model));
+ purple_notification_manager_clear(manager); g_clear_object(¬ification);
/* Add a single notification with the account */
@@ -191,7 +191,7 @@
purple_notification_manager_add(manager, notification);
purple_notification_manager_remove_with_account(manager, account, TRUE);
g_assert_cmpuint(0, ==, g_list_model_get_n_items(model));
- g_list_store_remove_all(G_LIST_STORE(model));
+ purple_notification_manager_clear(manager); g_clear_object(¬ification);
g_clear_object(&manager);
@@ -208,7 +208,7 @@
manager = g_object_new(PURPLE_TYPE_NOTIFICATION_MANAGER, NULL);
- model = purple_notification_manager_get_model(manager);
+ model = G_LIST_MODEL(manager); accounts[0] = purple_account_new("account1", "account1");
accounts[1] = purple_account_new("account2", "account2");
@@ -255,20 +255,24 @@
* and generic. We will also add a generic notification with no account, to
* make sure notifications don't accidentally get removed.
+ * Since the notification manager prepends items and leaves sorting up to + * the user interface, the order of the notifications will be the opposite + * of the order they were added. * This ordering is specifically done because we batch remove items from
* the list. So by calling purple_notification_manager_remove_with_account
- * with `all` set to FALSE, we should remove the first and third items,
- * but leave the second and fourth items.
+ * with `all` set to FALSE, we should remove the second and fourth items, + * but leave the first and third items. * We then call purple_notification_manager_remove_with_account with `all`
* set to TRUE, which will remove the connection error notification which
- * is now the first item.
+ * is now the second item. * Finally, we empty the manager and verify that its count is at 0.
manager = g_object_new(PURPLE_TYPE_NOTIFICATION_MANAGER, NULL);
- model = purple_notification_manager_get_model(manager);
+ model = G_LIST_MODEL(manager); account = purple_account_new("test", "test");
/* Make sure that nothing happens on an empty list. */
@@ -299,7 +303,6 @@
purple_notification_manager_add(manager, notification);
g_clear_object(¬ification);
/* Verify that we have all of the notifications in the manager. */
g_assert_cmpuint(4, ==, g_list_model_get_n_items(model));
@@ -307,8 +310,8 @@
purple_notification_manager_remove_with_account(manager, account, FALSE);
g_assert_cmpuint(2, ==, g_list_model_get_n_items(model));
- /* Make sure that the first item is the connection error. */
- notification = g_list_model_get_item(G_LIST_MODEL(model), 0);
+ /* Make sure that the second item is the connection error. */ + notification = g_list_model_get_item(G_LIST_MODEL(model), 1); g_assert_cmpint(purple_notification_get_notification_type(notification),
==, PURPLE_NOTIFICATION_TYPE_CONNECTION_ERROR);
g_clear_object(¬ification);
@@ -318,7 +321,7 @@
g_assert_cmpuint(1, ==, g_list_model_get_n_items(model));
/* Remove the generic notification that's not tied to an account. */
- g_list_store_remove_all(G_LIST_STORE(model));
+ purple_notification_manager_clear(manager); g_assert_cmpuint(0, ==, g_list_model_get_n_items(model));
g_clear_object(&manager);