# HG changeset patch
# Node ID eb1c4a46c6a1
# Parent  dddaf5b1a795
diff --git a/libpurple/purplenotificationmanager.c b/libpurple/purplenotificationmanager.c
--- a/libpurple/purplenotificationmanager.c
+++ b/libpurple/purplenotificationmanager.c
@@ -357,10 +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 it's
+			 * 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(&notification);
+	}
+
+	/* 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
diff --git a/libpurple/tests/test_notification_manager.c b/libpurple/tests/test_notification_manager.c
--- a/libpurple/tests/test_notification_manager.c
+++ b/libpurple/tests/test_notification_manager.c
@@ -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(&notification);
+
+	/* 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(&notification);
+
+	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[4];
+	GListModel *model = NULL;
+	gint pattern[] = {1, 1, 2, 1, 3, 2, 1, 1, 2, 3, 1, 2, 1, 1, 0};
+	gint i = 0;
+
+	manager = g_object_new(PURPLE_TYPE_NOTIFICATION_MANAGER, NULL);
+	model = purple_notification_manager_get_model(manager);
+
+	accounts[1] = purple_account_new("account1", "account1");
+	accounts[2] = purple_account_new("account2", "account2");
+	accounts[3] = 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(&notification);
+	}
+
+	g_assert_cmpuint(14, ==, g_list_model_get_n_items(model));
+	purple_notification_manager_remove_with_account(manager, accounts[1]);
+	g_assert_cmpuint(6, ==, g_list_model_get_n_items(model));
+
+	g_clear_object(&manager);
+	g_clear_object(&accounts[1]);
+	g_clear_object(&accounts[2]);
+	g_clear_object(&accounts[3]);
+}
+
+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);
 
