core/timeline: Add plumbing for models

Add a GtkFlattenListModel that flattens out all events from the
calendar monitors. Keep a GtkFilterListModel, and a GtkSortListModel,
for each subscriber. Manually update the filter when the range of
the subscriber changes.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/742>
This commit is contained in:
Georges Basile Stavracas Neto
2026-05-03 16:57:50 +00:00
committed by Marge Bot
parent 5f5e438955
commit 71c5d0b70d
+125 -7
View File
@@ -73,6 +73,7 @@ struct _GcalTimeline
gboolean complete;
GListStore *calendar_monitors;
GListModel *events_model;
GHashTable *subscribers; /* GcalTimelineSubscriber* -> SubscriberData* */
GcalRangeTree *subscriber_ranges;
@@ -97,6 +98,73 @@ enum
static GParamSpec *properties [N_PROPS] = { NULL, };
/*
* SubscriberData
*/
typedef struct
{
GcalRange *range;
GtkFilterListModel *events;
GtkSortListModel *sorted_events;
} SubscriberData;
static void
subscriber_data_free (SubscriberData *data)
{
gtk_filter_list_model_set_model (data->events, NULL);
g_clear_pointer (&data->range, gcal_range_unref);
g_clear_object (&data->sorted_events);
g_clear_object (&data->events);
g_clear_pointer (&data, g_free);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SubscriberData, subscriber_data_free)
static gboolean
event_in_subscriber_range_func (gpointer item,
gpointer user_data)
{
SubscriberData *data = user_data;
g_assert (GCAL_IS_EVENT (item));
if (!data->range)
return FALSE;
return gcal_event_overlaps (item, data->range);
}
static gint
compare_events_cb (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
GcalEvent *event_a = (GcalEvent *) a;
GcalEvent *event_b = (GcalEvent *) b;
return gcal_range_compare (gcal_event_get_range (event_b), gcal_event_get_range (event_a));
}
static SubscriberData *
subscriber_data_new (GcalTimeline *self,
GcalTimelineSubscriber *subscriber)
{
g_autoptr (GtkCustomFilter) filter = NULL;
g_autoptr (SubscriberData) data = NULL;
data = g_new0 (SubscriberData, 1);
data->range = gcal_timeline_subscriber_get_range (subscriber);
filter = gtk_custom_filter_new (event_in_subscriber_range_func, data, NULL);
data->events = gtk_filter_list_model_new (g_object_ref (self->events_model), GTK_FILTER (g_steal_pointer (&filter)));
data->sorted_events = gtk_sort_list_model_new (G_LIST_MODEL (g_object_ref (data->events)),
GTK_SORTER (gtk_custom_sorter_new (compare_events_cb, NULL, NULL)));
return g_steal_pointer (&data);
}
/*
* Auxiliary methods
*/
@@ -266,6 +334,21 @@ remove_event_from_subscriber (GcalTimelineSubscriber *subscriber,
GCAL_TIMELINE_SUBSCRIBER_GET_IFACE (subscriber)->remove_event (subscriber, event);
}
static void
set_model_on_subscriber (GcalTimelineSubscriber *subscriber,
GListModel *model)
{
/* Optional while we transition away from add/update/remove_event */
if (!GCAL_TIMELINE_SUBSCRIBER_GET_IFACE (subscriber)->set_model)
return;
GCAL_TRACE_MSG ("Setting event model %p on subscriber %s",
model,
G_OBJECT_TYPE_NAME (subscriber));
GCAL_TIMELINE_SUBSCRIBER_GET_IFACE (subscriber)->set_model (subscriber, model);
}
static void
update_range (GcalTimeline *self)
{
@@ -485,10 +568,14 @@ update_subscriber_range (GcalTimeline *self,
{
g_autoptr (GcalRange) old_range = NULL;
g_autoptr (GcalRange) new_range = NULL;
SubscriberData *subscriber_data;
GtkFilter *filter;
GCAL_ENTRY;
old_range = g_hash_table_lookup (self->subscribers, subscriber);
subscriber_data = g_hash_table_lookup (self->subscribers, subscriber);
old_range = g_steal_pointer (&subscriber_data->range);
g_assert (old_range != NULL);
new_range = gcal_timeline_subscriber_get_range (subscriber);
@@ -500,8 +587,36 @@ update_subscriber_range (GcalTimeline *self,
gcal_range_tree_remove_data (self->subscriber_ranges, subscriber);
gcal_range_tree_add_range (self->subscriber_ranges, new_range, subscriber);
g_hash_table_insert (self->subscribers, g_object_ref (subscriber), g_steal_pointer (&new_range));
g_steal_pointer (&old_range);
subscriber_data->range = gcal_range_ref (new_range);
g_assert (old_range != NULL);
g_assert (new_range != NULL);
filter = gtk_filter_list_model_get_filter (subscriber_data->events);
g_assert (GTK_IS_FILTER (filter));
switch (gcal_range_calculate_overlap (new_range, old_range, NULL))
{
case GCAL_RANGE_NO_OVERLAP:
case GCAL_RANGE_INTERSECTS:
gtk_filter_changed (filter, GTK_FILTER_CHANGE_DIFFERENT);
break;
case GCAL_RANGE_SUBSET:
gtk_filter_changed (filter, GTK_FILTER_CHANGE_MORE_STRICT);
break;
case GCAL_RANGE_EQUAL:
/* No need to refilter */
break;
case GCAL_RANGE_SUPERSET:
gtk_filter_changed (filter, GTK_FILTER_CHANGE_LESS_STRICT);
break;
default:
g_assert_not_reached ();
}
GCAL_EXIT;
}
@@ -942,12 +1057,13 @@ gcal_timeline_init (GcalTimeline *self)
self->cancellable = g_cancellable_new ();
self->events = gcal_range_tree_new_with_free_func (g_object_unref);
self->calendars = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
self->subscribers = g_hash_table_new_full (NULL, NULL, g_object_unref, (GDestroyNotify) gcal_range_unref);
self->subscribers = g_hash_table_new_full (NULL, NULL, g_object_unref, (GDestroyNotify) subscriber_data_free);
self->subscriber_ranges = gcal_range_tree_new ();
self->event_queue = g_queue_new ();
self->queued_adds = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
self->calendar_monitors = g_list_store_new (GCAL_TYPE_CALENDAR_MONITOR);
self->events_model = G_LIST_MODEL (gtk_flatten_list_model_new (g_object_ref (G_LIST_MODEL (self->calendar_monitors))));
/* Timeline source */
timeline_source = (TimelineSource*) g_source_new (&timeline_source_funcs, sizeof (TimelineSource));
@@ -1045,7 +1161,7 @@ void
gcal_timeline_add_subscriber (GcalTimeline *self,
GcalTimelineSubscriber *subscriber)
{
g_autoptr (GcalRange) subscriber_range = NULL;
g_autoptr (SubscriberData) subscriber_data = NULL;
g_return_if_fail (GCAL_IS_TIMELINE (self));
g_return_if_fail (GCAL_IS_TIMELINE_SUBSCRIBER (subscriber));
@@ -1057,9 +1173,11 @@ gcal_timeline_add_subscriber (GcalTimeline *self,
g_debug ("Adding subscriber %s to timeline %p", G_OBJECT_TYPE_NAME (subscriber), self);
subscriber_range = gcal_timeline_subscriber_get_range (subscriber);
subscriber_data = subscriber_data_new (self, subscriber);
g_hash_table_insert (self->subscribers, g_object_ref (subscriber), gcal_range_ref (subscriber_range));
set_model_on_subscriber (subscriber, G_LIST_MODEL (subscriber_data->sorted_events));
g_hash_table_insert (self->subscribers, g_object_ref (subscriber), g_steal_pointer (&subscriber_data));
g_signal_connect_object (subscriber,
"range-changed",
G_CALLBACK (on_subscriber_range_changed_cb),