aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/pcs/cal-backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'calendar/pcs/cal-backend.c')
-rw-r--r--calendar/pcs/cal-backend.c373
1 files changed, 336 insertions, 37 deletions
diff --git a/calendar/pcs/cal-backend.c b/calendar/pcs/cal-backend.c
index 876ef0d5ec..f797f3f67d 100644
--- a/calendar/pcs/cal-backend.c
+++ b/calendar/pcs/cal-backend.c
@@ -32,6 +32,29 @@
+/* A category that exists in some of the objects of the calendar */
+typedef struct {
+ /* Category name, also used as the key in the categories hash table */
+ char *name;
+
+ /* Number of objects that have this category */
+ int refcount;
+} CalBackendCategory;
+
+/* Private part of the CalBackend structure */
+struct _CalBackendPrivate {
+ /* List of Cal objects with their listeners */
+ GList *clients;
+
+ /* Hash table of live categories, temporary hash of
+ * added/removed categories, and idle handler for sending
+ * category_changed.
+ */
+ GHashTable *categories;
+ GHashTable *changed_categories;
+ guint category_idle_id;
+};
+
/* Signal IDs */
enum {
LAST_CLIENT_GONE,
@@ -41,13 +64,20 @@ enum {
OBJ_REMOVED,
LAST_SIGNAL
};
+static guint cal_backend_signals[LAST_SIGNAL];
static void cal_backend_class_init (CalBackendClass *class);
+static void cal_backend_init (CalBackend *backend);
+static void cal_backend_finalize (GObject *object);
-static guint cal_backend_signals[LAST_SIGNAL];
+static char *get_object (CalBackend *backend, const char *uid);
+
+static void notify_categories_changed (CalBackend *backend);
#define CLASS(backend) (CAL_BACKEND_CLASS (G_OBJECT_GET_CLASS (backend)))
+static GObjectClass *parent_class;
+
/**
@@ -73,7 +103,7 @@ cal_backend_get_type (void)
NULL, NULL,
sizeof (CalBackend),
0,
- (GInstanceInitFunc) NULL
+ (GInstanceInitFunc) cal_backend_init,
};
cal_backend_type = g_type_register_static (G_TYPE_OBJECT, "CalBackend", &info, 0);
}
@@ -87,6 +117,8 @@ cal_backend_class_init (CalBackendClass *class)
{
GObjectClass *object_class;
+ parent_class = (GObjectClass *) g_type_class_peek_parent (class);
+
object_class = (GObjectClass *) class;
cal_backend_signals[LAST_CLIENT_GONE] =
@@ -134,6 +166,8 @@ cal_backend_class_init (CalBackendClass *class)
G_TYPE_NONE, 1,
G_TYPE_STRING);
+ object_class->finalize = cal_backend_finalize;
+
class->last_client_gone = NULL;
class->opened = NULL;
class->obj_updated = NULL;
@@ -148,7 +182,7 @@ cal_backend_class_init (CalBackendClass *class)
class->get_mode = NULL;
class->set_mode = NULL;
class->get_n_objects = NULL;
- class->get_object = NULL;
+ class->get_object = get_object;
class->get_object_component = NULL;
class->get_timezone_object = NULL;
class->get_uids = NULL;
@@ -162,6 +196,51 @@ cal_backend_class_init (CalBackendClass *class)
class->send_object = NULL;
}
+/* Object initialization func for the calendar backend */
+void
+cal_backend_init (CalBackend *backend)
+{
+ CalBackendPrivate *priv;
+
+ priv = g_new0 (CalBackendPrivate, 1);
+ backend->priv = priv;
+
+ priv->categories = g_hash_table_new (g_str_hash, g_str_equal);
+ priv->changed_categories = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+/* Used from g_hash_table_foreach(), frees a CalBackendCategory structure */
+static void
+free_category_cb (gpointer key, gpointer value, gpointer data)
+{
+ CalBackendCategory *c = value;
+
+ g_free (c->name);
+ g_free (c);
+}
+
+void
+cal_backend_finalize (GObject *object)
+{
+ CalBackend *backend = (CalBackend *)object;
+ CalBackendPrivate *priv;
+
+ priv = backend->priv;
+
+ g_assert (priv->clients == NULL);
+
+ g_hash_table_foreach (priv->categories, free_category_cb, NULL);
+ g_hash_table_destroy (priv->categories);
+
+ g_hash_table_foreach (priv->changed_categories, free_category_cb, NULL);
+ g_hash_table_destroy (priv->changed_categories);
+
+ if (priv->category_idle_id)
+ g_source_remove (priv->category_idle_id);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
/**
@@ -204,37 +283,17 @@ cal_backend_get_email_address (CalBackend *backend)
/* Callback used when a Cal is destroyed */
static void
-cal_destroy_cb (GObject *object, gpointer data)
+cal_destroy_cb (gpointer data, GObject *where_cal_was)
{
- Cal *cal;
- Cal *lcal;
- CalBackend *backend;
- GList *l;
-
- cal = CAL (object);
+ CalBackend *backend = CAL_BACKEND (data);
+ CalBackendPrivate *priv = backend->priv;
- backend = CAL_BACKEND (data);
-
- /* Find the cal in the list of clients */
-
- for (l = backend->clients; l; l = l->next) {
- lcal = CAL (l->data);
-
- if (lcal == cal)
- break;
- }
-
- g_assert (l != NULL);
-
- /* Disconnect */
-
- backend->clients = g_list_remove_link (backend->clients, l);
- g_list_free_1 (l);
+ priv->clients = g_list_remove (priv->clients, where_cal_was);
/* When all clients go away, notify the parent factory about it so that
* it may decide whether to kill the backend or not.
*/
- if (!backend->clients)
+ if (!priv->clients)
cal_backend_last_client_gone (backend);
}
@@ -249,22 +308,25 @@ cal_destroy_cb (GObject *object, gpointer data)
void
cal_backend_add_cal (CalBackend *backend, Cal *cal)
{
+ CalBackendPrivate *priv = backend->priv;
+
g_return_if_fail (backend != NULL);
g_return_if_fail (IS_CAL_BACKEND (backend));
g_return_if_fail (IS_CAL (cal));
- /* we do not keep a reference to the Cal since the Calendar
- * user agent owns it */
- g_signal_connect (G_OBJECT (cal), "destroy",
- G_CALLBACK (cal_destroy_cb),
- backend);
+ /* we do not keep a (strong) reference to the Cal since the
+ * Calendar user agent owns it */
+ g_object_weak_ref (G_OBJECT (cal), cal_destroy_cb, backend);
- backend->clients = g_list_prepend (backend->clients, cal);
+ priv->clients = g_list_prepend (priv->clients, cal);
+
+ /* Tell the new client about the list of categories.
+ * (Ends up telling all the other clients too, but *shrug*.)
+ */
+ notify_categories_changed (backend);
/* notify backend that a new Cal has been added */
- g_signal_emit (G_OBJECT (backend),
- cal_backend_signals[CAL_ADDED],
- 0, cal);
+ g_signal_emit (backend, cal_backend_signals[CAL_ADDED], 0, cal);
}
/**
@@ -426,6 +488,19 @@ cal_backend_get_n_objects (CalBackend *backend, CalObjType type)
return (* CLASS (backend)->get_n_objects) (backend, type);
}
+/* Default cal_backend_get_object implementation */
+static char *
+get_object (CalBackend *backend, const char *uid)
+{
+ CalComponent *comp;
+
+ comp = cal_backend_get_object_component (backend, uid);
+ if (!comp)
+ return NULL;
+
+ return cal_component_get_as_string (comp);
+}
+
/**
* cal_backend_get_object:
* @backend: A calendar backend.
@@ -902,3 +977,227 @@ cal_backend_set_default_timezone (CalBackend *backend, const char *tzid)
return (* CLASS (backend)->set_default_timezone) (backend, tzid);
}
+
+/**
+ * cal_backend_notify_mode:
+ * @backend: A calendar backend.
+ * @status: Status of the mode set
+ * @mode: the current mode
+ *
+ * Notifies each of the backend's listeners about the results of a
+ * setMode call.
+ **/
+void
+cal_backend_notify_mode (CalBackend *backend,
+ GNOME_Evolution_Calendar_Listener_SetModeStatus status,
+ GNOME_Evolution_Calendar_CalMode mode)
+{
+ CalBackendPrivate *priv = backend->priv;
+ GList *l;
+
+ for (l = priv->clients; l; l = l->next)
+ cal_notify_mode (l->data, status, mode);
+}
+
+/**
+ * cal_backend_notify_update:
+ * @backend: A calendar backend.
+ * @uid: UID of object that was updated.
+ *
+ * Notifies each of the backend's listeners about an update to a
+ * calendar object.
+ **/
+void
+cal_backend_notify_update (CalBackend *backend, const char *uid)
+{
+ CalBackendPrivate *priv = backend->priv;
+ GList *l;
+
+ cal_backend_obj_updated (backend, uid);
+ for (l = priv->clients; l; l = l->next)
+ cal_notify_update (l->data, uid);
+}
+
+/**
+ * cal_backend_notify_remove:
+ * @backend: A calendar backend.
+ * @uid: UID of object that was removed.
+ *
+ * Notifies each of the backend's listeners about a calendar object
+ * that was removed.
+ **/
+void
+cal_backend_notify_remove (CalBackend *backend, const char *uid)
+{
+ CalBackendPrivate *priv = backend->priv;
+ GList *l;
+
+ cal_backend_obj_removed (backend, uid);
+ for (l = priv->clients; l; l = l->next)
+ cal_notify_remove (l->data, uid);
+}
+
+/**
+ * cal_backend_notify_error:
+ * @backend: A calendar backend.
+ * @message: Error message
+ *
+ * Notifies each of the backend's listeners about an error
+ **/
+void
+cal_backend_notify_error (CalBackend *backend, const char *message)
+{
+ CalBackendPrivate *priv = backend->priv;
+ GList *l;
+
+ for (l = priv->clients; l; l = l->next)
+ cal_notify_error (l->data, message);
+}
+
+static void
+add_category_cb (gpointer name, gpointer category, gpointer data)
+{
+ GNOME_Evolution_Calendar_StringSeq *seq = data;
+
+ seq->_buffer[seq->_length++] = CORBA_string_dup (name);
+}
+
+static void
+notify_categories_changed (CalBackend *backend)
+{
+ CalBackendPrivate *priv = backend->priv;
+ GNOME_Evolution_Calendar_StringSeq *seq;
+ GList *l;
+
+ /* Build the sequence of category names */
+ seq = GNOME_Evolution_Calendar_StringSeq__alloc ();
+ seq->_length = 0;
+ seq->_maximum = g_hash_table_size (priv->categories);
+ seq->_buffer = CORBA_sequence_CORBA_string_allocbuf (seq->_maximum);
+ CORBA_sequence_set_release (seq, TRUE);
+
+ g_hash_table_foreach (priv->categories, add_category_cb, seq);
+
+ /* Notify the clients */
+ for (l = priv->clients; l; l = l->next)
+ cal_notify_categories_changed (l->data, seq);
+
+ CORBA_free (seq);
+}
+
+static gboolean
+prune_changed_categories (gpointer key, gpointer value, gpointer data)
+{
+ CalBackendCategory *category = value;
+
+ if (!category->refcount) {
+ g_free (category->name);
+ g_free (category);
+ }
+ return TRUE;
+}
+
+static gboolean
+idle_notify_categories_changed (gpointer data)
+{
+ CalBackend *backend = CAL_BACKEND (data);
+ CalBackendPrivate *priv = backend->priv;
+
+ if (g_hash_table_size (priv->changed_categories)) {
+ notify_categories_changed (backend);
+ g_hash_table_foreach_remove (priv->changed_categories, prune_changed_categories, NULL);
+ }
+ return FALSE;
+}
+
+/**
+ * cal_backend_ref_categories:
+ * @backend: A calendar backend
+ * @categories: a list of categories
+ *
+ * Adds 1 to the refcount of each of the named categories. If any of
+ * the categories are new, clients will be notified of the updated
+ * category list at idle time.
+ **/
+void
+cal_backend_ref_categories (CalBackend *backend, GSList *categories)
+{
+ CalBackendPrivate *priv;
+ CalBackendCategory *c;
+ const char *name;
+
+ priv = backend->priv;
+
+ while (categories) {
+ name = categories->data;
+ c = g_hash_table_lookup (priv->categories, name);
+
+ if (c)
+ c->refcount++;
+ else {
+ /* See if it was recently removed */
+
+ c = g_hash_table_lookup (priv->changed_categories, name);
+ if (c && c->refcount == 0) {
+ /* Move it back to the set of live categories */
+ g_hash_table_remove (priv->changed_categories, c->name);
+
+ c->refcount = 1;
+ g_hash_table_insert (priv->categories, c->name, c);
+ } else {
+ /* Create a new category */
+ c = g_new (CalBackendCategory, 1);
+ c->name = g_strdup (name);
+ c->refcount = 1;
+ g_hash_table_insert (priv->categories, c->name, c);
+ g_hash_table_insert (priv->changed_categories, c->name, c);
+ }
+ }
+
+ categories = categories->next;
+ }
+
+ if (g_hash_table_size (priv->changed_categories) &&
+ !priv->category_idle_id)
+ priv->category_idle_id = g_idle_add (idle_notify_categories_changed, backend);
+}
+
+/**
+ * cal_backend_unref_categories:
+ * @backend: A calendar backend
+ * @categories: a list of categories
+ *
+ * Subtracts 1 from the refcount of each of the named categories. If
+ * any of the refcounts go down to 0, clients will be notified of the
+ * updated category list at idle time.
+ **/
+void
+cal_backend_unref_categories (CalBackend *backend, GSList *categories)
+{
+ CalBackendPrivate *priv;
+ CalBackendCategory *c;
+ const char *name;
+
+ priv = backend->priv;
+
+ while (categories) {
+ name = categories->data;
+ c = g_hash_table_lookup (priv->categories, name);
+
+ if (c) {
+ g_assert (c != NULL);
+ g_assert (c->refcount > 0);
+
+ c->refcount--;
+
+ if (c->refcount == 0) {
+ g_hash_table_remove (priv->categories, c->name);
+ g_hash_table_insert (priv->changed_categories, c->name, c);
+ }
+ }
+ }
+
+ if (g_hash_table_size (priv->changed_categories) &&
+ !priv->category_idle_id)
+ priv->category_idle_id = g_idle_add (idle_notify_categories_changed, backend);
+}