diff --git a/src/gcal-calendar.c b/src/gcal-calendar.c new file mode 100644 index 00000000..e11860b0 --- /dev/null +++ b/src/gcal-calendar.c @@ -0,0 +1,605 @@ +/* gcal-calendar.c + * + * Copyright 2019 Georges Basile Stavracas Neto + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "GcalCalendar" + +#include "gcal-debug.h" +#include "gcal-calendar.h" + +struct _GcalCalendar +{ + GObject parent; + + GdkRGBA color; + ESource *source; + ECalClient *client; + + gboolean read_only; + + guint color_changed_handler_id; + guint name_changed_handler_id; + guint readonly_changed_handler_id; + guint visible_changed_handler_id; + gboolean initialized; +}; + +static void g_initable_iface_init (GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GcalCalendar, gcal_calendar, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, g_initable_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, NULL)) + +G_DEFINE_QUARK (GcalCalendar, gcal_calendar_error); + +enum +{ + PROP_0, + PROP_CLIENT, + PROP_COLOR, + PROP_ID, + PROP_NAME, + PROP_READ_ONLY, + PROP_SOURCE, + PROP_VISIBLE, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + + +/* + * Auxiliary methods + */ + +static void +update_color (GcalCalendar *self) +{ + ESourceSelectable *selectable_extension; + const gchar *color; + + selectable_extension = e_source_get_extension (self->source, E_SOURCE_EXTENSION_CALENDAR); + color = e_source_selectable_get_color (selectable_extension); + + if (!gdk_rgba_parse (&self->color, color)) + gdk_rgba_parse (&self->color, "#ffffff"); +} + + +/* + * Callbacks + */ + +static void +on_source_color_changed_cb (ESourceSelectable *source, + GParamSpec *pspec, + GcalCalendar *self) +{ + update_color (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]); +} + +static void +on_source_name_changed_cb (ESource *source, + GParamSpec *pspec, + GcalCalendar *self) +{ + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]); +} + +static void +on_client_readonly_changed_cb (EClient *client, + GParamSpec *pspec, + GcalCalendar *self) +{ + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_READ_ONLY]); +} + +static void +on_source_visible_changed_cb (ESourceSelectable *source, + GParamSpec *pspec, + GcalCalendar *self) +{ + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VISIBLE]); +} + + +/* + * GInitable iface + */ + +static gboolean +gcal_calendar_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + g_autoptr (EClient) client = NULL; + g_autoptr (GError) local_error = NULL; + ESourceSelectable *selectable_extension; + GcalCalendar *self; + + GCAL_ENTRY; + + self = GCAL_CALENDAR (initable); + + g_assert (!self->initialized); + self->initialized = TRUE; + + if (!e_source_has_extension (self->source, E_SOURCE_EXTENSION_CALENDAR)) + { + g_set_error (error, + GCAL_CALENDAR_ERROR, + GCAL_CALENDAR_ERROR_NOT_CALENDAR, + "ESource is not a calendar"); + GCAL_RETURN (FALSE); + } + + selectable_extension = e_source_get_extension (self->source, E_SOURCE_EXTENSION_CALENDAR); + + update_color (self); + self->color_changed_handler_id = g_signal_connect (selectable_extension, + "notify::color", + G_CALLBACK (on_source_color_changed_cb), + self); + + self->visible_changed_handler_id = g_signal_connect (selectable_extension, + "notify::selected", + G_CALLBACK (on_source_visible_changed_cb), + self); + + self->name_changed_handler_id = g_signal_connect (self->source, + "notify::display-name", + G_CALLBACK (on_source_name_changed_cb), + self); + + /* 15s is arbitrary, but very rarely this will be reached */ + client = e_cal_client_connect_sync (self->source, + E_CAL_CLIENT_SOURCE_TYPE_EVENTS, + 15, + cancellable, + &local_error); + + if (local_error) + { + g_propagate_error (error, local_error); + GCAL_RETURN (FALSE); + } + + g_assert (E_IS_CAL_CLIENT (client)); + self->client = E_CAL_CLIENT (client); + + self->readonly_changed_handler_id = g_signal_connect (client, + "notify::readonly", + G_CALLBACK (on_client_readonly_changed_cb), + self); + + GCAL_RETURN (TRUE); +} + +static void +g_initable_iface_init (GInitableIface *iface) +{ + iface->init = gcal_calendar_initable_init; +} + + +/* + * GObject overrides + */ + +static void +gcal_calendar_finalize (GObject *object) +{ + GcalCalendar *self = (GcalCalendar *)object; + + g_signal_handler_disconnect (self->source, self->color_changed_handler_id); + self->color_changed_handler_id = 0; + + g_signal_handler_disconnect (self->source, self->name_changed_handler_id); + self->name_changed_handler_id = 0; + + g_signal_handler_disconnect (self->client, self->readonly_changed_handler_id); + self->readonly_changed_handler_id = 0; + + g_clear_object (&self->client); + g_clear_object (&self->source); + + G_OBJECT_CLASS (gcal_calendar_parent_class)->finalize (object); +} + +static void +gcal_calendar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcalCalendar *self = GCAL_CALENDAR (object); + + switch (prop_id) + { + case PROP_CLIENT: + g_value_set_object (value, self->client); + break; + + case PROP_COLOR: + g_value_set_boxed (value, &self->color); + break; + + case PROP_ID: + g_value_set_string (value, gcal_calendar_get_id (self)); + break; + + case PROP_NAME: + g_value_set_string (value, e_source_get_display_name (self->source)); + break; + + case PROP_READ_ONLY: + g_value_set_boolean (value, gcal_calendar_is_read_only (self)); + break; + + case PROP_SOURCE: + g_value_set_object (value, self->source); + break; + + case PROP_VISIBLE: + g_value_set_boolean (value, gcal_calendar_get_visible (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_calendar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalCalendar *self = GCAL_CALENDAR (object); + + switch (prop_id) + { + case PROP_CLIENT: + g_assert_not_reached (); + break; + + case PROP_COLOR: + gcal_calendar_set_color (self, g_value_get_boxed (value)); + break; + + case PROP_ID: + g_assert_not_reached (); + break; + + case PROP_NAME: + gcal_calendar_set_name (self, g_value_get_string (value)); + break; + + case PROP_READ_ONLY: + g_assert_not_reached (); + break; + + case PROP_SOURCE: + g_assert (self->source == NULL); + self->source = g_value_dup_object (value); + break; + + case PROP_VISIBLE: + gcal_calendar_set_visible (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_calendar_class_init (GcalCalendarClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gcal_calendar_finalize; + object_class->get_property = gcal_calendar_get_property; + object_class->set_property = gcal_calendar_set_property; + + properties[PROP_CLIENT] = g_param_spec_object ("client", + "Calendar client", + "Calendar client", + E_TYPE_CAL_CLIENT, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + properties[PROP_COLOR] = g_param_spec_boxed ("color", + "Color", + "Color of the calendar", + GDK_TYPE_RGBA, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + properties[PROP_ID] = g_param_spec_string ("id", + "Identifier", + "Unique identifier of the calendar", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + properties[PROP_NAME] = g_param_spec_string ("name", + "Name", + "Name of the calendar", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + properties[PROP_READ_ONLY] = g_param_spec_boolean ("read-only", + "Read-only", + "Whether the calendar is read-only or not", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + properties[PROP_SOURCE] = g_param_spec_object ("source", + "Source", + "Source", + E_TYPE_SOURCE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + properties[PROP_VISIBLE] = g_param_spec_boolean ("visible", + "Visible", + "Whether the calendar is visible", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +gcal_calendar_init (GcalCalendar *self) +{ +} + +/** + * gcal_calendar_new: + * @source: an #ESource + * @cancellable: (nullable): a cancellable + * @callback: + * @user_data: + * + * Asynchronously creates and initializes a #GcalCalendar + * from @source. + */ +void +gcal_calendar_new (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + return g_async_initable_new_async (GCAL_TYPE_CALENDAR, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + "source", source, + NULL); +} + +/** + * @result: + * @error: (nullable): return location for a #GError + * + * Finishes the operation started by gcal_calendar_new(). + * + * Returns: (transfer full)(nullable): a #GcalCalendar + */ +GcalCalendar* +gcal_calendar_new_finish (GAsyncResult *result, + GError **error) +{ + g_autoptr (GObject) source_object = NULL; + g_autoptr (GObject) result_object = NULL; + + g_return_val_if_fail (G_IS_TASK (result), NULL); + g_return_val_if_fail (!error || !*error, NULL); + + source_object = g_async_result_get_source_object (result); + g_assert (source_object != NULL); + + result_object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), + result, + error); + + return (GcalCalendar*) g_steal_pointer (&result_object); +} + +/** + * gcal_calendar_get_color: + * @self: a #GcalCalendar + * + * Retrieves the color of @self. + * + * Returns: (transfer none): a #GdkRGBA + */ +const GdkRGBA* +gcal_calendar_get_color (GcalCalendar *self) +{ + g_return_val_if_fail (GCAL_IS_CALENDAR (self), NULL); + + return &self->color; +} + +/** + * gcal_calendar_set_color: + * @self: a #GcalCalendar + * @color: the new color + * + * Sets the color of @self. + */ +void +gcal_calendar_set_color (GcalCalendar *self, + const GdkRGBA *color) +{ + g_autofree gchar *color_string = NULL; + ESourceSelectable *selectable_extension; + + g_return_if_fail (GCAL_IS_CALENDAR (self)); + g_return_if_fail (color != NULL); + + if (gdk_rgba_equal (&self->color, color)) + return; + + self->color = *color; + + color_string = gdk_rgba_to_string (color); + selectable_extension = e_source_get_extension (self->source, E_SOURCE_EXTENSION_CALENDAR); + e_source_selectable_set_color (selectable_extension, color_string); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR]); +} + +/** + * gcal_calendar_get_id: + * @self: a #GcalCalendar + * + * Retrieves the unique identifier of @self. + * + * Returns: (transfer none): a string + */ +const gchar* +gcal_calendar_get_id (GcalCalendar *self) +{ + g_return_val_if_fail (GCAL_IS_CALENDAR (self), NULL); + + return e_source_get_uid (self->source); +} + +/** + * gcal_calendar_get_name: + * @self: a #GcalCalendar + * + * Retrieves the user-visible name of @self. + * + * Returns: (transfer none)(nullable): a string + */ +const gchar* +gcal_calendar_get_name (GcalCalendar *self) +{ + g_return_val_if_fail (GCAL_IS_CALENDAR (self), NULL); + + return e_source_get_display_name (self->source); +} + +/** + * gcal_calendar_set_name: + * @self: a #GcalCalendar + * @name: new user-visible name + * + * Sets the user-visible name of @self. + */ +void +gcal_calendar_set_name (GcalCalendar *self, + const gchar *name) +{ + g_return_if_fail (GCAL_IS_CALENDAR (self)); + + e_source_set_display_name (self->source, name); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]); +} + +/** + * gcal_calendar_is_read_only: + * @self: a #GcalCalendar + * + * Retrieves whether @self is read-only or not. + * + * Returns: %TRUE if @self is read-only, %FALSE otherwise + */ +gboolean +gcal_calendar_is_read_only (GcalCalendar *self) +{ + g_return_val_if_fail (GCAL_IS_CALENDAR (self), FALSE); + + return e_client_is_readonly (E_CLIENT (self->client)); +} + +/** + * gcal_calendar_get_source: + * @self: a #GcalCalendar + * + * Retrieves the #ESource that is backing @self. + * + * Returns: (transfer none): an #ESource + */ +ESource* +gcal_calendar_get_source (GcalCalendar *self) +{ + g_return_val_if_fail (GCAL_IS_CALENDAR (self), NULL); + + return self->source; +} + +/** + * gcal_calendar_get_client: + * @self: a #GcalCalendar + * + * Retrieves the #ECalClient that is backing @self. + * + * Returns: (transfer none): an #ECalClient + */ +ECalClient* +gcal_calendar_get_client (GcalCalendar *self) +{ + g_return_val_if_fail (GCAL_IS_CALENDAR (self), NULL); + + return self->client; +} + +/** + * gcal_calendar_get_visible: + * @self: a #GcalCalendar + * + * Retrieves whether @self is visible to the user or not. + * + * Returns: %TRUE if @self is visible, %FALSE otherwise + */ +gboolean +gcal_calendar_get_visible (GcalCalendar *self) +{ + ESourceSelectable *selectable_extension; + + g_return_val_if_fail (GCAL_IS_CALENDAR (self), FALSE); + + selectable_extension = e_source_get_extension (self->source, E_SOURCE_EXTENSION_CALENDAR); + return e_source_selectable_get_selected (selectable_extension); +} + +/** + * gcal_calendar_set_visible: + * @self: a #GcalCalendar + * @visible: visibility of the calendar + * + * Sets whether @self is visible or not. + */ +void +gcal_calendar_set_visible (GcalCalendar *self, + gboolean visible) +{ + ESourceSelectable *selectable_extension; + + g_return_if_fail (GCAL_IS_CALENDAR (self)); + + selectable_extension = e_source_get_extension (self->source, E_SOURCE_EXTENSION_CALENDAR); + e_source_selectable_set_selected (selectable_extension, visible); +} + diff --git a/src/gcal-calendar.h b/src/gcal-calendar.h new file mode 100644 index 00000000..30ce125c --- /dev/null +++ b/src/gcal-calendar.h @@ -0,0 +1,78 @@ +/* gcal-calendar.h + * + * Copyright 2019 Georges Basile Stavracas Neto + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +/** + * GcalCalendarError: + * @GCAL_CALENDAR_ERROR_NOT_CALENDAR: the #ESource is not a calendar + * + * Errors that #GcalCalendar can generate. + */ +typedef enum +{ + GCAL_CALENDAR_ERROR_NOT_CALENDAR, +} GcalCalendarError; + +#define GCAL_CALENDAR_ERROR (gcal_calendar_error_quark ()) + + +#define GCAL_TYPE_CALENDAR (gcal_calendar_get_type()) +G_DECLARE_FINAL_TYPE (GcalCalendar, gcal_calendar, GCAL, CALENDAR, GObject) + +GQuark gcal_calendar_error_quark (void); + +void gcal_calendar_new (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GcalCalendar* gcal_calendar_new_finish (GAsyncResult *result, + GError **error); + +ECalClient* gcal_calendar_get_client (GcalCalendar *self); + +const GdkRGBA* gcal_calendar_get_color (GcalCalendar *self); + +void gcal_calendar_set_color (GcalCalendar *self, + const GdkRGBA *color); + +const gchar* gcal_calendar_get_id (GcalCalendar *self); + +const gchar* gcal_calendar_get_name (GcalCalendar *self); + +void gcal_calendar_set_name (GcalCalendar *self, + const gchar *name); + +gboolean gcal_calendar_is_read_only (GcalCalendar *self); + +ESource* gcal_calendar_get_source (GcalCalendar *self); + +gboolean gcal_calendar_get_visible (GcalCalendar *self); + +void gcal_calendar_set_visible (GcalCalendar *self, + gboolean visible); + +G_END_DECLS diff --git a/src/meson.build b/src/meson.build index f2142960..bf3f2ce8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -42,6 +42,7 @@ sources = files( 'weather/gcal-weather-settings.c', 'gcal-application.c', 'gcal-clock.c', + 'gcal-calendar.c', 'gcal-context.c', 'gcal-date-chooser-day.c', 'gcal-date-chooser.c',