aboutsummaryrefslogtreecommitdiffstats
path: root/calendar
diff options
context:
space:
mode:
authorFederico Mena Quintero <federico@helixcode.com>2000-12-23 01:29:39 +0800
committerFederico Mena Quintero <federico@src.gnome.org>2000-12-23 01:29:39 +0800
commit4369c400fc801eef03a6fdda2b5256972f018246 (patch)
treee6b3b8486921d9a155026b3a0e6f3d11c221596f /calendar
parent8acc182b76bc6987c140fb49e83405246d61d0e8 (diff)
downloadgsoc2013-evolution-4369c400fc801eef03a6fdda2b5256972f018246.tar
gsoc2013-evolution-4369c400fc801eef03a6fdda2b5256972f018246.tar.gz
gsoc2013-evolution-4369c400fc801eef03a6fdda2b5256972f018246.tar.bz2
gsoc2013-evolution-4369c400fc801eef03a6fdda2b5256972f018246.tar.lz
gsoc2013-evolution-4369c400fc801eef03a6fdda2b5256972f018246.tar.xz
gsoc2013-evolution-4369c400fc801eef03a6fdda2b5256972f018246.tar.zst
gsoc2013-evolution-4369c400fc801eef03a6fdda2b5256972f018246.zip
Alarm trigger queueing for the GUI part.
2000-12-21 Federico Mena Quintero <federico@helixcode.com> Alarm trigger queueing for the GUI part. * gui/alarm-notify.[ch]: New files with the high-level alarm notification system; mostly moved over from gnome-cal.c. The low-level timer stuff is still in alarm.[ch]. * gui/alarm-notify.c (alarm_notify_init): New function to initialize the alarm notification system. (alarm_notify_done): New function to shut down the alarm notification system. (alarm_notify_add_client): New function to start monitoring a calendar client for alarm notification. (alarm_notify_remove_client): New function to stop monitoring a client. * gui/alarm.h (AlarmDestroyNotify): Also pass in the alarm ID so the callback may know which ID is being destroyed. * gui/alarm.c (clear_itimer): New function. (pop_alarm): Use clear_itimer(). (alarm_done): New function to shut down the timer system. (alarm_add): Add some preconditions. Do not call the destroy notification function if we could not create the alarm. (alarm_ready): Pass the alarm ID to the destroy notify function. (alarm_remove): Likewise. Also, add some preconditions. * gui/gnome-cal.c: Removed the alarm notification functions from here since they are now in alarm-notify.c. (gnome_calendar_construct): Register the client with alarm_notify_add_client(). (gnome_calendar_destroy): Use alarm_notify_remove_client() to unregister the client. (obj_updated_cb): Do not do any alarm-related stuff. (obj_removed_cb): Likewise. * gui/main.c (main): Shut down the alarm timer system. (main): Initialize and shut down the alarm notification system. * gui/Makefile.am (evolution_calendar_SOURCES): Added alarm-notify.[ch] to the list of sources. * gui/calendar-model.c (calendar_model_set_cal_client): Only connect to the "cal_loaded" signal if the client is not already loaded. * gui/e-day-view.c (e_day_view_set_cal_client): Likewise. * gui/e-week-view.c (e_week_view_set_cal_client): Likewise. * gui/e-itip-control.c (update_calendar): Connect to "cal_loaded" before issuing the load request. svn path=/trunk/; revision=7130
Diffstat (limited to 'calendar')
-rw-r--r--calendar/ChangeLog54
-rw-r--r--calendar/cal-client/cal-client.c4
-rw-r--r--calendar/gui/Makefile.am2
-rw-r--r--calendar/gui/alarm-notify.c511
-rw-r--r--calendar/gui/alarm-notify.h35
-rw-r--r--calendar/gui/alarm-notify/alarm-queue.c511
-rw-r--r--calendar/gui/alarm-notify/alarm-queue.h35
-rw-r--r--calendar/gui/alarm-notify/alarm.c91
-rw-r--r--calendar/gui/alarm-notify/alarm.h15
-rw-r--r--calendar/gui/alarm.c91
-rw-r--r--calendar/gui/alarm.h15
-rw-r--r--calendar/gui/calendar-model.c6
-rw-r--r--calendar/gui/e-day-view.c6
-rw-r--r--calendar/gui/e-itip-control.c7
-rw-r--r--calendar/gui/e-week-view.c6
-rw-r--r--calendar/gui/gnome-cal.c240
-rw-r--r--calendar/gui/main.c4
17 files changed, 1378 insertions, 255 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog
index 994d22fa2d..74751b91ca 100644
--- a/calendar/ChangeLog
+++ b/calendar/ChangeLog
@@ -1,3 +1,57 @@
+2000-12-21 Federico Mena Quintero <federico@helixcode.com>
+
+ Alarm trigger queueing for the GUI part.
+
+ * gui/alarm-notify.[ch]: New files with the high-level alarm
+ notification system; mostly moved over from gnome-cal.c. The
+ low-level timer stuff is still in alarm.[ch].
+
+ * gui/alarm-notify.c (alarm_notify_init): New function to
+ initialize the alarm notification system.
+ (alarm_notify_done): New function to shut down the alarm
+ notification system.
+ (alarm_notify_add_client): New function to start monitoring a
+ calendar client for alarm notification.
+ (alarm_notify_remove_client): New function to stop monitoring a
+ client.
+
+ * gui/alarm.h (AlarmDestroyNotify): Also pass in the alarm ID so
+ the callback may know which ID is being destroyed.
+
+ * gui/alarm.c (clear_itimer): New function.
+ (pop_alarm): Use clear_itimer().
+ (alarm_done): New function to shut down the timer system.
+ (alarm_add): Add some preconditions. Do not call the destroy
+ notification function if we could not create the alarm.
+ (alarm_ready): Pass the alarm ID to the destroy notify function.
+ (alarm_remove): Likewise. Also, add some preconditions.
+
+ * gui/gnome-cal.c: Removed the alarm notification functions from
+ here since they are now in alarm-notify.c.
+ (gnome_calendar_construct): Register the client with
+ alarm_notify_add_client().
+ (gnome_calendar_destroy): Use alarm_notify_remove_client() to
+ unregister the client.
+ (obj_updated_cb): Do not do any alarm-related stuff.
+ (obj_removed_cb): Likewise.
+
+ * gui/main.c (main): Shut down the alarm timer system.
+ (main): Initialize and shut down the alarm notification system.
+
+ * gui/Makefile.am (evolution_calendar_SOURCES): Added
+ alarm-notify.[ch] to the list of sources.
+
+ * gui/calendar-model.c (calendar_model_set_cal_client): Only
+ connect to the "cal_loaded" signal if the client is not already
+ loaded.
+
+ * gui/e-day-view.c (e_day_view_set_cal_client): Likewise.
+
+ * gui/e-week-view.c (e_week_view_set_cal_client): Likewise.
+
+ * gui/e-itip-control.c (update_calendar): Connect to "cal_loaded"
+ before issuing the load request.
+
2000-12-21 Iain Holmes <iain@helixcode.com>
* gui/calendar-summary.c: Updated for new executive summary.
diff --git a/calendar/cal-client/cal-client.c b/calendar/cal-client/cal-client.c
index fb4e673ae7..344dd91545 100644
--- a/calendar/cal-client/cal-client.c
+++ b/calendar/cal-client/cal-client.c
@@ -1247,7 +1247,9 @@ build_component_alarms_list (GNOME_Evolution_Calendar_CalComponentAlarmsSeq *seq
* time.
*
* Return value: A list of #CalComponentAlarms structures. This should be freed
- * using the cal_client_free_alarms() function.
+ * using the cal_client_free_alarms() function, or by freeing each element
+ * separately with cal_component_alarms_free() and then freeing the list with
+ * g_slist_free().
**/
GSList *
cal_client_get_alarms_in_range (CalClient *client, time_t start, time_t end)
diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am
index ccba4abeeb..48ccd78e95 100644
--- a/calendar/gui/Makefile.am
+++ b/calendar/gui/Makefile.am
@@ -62,6 +62,8 @@ evolution_calendar_SOURCES = \
$(IDL_GENERATED) \
alarm.c \
alarm.h \
+ alarm-notify.c \
+ alarm-notify.h \
calendar-config.c \
calendar-config.h \
calendar-commands.c \
diff --git a/calendar/gui/alarm-notify.c b/calendar/gui/alarm-notify.c
new file mode 100644
index 0000000000..6f0e8e6759
--- /dev/null
+++ b/calendar/gui/alarm-notify.c
@@ -0,0 +1,511 @@
+/* Evolution calendar - Alarm notification engine
+ *
+ * Copyright (C) 2000 Helix Code, Inc.
+ *
+ * Authors: Federico Mena-Quintero <federico@helixcode.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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtksignal.h>
+#include <cal-util/timeutil.h>
+#include "alarm.h"
+#include "alarm-notify.h"
+
+
+
+/* Whether the notification system has been initialized */
+static gboolean alarm_notify_inited;
+
+/* Clients we are monitoring for alarms */
+static GHashTable *client_alarms_hash = NULL;
+
+/* Structure that stores a client we are monitoring */
+typedef struct {
+ /* Monitored client */
+ CalClient *client;
+
+ /* Number of times this client has been registered */
+ int refcount;
+
+ /* Hash table of component UID -> CompQueuedAlarms. If an element is
+ * present here, then it means its cqa->queued_alarms contains at least
+ * one queued alarm. When all the alarms for a component have been
+ * dequeued, the CompQueuedAlarms structure is removed from the hash
+ * table. Thus a CQA exists <=> it has queued alarms.
+ */
+ GHashTable *uid_alarms_hash;
+} ClientAlarms;
+
+/* Pair of a CalComponentAlarms and the mapping from queued alarm IDs to the
+ * actual alarm instance structures.
+ */
+typedef struct {
+ /* The parent client alarms structure */
+ ClientAlarms *parent_client;
+
+ /* The actual component and its alarm instances */
+ CalComponentAlarms *alarms;
+
+ /* List of QueuedAlarm structures */
+ GSList *queued_alarms;
+} CompQueuedAlarms;
+
+/* Pair of a queued alarm ID and the alarm trigger instance it refers to */
+typedef struct {
+ /* Alarm ID from alarm.h */
+ gpointer alarm_id;
+
+ /* Instance from our parent CompAlarms->alarms list */
+ CalAlarmInstance *instance;
+} QueuedAlarm;
+
+/* Alarm ID for the midnight refresh function */
+static gpointer midnight_refresh_id = NULL;
+
+
+
+static void load_alarms (ClientAlarms *ca);
+static void midnight_refresh_cb (gpointer alarm_id, time_t trigger, gpointer data);
+
+/* Queues an alarm trigger for midnight so that we can load the next day's worth
+ * of alarms.
+ */
+static void
+queue_midnight_refresh (void)
+{
+ time_t midnight;
+
+ g_assert (midnight_refresh_id == NULL);
+
+ midnight = time_day_end (time (NULL));
+
+ midnight_refresh_id = alarm_add (midnight, midnight_refresh_cb, NULL, NULL);
+ if (!midnight_refresh_id) {
+ g_message ("alarm_notify_init(): Could not set up the midnight refresh alarm!");
+ /* FIXME: what to do? */
+ }
+}
+
+/* Loads a client's alarms; called from g_hash_table_foreach() */
+static void
+add_client_alarms_cb (gpointer key, gpointer value, gpointer data)
+{
+ ClientAlarms *ca;
+
+ ca = value;
+ load_alarms (ca);
+}
+
+/* Loads the alarms for the new day every midnight */
+static void
+midnight_refresh_cb (gpointer alarm_id, time_t trigger, gpointer data)
+{
+ /* Re-load the alarms for all clients */
+
+ g_hash_table_foreach (client_alarms_hash, add_client_alarms_cb, NULL);
+
+ /* Re-schedule the midnight update */
+
+ midnight_refresh_id = NULL;
+ queue_midnight_refresh ();
+}
+
+/* Looks up a client in the client alarms hash table */
+static ClientAlarms *
+lookup_client (CalClient *client)
+{
+ return g_hash_table_lookup (client_alarms_hash, client);
+}
+
+/* Callback used when an alarm triggers */
+static void
+alarm_trigger_cb (gpointer alarm_id, time_t trigger, gpointer data)
+{
+ CompQueuedAlarms *cqa;
+
+ cqa = data;
+
+ /* FIXME */
+
+ g_message ("alarm_trigger_cb(): Triggered!");
+}
+
+/* Callback used when an alarm must be destroyed */
+static void
+alarm_destroy_cb (gpointer alarm_id, gpointer data)
+{
+ CompQueuedAlarms *cqa;
+ GSList *l;
+ QueuedAlarm *qa;
+ const char *uid;
+
+ cqa = data;
+
+ qa = NULL; /* Keep GCC happy */
+
+ /* Find the alarm in the queued alarms */
+
+ for (l = cqa->queued_alarms; l; l = l->next) {
+ qa = l->data;
+ if (qa->alarm_id == alarm_id)
+ break;
+ }
+
+ g_assert (l != NULL);
+
+ /* Remove it and free it */
+
+ cqa->queued_alarms = g_slist_remove_link (cqa->queued_alarms, l);
+ g_slist_free_1 (l);
+
+ g_free (qa);
+
+ /* If this was the last queued alarm for this component, remove the
+ * component itself.
+ */
+
+ if (cqa->queued_alarms != NULL)
+ return;
+
+ cal_component_get_uid (cqa->alarms->comp, &uid);
+ g_hash_table_remove (cqa->parent_client->uid_alarms_hash, uid);
+ cqa->parent_client = NULL;
+
+ cal_component_alarms_free (cqa->alarms);
+ cqa->alarms = NULL;
+
+ g_free (cqa);
+}
+
+/* Adds the alarms in a CalComponentAlarms structure to the alarms queued for a
+ * particular client. Also puts the triggers in the alarm timer queue.
+ */
+static void
+add_component_alarms (ClientAlarms *ca, CalComponentAlarms *alarms)
+{
+ const char *uid;
+ CompQueuedAlarms *cqa;
+ GSList *l;
+
+ cqa = g_new (CompQueuedAlarms, 1);
+ cqa->parent_client = ca;
+ cqa->alarms = alarms;
+
+ cqa->queued_alarms = NULL;
+
+ for (l = alarms->alarms; l; l = l->next) {
+ CalAlarmInstance *instance;
+ gpointer alarm_id;
+ QueuedAlarm *qa;
+
+ instance = l->data;
+
+ alarm_id = alarm_add (instance->trigger, alarm_trigger_cb, cqa, alarm_destroy_cb);
+ if (!alarm_id) {
+ g_message ("add_component_alarms(): Could not schedule a trigger for "
+ "%ld, discarding...", (long) instance->trigger);
+ continue;
+ }
+
+ qa = g_new (QueuedAlarm, 1);
+ qa->alarm_id = alarm_id;
+ qa->instance = instance;
+
+ cqa->queued_alarms = g_slist_prepend (cqa->queued_alarms, qa);
+ }
+
+ cal_component_get_uid (alarms->comp, &uid);
+
+ /* If we failed to add all the alarms, then we should get rid of the cqa */
+ if (cqa->queued_alarms == NULL) {
+ g_message ("add_component_alarms(): Could not add any of the alarms "
+ "for the component `%s'; discarding it...", uid);
+
+ cal_component_alarms_free (cqa->alarms);
+ cqa->alarms = NULL;
+
+ g_free (cqa);
+ return;
+ }
+
+ cqa->queued_alarms = g_slist_reverse (cqa->queued_alarms);
+ g_hash_table_insert (ca->uid_alarms_hash, (char *) uid, cqa);
+}
+
+/* Loads today's remaining alarms for a client */
+static void
+load_alarms (ClientAlarms *ca)
+{
+ time_t now, day_end;
+ GSList *comp_alarms;
+ GSList *l;
+
+ now = time (NULL);
+ day_end = time_day_end (now);
+
+ comp_alarms = cal_client_get_alarms_in_range (ca->client, now, day_end);
+
+ /* All of the last day's alarms should have already triggered and should
+ * have been removed, so we should have no pending components.
+ */
+ g_assert (g_hash_table_size (ca->uid_alarms_hash) == 0);
+
+ for (l = comp_alarms; l; l = l->next) {
+ CalComponentAlarms *alarms;
+
+ alarms = l->data;
+ add_component_alarms (ca, alarms);
+ }
+
+ g_slist_free (comp_alarms);
+}
+
+/* Called when a calendar client finished loading; we load its alarms */
+static void
+cal_loaded_cb (CalClient *client, CalClientLoadStatus status, gpointer data)
+{
+ ClientAlarms *ca;
+
+ ca = data;
+
+ if (status != CAL_CLIENT_LOAD_SUCCESS)
+ return;
+
+ load_alarms (ca);
+}
+
+/* Looks up a component's queued alarm structure in a client alarms structure */
+static CompQueuedAlarms *
+lookup_comp_queued_alarms (ClientAlarms *ca, const char *uid)
+{
+ return g_hash_table_lookup (ca->uid_alarms_hash, uid);
+}
+
+/* Removes a component an its alarms */
+static void
+remove_comp (ClientAlarms *ca, const char *uid)
+{
+ CompQueuedAlarms *cqa;
+ GSList *l;
+
+ cqa = lookup_comp_queued_alarms (ca, uid);
+ if (!cqa)
+ return;
+
+ /* If a component is present, then it means we must have alarms queued
+ * for it.
+ */
+ g_assert (cqa->queued_alarms != NULL);
+
+ for (l = cqa->queued_alarms; l;) {
+ QueuedAlarm *qa;
+
+ qa = l->data;
+
+ /* Get the next element here because the list element will go
+ * away. Also, we do not free the qa here because it will be
+ * freed by the destroy notification function.
+ */
+ l = l->next;
+
+ alarm_remove (qa->alarm_id);
+ }
+
+ /* The list should be empty now, and thus the queued component alarms
+ * structure should have been freed and removed from the hash table.
+ */
+ g_assert (lookup_comp_queued_alarms (ca, uid) == NULL);
+}
+
+/* Called when a calendar component changes; we must reload its corresponding
+ * alarms.
+ */
+static void
+obj_updated_cb (CalClient *client, const char *uid, gpointer data)
+{
+ ClientAlarms *ca;
+ time_t now, day_end;
+ CalComponentAlarms *alarms;
+ gboolean found;
+
+ ca = data;
+
+ remove_comp (ca, uid);
+
+ now = time (NULL);
+ day_end = time_day_end (now);
+
+ found = cal_client_get_alarms_for_object (ca->client, uid, now, day_end, &alarms);
+
+ if (!found)
+ return;
+
+ add_component_alarms (ca, alarms);
+}
+
+/* Called when a calendar component is removed; we must delete its corresponding
+ * alarms.
+ */
+static void
+obj_removed_cb (CalClient *client, const char *uid, gpointer data)
+{
+ ClientAlarms *ca;
+
+ ca = data;
+
+ remove_comp (ca, uid);
+}
+
+
+
+/**
+ * alarm_notify_init:
+ *
+ * Initializes the alarm notification system. This should be called near the
+ * beginning of the program, after calling alarm_init().
+ **/
+void
+alarm_notify_init (void)
+{
+ g_return_if_fail (alarm_notify_inited == FALSE);
+
+ client_alarms_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+ queue_midnight_refresh ();
+
+ alarm_notify_inited = TRUE;
+}
+
+/**
+ * alarm_notify_done:
+ *
+ * Shuts down the alarm notification system. This should be called near the end
+ * of the program. All the monitored calendar clients should already have been
+ * unregistered with alarm_notify_remove_client().
+ **/
+void
+alarm_notify_done (void)
+{
+ g_return_if_fail (alarm_notify_inited);
+
+ /* All clients must be unregistered by now */
+ g_return_if_fail (g_hash_table_size (client_alarms_hash) == 0);
+
+ g_hash_table_destroy (client_alarms_hash);
+ client_alarms_hash = NULL;
+
+ g_assert (midnight_refresh_id != NULL);
+ alarm_remove (midnight_refresh_id);
+ midnight_refresh_id = NULL;
+
+ alarm_notify_inited = FALSE;
+}
+
+/**
+ * alarm_notify_add_client:
+ * @client: A calendar client.
+ *
+ * Adds a calendar client to the alarm notification system. Alarm trigger
+ * notifications will be presented at the appropriate times. The client should
+ * be removed with alarm_notify_remove_client() when receiving notifications
+ * from it is no longer desired.
+ *
+ * A client can be added any number of times to the alarm notification system,
+ * but any single alarm trigger will only be presented once for a particular
+ * client. The client must still be removed the same number of times from the
+ * notification system when it is no longer wanted.
+ **/
+void
+alarm_notify_add_client (CalClient *client)
+{
+ ClientAlarms *ca;
+
+ g_return_if_fail (alarm_notify_inited);
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (IS_CAL_CLIENT (client));
+
+ ca = lookup_client (client);
+ if (ca) {
+ ca->refcount++;
+ return;
+ }
+
+ ca = g_new (ClientAlarms, 1);
+
+ ca->client = client;
+ gtk_object_ref (GTK_OBJECT (ca->client));
+
+ ca->refcount = 1;
+ g_hash_table_insert (client_alarms_hash, client, ca);
+
+ ca->uid_alarms_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ if (!cal_client_is_loaded (client))
+ gtk_signal_connect (GTK_OBJECT (client), "cal_loaded",
+ GTK_SIGNAL_FUNC (cal_loaded_cb), ca);
+
+ gtk_signal_connect (GTK_OBJECT (client), "obj_updated",
+ GTK_SIGNAL_FUNC (obj_updated_cb), ca);
+ gtk_signal_connect (GTK_OBJECT (client), "obj_removed",
+ GTK_SIGNAL_FUNC (obj_removed_cb), ca);
+
+ if (cal_client_is_loaded (client))
+ load_alarms (ca);
+}
+
+/**
+ * alarm_notify_remove_client:
+ * @client: A calendar client.
+ *
+ * Removes a calendar client from the alarm notification system.
+ **/
+void
+alarm_notify_remove_client (CalClient *client)
+{
+ ClientAlarms *ca;
+
+ g_return_if_fail (alarm_notify_inited);
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (IS_CAL_CLIENT (client));
+
+ ca = lookup_client (client);
+ g_return_if_fail (ca != NULL);
+
+ g_assert (ca->refcount > 0);
+ ca->refcount--;
+
+ if (ca->refcount > 0)
+ return;
+
+ /* FIXME: remove alarms */
+
+ /* Clean up */
+
+ gtk_signal_disconnect_by_data (GTK_OBJECT (ca->client), ca);
+
+ gtk_object_unref (GTK_OBJECT (ca->client));
+ ca->client = NULL;
+
+ g_hash_table_destroy (ca->uid_alarms_hash);
+ ca->uid_alarms_hash = NULL;
+
+ g_free (ca);
+
+ g_hash_table_remove (client_alarms_hash, client);
+}
diff --git a/calendar/gui/alarm-notify.h b/calendar/gui/alarm-notify.h
new file mode 100644
index 0000000000..f386a56ae4
--- /dev/null
+++ b/calendar/gui/alarm-notify.h
@@ -0,0 +1,35 @@
+/* Evolution calendar - Alarm notification engine
+ *
+ * Copyright (C) 2000 Helix Code, Inc.
+ *
+ * Authors: Federico Mena-Quintero <federico@helixcode.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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ALARM_NOTIFY_H
+#define ALARM_NOTIFY_H
+
+#include <cal-client/cal-client.h>
+
+
+void alarm_notify_init (void);
+void alarm_notify_done (void);
+
+void alarm_notify_add_client (CalClient *client);
+void alarm_notify_remove_client (CalClient *client);
+
+
+#endif
diff --git a/calendar/gui/alarm-notify/alarm-queue.c b/calendar/gui/alarm-notify/alarm-queue.c
new file mode 100644
index 0000000000..6f0e8e6759
--- /dev/null
+++ b/calendar/gui/alarm-notify/alarm-queue.c
@@ -0,0 +1,511 @@
+/* Evolution calendar - Alarm notification engine
+ *
+ * Copyright (C) 2000 Helix Code, Inc.
+ *
+ * Authors: Federico Mena-Quintero <federico@helixcode.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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtksignal.h>
+#include <cal-util/timeutil.h>
+#include "alarm.h"
+#include "alarm-notify.h"
+
+
+
+/* Whether the notification system has been initialized */
+static gboolean alarm_notify_inited;
+
+/* Clients we are monitoring for alarms */
+static GHashTable *client_alarms_hash = NULL;
+
+/* Structure that stores a client we are monitoring */
+typedef struct {
+ /* Monitored client */
+ CalClient *client;
+
+ /* Number of times this client has been registered */
+ int refcount;
+
+ /* Hash table of component UID -> CompQueuedAlarms. If an element is
+ * present here, then it means its cqa->queued_alarms contains at least
+ * one queued alarm. When all the alarms for a component have been
+ * dequeued, the CompQueuedAlarms structure is removed from the hash
+ * table. Thus a CQA exists <=> it has queued alarms.
+ */
+ GHashTable *uid_alarms_hash;
+} ClientAlarms;
+
+/* Pair of a CalComponentAlarms and the mapping from queued alarm IDs to the
+ * actual alarm instance structures.
+ */
+typedef struct {
+ /* The parent client alarms structure */
+ ClientAlarms *parent_client;
+
+ /* The actual component and its alarm instances */
+ CalComponentAlarms *alarms;
+
+ /* List of QueuedAlarm structures */
+ GSList *queued_alarms;
+} CompQueuedAlarms;
+
+/* Pair of a queued alarm ID and the alarm trigger instance it refers to */
+typedef struct {
+ /* Alarm ID from alarm.h */
+ gpointer alarm_id;
+
+ /* Instance from our parent CompAlarms->alarms list */
+ CalAlarmInstance *instance;
+} QueuedAlarm;
+
+/* Alarm ID for the midnight refresh function */
+static gpointer midnight_refresh_id = NULL;
+
+
+
+static void load_alarms (ClientAlarms *ca);
+static void midnight_refresh_cb (gpointer alarm_id, time_t trigger, gpointer data);
+
+/* Queues an alarm trigger for midnight so that we can load the next day's worth
+ * of alarms.
+ */
+static void
+queue_midnight_refresh (void)
+{
+ time_t midnight;
+
+ g_assert (midnight_refresh_id == NULL);
+
+ midnight = time_day_end (time (NULL));
+
+ midnight_refresh_id = alarm_add (midnight, midnight_refresh_cb, NULL, NULL);
+ if (!midnight_refresh_id) {
+ g_message ("alarm_notify_init(): Could not set up the midnight refresh alarm!");
+ /* FIXME: what to do? */
+ }
+}
+
+/* Loads a client's alarms; called from g_hash_table_foreach() */
+static void
+add_client_alarms_cb (gpointer key, gpointer value, gpointer data)
+{
+ ClientAlarms *ca;
+
+ ca = value;
+ load_alarms (ca);
+}
+
+/* Loads the alarms for the new day every midnight */
+static void
+midnight_refresh_cb (gpointer alarm_id, time_t trigger, gpointer data)
+{
+ /* Re-load the alarms for all clients */
+
+ g_hash_table_foreach (client_alarms_hash, add_client_alarms_cb, NULL);
+
+ /* Re-schedule the midnight update */
+
+ midnight_refresh_id = NULL;
+ queue_midnight_refresh ();
+}
+
+/* Looks up a client in the client alarms hash table */
+static ClientAlarms *
+lookup_client (CalClient *client)
+{
+ return g_hash_table_lookup (client_alarms_hash, client);
+}
+
+/* Callback used when an alarm triggers */
+static void
+alarm_trigger_cb (gpointer alarm_id, time_t trigger, gpointer data)
+{
+ CompQueuedAlarms *cqa;
+
+ cqa = data;
+
+ /* FIXME */
+
+ g_message ("alarm_trigger_cb(): Triggered!");
+}
+
+/* Callback used when an alarm must be destroyed */
+static void
+alarm_destroy_cb (gpointer alarm_id, gpointer data)
+{
+ CompQueuedAlarms *cqa;
+ GSList *l;
+ QueuedAlarm *qa;
+ const char *uid;
+
+ cqa = data;
+
+ qa = NULL; /* Keep GCC happy */
+
+ /* Find the alarm in the queued alarms */
+
+ for (l = cqa->queued_alarms; l; l = l->next) {
+ qa = l->data;
+ if (qa->alarm_id == alarm_id)
+ break;
+ }
+
+ g_assert (l != NULL);
+
+ /* Remove it and free it */
+
+ cqa->queued_alarms = g_slist_remove_link (cqa->queued_alarms, l);
+ g_slist_free_1 (l);
+
+ g_free (qa);
+
+ /* If this was the last queued alarm for this component, remove the
+ * component itself.
+ */
+
+ if (cqa->queued_alarms != NULL)
+ return;
+
+ cal_component_get_uid (cqa->alarms->comp, &uid);
+ g_hash_table_remove (cqa->parent_client->uid_alarms_hash, uid);
+ cqa->parent_client = NULL;
+
+ cal_component_alarms_free (cqa->alarms);
+ cqa->alarms = NULL;
+
+ g_free (cqa);
+}
+
+/* Adds the alarms in a CalComponentAlarms structure to the alarms queued for a
+ * particular client. Also puts the triggers in the alarm timer queue.
+ */
+static void
+add_component_alarms (ClientAlarms *ca, CalComponentAlarms *alarms)
+{
+ const char *uid;
+ CompQueuedAlarms *cqa;
+ GSList *l;
+
+ cqa = g_new (CompQueuedAlarms, 1);
+ cqa->parent_client = ca;
+ cqa->alarms = alarms;
+
+ cqa->queued_alarms = NULL;
+
+ for (l = alarms->alarms; l; l = l->next) {
+ CalAlarmInstance *instance;
+ gpointer alarm_id;
+ QueuedAlarm *qa;
+
+ instance = l->data;
+
+ alarm_id = alarm_add (instance->trigger, alarm_trigger_cb, cqa, alarm_destroy_cb);
+ if (!alarm_id) {
+ g_message ("add_component_alarms(): Could not schedule a trigger for "
+ "%ld, discarding...", (long) instance->trigger);
+ continue;
+ }
+
+ qa = g_new (QueuedAlarm, 1);
+ qa->alarm_id = alarm_id;
+ qa->instance = instance;
+
+ cqa->queued_alarms = g_slist_prepend (cqa->queued_alarms, qa);
+ }
+
+ cal_component_get_uid (alarms->comp, &uid);
+
+ /* If we failed to add all the alarms, then we should get rid of the cqa */
+ if (cqa->queued_alarms == NULL) {
+ g_message ("add_component_alarms(): Could not add any of the alarms "
+ "for the component `%s'; discarding it...", uid);
+
+ cal_component_alarms_free (cqa->alarms);
+ cqa->alarms = NULL;
+
+ g_free (cqa);
+ return;
+ }
+
+ cqa->queued_alarms = g_slist_reverse (cqa->queued_alarms);
+ g_hash_table_insert (ca->uid_alarms_hash, (char *) uid, cqa);
+}
+
+/* Loads today's remaining alarms for a client */
+static void
+load_alarms (ClientAlarms *ca)
+{
+ time_t now, day_end;
+ GSList *comp_alarms;
+ GSList *l;
+
+ now = time (NULL);
+ day_end = time_day_end (now);
+
+ comp_alarms = cal_client_get_alarms_in_range (ca->client, now, day_end);
+
+ /* All of the last day's alarms should have already triggered and should
+ * have been removed, so we should have no pending components.
+ */
+ g_assert (g_hash_table_size (ca->uid_alarms_hash) == 0);
+
+ for (l = comp_alarms; l; l = l->next) {
+ CalComponentAlarms *alarms;
+
+ alarms = l->data;
+ add_component_alarms (ca, alarms);
+ }
+
+ g_slist_free (comp_alarms);
+}
+
+/* Called when a calendar client finished loading; we load its alarms */
+static void
+cal_loaded_cb (CalClient *client, CalClientLoadStatus status, gpointer data)
+{
+ ClientAlarms *ca;
+
+ ca = data;
+
+ if (status != CAL_CLIENT_LOAD_SUCCESS)
+ return;
+
+ load_alarms (ca);
+}
+
+/* Looks up a component's queued alarm structure in a client alarms structure */
+static CompQueuedAlarms *
+lookup_comp_queued_alarms (ClientAlarms *ca, const char *uid)
+{
+ return g_hash_table_lookup (ca->uid_alarms_hash, uid);
+}
+
+/* Removes a component an its alarms */
+static void
+remove_comp (ClientAlarms *ca, const char *uid)
+{
+ CompQueuedAlarms *cqa;
+ GSList *l;
+
+ cqa = lookup_comp_queued_alarms (ca, uid);
+ if (!cqa)
+ return;
+
+ /* If a component is present, then it means we must have alarms queued
+ * for it.
+ */
+ g_assert (cqa->queued_alarms != NULL);
+
+ for (l = cqa->queued_alarms; l;) {
+ QueuedAlarm *qa;
+
+ qa = l->data;
+
+ /* Get the next element here because the list element will go
+ * away. Also, we do not free the qa here because it will be
+ * freed by the destroy notification function.
+ */
+ l = l->next;
+
+ alarm_remove (qa->alarm_id);
+ }
+
+ /* The list should be empty now, and thus the queued component alarms
+ * structure should have been freed and removed from the hash table.
+ */
+ g_assert (lookup_comp_queued_alarms (ca, uid) == NULL);
+}
+
+/* Called when a calendar component changes; we must reload its corresponding
+ * alarms.
+ */
+static void
+obj_updated_cb (CalClient *client, const char *uid, gpointer data)
+{
+ ClientAlarms *ca;
+ time_t now, day_end;
+ CalComponentAlarms *alarms;
+ gboolean found;
+
+ ca = data;
+
+ remove_comp (ca, uid);
+
+ now = time (NULL);
+ day_end = time_day_end (now);
+
+ found = cal_client_get_alarms_for_object (ca->client, uid, now, day_end, &alarms);
+
+ if (!found)
+ return;
+
+ add_component_alarms (ca, alarms);
+}
+
+/* Called when a calendar component is removed; we must delete its corresponding
+ * alarms.
+ */
+static void
+obj_removed_cb (CalClient *client, const char *uid, gpointer data)
+{
+ ClientAlarms *ca;
+
+ ca = data;
+
+ remove_comp (ca, uid);
+}
+
+
+
+/**
+ * alarm_notify_init:
+ *
+ * Initializes the alarm notification system. This should be called near the
+ * beginning of the program, after calling alarm_init().
+ **/
+void
+alarm_notify_init (void)
+{
+ g_return_if_fail (alarm_notify_inited == FALSE);
+
+ client_alarms_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+ queue_midnight_refresh ();
+
+ alarm_notify_inited = TRUE;
+}
+
+/**
+ * alarm_notify_done:
+ *
+ * Shuts down the alarm notification system. This should be called near the end
+ * of the program. All the monitored calendar clients should already have been
+ * unregistered with alarm_notify_remove_client().
+ **/
+void
+alarm_notify_done (void)
+{
+ g_return_if_fail (alarm_notify_inited);
+
+ /* All clients must be unregistered by now */
+ g_return_if_fail (g_hash_table_size (client_alarms_hash) == 0);
+
+ g_hash_table_destroy (client_alarms_hash);
+ client_alarms_hash = NULL;
+
+ g_assert (midnight_refresh_id != NULL);
+ alarm_remove (midnight_refresh_id);
+ midnight_refresh_id = NULL;
+
+ alarm_notify_inited = FALSE;
+}
+
+/**
+ * alarm_notify_add_client:
+ * @client: A calendar client.
+ *
+ * Adds a calendar client to the alarm notification system. Alarm trigger
+ * notifications will be presented at the appropriate times. The client should
+ * be removed with alarm_notify_remove_client() when receiving notifications
+ * from it is no longer desired.
+ *
+ * A client can be added any number of times to the alarm notification system,
+ * but any single alarm trigger will only be presented once for a particular
+ * client. The client must still be removed the same number of times from the
+ * notification system when it is no longer wanted.
+ **/
+void
+alarm_notify_add_client (CalClient *client)
+{
+ ClientAlarms *ca;
+
+ g_return_if_fail (alarm_notify_inited);
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (IS_CAL_CLIENT (client));
+
+ ca = lookup_client (client);
+ if (ca) {
+ ca->refcount++;
+ return;
+ }
+
+ ca = g_new (ClientAlarms, 1);
+
+ ca->client = client;
+ gtk_object_ref (GTK_OBJECT (ca->client));
+
+ ca->refcount = 1;
+ g_hash_table_insert (client_alarms_hash, client, ca);
+
+ ca->uid_alarms_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ if (!cal_client_is_loaded (client))
+ gtk_signal_connect (GTK_OBJECT (client), "cal_loaded",
+ GTK_SIGNAL_FUNC (cal_loaded_cb), ca);
+
+ gtk_signal_connect (GTK_OBJECT (client), "obj_updated",
+ GTK_SIGNAL_FUNC (obj_updated_cb), ca);
+ gtk_signal_connect (GTK_OBJECT (client), "obj_removed",
+ GTK_SIGNAL_FUNC (obj_removed_cb), ca);
+
+ if (cal_client_is_loaded (client))
+ load_alarms (ca);
+}
+
+/**
+ * alarm_notify_remove_client:
+ * @client: A calendar client.
+ *
+ * Removes a calendar client from the alarm notification system.
+ **/
+void
+alarm_notify_remove_client (CalClient *client)
+{
+ ClientAlarms *ca;
+
+ g_return_if_fail (alarm_notify_inited);
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (IS_CAL_CLIENT (client));
+
+ ca = lookup_client (client);
+ g_return_if_fail (ca != NULL);
+
+ g_assert (ca->refcount > 0);
+ ca->refcount--;
+
+ if (ca->refcount > 0)
+ return;
+
+ /* FIXME: remove alarms */
+
+ /* Clean up */
+
+ gtk_signal_disconnect_by_data (GTK_OBJECT (ca->client), ca);
+
+ gtk_object_unref (GTK_OBJECT (ca->client));
+ ca->client = NULL;
+
+ g_hash_table_destroy (ca->uid_alarms_hash);
+ ca->uid_alarms_hash = NULL;
+
+ g_free (ca);
+
+ g_hash_table_remove (client_alarms_hash, client);
+}
diff --git a/calendar/gui/alarm-notify/alarm-queue.h b/calendar/gui/alarm-notify/alarm-queue.h
new file mode 100644
index 0000000000..f386a56ae4
--- /dev/null
+++ b/calendar/gui/alarm-notify/alarm-queue.h
@@ -0,0 +1,35 @@
+/* Evolution calendar - Alarm notification engine
+ *
+ * Copyright (C) 2000 Helix Code, Inc.
+ *
+ * Authors: Federico Mena-Quintero <federico@helixcode.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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ALARM_NOTIFY_H
+#define ALARM_NOTIFY_H
+
+#include <cal-client/cal-client.h>
+
+
+void alarm_notify_init (void);
+void alarm_notify_done (void);
+
+void alarm_notify_add_client (CalClient *client);
+void alarm_notify_remove_client (CalClient *client);
+
+
+#endif
diff --git a/calendar/gui/alarm-notify/alarm.c b/calendar/gui/alarm-notify/alarm.c
index 087ec518bc..a0fe90f337 100644
--- a/calendar/gui/alarm-notify/alarm.c
+++ b/calendar/gui/alarm-notify/alarm.c
@@ -1,4 +1,4 @@
-/* Evolution calendar - alarm notification support
+/* Evolution calendar - Low-level alarm timer mechanism
*
* Copyright (C) 2000 Helix Code, Inc.
*
@@ -21,15 +21,19 @@
*/
#include <config.h>
+#include <unistd.h>
#include <time.h>
-#include <gnome.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
+#include <gdk/gdk.h>
#include "alarm.h"
+/* Whether the timer system has been initialized */
+static gboolean alarm_inited;
+
/* The pipes used to notify about an alarm */
static int alarm_pipes [2];
@@ -72,6 +76,13 @@ setup_itimer (time_t diff)
return (v == 0) ? TRUE : FALSE;
}
+/* Clears the itimer we have pending */
+static gboolean
+clear_itimer (void)
+{
+ return setup_itimer (0);
+}
+
/* Removes the head alarm, returns it, and schedules the next alarm in the
* queue.
*/
@@ -106,20 +117,10 @@ pop_alarm (void)
* will fail?
*/
}
- } else {
- struct itimerval itimer;
- int v;
-
- itimer.it_interval.tv_sec = 0;
- itimer.it_interval.tv_usec = 0;
- itimer.it_value.tv_sec = 0;
- itimer.it_value.tv_usec = 0;
-
- v = setitimer (ITIMER_REAL, &itimer, NULL);
- if (v != 0)
+ } else
+ if (!clear_itimer ())
g_message ("pop_alarm(): Could not clear the timer! "
"Weird things may happen.");
- }
return ar;
}
@@ -144,7 +145,7 @@ alarm_ready (gpointer data, gint fd, GdkInputCondition cond)
(* ar->alarm_fn) (ar, ar->trigger, ar->data);
if (ar->destroy_notify_fn)
- (* ar->destroy_notify_fn) (ar->data);
+ (* ar->destroy_notify_fn) (ar, ar->data);
g_free (ar);
}
@@ -216,6 +217,10 @@ alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data,
time_t now;
AlarmRecord *ar;
+ g_return_val_if_fail (alarm_inited, NULL);
+ g_return_val_if_fail (trigger != -1, NULL);
+ g_return_val_if_fail (alarm_fn != NULL, NULL);
+
now = time (NULL);
if (trigger < now)
return NULL;
@@ -229,9 +234,6 @@ alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data,
g_print ("alarm_add(): Adding alarm for %s\n", ctime (&trigger));
if (!queue_alarm (now, ar)) {
- if (ar->destroy_notify_fn)
- (* ar->destroy_notify_fn) (ar->data);
-
g_free (ar);
ar = NULL;
}
@@ -252,6 +254,7 @@ alarm_remove (gpointer alarm)
AlarmRecord *old_head;
GList *l;
+ g_return_if_fail (alarm_inited);
g_return_if_fail (alarm != NULL);
ar = alarm;
@@ -272,7 +275,7 @@ alarm_remove (gpointer alarm)
}
if (ar->destroy_notify_fn)
- (* ar->destroy_notify_fn) (ar->data);
+ (* ar->destroy_notify_fn) (ar, ar->data);
g_free (ar);
}
@@ -280,7 +283,7 @@ alarm_remove (gpointer alarm)
/**
* alarm_init:
*
- * Initializes the alarm notification system. This must be called near the
+ * Initializes the alarm timer mechanism. This must be called near the
* beginning of the program.
**/
void
@@ -289,6 +292,8 @@ alarm_init (void)
struct sigaction sa;
int flags;
+ g_return_if_fail (alarm_inited == FALSE);
+
pipe (alarm_pipes);
/* set non blocking mode */
@@ -302,4 +307,50 @@ alarm_init (void)
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction (SIGALRM, &sa, NULL);
+
+ alarm_inited = TRUE;
+}
+
+/**
+ * alarm_done:
+ *
+ * Terminates the alarm timer mechanism. This should be called at the end of
+ * the program.
+ **/
+void
+alarm_done (void)
+{
+ GList *l;
+
+ g_return_if_fail (alarm_inited);
+
+ if (!clear_itimer ())
+ g_message ("alarm_done(): Could not clear the timer! "
+ "Weird things may happen.");
+
+ for (l = alarms; l; l = l->next) {
+ AlarmRecord *ar;
+
+ ar = l->data;
+
+ if (ar->destroy_notify_fn)
+ (* ar->destroy_notify_fn) (ar, ar->data);
+
+ g_free (ar);
+ }
+
+ g_list_free (alarms);
+ alarms = NULL;
+
+ if (close (alarm_pipes[0]) != 0)
+ g_message ("alarm_done(): Could not close the input pipe for notification");
+
+ alarm_pipes[0] = -1;
+
+ if (close (alarm_pipes[1]) != 0)
+ g_message ("alarm_done(): Could not close the output pipe for notification");
+
+ alarm_pipes[1] = -1;
+
+ alarm_inited = FALSE;
}
diff --git a/calendar/gui/alarm-notify/alarm.h b/calendar/gui/alarm-notify/alarm.h
index 2394d28024..afa79edfd8 100644
--- a/calendar/gui/alarm-notify/alarm.h
+++ b/calendar/gui/alarm-notify/alarm.h
@@ -1,4 +1,4 @@
-/* Evolution calendar - alarm notification support
+/* Evolution calendar - Low-level alarm timer mechanism
*
* Copyright (C) 2000 Helix Code, Inc.
*
@@ -24,16 +24,19 @@
#define ALARM_H
#include <time.h>
+#include <glib.h>
typedef void (* AlarmFunction) (gpointer alarm_id, time_t trigger, gpointer data);
-typedef void (* AlarmDestroyNotify) (gpointer data);
+typedef void (* AlarmDestroyNotify) (gpointer alarm_id, gpointer data);
-void alarm_init (void);
-gpointer alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data,
- AlarmDestroyNotify destroy_notify_fn);
-void alarm_remove (gpointer alarm);
+void alarm_init (void);
+void alarm_done (void);
+
+gpointer alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data,
+ AlarmDestroyNotify destroy_notify_fn);
+void alarm_remove (gpointer alarm);
diff --git a/calendar/gui/alarm.c b/calendar/gui/alarm.c
index 087ec518bc..a0fe90f337 100644
--- a/calendar/gui/alarm.c
+++ b/calendar/gui/alarm.c
@@ -1,4 +1,4 @@
-/* Evolution calendar - alarm notification support
+/* Evolution calendar - Low-level alarm timer mechanism
*
* Copyright (C) 2000 Helix Code, Inc.
*
@@ -21,15 +21,19 @@
*/
#include <config.h>
+#include <unistd.h>
#include <time.h>
-#include <gnome.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
+#include <gdk/gdk.h>
#include "alarm.h"
+/* Whether the timer system has been initialized */
+static gboolean alarm_inited;
+
/* The pipes used to notify about an alarm */
static int alarm_pipes [2];
@@ -72,6 +76,13 @@ setup_itimer (time_t diff)
return (v == 0) ? TRUE : FALSE;
}
+/* Clears the itimer we have pending */
+static gboolean
+clear_itimer (void)
+{
+ return setup_itimer (0);
+}
+
/* Removes the head alarm, returns it, and schedules the next alarm in the
* queue.
*/
@@ -106,20 +117,10 @@ pop_alarm (void)
* will fail?
*/
}
- } else {
- struct itimerval itimer;
- int v;
-
- itimer.it_interval.tv_sec = 0;
- itimer.it_interval.tv_usec = 0;
- itimer.it_value.tv_sec = 0;
- itimer.it_value.tv_usec = 0;
-
- v = setitimer (ITIMER_REAL, &itimer, NULL);
- if (v != 0)
+ } else
+ if (!clear_itimer ())
g_message ("pop_alarm(): Could not clear the timer! "
"Weird things may happen.");
- }
return ar;
}
@@ -144,7 +145,7 @@ alarm_ready (gpointer data, gint fd, GdkInputCondition cond)
(* ar->alarm_fn) (ar, ar->trigger, ar->data);
if (ar->destroy_notify_fn)
- (* ar->destroy_notify_fn) (ar->data);
+ (* ar->destroy_notify_fn) (ar, ar->data);
g_free (ar);
}
@@ -216,6 +217,10 @@ alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data,
time_t now;
AlarmRecord *ar;
+ g_return_val_if_fail (alarm_inited, NULL);
+ g_return_val_if_fail (trigger != -1, NULL);
+ g_return_val_if_fail (alarm_fn != NULL, NULL);
+
now = time (NULL);
if (trigger < now)
return NULL;
@@ -229,9 +234,6 @@ alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data,
g_print ("alarm_add(): Adding alarm for %s\n", ctime (&trigger));
if (!queue_alarm (now, ar)) {
- if (ar->destroy_notify_fn)
- (* ar->destroy_notify_fn) (ar->data);
-
g_free (ar);
ar = NULL;
}
@@ -252,6 +254,7 @@ alarm_remove (gpointer alarm)
AlarmRecord *old_head;
GList *l;
+ g_return_if_fail (alarm_inited);
g_return_if_fail (alarm != NULL);
ar = alarm;
@@ -272,7 +275,7 @@ alarm_remove (gpointer alarm)
}
if (ar->destroy_notify_fn)
- (* ar->destroy_notify_fn) (ar->data);
+ (* ar->destroy_notify_fn) (ar, ar->data);
g_free (ar);
}
@@ -280,7 +283,7 @@ alarm_remove (gpointer alarm)
/**
* alarm_init:
*
- * Initializes the alarm notification system. This must be called near the
+ * Initializes the alarm timer mechanism. This must be called near the
* beginning of the program.
**/
void
@@ -289,6 +292,8 @@ alarm_init (void)
struct sigaction sa;
int flags;
+ g_return_if_fail (alarm_inited == FALSE);
+
pipe (alarm_pipes);
/* set non blocking mode */
@@ -302,4 +307,50 @@ alarm_init (void)
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction (SIGALRM, &sa, NULL);
+
+ alarm_inited = TRUE;
+}
+
+/**
+ * alarm_done:
+ *
+ * Terminates the alarm timer mechanism. This should be called at the end of
+ * the program.
+ **/
+void
+alarm_done (void)
+{
+ GList *l;
+
+ g_return_if_fail (alarm_inited);
+
+ if (!clear_itimer ())
+ g_message ("alarm_done(): Could not clear the timer! "
+ "Weird things may happen.");
+
+ for (l = alarms; l; l = l->next) {
+ AlarmRecord *ar;
+
+ ar = l->data;
+
+ if (ar->destroy_notify_fn)
+ (* ar->destroy_notify_fn) (ar, ar->data);
+
+ g_free (ar);
+ }
+
+ g_list_free (alarms);
+ alarms = NULL;
+
+ if (close (alarm_pipes[0]) != 0)
+ g_message ("alarm_done(): Could not close the input pipe for notification");
+
+ alarm_pipes[0] = -1;
+
+ if (close (alarm_pipes[1]) != 0)
+ g_message ("alarm_done(): Could not close the output pipe for notification");
+
+ alarm_pipes[1] = -1;
+
+ alarm_inited = FALSE;
}
diff --git a/calendar/gui/alarm.h b/calendar/gui/alarm.h
index 2394d28024..afa79edfd8 100644
--- a/calendar/gui/alarm.h
+++ b/calendar/gui/alarm.h
@@ -1,4 +1,4 @@
-/* Evolution calendar - alarm notification support
+/* Evolution calendar - Low-level alarm timer mechanism
*
* Copyright (C) 2000 Helix Code, Inc.
*
@@ -24,16 +24,19 @@
#define ALARM_H
#include <time.h>
+#include <glib.h>
typedef void (* AlarmFunction) (gpointer alarm_id, time_t trigger, gpointer data);
-typedef void (* AlarmDestroyNotify) (gpointer data);
+typedef void (* AlarmDestroyNotify) (gpointer alarm_id, gpointer data);
-void alarm_init (void);
-gpointer alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data,
- AlarmDestroyNotify destroy_notify_fn);
-void alarm_remove (gpointer alarm);
+void alarm_init (void);
+void alarm_done (void);
+
+gpointer alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data,
+ AlarmDestroyNotify destroy_notify_fn);
+void alarm_remove (gpointer alarm);
diff --git a/calendar/gui/calendar-model.c b/calendar/gui/calendar-model.c
index beb9f338c8..67904b451a 100644
--- a/calendar/gui/calendar-model.c
+++ b/calendar/gui/calendar-model.c
@@ -1840,8 +1840,10 @@ calendar_model_set_cal_client (CalendarModel *model, CalClient *client, CalObjTy
priv->type = type;
if (priv->client) {
- gtk_signal_connect (GTK_OBJECT (priv->client), "cal_loaded",
- GTK_SIGNAL_FUNC (cal_loaded_cb), model);
+ if (!cal_client_is_loaded (priv->client))
+ gtk_signal_connect (GTK_OBJECT (priv->client), "cal_loaded",
+ GTK_SIGNAL_FUNC (cal_loaded_cb), model);
+
gtk_signal_connect (GTK_OBJECT (priv->client), "obj_updated",
GTK_SIGNAL_FUNC (obj_updated_cb), model);
gtk_signal_connect (GTK_OBJECT (priv->client), "obj_removed",
diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c
index 861b2e04ad..9a2bd82732 100644
--- a/calendar/gui/e-day-view.c
+++ b/calendar/gui/e-day-view.c
@@ -1449,8 +1449,10 @@ e_day_view_set_cal_client (EDayView *day_view,
day_view->client = client;
if (day_view->client) {
- gtk_signal_connect (GTK_OBJECT (day_view->client), "cal_loaded",
- GTK_SIGNAL_FUNC (cal_loaded_cb), day_view);
+ if (!cal_client_is_loaded (day_view->client))
+ gtk_signal_connect (GTK_OBJECT (day_view->client), "cal_loaded",
+ GTK_SIGNAL_FUNC (cal_loaded_cb), day_view);
+
gtk_signal_connect (GTK_OBJECT (day_view->client), "obj_updated",
GTK_SIGNAL_FUNC (obj_updated_cb), day_view);
gtk_signal_connect (GTK_OBJECT (day_view->client), "obj_removed",
diff --git a/calendar/gui/e-itip-control.c b/calendar/gui/e-itip-control.c
index 8e1ee0df22..4292878c9b 100644
--- a/calendar/gui/e-itip-control.c
+++ b/calendar/gui/e-itip-control.c
@@ -168,6 +168,10 @@ update_calendar (EItipControlPrivate *priv)
snprintf (cal_uri, 250, "%s/local/Calendar/calendar.ics", evolution_dir);
client = cal_client_new ();
+
+ gtk_signal_connect (GTK_OBJECT (client), "cal_loaded",
+ GTK_SIGNAL_FUNC (cal_loaded_cb), priv);
+
if (cal_client_load_calendar (client, cal_uri) == FALSE) {
GtkWidget *dialog;
@@ -178,9 +182,6 @@ update_calendar (EItipControlPrivate *priv)
return;
}
- gtk_signal_connect (GTK_OBJECT (client), "cal_loaded",
- GTK_SIGNAL_FUNC (cal_loaded_cb), priv);
-
gtk_progress_bar_update (GTK_PROGRESS_BAR (priv->loading_progress), 0.5);
gtk_widget_show (priv->loading_progress);
diff --git a/calendar/gui/e-week-view.c b/calendar/gui/e-week-view.c
index 916034991b..d5d5520105 100644
--- a/calendar/gui/e-week-view.c
+++ b/calendar/gui/e-week-view.c
@@ -976,8 +976,10 @@ e_week_view_set_cal_client (EWeekView *week_view,
week_view->client = client;
if (week_view->client) {
- gtk_signal_connect (GTK_OBJECT (week_view->client), "cal_loaded",
- GTK_SIGNAL_FUNC (cal_loaded_cb), week_view);
+ if (!cal_client_is_loaded (week_view->client))
+ gtk_signal_connect (GTK_OBJECT (week_view->client), "cal_loaded",
+ GTK_SIGNAL_FUNC (cal_loaded_cb), week_view);
+
gtk_signal_connect (GTK_OBJECT (week_view->client), "obj_updated",
GTK_SIGNAL_FUNC (obj_updated_cb), week_view);
gtk_signal_connect (GTK_OBJECT (week_view->client), "obj_removed",
diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c
index 3177912695..e3dae90a4d 100644
--- a/calendar/gui/gnome-cal.c
+++ b/calendar/gui/gnome-cal.c
@@ -18,7 +18,7 @@
#include <gal/e-paned/e-vpaned.h>
#include <cal-util/timeutil.h>
#include "dialogs/alarm-notify-dialog.h"
-#include "alarm.h"
+#include "alarm-notify.h"
#include "e-calendar-table.h"
#include "e-day-view.h"
#include "e-week-view.h"
@@ -109,7 +109,7 @@ struct _GnomeCalendarPrivate {
/* Alarm ID for the midnight refresh function */
gpointer midnight_alarm_refresh_id;
- /* UID->alarms hash */
+ /* UID->ObjectAlarms hash */
GHashTable *alarms;
/* Whether we are being destroyed and should not mess with the object
@@ -120,12 +120,24 @@ struct _GnomeCalendarPrivate {
-/* An entry in the UID->alarms hash table. The UID key *is* the uid field in
- * this structure, so don't free it separately.
- */
+/* A queued alarm for a component */
typedef struct {
- char *uid;
- GList *alarm_ids;
+ /* Alarm ID from alarm.h */
+ gpointer alarm_id;
+
+ /* Trigger instance this queued alarm refers to */
+ CalAlarmInstance *instance;
+} QueuedAlarm;
+
+/* An entry in the UID->alarms hash table */
+typedef struct {
+ /* The actual component we keep around; its UID *is* the key in the hash
+ * table.
+ */
+ CalComponentAlarms *alarms;
+
+ /* List of QueuedAlarm structures */
+ GList *queued_alarms;
} ObjectAlarms;
@@ -323,16 +335,30 @@ static void
free_object_alarms (gpointer key, gpointer value, gpointer data)
{
ObjectAlarms *oa;
+ GList *l;
oa = value;
- g_assert (oa->uid != NULL);
- g_free (oa->uid);
- oa->uid = NULL;
+ g_assert (oa->alarms != NULL);
+ cal_component_alarms_free (oa->alarms);
+ oa->alarms = NULL;
- g_assert (oa->alarm_ids != NULL);
- g_list_free (oa->alarm_ids);
- oa->alarm_ids = NULL;
+ g_assert (oa->queued_alarms != NULL);
+
+ for (l = oa->queued_alarms; l; l = l->next) {
+ QueuedAlarm *qa;
+
+ qa = l->data;
+
+ /* The instance structures were already freed by the call to
+ * cal_component_alarms_free().
+ */
+
+ g_free (qa);
+ }
+
+ g_list_free (oa->queued_alarms);
+ oa->queued_alarms = NULL;
g_free (oa);
}
@@ -373,6 +399,9 @@ gnome_calendar_destroy (GtkObject *object)
}
if (priv->client) {
+ if (cal_client_is_loaded (priv->client))
+ alarm_notify_remove_client (priv->client);
+
gtk_object_unref (GTK_OBJECT (priv->client));
priv->client = NULL;
}
@@ -880,13 +909,6 @@ audio_notification (time_t trigger, time_t occur, CalComponent *comp, GnomeCalen
/* FIXME */
}
-struct trigger_alarm_closure {
- GnomeCalendar *gcal;
- char *uid;
- CalComponentAlarmAction type;
- time_t occur;
-};
-
/* Callback function used when an alarm is triggered */
static void
trigger_alarm_cb (gpointer alarm_id, time_t trigger, gpointer data)
@@ -973,108 +995,7 @@ trigger_alarm_cb (gpointer alarm_id, time_t trigger, gpointer data)
gtk_object_unref (GTK_OBJECT (comp));
}
-/* Frees a struct trigger_alarm_closure */
-static void
-free_trigger_alarm_closure (gpointer data)
-{
- struct trigger_alarm_closure *c;
-
- c = data;
- g_free (c->uid);
- g_free (c);
-}
-
-/* Queues the specified alarm */
-static void
-setup_alarm (GnomeCalendar *gcal, CalAlarmInstance *ai)
-{
- GnomeCalendarPrivate *priv;
- struct trigger_alarm_closure *c;
- gpointer alarm;
- ObjectAlarms *oa;
-
- priv = gcal->priv;
-
- c = g_new (struct trigger_alarm_closure, 1);
- c->gcal = gcal;
- c->uid = g_strdup (ai->uid);
-#if 0
- c->type = ai->type;
#endif
- c->occur = ai->occur;
-
- alarm = alarm_add (ai->trigger, trigger_alarm_cb, c, free_trigger_alarm_closure);
- if (!alarm) {
- g_message ("setup_alarm(): Could not set up alarm");
- g_free (c->uid);
- g_free (c);
- return;
- }
-
- oa = g_hash_table_lookup (priv->alarms, ai->uid);
- if (oa)
- oa->alarm_ids = g_list_prepend (oa->alarm_ids, alarm);
- else {
- oa = g_new (ObjectAlarms, 1);
- oa->uid = g_strdup (ai->uid);
- oa->alarm_ids = g_list_prepend (NULL, alarm);
-
- g_hash_table_insert (priv->alarms, oa->uid, oa);
- }
-}
-
-static void load_alarms (GnomeCalendar *cal);
-
-/* Called nightly to refresh the day's alarms */
-static void
-midnight_refresh_cb (gpointer alarm_id, time_t trigger, gpointer data)
-{
- GnomeCalendar *cal;
- GnomeCalendarPrivate *priv;
-
- cal = GNOME_CALENDAR (data);
- priv = cal->priv;
-
- priv->midnight_alarm_refresh_id = NULL;
-
- load_alarms (cal);
-}
-
-#endif
-
-/* Loads and queues the alarms from the current time up to midnight. */
-static void
-load_alarms (GnomeCalendar *gcal)
-{
-#if 0
- GnomeCalendarPrivate *priv;
- time_t now;
- time_t end_of_day;
- GList *alarms, *l;
-
- priv = gcal->priv;
-
- now = time (NULL);
- end_of_day = time_day_end (now);
-
- /* Queue alarms */
-
- alarms = cal_client_get_alarms_in_range (priv->client, now, end_of_day);
-
- for (l = alarms; l; l = l->next)
- setup_alarm (gcal, l->data);
-
- cal_alarm_instance_list_free (alarms);
-
- /* Queue the midnight alarm refresh */
-
- priv->midnight_alarm_refresh_id = alarm_add (end_of_day, midnight_refresh_cb, gcal, NULL);
- if (!priv->midnight_alarm_refresh_id) {
- g_message ("load_alarms(): Could not set up the midnight refresh alarm!");
- /* FIXME: what to do? */
- }
-#endif
-}
/* Loads the initial data into the calendar; this should be called right after
* the cal_loaded signal from the client is invoked.
@@ -1086,69 +1007,9 @@ initial_load (GnomeCalendar *gcal)
priv = gcal->priv;
- load_alarms (gcal);
tag_calendar_by_client (priv->date_navigator, priv->client);
}
-#if 0
-/* Removes any queued alarms for the specified UID */
-static void
-remove_alarms_for_object (GnomeCalendar *gcal, const char *uid)
-{
- GnomeCalendarPrivate *priv;
- ObjectAlarms *oa;
- GList *l;
-
- priv = gcal->priv;
-
- oa = g_hash_table_lookup (priv->alarms, uid);
- if (!oa)
- return;
-
- for (l = oa->alarm_ids; l; l = l->next) {
- gpointer alarm_id;
-
- alarm_id = l->data;
- alarm_remove (alarm_id);
- }
-
- g_hash_table_remove (priv->alarms, uid);
-
- g_free (oa->uid);
- g_list_free (oa->alarm_ids);
- g_free (oa);
-}
-
-/* Adds today's alarms for the specified object */
-static void
-add_alarms_for_object (GnomeCalendar *gcal, const char *uid)
-{
- GnomeCalendarPrivate *priv;
- GList *alarms;
- gboolean result;
- time_t now, end_of_day;
- GList *l;
-
- priv = gcal->priv;
-
- now = time (NULL);
- end_of_day = time_day_end (now);
-
- result = cal_client_get_alarms_for_object (priv->client, uid, now, end_of_day, &alarms);
- if (!result) {
- /* FIXME: should we warn here, or is it OK if the object
- * disappeared in the meantime?
- */
- return;
- }
-
- for (l = alarms; l; l = l->next)
- setup_alarm (gcal, l->data);
-
- cal_alarm_instance_list_free (alarms);
-}
-#endif
-
/* Displays an error to indicate that loading a calendar failed */
static void
load_error (GnomeCalendar *gcal, const char *uri)
@@ -1282,11 +1143,6 @@ obj_updated_cb (CalClient *client, const char *uid, gpointer data)
gcal = GNOME_CALENDAR (data);
priv = gcal->priv;
-#if 0
- remove_alarms_for_object (gcal, uid);
- add_alarms_for_object (gcal, uid);
-#endif
-
tag_calendar_by_client (priv->date_navigator, priv->client);
}
@@ -1300,10 +1156,6 @@ obj_removed_cb (CalClient *client, const char *uid, gpointer data)
gcal = GNOME_CALENDAR (data);
priv = gcal->priv;
-#if 0
- remove_alarms_for_object (gcal, uid);
-#endif
-
tag_calendar_by_client (priv->date_navigator, priv->client);
}
@@ -1331,6 +1183,8 @@ gnome_calendar_construct (GnomeCalendar *gcal)
gtk_signal_connect (GTK_OBJECT (priv->client), "obj_removed",
GTK_SIGNAL_FUNC (obj_removed_cb), gcal);
+ alarm_notify_add_client (priv->client);
+
e_calendar_table_set_cal_client (E_CALENDAR_TABLE (priv->todo), priv->client);
e_day_view_set_cal_client (E_DAY_VIEW (priv->day_view), priv->client);
@@ -1378,9 +1232,9 @@ gnome_calendar_new (void)
/**
* gnome_calendar_get_cal_client:
* @gcal: A calendar view.
- *
+ *
* Queries the calendar client interface object that a calendar view is using.
- *
+ *
* Return value: A calendar client interface object.
**/
CalClient *
@@ -1689,7 +1543,7 @@ gnome_calendar_set_selected_time_range (GnomeCalendar *gcal,
* @gcal: A calendar view.
* @start_time: Return value for the start of the time selection.
* @end_time: Return value for the end of the time selection.
- *
+ *
* Queries the time selection range on the calendar view.
**/
void
diff --git a/calendar/gui/main.c b/calendar/gui/main.c
index 4f28031201..183d7bfd90 100644
--- a/calendar/gui/main.c
+++ b/calendar/gui/main.c
@@ -34,6 +34,7 @@
#include <gal/widgets/e-cursors.h>
#include "alarm.h"
+#include "alarm-notify.h"
#include "calendar-commands.h"
#include "calendar-config.h"
#include "component-factory.h"
@@ -63,6 +64,7 @@ main (int argc, char **argv)
init_bonobo (argc, argv);
glade_gnome_init ();
alarm_init ();
+ alarm_notify_init ();
e_cursors_init ();
init_calendar ();
@@ -81,6 +83,8 @@ main (int argc, char **argv)
bonobo_main ();
calendar_config_write_on_exit ();
+ alarm_notify_done ();
+ alarm_done ();
return 0;
}