From 4369c400fc801eef03a6fdda2b5256972f018246 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Fri, 22 Dec 2000 17:29:39 +0000 Subject: Alarm trigger queueing for the GUI part. 2000-12-21 Federico Mena Quintero 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 --- calendar/ChangeLog | 54 ++++ calendar/cal-client/cal-client.c | 4 +- calendar/gui/Makefile.am | 2 + calendar/gui/alarm-notify.c | 511 ++++++++++++++++++++++++++++++++ calendar/gui/alarm-notify.h | 35 +++ calendar/gui/alarm-notify/alarm-queue.c | 511 ++++++++++++++++++++++++++++++++ calendar/gui/alarm-notify/alarm-queue.h | 35 +++ calendar/gui/alarm-notify/alarm.c | 91 ++++-- calendar/gui/alarm-notify/alarm.h | 15 +- calendar/gui/alarm.c | 91 ++++-- calendar/gui/alarm.h | 15 +- calendar/gui/calendar-model.c | 6 +- calendar/gui/e-day-view.c | 6 +- calendar/gui/e-itip-control.c | 7 +- calendar/gui/e-week-view.c | 6 +- calendar/gui/gnome-cal.c | 240 +++------------ calendar/gui/main.c | 4 + 17 files changed, 1378 insertions(+), 255 deletions(-) create mode 100644 calendar/gui/alarm-notify.c create mode 100644 calendar/gui/alarm-notify.h create mode 100644 calendar/gui/alarm-notify/alarm-queue.c create mode 100644 calendar/gui/alarm-notify/alarm-queue.h (limited to 'calendar') 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 + + 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 * 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 + * + * 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 +#endif + +#include +#include +#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 + * + * 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 + + +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 + * + * 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 +#endif + +#include +#include +#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 + * + * 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 + + +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 +#include #include -#include #include #include #include +#include #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 +#include 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 +#include #include -#include #include #include #include +#include #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 +#include 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 #include #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 #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; } -- cgit v1.2.3