Introduce the new calendar monitoring API

It is, as expected, heavily inspired by the ECalDataModel code,
except it uses GDateTime, is timezone aware, and can handle all
day events correctly.
This commit is contained in:
Georges Basile Stavracas Neto
2020-04-01 21:06:51 -03:00
parent 94413c26b1
commit 3692a0ccf6
10 changed files with 2118 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+41
View File
@@ -0,0 +1,41 @@
/* gcal-calendar-monitor.h
*
* Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <glib-object.h>
#include "gcal-types.h"
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);
void gcal_calendar_monitor_set_range (GcalCalendarMonitor *self,
GDateTime *range_start,
GDateTime *range_end);
GcalEvent* gcal_calendar_monitor_get_cached_event (GcalCalendarMonitor *self,
const gchar *event_id);
G_END_DECLS
+21
View File
@@ -1829,3 +1829,24 @@ gcal_event_format_date (GcalEvent *self)
return g_steal_pointer (&formatted_string);
}
gboolean
gcal_event_is_within_range (GcalEvent *self,
GDateTime *range_start,
GDateTime *range_end)
{
GDateTime *event_start;
GDateTime *event_end;
event_start = gcal_event_get_date_start (self);
event_end = gcal_event_get_date_end (self);
if (gcal_event_get_all_day (self))
{
return gcal_date_time_compare_date (range_end, event_start) < 0 &&
gcal_date_time_compare_date (range_start, event_end) > 0;
}
return g_date_time_compare (range_end, event_start) > 0 &&
g_date_time_compare (event_end, range_start) > 0;
}
+4
View File
@@ -131,6 +131,10 @@ void gcal_event_get_original_timezones (GcalEvent
gchar* gcal_event_format_date (GcalEvent *self);
gboolean gcal_event_is_within_range (GcalEvent *self,
GDateTime *range_start,
GDateTime *range_end);
G_END_DECLS
#endif /* GCAL_EVENT_H */
+96
View File
@@ -0,0 +1,96 @@
/* gcal-timeline-subscriber.c
*
* Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "gcal-timeline-subscriber.h"
G_DEFINE_INTERFACE (GcalTimelineSubscriber, gcal_timeline_subscriber, G_TYPE_OBJECT)
enum
{
RANGE_CHANGED,
N_SIGNALS,
};
static guint signals[N_SIGNALS] = { 0, };
static void
gcal_timeline_subscriber_default_init (GcalTimelineSubscriberInterface *iface)
{
/**
* GcalTimelineSubscriber::range-changed:
*
* Emitted when the subscriber range is changed.
*/
signals[RANGE_CHANGED] = g_signal_new ("range-changed",
GCAL_TYPE_TIMELINE_SUBSCRIBER,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
0);
}
/**
* gcal_timeline_subscriber_get_range_start:
* @self: a #GcalTimelineSubscriber
*
* Retrieves the start of the range of @self.
*
* Returns: (transfer full): a #GDateTime
*/
GDateTime*
gcal_timeline_subscriber_get_range_start (GcalTimelineSubscriber *self)
{
g_return_val_if_fail (GCAL_IS_TIMELINE_SUBSCRIBER (self), NULL);
return GCAL_TIMELINE_SUBSCRIBER_GET_IFACE (self)->get_range_start (self);
}
/**
* gcal_timeline_subscriber_get_range_end:
* @self: a #GcalTimelineSubscriber
*
* Retrieves the end of the range of @self.
*
* Returns: (transfer full): a #GDateTime
*/
GDateTime*
gcal_timeline_subscriber_get_range_end (GcalTimelineSubscriber *self)
{
g_return_val_if_fail (GCAL_IS_TIMELINE_SUBSCRIBER (self), NULL);
return GCAL_TIMELINE_SUBSCRIBER_GET_IFACE (self)->get_range_end (self);
}
/**
* gcal_timeline_subscriber_range_changed:
* @self: a #GcalTimelineSubscriber
*
* Emits the "range-changed" signal for @self. Only
* implementations of #GcalTimelineSubscriber are
* expected to use this method.
*/
void
gcal_timeline_subscriber_range_changed (GcalTimelineSubscriber *self)
{
g_return_if_fail (GCAL_IS_TIMELINE_SUBSCRIBER (self));
g_signal_emit (self, signals[RANGE_CHANGED], 0);
}
+59
View File
@@ -0,0 +1,59 @@
/* gcal-timeline-subscriber.h
*
* Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <glib-object.h>
#define LIBICAL_GLIB_UNSTABLE_API
#include <libical-glib/libical-glib.h>
#include "gcal-types.h"
G_BEGIN_DECLS
#define GCAL_TYPE_TIMELINE_SUBSCRIBER (gcal_timeline_subscriber_get_type ())
G_DECLARE_INTERFACE (GcalTimelineSubscriber, gcal_timeline_subscriber, GCAL, TIMELINE_SUBSCRIBER, GObject)
struct _GcalTimelineSubscriberInterface
{
GTypeInterface parent;
GDateTime* (*get_range_start) (GcalTimelineSubscriber *self);
GDateTime* (*get_range_end) (GcalTimelineSubscriber *self);
void (*add_event) (GcalTimelineSubscriber *self,
GcalEvent *event);
void (*update_event) (GcalTimelineSubscriber *self,
GcalEvent *event);
void (*remove_event) (GcalTimelineSubscriber *self,
GcalEvent *event);
};
GDateTime* gcal_timeline_subscriber_get_range_start (GcalTimelineSubscriber *self);
GDateTime* gcal_timeline_subscriber_get_range_end (GcalTimelineSubscriber *self);
void gcal_timeline_subscriber_range_changed (GcalTimelineSubscriber *self);
G_END_DECLS
+713
View File
@@ -0,0 +1,713 @@
/* gcal-timeline.c
*
* Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "GcalTimeline"
#include "gcal-calendar.h"
#include "gcal-calendar-monitor.h"
#include "gcal-context.h"
#include "gcal-date-time-utils.h"
#include "gcal-debug.h"
#include "gcal-event.h"
#include "gcal-range-tree.h"
#include "gcal-timeline.h"
#include "gcal-timeline-subscriber.h"
#include <libedataserver/libedataserver.h>
typedef struct
{
GDateTime *range_start;
GDateTime *range_end;
} SubscriberData;
struct _GcalTimeline
{
GObject parent_instance;
gulong update_range_idle_id;
GDateTime *range_start;
GDateTime *range_end;
GcalRangeTree *events;
GHashTable *calendars; /* GcalCalendar* -> GcalCalendarMonitor* */
GHashTable *subscribers; /* GcalTimelineSubscriber* -> SubscriberData* */
GcalRangeTree *subscriber_ranges;
GCancellable *cancellable;
GcalContext *context;
};
G_DEFINE_TYPE (GcalTimeline, gcal_timeline, G_TYPE_OBJECT)
enum
{
PROP_0,
PROP_CONTEXT,
N_PROPS
};
static GParamSpec *properties [N_PROPS] = { NULL, };
/*
* Auxiliary methods
*/
static SubscriberData*
subscriber_data_new (GcalTimelineSubscriber *subscriber)
{
SubscriberData *subscriber_data;
subscriber_data = g_new0 (SubscriberData, 1);
subscriber_data->range_start = gcal_timeline_subscriber_get_range_start (subscriber);
subscriber_data->range_end = gcal_timeline_subscriber_get_range_end (subscriber);
return g_steal_pointer (&subscriber_data);
}
static void
subscriber_data_free (SubscriberData *subscriber_data)
{
gcal_clear_date_time (&subscriber_data->range_start);
gcal_clear_date_time (&subscriber_data->range_end);
g_free (subscriber_data);
}
static void
add_event_to_subscriber (GcalTimelineSubscriber *subscriber,
GcalEvent *event)
{
GCAL_TRACE_MSG ("Adding event %s to subscriber %s",
gcal_event_get_uid (event),
G_OBJECT_TYPE_NAME (subscriber));
GCAL_TIMELINE_SUBSCRIBER_GET_IFACE (subscriber)->add_event (subscriber, event);
}
static void
update_subscriber_event (GcalTimelineSubscriber *subscriber,
GcalEvent *event)
{
GCAL_TRACE_MSG ("Updating event '%s' (%s) at subscriber %s",
gcal_event_get_summary (event),
gcal_event_get_uid (event),
G_OBJECT_TYPE_NAME (subscriber));
GCAL_TIMELINE_SUBSCRIBER_GET_IFACE (subscriber)->update_event (subscriber, event);
}
static void
remove_event_from_subscriber (GcalTimelineSubscriber *subscriber,
GcalEvent *event)
{
GCAL_TRACE_MSG ("Removing event '%s' (%s) from subscriber %s",
gcal_event_get_summary (event),
gcal_event_get_uid (event),
G_OBJECT_TYPE_NAME (subscriber));
GCAL_TIMELINE_SUBSCRIBER_GET_IFACE (subscriber)->remove_event (subscriber, event);
}
static gboolean
event_is_really_within_range (GcalEvent *event,
GDateTime *range_start,
GDateTime *range_end)
{
GDateTime *event_start;
GDateTime *event_end;
/* Assume that ECalClientView did its job correctly */
if (!gcal_event_get_all_day (event))
return TRUE;
event_start = gcal_event_get_date_start (event);
event_end = gcal_event_get_date_end (event);
return gcal_date_time_compare_date (range_end, event_start) < 0 &&
gcal_date_time_compare_date (range_start, event_end) > 0;
}
static gboolean
subscriber_contains_event (GcalTimelineSubscriber *subscriber,
GcalEvent *event)
{
g_autoptr (GDateTime) subscriber_range_start = NULL;
g_autoptr (GDateTime) subscriber_range_end = NULL;
subscriber_range_start = gcal_timeline_subscriber_get_range_start (subscriber);
subscriber_range_end = gcal_timeline_subscriber_get_range_end (subscriber);
return event_is_really_within_range (event, subscriber_range_start, subscriber_range_end);
}
static void
update_range (GcalTimeline *self)
{
g_autoptr (GDateTime) new_range_start = NULL;
g_autoptr (GDateTime) new_range_end = NULL;
GcalTimelineSubscriber *subscriber;
GHashTableIter iter;
gboolean has_subscribers;
gboolean range_changed;
GCAL_ENTRY;
range_changed = FALSE;
has_subscribers = g_hash_table_size (self->subscribers) > 0;
if (has_subscribers)
{
g_hash_table_iter_init (&iter, self->subscribers);
while (g_hash_table_iter_next (&iter, (gpointer*) &subscriber, NULL))
{
g_autoptr (GDateTime) subscriber_start = NULL;
g_autoptr (GDateTime) subscriber_end = NULL;
subscriber_start = gcal_timeline_subscriber_get_range_start (subscriber);
subscriber_end = gcal_timeline_subscriber_get_range_end (subscriber);
if (!new_range_start || g_date_time_compare (subscriber_start, new_range_start) < 0)
{
gcal_clear_date_time (&new_range_start);
new_range_start = g_steal_pointer (&subscriber_start);
}
if (!new_range_end || g_date_time_compare (subscriber_end, new_range_end) > 0)
{
gcal_clear_date_time (&new_range_end);
new_range_end = g_steal_pointer (&subscriber_end);
}
}
if (!self->range_start || g_date_time_compare (self->range_start, new_range_start) != 0)
{
gcal_clear_date_time (&self->range_start);
self->range_start = g_steal_pointer (&new_range_start);
range_changed = TRUE;
}
if (!self->range_end || g_date_time_compare (self->range_end, new_range_end) != 0)
{
gcal_clear_date_time (&self->range_end);
self->range_end = g_steal_pointer (&new_range_end);
range_changed = TRUE;
}
}
else if (self->range_start || self->range_end)
{
gcal_clear_date_time (&self->range_start);
gcal_clear_date_time (&self->range_end);
range_changed = TRUE;
}
if (range_changed)
{
GcalCalendarMonitor *monitor;
g_hash_table_iter_init (&iter, self->calendars);
while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &monitor))
gcal_calendar_monitor_set_range (monitor, self->range_start, self->range_end);
}
GCAL_EXIT;
}
static void
calculate_changed_events (GcalTimeline *self,
GcalTimelineSubscriber *subscriber,
GDateTime *old_range_start,
GDateTime *old_range_end,
GDateTime *new_range_start,
GDateTime *new_range_end)
{
g_autoptr (GPtrArray) events_to_remove = NULL;
g_autoptr (GPtrArray) events_to_add = NULL;
gint range_diff;
gint i;
events_to_add = g_ptr_array_new ();
events_to_remove = g_ptr_array_new ();
/* Start ranges diff */
range_diff = g_date_time_compare (old_range_start, new_range_start);
if (range_diff != 0)
{
g_autoptr (GPtrArray) events = NULL;
if (range_diff < 0)
{
/* Removed */
events = gcal_range_tree_get_data_at_range (self->events, old_range_start, new_range_start);
if (events)
g_ptr_array_extend (events_to_remove, events, NULL, NULL);
}
else if (range_diff > 0)
{
/* Added */
events = gcal_range_tree_get_data_at_range (self->events, new_range_start, old_range_start);
if (events)
g_ptr_array_extend (events_to_add, events, NULL, NULL);
}
}
/* End ranges diff */
range_diff = g_date_time_compare (old_range_end, new_range_end);
if (range_diff != 0)
{
g_autoptr (GPtrArray) events = NULL;
if (range_diff < 0)
{
events = gcal_range_tree_get_data_at_range (self->events, old_range_end, new_range_end);
if (events)
g_ptr_array_extend (events_to_add, events, NULL, NULL);
}
else if (range_diff > 0)
{
events = gcal_range_tree_get_data_at_range (self->events, new_range_end, old_range_end);
if (events)
g_ptr_array_extend (events_to_remove, events, NULL, NULL);
}
}
for (i = 0; i < events_to_remove->len; i++)
{
GcalEvent *event = g_ptr_array_index (events_to_remove, i);
if (!event_is_really_within_range (event, old_range_start, old_range_end))
continue;
remove_event_from_subscriber (subscriber, event);
}
for (i = 0; i < events_to_add->len; i++)
{
GcalEvent *event = g_ptr_array_index (events_to_add, i);
if (!event_is_really_within_range (event, new_range_start, new_range_end))
continue;
add_event_to_subscriber (subscriber, event);
}
}
static void
update_subscriber_range (GcalTimeline *self,
GcalTimelineSubscriber *subscriber)
{
g_autoptr (GDateTime) new_range_start = NULL;
g_autoptr (GDateTime) new_range_end = NULL;
SubscriberData *subscriber_data;
GCAL_ENTRY;
subscriber_data = g_hash_table_lookup (self->subscribers, subscriber);
g_assert (subscriber_data != NULL);
new_range_start = gcal_timeline_subscriber_get_range_start (subscriber);
new_range_end = gcal_timeline_subscriber_get_range_end (subscriber);
/* Diff new and old event ranges */
calculate_changed_events (self,
subscriber,
subscriber_data->range_start,
subscriber_data->range_end,
new_range_start,
new_range_end);
/* Update the subscriber range */
gcal_range_tree_remove_data (self->subscriber_ranges, subscriber);
gcal_range_tree_add_range (self->subscriber_ranges,
new_range_start,
new_range_end,
subscriber);
gcal_set_date_time (&subscriber_data->range_start, new_range_start);
gcal_set_date_time (&subscriber_data->range_end, new_range_end);
GCAL_EXIT;
}
/*
* Callbacks
*/
static void
on_calendar_monitor_event_added_cb (GcalCalendarMonitor *monitor,
GcalEvent *event,
GcalTimeline *self)
{
g_autoptr (GPtrArray) subscribers_at_range = NULL;
GDateTime *event_start;
GDateTime *event_end;
gint i;
GCAL_ENTRY;
event_start = gcal_event_get_date_start (event);
event_end = gcal_event_get_date_end (event);
gcal_range_tree_add_range (self->events, event_start, event_end, g_object_ref (event));
/* Add to all subscribers within the event range */
subscribers_at_range = gcal_range_tree_get_data_at_range (self->subscriber_ranges, event_start, event_end);
for (i = 0; subscribers_at_range && i < subscribers_at_range->len; i++)
{
GcalTimelineSubscriber *subscriber = g_ptr_array_index (subscribers_at_range, i);
if (!subscriber_contains_event (subscriber, event))
continue;
add_event_to_subscriber (subscriber, event);
}
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;
GDateTime *old_event_start;
GDateTime *old_event_end;
GDateTime *event_start;
GDateTime *event_end;
gint i;
GCAL_ENTRY;
/* Remove the old event */
old_event_start = gcal_event_get_date_start (old_event);
old_event_end = gcal_event_get_date_end (old_event);
gcal_range_tree_remove_range (self->events, old_event_start, old_event_end, old_event);
/* Add the updated event to the (potentially new) range */
event_start = gcal_event_get_date_start (event);
event_end = gcal_event_get_date_end (event);
gcal_range_tree_add_range (self->events, event_start, event_end, g_object_ref (event));
/* Add to all subscribers within the event range */
subscribers_at_range = gcal_range_tree_get_data_at_range (self->subscriber_ranges, event_start, event_end);
for (i = 0; subscribers_at_range && i < subscribers_at_range->len; i++)
{
GcalTimelineSubscriber *subscriber = g_ptr_array_index (subscribers_at_range, i);
if (!subscriber_contains_event (subscriber, event))
continue;
update_subscriber_event (subscriber, event);
}
GCAL_EXIT;
}
static void
on_calendar_monitor_event_removed_cb (GcalCalendarMonitor *monitor,
GcalEvent *event,
GcalTimeline *self)
{
g_autoptr (GPtrArray) subscribers_at_range = NULL;
GDateTime *event_start;
GDateTime *event_end;
gint i;
GCAL_ENTRY;
event_start = gcal_event_get_date_start (event);
event_end = gcal_event_get_date_end (event);
/* Add to all subscribers within the event range */
subscribers_at_range = gcal_range_tree_get_data_at_range (self->subscriber_ranges, event_start, event_end);
for (i = 0; subscribers_at_range && i < subscribers_at_range->len; i++)
{
GcalTimelineSubscriber *subscriber = g_ptr_array_index (subscribers_at_range, i);
if (!subscriber_contains_event (subscriber, event))
continue;
remove_event_from_subscriber (subscriber, event);
}
gcal_range_tree_remove_range (self->events, event_start, event_end, event);
GCAL_EXIT;
}
static gboolean
update_timeline_range_in_idle_cb (gpointer user_data)
{
GcalTimeline *self;
GCAL_ENTRY;
self = GCAL_TIMELINE (user_data);
update_range (self);
self->update_range_idle_id = 0;
GCAL_RETURN (G_SOURCE_REMOVE);
}
static void
on_subscriber_range_changed_cb (GcalTimelineSubscriber *subscriber,
GcalTimeline *self)
{
update_subscriber_range (self, subscriber);
if (self->update_range_idle_id)
return;
self->update_range_idle_id = g_idle_add (update_timeline_range_in_idle_cb, self);
}
/*
* GObject overrides
*/
static void
gcal_timeline_finalize (GObject *object)
{
GcalTimeline *self = (GcalTimeline *)object;
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
if (self->update_range_idle_id > 0)
{
g_source_remove (self->update_range_idle_id);
self->update_range_idle_id = 0;
}
g_clear_pointer (&self->events, gcal_range_tree_unref);
g_clear_pointer (&self->calendars, g_hash_table_destroy);
g_clear_pointer (&self->subscribers, g_hash_table_destroy);
g_clear_pointer (&self->subscriber_ranges, gcal_range_tree_unref);
G_OBJECT_CLASS (gcal_timeline_parent_class)->finalize (object);
}
static void
gcal_timeline_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GcalTimeline *self = GCAL_TIMELINE (object);
switch (prop_id)
{
case PROP_CONTEXT:
g_value_set_object (value, self->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_timeline_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GcalTimeline *self = GCAL_TIMELINE (object);
switch (prop_id)
{
case PROP_CONTEXT:
g_assert (self->context == NULL);
self->context = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_timeline_class_init (GcalTimelineClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gcal_timeline_finalize;
object_class->get_property = gcal_timeline_get_property;
object_class->set_property = gcal_timeline_set_property;
/**
* GcalSearchEngine::context:
*
* The #GcalContext of the application.
*/
properties[PROP_CONTEXT] = g_param_spec_object ("context",
"Data context",
"Data context",
GCAL_TYPE_CONTEXT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
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) subscriber_data_free);
self->subscriber_ranges = gcal_range_tree_new ();
}
/**
* gcal_timeline_new:
* @context: a #GcalContext
*
* Creates a new #GcalTimeline.
*
* Returns: (transfer full): a #GcalTimeline
*/
GcalTimeline*
gcal_timeline_new (GcalContext *context)
{
return g_object_new (GCAL_TYPE_TIMELINE,
"context", context,
NULL);
}
/**
* gcal_timeline_add_calendar:
* @self: a #GcalTimeline
* @calendar: a #GcalCalendar
*
* Adds @calendar to @self. The events in @calendar will be added
* respectively to
*
*/
void
gcal_timeline_add_calendar (GcalTimeline *self,
GcalCalendar *calendar)
{
g_autoptr (GcalCalendarMonitor) monitor = NULL;
g_return_if_fail (GCAL_IS_TIMELINE (self));
g_return_if_fail (GCAL_IS_CALENDAR (calendar));
GCAL_ENTRY;
if (g_hash_table_contains (self->calendars, calendar))
return;
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);
g_hash_table_insert (self->calendars, calendar, g_object_ref (monitor));
if (self->range_start && self->range_end)
gcal_calendar_monitor_set_range (monitor, self->range_start, self->range_end);
GCAL_EXIT;
}
void
gcal_timeline_remove_calendar (GcalTimeline *self,
GcalCalendar *calendar)
{
g_return_if_fail (GCAL_IS_TIMELINE (self));
g_return_if_fail (GCAL_IS_CALENDAR (calendar));
GCAL_ENTRY;
g_object_ref (calendar);
if (g_hash_table_remove (self->calendars, calendar))
GCAL_TRACE_MSG ("Removing calendar '%s' from timeline %p", gcal_calendar_get_name (calendar), self);
g_object_unref (calendar);
GCAL_EXIT;
}
void
gcal_timeline_add_subscriber (GcalTimeline *self,
GcalTimelineSubscriber *subscriber)
{
SubscriberData *subscriber_data;
g_return_if_fail (GCAL_IS_TIMELINE (self));
g_return_if_fail (GCAL_IS_TIMELINE_SUBSCRIBER (subscriber));
GCAL_ENTRY;
if (g_hash_table_contains (self->subscribers, subscriber))
GCAL_RETURN ();
g_debug ("Adding subscriber %s to timeline %p", G_OBJECT_TYPE_NAME (subscriber), self);
subscriber_data = subscriber_data_new (subscriber);
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),
self,
0);
update_subscriber_range (self, subscriber);
update_range (self);
GCAL_EXIT;
}
void
gcal_timeline_remove_subscriber (GcalTimeline *self,
GcalTimelineSubscriber *subscriber)
{
g_return_if_fail (GCAL_IS_TIMELINE (self));
g_return_if_fail (GCAL_IS_TIMELINE_SUBSCRIBER (subscriber));
GCAL_ENTRY;
if (!g_hash_table_contains (self->subscribers, subscriber))
GCAL_RETURN ();
g_debug ("Removing subscriber %s from timeline %p", G_OBJECT_TYPE_NAME (subscriber), self);
g_signal_handlers_disconnect_by_func (subscriber, on_subscriber_range_changed_cb, self);
g_hash_table_remove (self->subscribers, subscriber);
update_range (self);
GCAL_EXIT;
}
+46
View File
@@ -0,0 +1,46 @@
/* gcal-timeline.h
*
* Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "gcal-types.h"
#include <glib-object.h>
G_BEGIN_DECLS
#define GCAL_TYPE_TIMELINE (gcal_timeline_get_type())
G_DECLARE_FINAL_TYPE (GcalTimeline, gcal_timeline, GCAL, TIMELINE, GObject)
GcalTimeline* gcal_timeline_new (GcalContext *context);
void gcal_timeline_add_calendar (GcalTimeline *self,
GcalCalendar *calendar);
void gcal_timeline_remove_calendar (GcalTimeline *self,
GcalCalendar *calendar);
void gcal_timeline_add_subscriber (GcalTimeline *self,
GcalTimelineSubscriber *subscriber);
void gcal_timeline_remove_subscriber (GcalTimeline *self,
GcalTimelineSubscriber *subscriber);
G_END_DECLS
+4
View File
@@ -24,6 +24,10 @@
G_BEGIN_DECLS
typedef struct _GcalCalendar GcalCalendar;
typedef struct _GcalContext GcalContext;
typedef struct _GcalEvent GcalEvent;
typedef struct _GcalTimeline GcalTimeline;
typedef struct _GcalTimelineSubscriber GcalTimelineSubscriber;
G_END_DECLS
+3
View File
@@ -93,6 +93,7 @@ calendar_incs += contrib_incs
sources = files(
'core/gcal-calendar.c',
'core/gcal-calendar-monitor.c',
'core/gcal-clock.c',
'core/gcal-context.c',
'core/gcal-event.c',
@@ -101,6 +102,8 @@ sources = files(
'core/gcal-night-light-monitor.c',
'core/gcal-recurrence.c',
'core/gcal-shell-search-provider.c',
'core/gcal-timeline.c',
'core/gcal-timeline-subscriber.c',
'core/gcal-timer.c',
'core/gcal-time-zone-monitor.c',
'gui/calendar-management/gcal-calendar-management-dialog.c',