core/calendar-monitor: Replace signals by callbacks
This should save quite some resources when manipulating events. Signals are relatively expensive, and 2 pointer chases are definitely better than a global lock + FFI stashing + signal manipulation.
This commit is contained in:
@@ -59,6 +59,9 @@ struct _GcalCalendarMonitor
|
||||
GcalCalendar *calendar;
|
||||
gboolean complete;
|
||||
|
||||
const GcalCalendarMonitorListener *listener;
|
||||
gpointer listener_user_data;
|
||||
|
||||
/*
|
||||
* These fields are only accessed on the monitor thread, and
|
||||
* never on the main thread.
|
||||
@@ -88,14 +91,6 @@ static gboolean complete_in_idle_cb (gpointer
|
||||
|
||||
G_DEFINE_TYPE (GcalCalendarMonitor, gcal_calendar_monitor, G_TYPE_OBJECT)
|
||||
|
||||
enum
|
||||
{
|
||||
EVENT_ADDED,
|
||||
EVENT_UPDATED,
|
||||
EVENT_REMOVED,
|
||||
N_SIGNALS,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
@@ -104,7 +99,6 @@ enum
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static gulong signals[N_SIGNALS] = { 0, };
|
||||
static GParamSpec *properties [N_PROPS] = { NULL, };
|
||||
|
||||
/*
|
||||
@@ -957,6 +951,7 @@ remove_events_outside_range (GcalCalendarMonitor *self,
|
||||
GcalRange *range)
|
||||
{
|
||||
g_autoptr (GRWLockWriterLocker) writer_locker = NULL;
|
||||
g_autoptr (GPtrArray) events_to_remove = NULL;
|
||||
GHashTableIter iter;
|
||||
GcalEvent *event;
|
||||
|
||||
@@ -964,6 +959,9 @@ remove_events_outside_range (GcalCalendarMonitor *self,
|
||||
|
||||
writer_locker = g_rw_lock_writer_locker_new (&self->shared.lock);
|
||||
|
||||
events_to_remove = g_ptr_array_new_full (g_hash_table_size (self->shared.events),
|
||||
g_object_unref);
|
||||
|
||||
g_hash_table_iter_init (&iter, self->shared.events);
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &event))
|
||||
{
|
||||
@@ -972,36 +970,28 @@ remove_events_outside_range (GcalCalendarMonitor *self,
|
||||
if (gcal_range_calculate_overlap (range, event_range, NULL) != GCAL_RANGE_NO_OVERLAP)
|
||||
continue;
|
||||
|
||||
g_object_ref (event);
|
||||
|
||||
g_hash_table_iter_remove (&iter);
|
||||
g_signal_emit (self, signals[EVENT_REMOVED], 0, event);
|
||||
|
||||
g_object_unref (event);
|
||||
g_ptr_array_add (events_to_remove, event);
|
||||
g_hash_table_iter_steal (&iter);
|
||||
}
|
||||
|
||||
if (events_to_remove->len > 0)
|
||||
self->listener->remove_events (self, events_to_remove, self->listener_user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_all_events (GcalCalendarMonitor *self)
|
||||
{
|
||||
g_autoptr (GRWLockWriterLocker) writer_locker = NULL;
|
||||
GHashTableIter iter;
|
||||
GcalEvent *event;
|
||||
g_autoptr (GPtrArray) events_to_remove = NULL;
|
||||
|
||||
GCAL_TRACE_MSG ("Removing all events from view");
|
||||
|
||||
writer_locker = g_rw_lock_writer_locker_new (&self->shared.lock);
|
||||
|
||||
g_hash_table_iter_init (&iter, self->shared.events);
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &event))
|
||||
{
|
||||
g_object_ref (event);
|
||||
events_to_remove = g_hash_table_steal_all_values (self->shared.events);
|
||||
|
||||
g_hash_table_iter_remove (&iter);
|
||||
g_signal_emit (self, signals[EVENT_REMOVED], 0, event);
|
||||
|
||||
g_object_unref (event);
|
||||
}
|
||||
if (events_to_remove->len > 0)
|
||||
self->listener->remove_events (self, events_to_remove, self->listener_user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1026,6 +1016,7 @@ static gboolean
|
||||
add_events_to_timeline_in_idle_cb (gpointer user_data)
|
||||
{
|
||||
g_autoptr (GRWLockWriterLocker) writer_locker = NULL;
|
||||
g_autoptr (GPtrArray) events_to_add = NULL;
|
||||
GcalCalendarMonitor *self;
|
||||
GPtrArray *events;
|
||||
IdleData *idle_data;
|
||||
@@ -1037,6 +1028,8 @@ add_events_to_timeline_in_idle_cb (gpointer user_data)
|
||||
events = idle_data->events;
|
||||
g_assert (idle_data->event_ids == NULL);
|
||||
|
||||
events_to_add = g_ptr_array_sized_new (events->len);
|
||||
|
||||
writer_locker = g_rw_lock_writer_locker_new (&self->shared.lock);
|
||||
for (guint i = 0; i < events->len; i++)
|
||||
{
|
||||
@@ -1049,10 +1042,13 @@ add_events_to_timeline_in_idle_cb (gpointer user_data)
|
||||
if (!g_hash_table_contains (self->shared.events, uid))
|
||||
{
|
||||
g_hash_table_insert (self->shared.events, g_strdup (uid), g_object_ref (event));
|
||||
g_signal_emit (self, signals[EVENT_ADDED], 0, event);
|
||||
g_ptr_array_add (events_to_add, event);
|
||||
}
|
||||
}
|
||||
|
||||
if (events_to_add->len > 0)
|
||||
self->listener->add_events (self, events_to_add, self->listener_user_data);
|
||||
|
||||
GCAL_RETURN (G_SOURCE_REMOVE);
|
||||
}
|
||||
|
||||
@@ -1060,6 +1056,8 @@ static gboolean
|
||||
update_events_in_idle_cb (gpointer user_data)
|
||||
{
|
||||
g_autoptr (GRWLockWriterLocker) writer_locker = NULL;
|
||||
g_autoptr (GPtrArray) old_events = NULL;
|
||||
g_autoptr (GPtrArray) new_events = NULL;
|
||||
g_autoptr (GPtrArray) events = NULL;
|
||||
GcalCalendarMonitor *self;
|
||||
IdleData *idle_data;
|
||||
@@ -1071,26 +1069,36 @@ update_events_in_idle_cb (gpointer user_data)
|
||||
events = g_steal_pointer (&idle_data->events);
|
||||
g_assert (idle_data->event_ids == NULL);
|
||||
|
||||
old_events = g_ptr_array_new_full (events->len, g_object_unref);
|
||||
new_events = g_ptr_array_sized_new (events->len);
|
||||
|
||||
writer_locker = g_rw_lock_writer_locker_new (&self->shared.lock);
|
||||
for (guint i = 0; i < events->len; i++)
|
||||
{
|
||||
g_autoptr (GcalEvent) old_event = NULL;
|
||||
g_autofree gchar *old_event_id = NULL;
|
||||
GcalEvent *event;
|
||||
const gchar *event_id;
|
||||
|
||||
event = g_ptr_array_index (events, i);
|
||||
event_id = gcal_event_get_uid (event);
|
||||
|
||||
old_event = g_hash_table_lookup (self->shared.events, event_id);
|
||||
if (old_event)
|
||||
if (g_hash_table_steal_extended (self->shared.events,
|
||||
event_id,
|
||||
(gpointer*) &old_event_id,
|
||||
(gpointer*) &old_event))
|
||||
{
|
||||
g_object_ref (old_event);
|
||||
|
||||
g_hash_table_insert (self->shared.events, g_strdup (event_id), g_object_ref (event));
|
||||
g_signal_emit (self, signals[EVENT_UPDATED], 0, old_event, event);
|
||||
g_ptr_array_add (old_events, g_steal_pointer (&old_event));
|
||||
g_ptr_array_add (new_events, event);
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (old_events->len == new_events->len);
|
||||
|
||||
if (new_events->len > 0)
|
||||
self->listener->update_events (self, old_events, new_events, self->listener_user_data);
|
||||
|
||||
GCAL_RETURN (G_SOURCE_REMOVE);
|
||||
}
|
||||
|
||||
@@ -1098,6 +1106,7 @@ static gboolean
|
||||
remove_events_from_timeline_in_idle_cb (gpointer user_data)
|
||||
{
|
||||
g_autoptr (GRWLockWriterLocker) writer_locker = NULL;
|
||||
g_autoptr (GPtrArray) events_to_remove = NULL;
|
||||
g_autoptr (GPtrArray) event_ids = NULL;
|
||||
GcalCalendarMonitor *self;
|
||||
IdleData *idle_data;
|
||||
@@ -1109,6 +1118,8 @@ remove_events_from_timeline_in_idle_cb (gpointer user_data)
|
||||
event_ids = g_steal_pointer (&idle_data->event_ids);
|
||||
g_assert (idle_data->events == NULL);
|
||||
|
||||
events_to_remove = g_ptr_array_new_full (event_ids->len, g_object_unref);
|
||||
|
||||
writer_locker = g_rw_lock_writer_locker_new (&self->shared.lock);
|
||||
|
||||
for (guint i = 0; i < event_ids->len; i++)
|
||||
@@ -1121,14 +1132,15 @@ remove_events_from_timeline_in_idle_cb (gpointer user_data)
|
||||
|
||||
if (event)
|
||||
{
|
||||
/* Keep the event alive until the signal is emitted */
|
||||
g_object_ref (event);
|
||||
|
||||
/* Keep the event alive until the listener process it*/
|
||||
g_ptr_array_add (events_to_remove, g_object_ref (event));
|
||||
g_hash_table_remove (self->shared.events, event_id);
|
||||
g_signal_emit (self, signals[EVENT_REMOVED], 0, event);
|
||||
}
|
||||
}
|
||||
|
||||
if (events_to_remove->len > 0)
|
||||
self->listener->remove_events (self, events_to_remove, self->listener_user_data);
|
||||
|
||||
GCAL_RETURN (G_SOURCE_REMOVE);
|
||||
}
|
||||
|
||||
@@ -1250,31 +1262,6 @@ gcal_calendar_monitor_class_init (GcalCalendarMonitorClass *klass)
|
||||
object_class->get_property = gcal_calendar_monitor_get_property;
|
||||
object_class->set_property = gcal_calendar_monitor_set_property;
|
||||
|
||||
signals[EVENT_ADDED] = g_signal_new ("event-added",
|
||||
GCAL_TYPE_CALENDAR_MONITOR,
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
GCAL_TYPE_EVENT);
|
||||
|
||||
signals[EVENT_UPDATED] = g_signal_new ("event-updated",
|
||||
GCAL_TYPE_CALENDAR_MONITOR,
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE,
|
||||
2,
|
||||
GCAL_TYPE_EVENT,
|
||||
GCAL_TYPE_EVENT);
|
||||
|
||||
signals[EVENT_REMOVED] = g_signal_new ("event-removed",
|
||||
GCAL_TYPE_CALENDAR_MONITOR,
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
GCAL_TYPE_EVENT);
|
||||
|
||||
properties[PROP_CALENDAR] = g_param_spec_object ("calendar",
|
||||
"Calendar",
|
||||
"Calendar to be monitored",
|
||||
@@ -1304,11 +1291,23 @@ gcal_calendar_monitor_init (GcalCalendarMonitor *self)
|
||||
}
|
||||
|
||||
GcalCalendarMonitor*
|
||||
gcal_calendar_monitor_new (GcalCalendar *calendar)
|
||||
gcal_calendar_monitor_new (GcalCalendar *calendar,
|
||||
const GcalCalendarMonitorListener *listener,
|
||||
gpointer user_data)
|
||||
{
|
||||
return g_object_new (GCAL_TYPE_CALENDAR_MONITOR,
|
||||
"calendar", calendar,
|
||||
NULL);
|
||||
g_autoptr (GcalCalendarMonitor) monitor = NULL;
|
||||
|
||||
g_assert (calendar != NULL && GCAL_IS_CALENDAR (calendar));
|
||||
g_assert (listener != NULL);
|
||||
|
||||
monitor = g_object_new (GCAL_TYPE_CALENDAR_MONITOR,
|
||||
"calendar", calendar,
|
||||
NULL);
|
||||
|
||||
monitor->listener = listener;
|
||||
monitor->listener_user_data = user_data;
|
||||
|
||||
return g_steal_pointer (&monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,24 @@ G_BEGIN_DECLS
|
||||
#define GCAL_TYPE_CALENDAR_MONITOR (gcal_calendar_monitor_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GcalCalendarMonitor, gcal_calendar_monitor, GCAL, CALENDAR_MONITOR, GObject)
|
||||
|
||||
GcalCalendarMonitor* gcal_calendar_monitor_new (GcalCalendar *calendar);
|
||||
typedef struct {
|
||||
void (*add_events) (GcalCalendarMonitor *self,
|
||||
GPtrArray *events,
|
||||
gpointer user_data);
|
||||
|
||||
void (*update_events) (GcalCalendarMonitor *self,
|
||||
GPtrArray *old_events,
|
||||
GPtrArray *events,
|
||||
gpointer user_data);
|
||||
|
||||
void (*remove_events) (GcalCalendarMonitor *self,
|
||||
GPtrArray *events,
|
||||
gpointer user_data);
|
||||
} GcalCalendarMonitorListener;
|
||||
|
||||
GcalCalendarMonitor* gcal_calendar_monitor_new (GcalCalendar *calendar,
|
||||
const GcalCalendarMonitorListener *listener,
|
||||
gpointer user_data);
|
||||
|
||||
void gcal_calendar_monitor_set_range (GcalCalendarMonitor *self,
|
||||
GcalRange *range);
|
||||
|
||||
+125
-90
@@ -473,97 +473,135 @@ update_calendar_monitor_filters (GcalTimeline *self)
|
||||
gcal_calendar_monitor_set_filter (monitor, self->filter);
|
||||
}
|
||||
|
||||
/*
|
||||
* GcalCalendarMonitorListener events
|
||||
*/
|
||||
|
||||
static void
|
||||
calendar_monitor_add_events_cb (GcalCalendarMonitor *monitor,
|
||||
GPtrArray *events,
|
||||
gpointer user_data)
|
||||
{
|
||||
GcalTimeline *self;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
self = GCAL_TIMELINE (user_data);
|
||||
|
||||
for (guint i = 0; i < events->len; i++)
|
||||
{
|
||||
g_autoptr (GPtrArray) subscribers_at_range = NULL;
|
||||
GcalRange *event_range;
|
||||
GcalEvent *event;
|
||||
|
||||
event = g_ptr_array_index (events, i);
|
||||
event_range = gcal_event_get_range (event);
|
||||
|
||||
/* Add to all subscribers within the event range */
|
||||
subscribers_at_range = gcal_range_tree_get_data_at_range (self->subscriber_ranges, event_range);
|
||||
|
||||
queue_event_data (self, ADD_EVENT, NULL, event, NULL, TRUE);
|
||||
|
||||
for (guint j = 0; subscribers_at_range && j < subscribers_at_range->len; j++)
|
||||
{
|
||||
GcalTimelineSubscriber *subscriber = g_ptr_array_index (subscribers_at_range, j);
|
||||
|
||||
queue_event_data (self, ADD_EVENT, subscriber, event, NULL, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
calendar_monitor_update_events_cb (GcalCalendarMonitor *monitor,
|
||||
GPtrArray *old_events,
|
||||
GPtrArray *events,
|
||||
gpointer user_data)
|
||||
{
|
||||
GcalTimeline *self;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
g_assert (old_events->len == events->len);
|
||||
|
||||
self = GCAL_TIMELINE (user_data);
|
||||
|
||||
for (guint i = 0; i < events->len; i++)
|
||||
{
|
||||
g_autoptr (GPtrArray) subscribers_at_range = NULL;
|
||||
GcalRange *event_range;
|
||||
GcalEvent *old_event;
|
||||
GcalEvent *event;
|
||||
|
||||
event = g_ptr_array_index (events, i);
|
||||
old_event = g_ptr_array_index (old_events, i);
|
||||
event_range = gcal_event_get_range (event);
|
||||
|
||||
/* Add to all subscribers within the event range */
|
||||
subscribers_at_range = gcal_range_tree_get_data_at_range (self->subscriber_ranges, event_range);
|
||||
|
||||
queue_event_data (self, UPDATE_EVENT, NULL, event, old_event, TRUE);
|
||||
|
||||
for (guint j = 0; subscribers_at_range && j < subscribers_at_range->len; j++)
|
||||
{
|
||||
GcalTimelineSubscriber *subscriber = g_ptr_array_index (subscribers_at_range, j);
|
||||
|
||||
queue_event_data (self, UPDATE_EVENT, subscriber, event, old_event, FALSE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
calendar_monitor_remove_events_cb (GcalCalendarMonitor *monitor,
|
||||
GPtrArray *events,
|
||||
gpointer user_data)
|
||||
{
|
||||
GcalTimeline *self;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
self = GCAL_TIMELINE (user_data);
|
||||
|
||||
for (guint i = 0; i < events->len; i++)
|
||||
{
|
||||
g_autoptr (GPtrArray) subscribers_at_range = NULL;
|
||||
GcalRange *event_range;
|
||||
GcalEvent *event;
|
||||
|
||||
event = g_ptr_array_index (events, i);
|
||||
event_range = gcal_event_get_range (event);
|
||||
|
||||
/* Add to all subscribers within the event range */
|
||||
subscribers_at_range = gcal_range_tree_get_data_at_range (self->subscriber_ranges, event_range);
|
||||
|
||||
for (guint j = 0; subscribers_at_range && j < subscribers_at_range->len; j++)
|
||||
{
|
||||
GcalTimelineSubscriber *subscriber = g_ptr_array_index (subscribers_at_range, j);
|
||||
|
||||
queue_event_data (self, REMOVE_EVENT, subscriber, event, NULL, FALSE);
|
||||
}
|
||||
|
||||
queue_event_data (self, REMOVE_EVENT, NULL, event, NULL, TRUE);
|
||||
}
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static const GcalCalendarMonitorListener monitor_listener = {
|
||||
calendar_monitor_add_events_cb,
|
||||
calendar_monitor_update_events_cb,
|
||||
calendar_monitor_remove_events_cb,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
static void
|
||||
on_calendar_monitor_event_added_cb (GcalCalendarMonitor *monitor,
|
||||
GcalEvent *event,
|
||||
GcalTimeline *self)
|
||||
{
|
||||
g_autoptr (GPtrArray) subscribers_at_range = NULL;
|
||||
GcalRange *event_range;
|
||||
gint i;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
event_range = gcal_event_get_range (event);
|
||||
|
||||
/* Add to all subscribers within the event range */
|
||||
subscribers_at_range = gcal_range_tree_get_data_at_range (self->subscriber_ranges, event_range);
|
||||
|
||||
queue_event_data (self, ADD_EVENT, NULL, event, NULL, TRUE);
|
||||
|
||||
for (i = 0; subscribers_at_range && i < subscribers_at_range->len; i++)
|
||||
{
|
||||
GcalTimelineSubscriber *subscriber = g_ptr_array_index (subscribers_at_range, i);
|
||||
|
||||
queue_event_data (self, ADD_EVENT, subscriber, event, NULL, FALSE);
|
||||
}
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_calendar_monitor_event_updated_cb (GcalCalendarMonitor *monitor,
|
||||
GcalEvent *old_event,
|
||||
GcalEvent *event,
|
||||
GcalTimeline *self)
|
||||
{
|
||||
g_autoptr (GPtrArray) subscribers_at_range = NULL;
|
||||
GcalRange *event_range;
|
||||
gint i;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
/* Add the updated event to the (potentially new) range */
|
||||
event_range = gcal_event_get_range (event);
|
||||
|
||||
/* Add to all subscribers within the event range */
|
||||
subscribers_at_range = gcal_range_tree_get_data_at_range (self->subscriber_ranges, event_range);
|
||||
|
||||
queue_event_data (self, UPDATE_EVENT, NULL, event, old_event, TRUE);
|
||||
|
||||
for (i = 0; subscribers_at_range && i < subscribers_at_range->len; i++)
|
||||
{
|
||||
GcalTimelineSubscriber *subscriber = g_ptr_array_index (subscribers_at_range, i);
|
||||
|
||||
queue_event_data (self, UPDATE_EVENT, subscriber, event, old_event, FALSE);
|
||||
}
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_calendar_monitor_event_removed_cb (GcalCalendarMonitor *monitor,
|
||||
GcalEvent *event,
|
||||
GcalTimeline *self)
|
||||
{
|
||||
g_autoptr (GPtrArray) subscribers_at_range = NULL;
|
||||
GcalRange *event_range;
|
||||
gint i;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
event_range = gcal_event_get_range (event);
|
||||
|
||||
/* Add to all subscribers within the event range */
|
||||
subscribers_at_range = gcal_range_tree_get_data_at_range (self->subscriber_ranges, event_range);
|
||||
|
||||
for (i = 0; subscribers_at_range && i < subscribers_at_range->len; i++)
|
||||
{
|
||||
GcalTimelineSubscriber *subscriber = g_ptr_array_index (subscribers_at_range, i);
|
||||
|
||||
queue_event_data (self, REMOVE_EVENT, subscriber, event, NULL, FALSE);
|
||||
}
|
||||
|
||||
queue_event_data (self, REMOVE_EVENT, NULL, event, NULL, TRUE);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_calendar_monitor_completed_cb (GcalCalendarMonitor *monitor,
|
||||
GParamSpec *pspec,
|
||||
@@ -933,10 +971,7 @@ gcal_timeline_add_calendar (GcalTimeline *self,
|
||||
|
||||
GCAL_TRACE_MSG ("Adding calendar '%s' to timeline %p", gcal_calendar_get_name (calendar), self);
|
||||
|
||||
monitor = gcal_calendar_monitor_new (calendar);
|
||||
g_signal_connect (monitor, "event-added", G_CALLBACK (on_calendar_monitor_event_added_cb), self);
|
||||
g_signal_connect (monitor, "event-updated", G_CALLBACK (on_calendar_monitor_event_updated_cb), self);
|
||||
g_signal_connect (monitor, "event-removed", G_CALLBACK (on_calendar_monitor_event_removed_cb), self);
|
||||
monitor = gcal_calendar_monitor_new (calendar, &monitor_listener, self);
|
||||
g_signal_connect (monitor, "notify::complete", G_CALLBACK (on_calendar_monitor_completed_cb), self);
|
||||
g_hash_table_insert (self->calendars, calendar, g_object_ref (monitor));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user