pidgin/pidgin

c4a96b5eecba
Parents 14beb80abb20
Children f34ff5a47b2e
Make PurpleNotificationManager implement GListModel

Also a few other clean ups, including making the user of the manager responsible
for sorting the list.

Testing Done:
Ran the unit tests and verified the notification list in pidgin was updating correctly.

Reviewed at https://reviews.imfreedom.org/r/2085/
--- 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 @@
******************************************************************************/
void
finch_notifications_window_show(void) {
- PurpleNotificationManager *manager = NULL;
GntWidget *wid, *box;
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 {
GObject parent;
- GListStore *notifications;
+ GPtrArray *notifications;
guint unread_count;
};
-G_DEFINE_TYPE(PurpleNotificationManager, purple_notification_manager,
- G_TYPE_OBJECT);
-
static PurpleNotificationManager *default_manager = NULL;
/******************************************************************************
@@ -114,8 +111,48 @@
}
/******************************************************************************
+ * GListModel Implementation
+ *****************************************************************************/
+static GType
+purple_notification_manager_get_item_type(G_GNUC_UNUSED GListModel *list) {
+ return PURPLE_TYPE_NOTIFICATION;
+}
+
+static guint
+purple_notification_manager_get_n_items(GListModel *list) {
+ PurpleNotificationManager *manager = PURPLE_NOTIFICATION_MANAGER(list);
+
+ return manager->notifications->len;
+}
+
+static gpointer
+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);
+ }
+
+ return notification;
+}
+
+static void
+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;
+}
+
+/******************************************************************************
* GObject Implementation
*****************************************************************************/
+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));
+
+
static void
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);
}
static void
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);
}
static void
@@ -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 @@
return default_manager;
}
+GListModel *
+purple_notification_manager_get_default_as_model(void) {
+ if(PURPLE_IS_NOTIFICATION_MANAGER(default_manager)) {
+ return G_LIST_MODEL(default_manager);
+ }
+
+ return NULL;
+}
+
void
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 @@
return;
}
- g_list_store_insert_sorted(manager->notifications, notification,
- (GCompareDataFunc)purple_notification_compare,
- NULL);
+ 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);
}
void
purple_notification_manager_remove(PurpleNotificationManager *manager,
PurpleNotification *notification)
{
- guint position;
+ guint index = 0;
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.
A
A B C
@@ -397,6 +446,7 @@
PurpleAccount *account,
gboolean all)
{
+ GListModel *model = NULL;
guint pos = 0, len = 0;
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),
- pos);
+ 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 @@
} else {
if(have_same) {
/* Remove the run of items from the list. */
- 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);
/* Adjust pos and len for the items that we removed. */
pos = pos - count;
@@ -448,13 +498,12 @@
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);
+ 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;
}
-GListModel *
-purple_notification_manager_get_model(PurpleNotificationManager *manager) {
- g_return_val_if_fail(PURPLE_IS_NOTIFICATION_MANAGER(manager), NULL);
+void
+purple_notification_manager_clear(PurpleNotificationManager *manager) {
+ guint count = 0;
+
+ g_return_if_fail(PURPLE_IS_NOTIFICATION_MANAGER(manager));
- if(manager->notifications == NULL) {
- return 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.
+ *
+ * Since: 3.0.0
+ */
+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
- * @manager.
- *
- * Returns: (transfer full): The model.
+ * Removes all notifications from @manager.
*
* Since: 3.0.0
*/
-GListModel *purple_notification_manager_get_model(PurpleNotificationManager *manager);
+void purple_notification_manager_clear(PurpleNotificationManager *manager);
G_END_DECLS
--- 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(&notification);
/* 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(&notification);
g_clear_object(&manager);
@@ -208,7 +208,7 @@
gint i = 0;
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(&notification);
-
/* 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(&notification);
@@ -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);
--- a/pidgin/pidginnotificationlist.c Wed Nov 23 23:03:30 2022 -0600
+++ b/pidgin/pidginnotificationlist.c Thu Nov 24 00:23:08 2022 -0600
@@ -96,15 +96,10 @@
*****************************************************************************/
static void
pidgin_notification_list_init(PidginNotificationList *list) {
- PurpleNotificationManager *manager = NULL;
- GListModel *model = NULL;
-
gtk_widget_init_template(GTK_WIDGET(list));
- manager = purple_notification_manager_get_default();
- model = purple_notification_manager_get_model(manager);
-
- gtk_list_box_bind_model(GTK_LIST_BOX(list->list_box), model,
+ gtk_list_box_bind_model(GTK_LIST_BOX(list->list_box),
+ purple_notification_manager_get_default_as_model(),
pidgin_notification_list_create_widget_func,
list, NULL);
}