[gnome-calendar] Introduce GcalCalendar



commit b9848e544380c3915b68aaff38f3ddd6fa6b607a
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Fri May 3 12:15:53 2019 -0300

    Introduce GcalCalendar
    
    GcalCalendar is an abstraction on top of ESource
    and ECalClient. Its initialization is asynchronous
    and failable. This gives us more flexibility when
    initializing and loading the calendars from EDS.

 src/gcal-calendar.c | 605 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/gcal-calendar.h |  78 +++++++
 src/meson.build     |   1 +
 3 files changed, 684 insertions(+)
---
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 <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 "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 <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 <libecal/libecal.h>
+#include <gdk/gdk.h>
+
+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',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]