Mon, 22 Aug 2022 03:20:05 -0500
Add purple_notification_manager_remove_with_account.
This is used when an account is disabled or removed to purge its notifications
from the manager.
Testing Done:
Ran the unit tests and forced them to fail with bad values to verify they were correct.
Reviewed at https://reviews.imfreedom.org/r/1612/
--- a/libpurple/purplenotificationmanager.c Fri Aug 19 00:27:38 2022 -0500 +++ b/libpurple/purplenotificationmanager.c Mon Aug 22 03:20:05 2022 -0500 @@ -357,6 +357,94 @@ } } +/* +This function uses the following algorithm to optimally remove items from the +GListStore. See the psuedo code below for an easier to follow version. + +A +A B C +B C +A A B C +B A C +B A A C +B C A +B C A A + +set len = number_of_items +set pos = 0 +set have_same = false +while pos < len + check item at pos + if same + if not have_same + reset count = 0 + set start = pos + set have_same = TRUE + + set count = count + 1 + else + if have_same + remove count items from start + set pos = pos - count + set len = len - count + set have_same = FALSE + set pos = pos + 1 +if have_same + remove count items from start +*/ +void +purple_notification_manager_remove_with_account(PurpleNotificationManager *manager, + PurpleAccount *account) +{ + guint pos = 0, len = 0; + guint start = 0, count = 0; + gboolean have_same = FALSE; + + 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++) { + PurpleAccount *account2 = NULL; + PurpleNotification *notification = NULL; + + notification = g_list_model_get_item(G_LIST_MODEL(manager->notifications), + pos); + + account2 = purple_notification_get_account(notification); + if(account == account2) { + /* If this is the first item with the right account store its position. */ + if(!have_same) { + count = 0; + start = pos; + have_same = TRUE; + } + + /* Increment the count of items starting at the start position. */ + count++; + } else { + if(have_same) { + /* Remove the run of items from the list. */ + g_list_store_splice(manager->notifications, start, count, NULL, + 0); + + /* Adjust pos and len for the items that we removed. */ + pos = pos - count; + len = len - count; + + have_same = FALSE; + } + } + + g_clear_object(¬ification); + } + + /* Clean up the last bit if the last item needs to be removed. */ + if(have_same) { + g_list_store_splice(manager->notifications, start, count, NULL, 0); + } +} + guint purple_notification_manager_get_unread_count(PurpleNotificationManager *manager) { g_return_val_if_fail(PURPLE_IS_NOTIFICATION_MANAGER(manager), 0);
--- a/libpurple/purplenotificationmanager.h Fri Aug 19 00:27:38 2022 -0500 +++ b/libpurple/purplenotificationmanager.h Mon Aug 22 03:20:05 2022 -0500 @@ -78,6 +78,17 @@ void purple_notification_manager_remove(PurpleNotificationManager *manager, PurpleNotification *notification); /** + * purple_notification_manager_remove_with_account: + * @manager: The instance. + * @account: The [class@Account] whose notifications to remove. + * + * Removes all notifications with @account from @manager. + * + * Since: 3.0.0 + */ +void purple_notification_manager_remove_with_account(PurpleNotificationManager *manager, PurpleAccount *account); + +/** * purple_notification_manager_get_unread_count: * @manager: The instance. *
--- a/libpurple/tests/test_notification_manager.c Fri Aug 19 00:27:38 2022 -0500 +++ b/libpurple/tests/test_notification_manager.c Mon Aug 22 03:20:05 2022 -0500 @@ -162,6 +162,78 @@ } static void +test_purple_notification_manager_remove_with_account_simple(void) { + PurpleNotificationManager *manager = NULL; + PurpleNotification *notification = NULL; + PurpleAccount *account = NULL; + GListModel *model = NULL; + + manager = g_object_new(PURPLE_TYPE_NOTIFICATION_MANAGER, NULL); + model = purple_notification_manager_get_model(manager); + account = purple_account_new("test", "test"); + + /* Make sure that nothing happens on an empty list. */ + purple_notification_manager_remove_with_account(manager, account); + g_assert_cmpuint(0, ==, g_list_model_get_n_items(model)); + + /* Add a single notification without the account */ + notification = purple_notification_new(PURPLE_NOTIFICATION_TYPE_GENERIC, + NULL, NULL, NULL); + purple_notification_manager_add(manager, notification); + purple_notification_manager_remove_with_account(manager, account); + g_assert_cmpuint(1, ==, g_list_model_get_n_items(model)); + g_list_store_remove_all(G_LIST_STORE(model)); + g_clear_object(¬ification); + + /* Add a single notification with the account */ + notification = purple_notification_new(PURPLE_NOTIFICATION_TYPE_GENERIC, + account, NULL, NULL); + purple_notification_manager_add(manager, notification); + purple_notification_manager_remove_with_account(manager, account); + g_assert_cmpuint(0, ==, g_list_model_get_n_items(model)); + g_list_store_remove_all(G_LIST_STORE(model)); + g_clear_object(¬ification); + + g_clear_object(&manager); + g_clear_object(&account); +} + +static void +test_purple_notification_manager_remove_with_account_mixed(void) { + PurpleNotificationManager *manager = NULL; + PurpleNotification *notification = NULL; + PurpleAccount *accounts[3]; + GListModel *model = NULL; + gint pattern[] = {0, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0, -1}; + gint i = 0; + + manager = g_object_new(PURPLE_TYPE_NOTIFICATION_MANAGER, NULL); + model = purple_notification_manager_get_model(manager); + + accounts[0] = purple_account_new("account1", "account1"); + accounts[1] = purple_account_new("account2", "account2"); + accounts[2] = purple_account_new("account3", "account3"); + + /* Add our notifications. */ + for(i = 0; pattern[i] >= 0; i++) { + notification = purple_notification_new(PURPLE_NOTIFICATION_TYPE_GENERIC, + accounts[pattern[i]], NULL, + NULL); + purple_notification_manager_add(manager, notification); + g_clear_object(¬ification); + } + + g_assert_cmpuint(14, ==, g_list_model_get_n_items(model)); + purple_notification_manager_remove_with_account(manager, accounts[0]); + g_assert_cmpuint(6, ==, g_list_model_get_n_items(model)); + + g_clear_object(&manager); + g_clear_object(&accounts[0]); + g_clear_object(&accounts[1]); + g_clear_object(&accounts[2]); +} + +static void test_purple_notification_manager_read_propagation(void) { PurpleNotificationManager *manager = NULL; PurpleNotification *notification = NULL; @@ -243,6 +315,11 @@ g_test_add_func("/notification-manager/double-remove", test_purple_notification_manager_double_remove); + g_test_add_func("/notification-manager/remove-with-account/simple", + test_purple_notification_manager_remove_with_account_simple); + g_test_add_func("/notification-manager/remove-with-account/mixed", + test_purple_notification_manager_remove_with_account_mixed); + g_test_add_func("/notification-manager/read-propagation", test_purple_notification_manager_read_propagation);