From d509f47a95ddc37b4ee566eb5980c27f6626c8dc Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Thu, 11 May 2000 17:31:30 +0000 Subject: Removed unused arguments. Load the initial alarms here. (load_alarms): New 2000-05-11 Federico Mena Quintero * gui/gnome-cal.c (gnome_calendar_update_all): Removed unused arguments. Load the initial alarms here. (load_alarms): New function to load a day's worth of alarms. (gnome_calendar_class_init): Eeeek! This was taking in an incorrect argument type. (gnome_calendar_init): Now the calendar keeps a hash table of UIDs->queued alarms. Create the hash table here. (gnome_calendar_destroy): Destroy the alarms hash table. (gnome_calendar_object_updated_cb): Remove the alarms for the object and regenerate them. (gnome_calendar_object_removed_cb): Remove the alarms for the object. * gui/alarm.c (alarm_add): Do not take in a CalendarAlarm, just the trigger time, the callback and the closure data. Return an opaque identifier for the alarm so that it can be removed by the client code if needed. Use the queue_alarm() helper function. (queue_alarm): Helper function to actually queue the alarm and set up the itimer. Deal with a nonzero return value from setitimer(). (alarm_remove): New function to remove an alarm based on its ID. (pop_alarm): New helper function; pops the first alarm of the queue and resets the timer as appropriate. (alarm_ready): Simplified a lot by using pop_alarm(). * idl/evolution-calendar.idl (Cal): Added get_alarms_in_range(). * pcs/cal.c (build_instance_seq): New function to build a CORBA sequence from the internal list of instances. (Cal_get_events_in_range): Use build_instance_seq(). (Cal_get_alarms_in_range): Implemented new method. * pcs/cal-backend.c (cal_backend_get_alarms_in_range): New function with the get_alarms_in_range() engine. * pcs/cal-backend-imc.c (cal_backend_imc_get_alarms_in_range): Implemented the get_alarms_in_range() method. * cal-client/cal-client.c (cal_client_get_alarms_in_range): New client-side function for getting the alarms. (build_instance_list): New helper function to build the CalObjInstance list from the CORBA sequence. (cal_client_get_events_in_range): Use build_instance_list(). * gui/calendar-commands.h: #include . #include "gnome-cal.h". * gui/e-week-view.c: #include "calendar-commands.h" instead of main.h; the latter is an obsolete file and will be killed. * gui/evolution-calendar-control.c (main): Call init_bonobo() before anything else. We need the GTK+ object system initialized. * gui/Makefile.am (evolution_calendar_SOURCES): Do not use main.h. * cal-util/cal-util.c (cal_alarm_instance_list_free): New function. svn path=/trunk/; revision=2987 --- calendar/ChangeLog | 59 ++++ calendar/cal-client/cal-client.c | 137 +++++++-- calendar/cal-client/cal-client.h | 2 + calendar/cal-util/cal-util.c | 29 +- calendar/cal-util/cal-util.h | 10 + calendar/gui/Makefile.am | 1 - calendar/gui/alarm-notify/alarm.c | 399 ++++++++++++++----------- calendar/gui/alarm-notify/alarm.h | 37 ++- calendar/gui/alarm.c | 399 ++++++++++++++----------- calendar/gui/alarm.h | 37 ++- calendar/gui/calendar-commands.c | 9 +- calendar/gui/calendar-commands.h | 4 +- calendar/gui/e-week-view.c | 2 +- calendar/gui/evolution-calendar-control.c | 4 +- calendar/gui/gnome-cal.c | 470 +++++++++++++++++++++++------- calendar/gui/gnome-cal.h | 6 + calendar/idl/evolution-calendar.idl | 24 ++ calendar/pcs/cal-backend-imc.c | 186 +++++++++++- calendar/pcs/cal-backend-imc.h | 1 + calendar/pcs/cal-backend.c | 22 ++ calendar/pcs/cal-backend.h | 3 + calendar/pcs/cal.c | 133 ++++++++- 22 files changed, 1461 insertions(+), 513 deletions(-) diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 5f90f5b41c..13e2b7e80f 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,62 @@ +2000-05-11 Federico Mena Quintero + + * gui/gnome-cal.c (gnome_calendar_update_all): Removed unused + arguments. Load the initial alarms here. + (load_alarms): New function to load a day's worth of alarms. + (gnome_calendar_class_init): Eeeek! This was taking in an + incorrect argument type. + (gnome_calendar_init): Now the calendar keeps a hash table of + UIDs->queued alarms. Create the hash table here. + (gnome_calendar_destroy): Destroy the alarms hash table. + (gnome_calendar_object_updated_cb): Remove the alarms for the + object and regenerate them. + (gnome_calendar_object_removed_cb): Remove the alarms for the + object. + + * gui/alarm.c (alarm_add): Do not take in a CalendarAlarm, just + the trigger time, the callback and the closure data. Return an + opaque identifier for the alarm so that it can be removed by the + client code if needed. Use the queue_alarm() helper function. + (queue_alarm): Helper function to actually queue the alarm and set + up the itimer. Deal with a nonzero return value from + setitimer(). + (alarm_remove): New function to remove an alarm based on its ID. + (pop_alarm): New helper function; pops the first alarm of the + queue and resets the timer as appropriate. + (alarm_ready): Simplified a lot by using pop_alarm(). + + * idl/evolution-calendar.idl (Cal): Added get_alarms_in_range(). + + * pcs/cal.c (build_instance_seq): New function to build a CORBA + sequence from the internal list of instances. + (Cal_get_events_in_range): Use build_instance_seq(). + (Cal_get_alarms_in_range): Implemented new method. + + * pcs/cal-backend.c (cal_backend_get_alarms_in_range): New + function with the get_alarms_in_range() engine. + + * pcs/cal-backend-imc.c (cal_backend_imc_get_alarms_in_range): + Implemented the get_alarms_in_range() method. + + * cal-client/cal-client.c (cal_client_get_alarms_in_range): New + client-side function for getting the alarms. + (build_instance_list): New helper function to build the + CalObjInstance list from the CORBA sequence. + (cal_client_get_events_in_range): Use build_instance_list(). + + * gui/calendar-commands.h: #include . #include + "gnome-cal.h". + + * gui/e-week-view.c: #include "calendar-commands.h" instead of + main.h; the latter is an obsolete file and will be killed. + + * gui/evolution-calendar-control.c (main): Call init_bonobo() + before anything else. We need the GTK+ object system initialized. + + * gui/Makefile.am (evolution_calendar_SOURCES): Do not use main.h. + + * cal-util/cal-util.c (cal_alarm_instance_list_free): New function. + 2000-05-10 Matt Loper * gui/calendar-commands.c (calendar_control_activate): Move diff --git a/calendar/cal-client/cal-client.c b/calendar/cal-client/cal-client.c index c7f5302d14..2e83345d27 100644 --- a/calendar/cal-client/cal-client.c +++ b/calendar/cal-client/cal-client.c @@ -654,6 +654,34 @@ cal_client_get_uids (CalClient *client, CalObjType type) return uids; } +/* Builds a GList of CalObjInstance structures from the CORBA sequence */ +static GList * +build_object_instance_list (Evolution_Calendar_CalObjInstanceSeq *seq) +{ + GList *list; + int i; + + /* Create the list in reverse order */ + + list = NULL; + for (i = 0; i < seq->_length; i++) { + Evolution_Calendar_CalObjInstance *corba_icoi; + CalObjInstance *icoi; + + corba_icoi = &seq->_buffer[i]; + icoi = g_new (CalObjInstance, 1); + + icoi->uid = g_strdup (corba_icoi->uid); + icoi->start = corba_icoi->start; + icoi->end = corba_icoi->end; + + list = g_list_prepend (list, icoi); + } + + list = g_list_reverse (list); + return list; +} + /** * cal_client_get_events_in_range: * @client: A calendar client. @@ -671,22 +699,18 @@ cal_client_get_events_in_range (CalClient *client, time_t start, time_t end) CalClientPrivate *priv; CORBA_Environment ev; Evolution_Calendar_CalObjInstanceSeq *seq; - GList *elist; - int i; + GList *events; g_return_val_if_fail (client != NULL, NULL); g_return_val_if_fail (IS_CAL_CLIENT (client), NULL); priv = client->priv; - /*g_return_val_if_fail (priv->load_state == LOAD_STATE_LOADED, NULL);*/ if (priv->load_state != LOAD_STATE_LOADED) return NULL; g_return_val_if_fail (start != -1 && end != -1, NULL); g_return_val_if_fail (start <= end, NULL); - priv = client->priv; - CORBA_exception_init (&ev); seq = Evolution_Calendar_Cal_get_events_in_range (priv->cal, start, end, &ev); @@ -697,28 +721,107 @@ cal_client_get_events_in_range (CalClient *client, time_t start, time_t end) } CORBA_exception_free (&ev); - /* Create the list in reverse order */ + events = build_object_instance_list (seq); + CORBA_free (seq); + + return events; +} + +/* Translates the CORBA representation of an AlarmType */ +static enum AlarmType +uncorba_alarm_type (Evolution_Calendar_AlarmType corba_type) +{ + switch (corba_type) { + case Evolution_Calendar_MAIL: + return ALARM_MAIL; + + case Evolution_Calendar_PROGRAM: + return ALARM_PROGRAM; + + case Evolution_Calendar_DISPLAY: + return ALARM_DISPLAY; + + case Evolution_Calendar_AUDIO: + return ALARM_AUDIO; + + default: + g_assert_not_reached (); + return ALARM_DISPLAY; + } +} + +/* Builds a GList of CalAlarmInstance structures from the CORBA sequence */ +static GList * +build_alarm_instance_list (Evolution_Calendar_CalAlarmInstanceSeq *seq) +{ + GList *list; + int i; - elist = NULL; + /* Create the list in reverse order */ + list = NULL; for (i = 0; i < seq->_length; i++) { - Evolution_Calendar_CalObjInstance *corba_icoi; - CalObjInstance *icoi; + Evolution_Calendar_CalAlarmInstance *corba_ai; + CalAlarmInstance *ai; - corba_icoi = &seq->_buffer[i]; - icoi = g_new (CalObjInstance, 1); + corba_ai = &seq->_buffer[i]; + ai = g_new (CalAlarmInstance, 1); - icoi->uid = g_strdup (corba_icoi->uid); - icoi->start = corba_icoi->start; - icoi->end = corba_icoi->end; + ai->uid = g_strdup (corba_ai->uid); + ai->type = uncorba_alarm_type (corba_ai->type); + ai->trigger = corba_ai->trigger; + ai->occur = corba_ai->occur; - elist = g_list_prepend (elist, icoi); + list = g_list_prepend (list, ai); } + list = g_list_reverse (list); + return list; +} + +/** + * cal_client_get_alarms_in_range: + * @client: A calendar client. + * @start: Start time for query. + * @end: End time for query. + * + * Queries a calendar for the alarms that trigger in the specified range of + * time. + * + * Return value: A list of #CalAlarmInstance structures. + **/ +GList * +cal_client_get_alarms_in_range (CalClient *client, time_t start, time_t end) +{ + CalClientPrivate *priv; + CORBA_Environment ev; + Evolution_Calendar_CalAlarmInstanceSeq *seq; + GList *alarms; + + g_return_val_if_fail (client != NULL, NULL); + g_return_val_if_fail (IS_CAL_CLIENT (client), NULL); + + priv = client->priv; + if (priv->load_state != LOAD_STATE_LOADED) + return NULL; + + g_return_val_if_fail (start != -1 && end != -1, NULL); + g_return_val_if_fail (start <= end, NULL); + + CORBA_exception_init (&ev); + + seq = Evolution_Calendar_Cal_get_alarms_in_range (priv->cal, start, end, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_message ("cal_client_get_alarms_in_range(): could not get the alarm range"); + CORBA_exception_free (&ev); + return NULL; + } + CORBA_exception_free (&ev); + + alarms = build_alarm_instance_list (seq); CORBA_free (seq); - elist = g_list_reverse (elist); - return elist; + return alarms; } /** diff --git a/calendar/cal-client/cal-client.h b/calendar/cal-client/cal-client.h index e4731980e0..5bb672229d 100644 --- a/calendar/cal-client/cal-client.h +++ b/calendar/cal-client/cal-client.h @@ -80,6 +80,8 @@ GList *cal_client_get_uids (CalClient *client, CalObjType type); GList *cal_client_get_events_in_range (CalClient *client, time_t start, time_t end); +GList *cal_client_get_alarms_in_range (CalClient *client, time_t start, time_t end); + gboolean cal_client_update_object (CalClient *client, const char *uid, const char *calobj); gboolean cal_client_remove_object (CalClient *client, const char *uid); diff --git a/calendar/cal-util/cal-util.c b/calendar/cal-util/cal-util.c index fc5a22dae9..40c4fe5bfe 100644 --- a/calendar/cal-util/cal-util.c +++ b/calendar/cal-util/cal-util.c @@ -28,9 +28,9 @@ /** * cal_obj_instance_list_free: - * @list: List of CalObjInstance structures. + * @list: List of #CalObjInstance structures. * - * Frees a list of CalObjInstance structures. + * Frees a list of #CalObjInstance structures. **/ void cal_obj_instance_list_free (GList *list) @@ -51,6 +51,31 @@ cal_obj_instance_list_free (GList *list) g_list_free (list); } +/** + * cal_alarm_instance_list_free: + * @list: List of #CalAlarmInstance structures. + * + * Frees a list of #CalAlarmInstance structures. + **/ +void +cal_alarm_instance_list_free (GList *list) +{ + CalAlarmInstance *i; + GList *l; + + for (l = list; l; l = l->next) { + i = l->data; + + g_assert (i != NULL); + g_assert (i->uid != NULL); + + g_free (i->uid); + g_free (i); + } + + g_list_free (list); +} + /** * cal_obj_uid_list_free: * @list: List of strings with unique identifiers. diff --git a/calendar/cal-util/cal-util.h b/calendar/cal-util/cal-util.h index 7821ce8ca4..b222ea8cba 100644 --- a/calendar/cal-util/cal-util.h +++ b/calendar/cal-util/cal-util.h @@ -42,6 +42,16 @@ typedef struct { void cal_obj_instance_list_free (GList *list); +/* Instance of an alarm trigger */ +typedef struct { + char *uid; /* UID of object */ + enum AlarmType type; /* Type of alarm */ + time_t trigger; /* Alarm trigger time */ + time_t occur; /* Occurrence time */ +} CalAlarmInstance; + +void cal_alarm_instance_list_free (GList *list); + /* Used for multiple UID queries */ typedef enum { CALOBJ_TYPE_EVENT = 1 << 0, diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am index 3785d3df08..cffe6aaa22 100644 --- a/calendar/gui/Makefile.am +++ b/calendar/gui/Makefile.am @@ -60,7 +60,6 @@ evolution_calendar_SOURCES = \ goto.c \ mark.c \ mark.h \ - main.h \ popup-menu.c \ popup-menu.h \ prop.c \ diff --git a/calendar/gui/alarm-notify/alarm.c b/calendar/gui/alarm-notify/alarm.c index ae76a0ce73..20f64ffd75 100644 --- a/calendar/gui/alarm-notify/alarm.c +++ b/calendar/gui/alarm-notify/alarm.c @@ -1,10 +1,25 @@ -/* - * Alarm handling for the GNOME Calendar. +/* Evolution calendar - alarm notification support * - * (C) 1998 the Free Software Foundation + * Copyright (C) 2000 Helix Code, Inc. * - * Author: Miguel de Icaza (miguel@kernel.org) + * Authors: Miguel de Icaza + * 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. */ + #include #include #include @@ -14,237 +29,277 @@ #include #include "alarm.h" + + /* The pipes used to notify about an alarm */ -int alarm_pipes [2]; +static int alarm_pipes [2]; /* The list of pending alarms */ static GList *alarms; -static void *head_alarm; - +/* A queued alarm structure */ typedef struct { - time_t activation_time; - AlarmFunction fn; - void *closure; - CalendarAlarm *alarm; + time_t trigger; + AlarmFunction alarm_fn; + gpointer data; + AlarmDestroyNotify destroy_notify_fn; } AlarmRecord; -enum DebugAction { - ALARM_ACTIVATED, - ALARM_ADDED, - ALARM_NOT_ADDED -}; - -void debug_alarm (AlarmRecord* ar, enum DebugAction action); -void calendar_notify (time_t time, CalendarAlarm *which, void *data); -extern int debug_alarms; + -/* - * SIGALRM handler. Notifies the callback about the alarm - */ +/* SIGALRM handler. Notifies the callback about the alarm. */ static void -alarm_activate () +alarm_signal (int arg) { char c = 0; write (alarm_pipes [1], &c, 1); } -/* - * SIGUSR1 handler. Toggles debugging output +/* Sets up an itimer and returns a success code */ +static gboolean +setup_itimer (time_t diff) +{ + struct itimerval itimer; + int v; + + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = 0; + itimer.it_value.tv_sec = diff; + itimer.it_value.tv_usec = 0; + + v = setitimer (ITIMER_REAL, &itimer, NULL); + + return (v == 0) ? TRUE : FALSE; +} + +/* Removes the head alarm, returns it, and schedules the next alarm in the + * queue. */ -static void -toggle_debugging () +static AlarmRecord * +pop_alarm (void) { - debug_alarms = !debug_alarms; + AlarmRecord *ar; + GList *l; + + if (!alarms) + return NULL; + + ar = alarms->data; + + l = alarms; + alarms = g_list_remove_link (alarms, l); + g_list_free_1 (l); + + if (alarms) { + time_t now; + AlarmRecord *new_ar; + + now = time (NULL); + new_ar = alarms->data; + + if (!setup_itimer (new_ar->trigger)) { + g_message ("pop_alarm(): Could not reset the timer! " + "Weird things will happen."); + + /* FIXME: should we free the alarm list? What + * about further alarm removal requests that + * 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) + g_message ("pop_alarm(): Could not clear the timer! " + "Weird things may happen."); + } + + return ar; } +/* Input handler for our own alarm notification pipe */ static void -alarm_ready (void *closure, int fd, GdkInputCondition cond) +alarm_ready (gpointer data, gint fd, GdkInputCondition cond) { - AlarmRecord *ar = head_alarm; - time_t now = time (NULL); + AlarmRecord *ar; char c; - if (read (alarm_pipes [0], &c, 1) != 1) - return; - - if (ar == NULL){ - g_warning ("Empty events. This should not happen\n"); + if (read (alarm_pipes [0], &c, 1) != 1) { + g_message ("alarm_ready(): Uh? Could not read from notification pipe."); return; } - while (head_alarm){ - if (debug_alarms) - debug_alarm (ar, ALARM_ACTIVATED); - (*ar->fn)(ar->activation_time, ar->alarm, ar->closure); - alarms = g_list_remove (alarms, head_alarm); - - /* Schedule next alarm */ - if (alarms){ - AlarmRecord *next; - - head_alarm = alarms->data; - next = head_alarm; - - if (next->activation_time > now){ - struct itimerval itimer; - - itimer.it_interval.tv_sec = 0; - itimer.it_interval.tv_usec = 0; - itimer.it_value.tv_sec = next->activation_time - now; - itimer.it_value.tv_usec = 0; - setitimer (ITIMER_REAL, &itimer, NULL); - break; - } else { - g_free (ar); - ar = next; - } - } else - head_alarm = NULL; - } + g_assert (alarms != NULL); + ar = pop_alarm (); + + g_message ("alarm_ready(): Notifying about alarm on %s", ctime (&ar->trigger)); + + (* ar->alarm_fn) (ar, ar->trigger, ar->data); + + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (ar->data); + g_free (ar); } static int -alarm_compare_by_time (gconstpointer a, gconstpointer b) +compare_alarm_by_time (gconstpointer a, gconstpointer b) { const AlarmRecord *ara = a; const AlarmRecord *arb = b; time_t diff; - - diff = ara->activation_time - arb->activation_time; + + diff = ara->trigger - arb->trigger; return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; } +/* Adds an alarm to the queue and sets up the timer */ +static gboolean +queue_alarm (time_t now, AlarmRecord *ar) +{ + time_t diff; + AlarmRecord *old_head; + + if (alarms) + old_head = alarms->data; + else + old_head = NULL; + + alarms = g_list_insert_sorted (alarms, ar, compare_alarm_by_time); + + if (old_head == alarms->data) + return TRUE; + + /* Set the timer for removal upon activation */ + + diff = ar->trigger - now; + if (!setup_itimer (diff)) { + GList *l; + + g_message ("queue_alarm(): Could not set up timer! Not queueing alarm."); + + l = g_list_find (alarms, ar); + g_assert (l != NULL); + + alarms = g_list_remove_link (alarms, l); + g_list_free_1 (l); + return FALSE; + } + + return TRUE; +} + /** * alarm_add: + * @trigger: Time at which alarm will trigger. + * @alarm_fn: Callback for trigger. + * @data: Closure data for callback. * - * Tries to schedule @alarm. + * Adds an alarm to trigger at the specified time. The @alarm_fn will be called + * with the provided data and the alarm will be removed from the trigger list. * - * Returns TRUE if the alarm was scheduled. - */ -gboolean -alarm_add (CalendarAlarm *alarm, AlarmFunction fn, void *closure) + * Return value: An identifier for this alarm; it can be used to remove the + * alarm later with alarm_remove(). If the trigger time occurs in the past, then + * the alarm will not be queued and the function will return NULL. + **/ +gpointer +alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data, + AlarmDestroyNotify destroy_notify_fn) { - time_t now = time (NULL); + time_t now; AlarmRecord *ar; - time_t alarm_time = alarm->trigger; - - ar = g_new0 (AlarmRecord, 1); - ar->activation_time = alarm_time; - ar->fn = fn; - ar->closure = closure; - ar->alarm = alarm; - - /* If it already expired, do not add it */ - if (alarm_time < now) { - if (debug_alarms) - debug_alarm (ar, ALARM_NOT_ADDED); - return FALSE; - } - alarms = g_list_insert_sorted (alarms, ar, alarm_compare_by_time); + now = time (NULL); + if (trigger < now) + return NULL; - /* If first alarm is not the previous first alarm, reschedule SIGALRM */ - if (head_alarm != alarms->data){ - struct itimerval itimer; - int v; - - /* Set the timer to disable upon activation */ - itimer.it_interval.tv_sec = 0; - itimer.it_interval.tv_usec = 0; - itimer.it_value.tv_sec = alarm_time - now; - itimer.it_value.tv_usec = 0; - v = setitimer (ITIMER_REAL, &itimer, NULL); - head_alarm = alarms->data; + ar = g_new (AlarmRecord, 1); + ar->trigger = trigger; + ar->alarm_fn = alarm_fn; + ar->data = data; + ar->destroy_notify_fn = destroy_notify_fn; + + g_message ("alarm_add(): Adding alarm for %s", ctime (&trigger)); + + if (!queue_alarm (now, ar)) { + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (ar->data); + + g_free (ar); + ar = NULL; } - if (debug_alarms) - debug_alarm (ar, ALARM_ADDED); - return TRUE; + + return ar; } -int -alarm_kill (void *closure_key) +/** + * alarm_remove: + * @alarm: A queued alarm identifier. + * + * Removes an alarm from the alarm queue. + **/ +void +alarm_remove (gpointer alarm) { - GList *p; - - for (p = alarms; p; p = p->next){ - AlarmRecord *ar = p->data; - - if (ar->closure == closure_key){ - alarms = g_list_remove (alarms, p->data); - if (alarms) - head_alarm = alarms->data; - else - head_alarm = NULL; - return 1; - } + AlarmRecord *ar; + AlarmRecord *old_head; + GList *l; + + ar = alarm; + + l = g_list_find (alarms, ar); + if (!l) { + g_message ("alarm_remove(): Requested removal of nonexistent alarm!"); + return; } - return 0; + + old_head = alarms->data; + + if (old_head == ar) + pop_alarm (); + else { + alarms = g_list_remove_link (alarms, l); + g_list_free_1 (l); + } + + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (ar->data); + + g_free (ar); } +/** + * alarm_init: + * @void: + * + * Initializes the alarm notification system. This must be called near the + * beginning of the program. + **/ void alarm_init (void) { struct sigaction sa; - struct sigaction debug_sa; - int flags = 0; - + int flags; + pipe (alarm_pipes); - + /* set non blocking mode */ + flags = 0; fcntl (alarm_pipes [0], F_GETFL, &flags); fcntl (alarm_pipes [0], F_SETFL, flags | O_NONBLOCK); - gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, 0); + gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, NULL); /* Setup the signal handler */ - sa.sa_handler = alarm_activate; + sa.sa_handler = alarm_signal; sigemptyset (&sa.sa_mask); - sa.sa_flags = SA_RESTART; + sa.sa_flags = SA_RESTART; sigaction (SIGALRM, &sa, NULL); - - /* Setup a signal handler to toggle debugging */ - debug_sa.sa_handler = toggle_debugging; - sigemptyset (&debug_sa.sa_mask); - debug_sa.sa_flags = SA_RESTART; - sigaction (SIGUSR1, &debug_sa, NULL); -} - -void -debug_alarm (AlarmRecord* ar, enum DebugAction action) -{ - time_t now = time (NULL); - iCalObject *ico = ar->closure; - printf ("%s", ctime(&now)); - switch (action) { - case ALARM_ADDED: - printf ("Added alarm for %s", ctime(&ar->activation_time)); - break; - case ALARM_NOT_ADDED: - printf ("Alarm not added for %s", ctime(&ar->activation_time)); - break; - case ALARM_ACTIVATED: - printf ("Activated alarm\n"); - break; - } - - if (ar->fn!=&calendar_notify) return; - printf ("--- Summary: %s\n", ico->summary); - switch (ar->alarm->type) { - case ALARM_MAIL: - printf ("--- Type: Mail\n"); - break; - case ALARM_PROGRAM: - printf ("--- Type: Program\n"); - break; - case ALARM_DISPLAY: - printf ("--- Type: Display\n"); - break; - case ALARM_AUDIO: - printf ("--- Type: Audio\n"); - break; - } } - - diff --git a/calendar/gui/alarm-notify/alarm.h b/calendar/gui/alarm-notify/alarm.h index efdfda997d..2394d28024 100644 --- a/calendar/gui/alarm-notify/alarm.h +++ b/calendar/gui/alarm-notify/alarm.h @@ -1,13 +1,40 @@ +/* Evolution calendar - alarm notification support + * + * Copyright (C) 2000 Helix Code, Inc. + * + * Authors: Miguel de Icaza + * 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_H #define ALARM_H #include -#include -typedef void (*AlarmFunction)(time_t time, CalendarAlarm *which, void *closuse); + + +typedef void (* AlarmFunction) (gpointer alarm_id, time_t trigger, gpointer data); +typedef void (* AlarmDestroyNotify) (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); -gboolean alarm_add (CalendarAlarm *alarm, AlarmFunction fn, void *closure); -int alarm_kill (void *closure); + #endif diff --git a/calendar/gui/alarm.c b/calendar/gui/alarm.c index ae76a0ce73..20f64ffd75 100644 --- a/calendar/gui/alarm.c +++ b/calendar/gui/alarm.c @@ -1,10 +1,25 @@ -/* - * Alarm handling for the GNOME Calendar. +/* Evolution calendar - alarm notification support * - * (C) 1998 the Free Software Foundation + * Copyright (C) 2000 Helix Code, Inc. * - * Author: Miguel de Icaza (miguel@kernel.org) + * Authors: Miguel de Icaza + * 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. */ + #include #include #include @@ -14,237 +29,277 @@ #include #include "alarm.h" + + /* The pipes used to notify about an alarm */ -int alarm_pipes [2]; +static int alarm_pipes [2]; /* The list of pending alarms */ static GList *alarms; -static void *head_alarm; - +/* A queued alarm structure */ typedef struct { - time_t activation_time; - AlarmFunction fn; - void *closure; - CalendarAlarm *alarm; + time_t trigger; + AlarmFunction alarm_fn; + gpointer data; + AlarmDestroyNotify destroy_notify_fn; } AlarmRecord; -enum DebugAction { - ALARM_ACTIVATED, - ALARM_ADDED, - ALARM_NOT_ADDED -}; - -void debug_alarm (AlarmRecord* ar, enum DebugAction action); -void calendar_notify (time_t time, CalendarAlarm *which, void *data); -extern int debug_alarms; + -/* - * SIGALRM handler. Notifies the callback about the alarm - */ +/* SIGALRM handler. Notifies the callback about the alarm. */ static void -alarm_activate () +alarm_signal (int arg) { char c = 0; write (alarm_pipes [1], &c, 1); } -/* - * SIGUSR1 handler. Toggles debugging output +/* Sets up an itimer and returns a success code */ +static gboolean +setup_itimer (time_t diff) +{ + struct itimerval itimer; + int v; + + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = 0; + itimer.it_value.tv_sec = diff; + itimer.it_value.tv_usec = 0; + + v = setitimer (ITIMER_REAL, &itimer, NULL); + + return (v == 0) ? TRUE : FALSE; +} + +/* Removes the head alarm, returns it, and schedules the next alarm in the + * queue. */ -static void -toggle_debugging () +static AlarmRecord * +pop_alarm (void) { - debug_alarms = !debug_alarms; + AlarmRecord *ar; + GList *l; + + if (!alarms) + return NULL; + + ar = alarms->data; + + l = alarms; + alarms = g_list_remove_link (alarms, l); + g_list_free_1 (l); + + if (alarms) { + time_t now; + AlarmRecord *new_ar; + + now = time (NULL); + new_ar = alarms->data; + + if (!setup_itimer (new_ar->trigger)) { + g_message ("pop_alarm(): Could not reset the timer! " + "Weird things will happen."); + + /* FIXME: should we free the alarm list? What + * about further alarm removal requests that + * 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) + g_message ("pop_alarm(): Could not clear the timer! " + "Weird things may happen."); + } + + return ar; } +/* Input handler for our own alarm notification pipe */ static void -alarm_ready (void *closure, int fd, GdkInputCondition cond) +alarm_ready (gpointer data, gint fd, GdkInputCondition cond) { - AlarmRecord *ar = head_alarm; - time_t now = time (NULL); + AlarmRecord *ar; char c; - if (read (alarm_pipes [0], &c, 1) != 1) - return; - - if (ar == NULL){ - g_warning ("Empty events. This should not happen\n"); + if (read (alarm_pipes [0], &c, 1) != 1) { + g_message ("alarm_ready(): Uh? Could not read from notification pipe."); return; } - while (head_alarm){ - if (debug_alarms) - debug_alarm (ar, ALARM_ACTIVATED); - (*ar->fn)(ar->activation_time, ar->alarm, ar->closure); - alarms = g_list_remove (alarms, head_alarm); - - /* Schedule next alarm */ - if (alarms){ - AlarmRecord *next; - - head_alarm = alarms->data; - next = head_alarm; - - if (next->activation_time > now){ - struct itimerval itimer; - - itimer.it_interval.tv_sec = 0; - itimer.it_interval.tv_usec = 0; - itimer.it_value.tv_sec = next->activation_time - now; - itimer.it_value.tv_usec = 0; - setitimer (ITIMER_REAL, &itimer, NULL); - break; - } else { - g_free (ar); - ar = next; - } - } else - head_alarm = NULL; - } + g_assert (alarms != NULL); + ar = pop_alarm (); + + g_message ("alarm_ready(): Notifying about alarm on %s", ctime (&ar->trigger)); + + (* ar->alarm_fn) (ar, ar->trigger, ar->data); + + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (ar->data); + g_free (ar); } static int -alarm_compare_by_time (gconstpointer a, gconstpointer b) +compare_alarm_by_time (gconstpointer a, gconstpointer b) { const AlarmRecord *ara = a; const AlarmRecord *arb = b; time_t diff; - - diff = ara->activation_time - arb->activation_time; + + diff = ara->trigger - arb->trigger; return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; } +/* Adds an alarm to the queue and sets up the timer */ +static gboolean +queue_alarm (time_t now, AlarmRecord *ar) +{ + time_t diff; + AlarmRecord *old_head; + + if (alarms) + old_head = alarms->data; + else + old_head = NULL; + + alarms = g_list_insert_sorted (alarms, ar, compare_alarm_by_time); + + if (old_head == alarms->data) + return TRUE; + + /* Set the timer for removal upon activation */ + + diff = ar->trigger - now; + if (!setup_itimer (diff)) { + GList *l; + + g_message ("queue_alarm(): Could not set up timer! Not queueing alarm."); + + l = g_list_find (alarms, ar); + g_assert (l != NULL); + + alarms = g_list_remove_link (alarms, l); + g_list_free_1 (l); + return FALSE; + } + + return TRUE; +} + /** * alarm_add: + * @trigger: Time at which alarm will trigger. + * @alarm_fn: Callback for trigger. + * @data: Closure data for callback. * - * Tries to schedule @alarm. + * Adds an alarm to trigger at the specified time. The @alarm_fn will be called + * with the provided data and the alarm will be removed from the trigger list. * - * Returns TRUE if the alarm was scheduled. - */ -gboolean -alarm_add (CalendarAlarm *alarm, AlarmFunction fn, void *closure) + * Return value: An identifier for this alarm; it can be used to remove the + * alarm later with alarm_remove(). If the trigger time occurs in the past, then + * the alarm will not be queued and the function will return NULL. + **/ +gpointer +alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data, + AlarmDestroyNotify destroy_notify_fn) { - time_t now = time (NULL); + time_t now; AlarmRecord *ar; - time_t alarm_time = alarm->trigger; - - ar = g_new0 (AlarmRecord, 1); - ar->activation_time = alarm_time; - ar->fn = fn; - ar->closure = closure; - ar->alarm = alarm; - - /* If it already expired, do not add it */ - if (alarm_time < now) { - if (debug_alarms) - debug_alarm (ar, ALARM_NOT_ADDED); - return FALSE; - } - alarms = g_list_insert_sorted (alarms, ar, alarm_compare_by_time); + now = time (NULL); + if (trigger < now) + return NULL; - /* If first alarm is not the previous first alarm, reschedule SIGALRM */ - if (head_alarm != alarms->data){ - struct itimerval itimer; - int v; - - /* Set the timer to disable upon activation */ - itimer.it_interval.tv_sec = 0; - itimer.it_interval.tv_usec = 0; - itimer.it_value.tv_sec = alarm_time - now; - itimer.it_value.tv_usec = 0; - v = setitimer (ITIMER_REAL, &itimer, NULL); - head_alarm = alarms->data; + ar = g_new (AlarmRecord, 1); + ar->trigger = trigger; + ar->alarm_fn = alarm_fn; + ar->data = data; + ar->destroy_notify_fn = destroy_notify_fn; + + g_message ("alarm_add(): Adding alarm for %s", ctime (&trigger)); + + if (!queue_alarm (now, ar)) { + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (ar->data); + + g_free (ar); + ar = NULL; } - if (debug_alarms) - debug_alarm (ar, ALARM_ADDED); - return TRUE; + + return ar; } -int -alarm_kill (void *closure_key) +/** + * alarm_remove: + * @alarm: A queued alarm identifier. + * + * Removes an alarm from the alarm queue. + **/ +void +alarm_remove (gpointer alarm) { - GList *p; - - for (p = alarms; p; p = p->next){ - AlarmRecord *ar = p->data; - - if (ar->closure == closure_key){ - alarms = g_list_remove (alarms, p->data); - if (alarms) - head_alarm = alarms->data; - else - head_alarm = NULL; - return 1; - } + AlarmRecord *ar; + AlarmRecord *old_head; + GList *l; + + ar = alarm; + + l = g_list_find (alarms, ar); + if (!l) { + g_message ("alarm_remove(): Requested removal of nonexistent alarm!"); + return; } - return 0; + + old_head = alarms->data; + + if (old_head == ar) + pop_alarm (); + else { + alarms = g_list_remove_link (alarms, l); + g_list_free_1 (l); + } + + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (ar->data); + + g_free (ar); } +/** + * alarm_init: + * @void: + * + * Initializes the alarm notification system. This must be called near the + * beginning of the program. + **/ void alarm_init (void) { struct sigaction sa; - struct sigaction debug_sa; - int flags = 0; - + int flags; + pipe (alarm_pipes); - + /* set non blocking mode */ + flags = 0; fcntl (alarm_pipes [0], F_GETFL, &flags); fcntl (alarm_pipes [0], F_SETFL, flags | O_NONBLOCK); - gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, 0); + gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, NULL); /* Setup the signal handler */ - sa.sa_handler = alarm_activate; + sa.sa_handler = alarm_signal; sigemptyset (&sa.sa_mask); - sa.sa_flags = SA_RESTART; + sa.sa_flags = SA_RESTART; sigaction (SIGALRM, &sa, NULL); - - /* Setup a signal handler to toggle debugging */ - debug_sa.sa_handler = toggle_debugging; - sigemptyset (&debug_sa.sa_mask); - debug_sa.sa_flags = SA_RESTART; - sigaction (SIGUSR1, &debug_sa, NULL); -} - -void -debug_alarm (AlarmRecord* ar, enum DebugAction action) -{ - time_t now = time (NULL); - iCalObject *ico = ar->closure; - printf ("%s", ctime(&now)); - switch (action) { - case ALARM_ADDED: - printf ("Added alarm for %s", ctime(&ar->activation_time)); - break; - case ALARM_NOT_ADDED: - printf ("Alarm not added for %s", ctime(&ar->activation_time)); - break; - case ALARM_ACTIVATED: - printf ("Activated alarm\n"); - break; - } - - if (ar->fn!=&calendar_notify) return; - printf ("--- Summary: %s\n", ico->summary); - switch (ar->alarm->type) { - case ALARM_MAIL: - printf ("--- Type: Mail\n"); - break; - case ALARM_PROGRAM: - printf ("--- Type: Program\n"); - break; - case ALARM_DISPLAY: - printf ("--- Type: Display\n"); - break; - case ALARM_AUDIO: - printf ("--- Type: Audio\n"); - break; - } } - - diff --git a/calendar/gui/alarm.h b/calendar/gui/alarm.h index efdfda997d..2394d28024 100644 --- a/calendar/gui/alarm.h +++ b/calendar/gui/alarm.h @@ -1,13 +1,40 @@ +/* Evolution calendar - alarm notification support + * + * Copyright (C) 2000 Helix Code, Inc. + * + * Authors: Miguel de Icaza + * 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_H #define ALARM_H #include -#include -typedef void (*AlarmFunction)(time_t time, CalendarAlarm *which, void *closuse); + + +typedef void (* AlarmFunction) (gpointer alarm_id, time_t trigger, gpointer data); +typedef void (* AlarmDestroyNotify) (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); -gboolean alarm_add (CalendarAlarm *alarm, AlarmFunction fn, void *closure); -int alarm_kill (void *closure); + #endif diff --git a/calendar/gui/calendar-commands.c b/calendar/gui/calendar-commands.c index e6d5adedb8..73994d4302 100644 --- a/calendar/gui/calendar-commands.c +++ b/calendar/gui/calendar-commands.c @@ -20,7 +20,6 @@ #include #include #include -#include "alarm.h" #include "eventedit.h" #include "gnome-cal.h" #include "calendar-commands.h" @@ -46,9 +45,6 @@ int day_begin, day_end; /* Whether weeks starts on Sunday or Monday */ int week_starts_on_monday; -/* If true, enable debug output for alarms */ -int debug_alarms = 0; - /* If AM/PM indicators should be used. This may not be supported by the new views. */ int am_pm_flag = 0; @@ -381,7 +377,7 @@ open_ok (GtkWidget *widget, GtkFileSelection *fs) ret = gnome_dialog_run (GNOME_DIALOG (error_dialog)); } else { /* FIXME: find out who owns this calendar and use that name */ -#warning "fix me: find out who owns this calendar and use that name" +#warning "FIXME: find out who owns this calendar and use that name" /* new_calendar ("Somebody", gtk_file_selection_get_filename (fs), NULL, NULL, FALSE); */ @@ -731,7 +727,8 @@ void calendar_set_uri (GnomeCalendar *gcal, char *calendar_file) /* * Initializes the calendar internal variables, loads defaults */ -void init_calendar (void) +void +init_calendar (void) { int i; char *cspec, *color; diff --git a/calendar/gui/calendar-commands.h b/calendar/gui/calendar-commands.h index 62e9d87512..50cc2b85b6 100644 --- a/calendar/gui/calendar-commands.h +++ b/calendar/gui/calendar-commands.h @@ -2,6 +2,8 @@ #define CALENDAR_COMMANDS_H #include +#include +#include "gnome-cal.h" /* This enum and the following array define the color preferences */ @@ -101,7 +103,7 @@ void calendar_set_uri (GnomeCalendar *gcal, char *calendar_file); /*----------------------------------------------------------------------*/ -/* FIX ME -- where should this stuff go? */ +/* FIXME -- where should this stuff go? */ /*----------------------------------------------------------------------*/ /* This is only used by the calendar_get_events_in_range routine to get diff --git a/calendar/gui/e-week-view.c b/calendar/gui/e-week-view.c index 0665a6e63d..26fce815f9 100644 --- a/calendar/gui/e-week-view.c +++ b/calendar/gui/e-week-view.c @@ -29,11 +29,11 @@ #include #include #include +#include "calendar-commands.h" #include "e-week-view.h" #include "e-week-view-event-item.h" #include "e-week-view-main-item.h" #include "e-week-view-titles-item.h" -#include "main.h" #include #include "popup-menu.h" #include "eventedit.h" diff --git a/calendar/gui/evolution-calendar-control.c b/calendar/gui/evolution-calendar-control.c index 972a670bde..ac0801bcc8 100644 --- a/calendar/gui/evolution-calendar-control.c +++ b/calendar/gui/evolution-calendar-control.c @@ -178,15 +178,15 @@ calendar_factory_init (void) int main (int argc, char **argv) { + init_bonobo (&argc, argv); alarm_init (); + init_calendar (); //g_log_set_always_fatal ((GLogLevelFlags) 0xFFFF); CORBA_exception_init (&ev); - init_bonobo (&argc, argv); - calendar_factory_init (); bonobo_main (); diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c index db46b113ee..4c90414f16 100644 --- a/calendar/gui/gnome-cal.c +++ b/calendar/gui/gnome-cal.c @@ -30,7 +30,18 @@ -static void gnome_calendar_class_init (GnomeCalendar *class); +/* An entry in the UID->alarms hash table. The UID key *is* the uid field in + * this structure, so don't free it separately. + */ +typedef struct { + char *uid; + GList *alarm_ids; +} ObjectAlarms; + + + +static void gnome_calendar_class_init (GnomeCalendarClass *class); +static void gnome_calendar_init (GnomeCalendar *gcal); static void gnome_calendar_destroy (GtkObject *object); static void gnome_calendar_update_view_times (GnomeCalendar *gcal, @@ -47,7 +58,7 @@ static void gnome_calendar_on_month_changed (GtkCalendar *calendar, static GtkVBoxClass *parent_class; - + guint gnome_calendar_get_type (void) @@ -56,10 +67,10 @@ gnome_calendar_get_type (void) if(!gnome_calendar_type) { GtkTypeInfo gnome_calendar_info = { "GnomeCalendar", - sizeof(GnomeCalendar), - sizeof(GnomeCalendarClass), + sizeof (GnomeCalendar), + sizeof (GnomeCalendarClass), (GtkClassInitFunc) gnome_calendar_class_init, - (GtkObjectInitFunc) NULL, + (GtkObjectInitFunc) gnome_calendar_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, }; @@ -74,15 +85,42 @@ gnome_calendar_get_type (void) return gnome_calendar_type; } - +/* Class initialization function for the gnome calendar */ static void -gnome_calendar_class_init (GnomeCalendar *class) +gnome_calendar_class_init (GnomeCalendarClass *class) { - GtkObjectClass *object_class; + GtkObjectClass *object_class; + object_class = (GtkObjectClass *) class; + object_class->destroy = gnome_calendar_destroy; } +/* Object initialization function for the gnome calendar */ +static void +gnome_calendar_init (GnomeCalendar *gcal) +{ + gcal->alarms = g_hash_table_new (g_str_hash, g_str_equal); +} + +/* Used from g_hash_table_foreach(); frees an object alarms entry */ +static void +free_object_alarms (gpointer key, gpointer value, gpointer data) +{ + ObjectAlarms *oa; + + oa = value; + + g_assert (oa->uid != NULL); + g_free (oa->uid); + oa->uid = NULL; + + g_assert (oa->alarm_ids != NULL); + g_list_free (oa->alarm_ids); + oa->alarm_ids = NULL; + + g_free (oa); +} static void gnome_calendar_destroy (GtkObject *object) @@ -96,6 +134,10 @@ gnome_calendar_destroy (GtkObject *object) gtk_object_unref (GTK_OBJECT (gcal->client)); + g_hash_table_foreach (gcal->alarms, free_object_alarms, NULL); + g_hash_table_destroy (gcal->alarms); + gcal->alarms = NULL; + if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } @@ -388,11 +430,292 @@ gnome_calendar_set_view (GnomeCalendar *gcal, char *page_name) gnome_calendar_update_gtk_calendar (gcal); } +/* Sends a mail notification of an alarm trigger */ +static void +mail_notification (char *mail_address, char *text, time_t app_time) +{ + pid_t pid; + int p [2]; + char *command; + + pipe (p); + pid = fork (); + if (pid == 0){ + int dev_null; + + dev_null = open ("/dev/null", O_RDWR); + dup2 (p [0], 0); + dup2 (dev_null, 1); + dup2 (dev_null, 2); + execl ("/usr/lib/sendmail", "/usr/lib/sendmail", + mail_address, NULL); + _exit (127); + } + command = g_strconcat ("To: ", mail_address, "\n", + "Subject: ", _("Reminder of your appointment at "), + ctime (&app_time), "\n\n", text, "\n", NULL); + write (p [1], command, strlen (command)); + close (p [1]); + close (p [0]); + g_free (command); +} + +static int +max_open_files (void) +{ + static int files; + + if (files) + return files; + + files = sysconf (_SC_OPEN_MAX); + if (files != -1) + return files; +#ifdef OPEN_MAX + return files = OPEN_MAX; +#else + return files = 256; +#endif +} + +/* Executes a program as a notification of an alarm trigger */ +static void +program_notification (char *command, int close_standard) +{ + struct sigaction ignore, save_intr, save_quit; + int status = 0, i; + pid_t pid; + + ignore.sa_handler = SIG_IGN; + sigemptyset (&ignore.sa_mask); + ignore.sa_flags = 0; + + sigaction (SIGINT, &ignore, &save_intr); + sigaction (SIGQUIT, &ignore, &save_quit); + + if ((pid = fork ()) < 0){ + fprintf (stderr, "\n\nfork () = -1\n"); + return; + } + if (pid == 0){ + pid = fork (); + if (pid == 0){ + const int top = max_open_files (); + sigaction (SIGINT, &save_intr, NULL); + sigaction (SIGQUIT, &save_quit, NULL); + + for (i = (close_standard ? 0 : 3); i < top; i++) + close (i); + + /* FIXME: As an excercise to the reader, copy the + * code from mc to setup shell properly instead of + * /bin/sh. Yes, this comment is larger than a cut and paste. + */ + execl ("/bin/sh", "/bin/sh", "-c", command, (char *) 0); + + _exit (127); + } else { + _exit (127); + } + } + wait (&status); + sigaction (SIGINT, &save_intr, NULL); + sigaction (SIGQUIT, &save_quit, NULL); +} + +/* Present a display notification of an alarm trigger */ +static void +display_notification (time_t trigger, time_t occur, iCalObject *ico, GnomeCalendar *gcal) +{ + g_message ("DISPLAY NOTIFICATION!"); + /* FIXME */ +} + +/* Present an audible notification of an alarm trigger */ +static void +audio_notification (time_t trigger, time_t occur, iCalObject *ico, GnomeCalendar *gcal) +{ + g_message ("AUDIO NOTIFICATION!"); + /* FIXME */ +} + +struct trigger_alarm_closure { + GnomeCalendar *gcal; + char *uid; + enum AlarmType 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) +{ + struct trigger_alarm_closure *c; + char *str_ico; + iCalObject *ico; + CalObjFindStatus status; + ObjectAlarms *oa; + GList *l; + + c = data; + + /* Fetch the object */ + + str_ico = cal_client_get_object (c->gcal->client, c->uid); + status = ical_object_find_in_string (c->uid, str_ico, &ico); + + switch (status) { + case CAL_OBJ_FIND_SUCCESS: + /* Go on */ + break; + + case CAL_OBJ_FIND_SYNTAX_ERROR: + g_message ("trigger_alarm_cb(): syntax error in fetched object"); + return; + + case CAL_OBJ_FIND_NOT_FOUND: + g_message ("trigger_alarm_cb(): could not find fetched object"); + return; + } + + g_assert (ico != NULL); + + /* Present notification */ + + switch (c->type) { + case ALARM_MAIL: + g_assert (ico->malarm.enabled); + mail_notification (ico->malarm.data, ico->summary, c->occur); + break; + + case ALARM_PROGRAM: + g_assert (ico->palarm.enabled); + program_notification (ico->palarm.data, FALSE); + break; + + case ALARM_DISPLAY: + g_assert (ico->dalarm.enabled); + display_notification (trigger, c->occur, ico, c->gcal); + break; + + case ALARM_AUDIO: + g_assert (ico->aalarm.enabled); + audio_notification (trigger, c->occur, ico, c->gcal); + break; + } + + /* Remove the alarm from the hash table */ + + oa = g_hash_table_lookup (c->gcal->alarms, ico->uid); + g_assert (oa != NULL); + + l = g_list_find (oa->alarm_ids, alarm_id); + g_assert (l != NULL); + + oa->alarm_ids = g_list_remove_link (oa->alarm_ids, l); + g_list_free_1 (l); + + if (!oa->alarm_ids) { + g_hash_table_remove (c->gcal->alarms, ico->uid); + g_free (oa->uid); + g_free (oa); + } +} + +/* 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 *cal, CalAlarmInstance *ai) +{ + struct trigger_alarm_closure *c; + gpointer alarm; + ObjectAlarms *oa; + + c = g_new (struct trigger_alarm_closure, 1); + c->gcal = cal; + c->uid = g_strdup (ai->uid); + c->type = ai->type; + 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 (cal->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 (cal->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; + + cal = GNOME_CALENDAR (data); + cal->midnight_alarm_refresh_id = NULL; + + load_alarms (cal); +} + +/* Loads and queues the alarms from the current time up to midnight. */ +static void +load_alarms (GnomeCalendar *cal) +{ + time_t now; + time_t end_of_day; + GList *alarms, *l; + + now = time (NULL); + end_of_day = time_day_end (now); + + /* Queue alarms */ + + alarms = cal_client_get_alarms_in_range (cal->client, now, end_of_day); + + for (l = alarms; l; l = l->next) + setup_alarm (cal, l->data); + + cal_alarm_instance_list_free (alarms); + + /* Queue the midnight alarm refresh */ + + cal->midnight_alarm_refresh_id = alarm_add (end_of_day, midnight_refresh_cb, cal, NULL); + if (!cal->midnight_alarm_refresh_id) { + g_message ("load_alarms(): Could not set up the midnight refresh alarm!"); + /* FIXME: what to do? */ + } +} /* This tells all components to reload all calendar objects. */ static void -gnome_calendar_update_all (GnomeCalendar *cal, iCalObject *object, int flags) +gnome_calendar_update_all (GnomeCalendar *cal) { + load_alarms (cal); + e_day_view_update_all_events (E_DAY_VIEW (cal->day_view)); e_day_view_update_all_events (E_DAY_VIEW (cal->work_week_view)); e_week_view_update_all_events (E_WEEK_VIEW (cal->week_view)); @@ -406,6 +729,30 @@ gnome_calendar_update_all (GnomeCalendar *cal, iCalObject *object, int flags) gnome_calendar_tag_calendar (cal, cal->gtk_calendar); } +/* Removes any queued alarms for the specified UID */ +static void +remove_alarms_for_object (GnomeCalendar *gcal, const char *uid) +{ + ObjectAlarms *oa; + GList *l; + + oa = g_hash_table_lookup (gcal->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 (gcal->alarms, uid); + + g_free (oa->uid); + g_list_free (oa->alarm_ids); + g_free (oa); +} static void gnome_calendar_object_updated_cb (GtkWidget *cal_client, @@ -415,6 +762,9 @@ gnome_calendar_object_updated_cb (GtkWidget *cal_client, g_message ("gnome-cal: got object changed_cb, uid='%s'", uid?uid:""); + remove_alarms_for_object (gcal, uid); + /* FIXME: put in new alarms */ + /* FIXME: do we really want each view to reload the event itself? Maybe we should keep track of events globally, maybe with ref counts. We also need to sort out where they get freed. */ @@ -441,6 +791,8 @@ gnome_calendar_object_removed_cb (GtkWidget *cal_client, g_message ("gnome-cal: got object removed _cb, uid='%s'", uid?uid:""); + remove_alarms_for_object (gcal, uid); + e_day_view_remove_event (E_DAY_VIEW (gcal->day_view), uid); e_day_view_remove_event (E_DAY_VIEW (gcal->work_week_view), uid); e_week_view_remove_event (E_WEEK_VIEW (gcal->week_view), uid); @@ -501,7 +853,7 @@ gnome_calendar_load_cb (GtkWidget *cal_client, switch (status) { case CAL_CLIENT_LOAD_SUCCESS: - gnome_calendar_update_all (locd->gcal, NULL, 0); + gnome_calendar_update_all (locd->gcal); g_message ("gnome_calendar_load_cb: success"); break; @@ -521,7 +873,7 @@ gnome_calendar_load_cb (GtkWidget *cal_client, cal_client_create_calendar (locd->gcal->client, locd->uri); - gnome_calendar_update_all (locd->gcal, NULL, 0); + gnome_calendar_update_all (locd->gcal); } break; @@ -613,97 +965,6 @@ gnome_calendar_object_changed (GnomeCalendar *gcal, iCalObject *obj, int flags) g_free (obj_string); } -static int -max_open_files (void) -{ - static int files; - - if (files) - return files; - - files = sysconf (_SC_OPEN_MAX); - if (files != -1) - return files; -#ifdef OPEN_MAX - return files = OPEN_MAX; -#else - return files = 256; -#endif -} - -static void -execute (char *command, int close_standard) -{ - struct sigaction ignore, save_intr, save_quit; - int status = 0, i; - pid_t pid; - - ignore.sa_handler = SIG_IGN; - sigemptyset (&ignore.sa_mask); - ignore.sa_flags = 0; - - sigaction (SIGINT, &ignore, &save_intr); - sigaction (SIGQUIT, &ignore, &save_quit); - - if ((pid = fork ()) < 0){ - fprintf (stderr, "\n\nfork () = -1\n"); - return; - } - if (pid == 0){ - pid = fork (); - if (pid == 0){ - const int top = max_open_files (); - sigaction (SIGINT, &save_intr, NULL); - sigaction (SIGQUIT, &save_quit, NULL); - - for (i = (close_standard ? 0 : 3); i < top; i++) - close (i); - - /* FIXME: As an excercise to the reader, copy the - * code from mc to setup shell properly instead of - * /bin/sh. Yes, this comment is larger than a cut and paste. - */ - execl ("/bin/sh", "/bin/sh", "-c", command, (char *) 0); - - _exit (127); - } else { - _exit (127); - } - } - wait (&status); - sigaction (SIGINT, &save_intr, NULL); - sigaction (SIGQUIT, &save_quit, NULL); -} - -static void -mail_notify (char *mail_address, char *text, time_t app_time) -{ - pid_t pid; - int p [2]; - char *command; - - pipe (p); - pid = fork (); - if (pid == 0){ - int dev_null; - - dev_null = open ("/dev/null", O_RDWR); - dup2 (p [0], 0); - dup2 (dev_null, 1); - dup2 (dev_null, 2); - execl ("/usr/lib/sendmail", "/usr/lib/sendmail", - mail_address, NULL); - _exit (127); - } - command = g_strconcat ("To: ", mail_address, "\n", - "Subject: ", _("Reminder of your appointment at "), - ctime (&app_time), "\n\n", text, "\n", NULL); - write (p [1], command, strlen (command)); - close (p [1]); - close (p [0]); - g_free (command); -} - static void stop_beeping (GtkObject* object, gpointer data) { @@ -736,6 +997,8 @@ timeout_beep (gpointer data) return FALSE; } +#if 0 + void calendar_notify (time_t activation_time, CalendarAlarm *which, void *data) { @@ -829,6 +1092,8 @@ calendar_notify (time_t activation_time, CalendarAlarm *which, void *data) } } +#endif + /* * Tags the dates with appointments in a GtkCalendar based on the * GnomeCalendar contents @@ -1079,4 +1344,3 @@ gnome_calendar_on_month_changed (GtkCalendar *calendar, { gnome_calendar_tag_calendar (gcal, gcal->gtk_calendar); } - diff --git a/calendar/gui/gnome-cal.h b/calendar/gui/gnome-cal.h index 20287c202b..48bd6af679 100644 --- a/calendar/gui/gnome-cal.h +++ b/calendar/gui/gnome-cal.h @@ -52,6 +52,12 @@ typedef struct { /* The signal handler id for our GtkCalendar "day_selected" handler. */ guint day_selected_id; + + /* Alarm ID for the midnight refresh function */ + gpointer midnight_alarm_refresh_id; + + /* UID->alarms hash */ + GHashTable *alarms; } GnomeCalendar; typedef struct { diff --git a/calendar/idl/evolution-calendar.idl b/calendar/idl/evolution-calendar.idl index eda35a3419..b08b8fd07f 100644 --- a/calendar/idl/evolution-calendar.idl +++ b/calendar/idl/evolution-calendar.idl @@ -34,6 +34,14 @@ module Calendar { const CalObjType TYPE_OTHER = 1 << 3; const CalObjType TYPE_ANY = 0x0f; + /* Types of alarms */ + enum AlarmType { + MAIL, + PROGRAM, + DISPLAY, + AUDIO + }; + /* Used to store a time_t */ typedef unsigned long Time_t; @@ -51,6 +59,16 @@ module Calendar { typedef sequence CalObjInstanceSeq; + /* An alarm trigger instance */ + struct CalAlarmInstance { + CalObjUID uid; + AlarmType type; + Time_t trigger; + Time_t occur; + }; + + typedef sequence CalAlarmInstanceSeq; + interface Listener; /* Calendar client interface */ @@ -73,6 +91,12 @@ module Calendar { CalObjInstanceSeq get_events_in_range (in Time_t start, in Time_t end) raises (InvalidRange); + /* Gets the objects whose alarms trigger in the specified time + * range. + */ + CalAlarmInstanceSeq get_alarms_in_range (in Time_t start, in Time_t end) + raises (InvalidRange); + /* Updates an object by adding it if it does not exist or by * changing an existing one. */ diff --git a/calendar/pcs/cal-backend-imc.c b/calendar/pcs/cal-backend-imc.c index 792abbb4ae..9492ad1241 100644 --- a/calendar/pcs/cal-backend-imc.c +++ b/calendar/pcs/cal-backend-imc.c @@ -4,6 +4,7 @@ * * Authors: Federico Mena-Quintero * Seth Alves + * Miguel de Icaza * * 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 @@ -76,6 +77,7 @@ static void cal_backend_imc_create (CalBackend *backend, GnomeVFSURI *uri); static char *cal_backend_imc_get_object (CalBackend *backend, const char *uid); static GList *cal_backend_imc_get_uids (CalBackend *backend, CalObjType type); static GList *cal_backend_imc_get_events_in_range (CalBackend *backend, time_t start, time_t end); +static GList *cal_backend_imc_get_alarms_in_range (CalBackend *backend, time_t start, time_t end); static gboolean cal_backend_imc_update_object (CalBackend *backend, const char *uid, const char *calobj); static gboolean cal_backend_imc_remove_object (CalBackend *backend, const char *uid); @@ -135,6 +137,7 @@ cal_backend_imc_class_init (CalBackendIMCClass *class) backend_class->get_object = cal_backend_imc_get_object; backend_class->get_uids = cal_backend_imc_get_uids; backend_class->get_events_in_range = cal_backend_imc_get_events_in_range; + backend_class->get_alarms_in_range = cal_backend_imc_get_alarms_in_range; backend_class->update_object = cal_backend_imc_update_object; backend_class->remove_object = cal_backend_imc_remove_object; @@ -905,6 +908,22 @@ cal_backend_imc_get_uids (CalBackend *backend, CalObjType type) return c.uid_list; } +/* Allocates and fills in a new CalObjInstance structure */ +static CalObjInstance * +build_cal_obj_instance (iCalObject *ico, time_t start, time_t end) +{ + CalObjInstance *icoi; + + g_assert (ico->uid != NULL); + + icoi = g_new (CalObjInstance, 1); + icoi->uid = g_strdup (ico->uid); + icoi->start = start; + icoi->end = end; + + return icoi; +} + struct build_event_list_closure { CalBackendIMC *cbimc; GList *event_list; @@ -921,13 +940,7 @@ build_event_list (iCalObject *ico, time_t start, time_t end, void *data) c = data; - icoi = g_new (CalObjInstance, 1); - - g_assert (ico->uid != NULL); - icoi->uid = g_strdup (ico->uid); - icoi->start = start; - icoi->end = end; - + icoi = build_cal_obj_instance (ico, start, end); c->event_list = g_list_prepend (c->event_list, icoi); return TRUE; @@ -953,11 +966,10 @@ compare_instance_func (gconstpointer a, gconstpointer b) static GList * cal_backend_imc_get_events_in_range (CalBackend *backend, time_t start, time_t end) { - struct build_event_list_closure c; - GList *l; - CalBackendIMC *cbimc; IMCPrivate *priv; + struct build_event_list_closure c; + GList *l; cbimc = CAL_BACKEND_IMC (backend); priv = cbimc->priv; @@ -979,10 +991,162 @@ cal_backend_imc_get_events_in_range (CalBackend *backend, time_t start, time_t e } c.event_list = g_list_sort (c.event_list, compare_instance_func); - return c.event_list; } +struct build_alarm_list_closure { + time_t start; + time_t end; + GList *alarms; +}; + +/* Computes the offset in minutes from an alarm trigger to the actual event */ +static int +compute_alarm_offset (CalendarAlarm *a) +{ + int ofs; + + if (!a->enabled) + return -1; + + switch (a->units) { + case ALARM_MINUTES: + ofs = a->count * 60; + break; + + case ALARM_HOURS: + ofs = a->count * 3600; + break; + + case ALARM_DAYS: + ofs = a->count * 24 * 3600; + break; + + default: + ofs = -1; + g_assert_not_reached (); + } + + return ofs; +} + +/* Allocates and fills in a new CalAlarmInstance structure */ +static CalAlarmInstance * +build_cal_alarm_instance (iCalObject *ico, enum AlarmType type, time_t trigger, time_t occur) +{ + CalAlarmInstance *ai; + + g_assert (ico->uid != NULL); + + ai = g_new (CalAlarmInstance, 1); + ai->uid = g_strdup (ico->uid); + ai->type = type; + ai->trigger = trigger; + ai->occur = occur; + + return ai; +} + +/* Adds the specified alarm to the list if its trigger time falls within the + * requested range. + */ +static void +try_add_alarm (time_t occur_start, iCalObject *ico, CalendarAlarm *alarm, + struct build_alarm_list_closure *c) +{ + int ofs; + time_t trigger; + CalAlarmInstance *ai; + + if (!alarm->enabled) + return; + + ofs = compute_alarm_offset (alarm); + g_assert (ofs != -1); + + trigger = occur_start - ofs; + + if (trigger < c->start || trigger > c->end) + return; + + ai = build_cal_alarm_instance (ico, alarm->type, trigger, occur_start); + c->alarms = g_list_prepend (c->alarms, ai); +} + +/* Builds a list of alarm instances. Used as a callback from + * ical_object_generate_events(). + */ +static int +build_alarm_list (iCalObject *ico, time_t start, time_t end, void *data) +{ + struct build_alarm_list_closure *c; + + c = data; + + try_add_alarm (start, ico, &ico->dalarm, c); + try_add_alarm (start, ico, &ico->aalarm, c); + try_add_alarm (start, ico, &ico->palarm, c); + try_add_alarm (start, ico, &ico->malarm, c); + + return TRUE; +} + +/* Adds all the alarm triggers that occur within the specified time range */ +static GList * +add_alarms_for_object (GList *alarms, iCalObject *ico, time_t start, time_t end) +{ + struct build_alarm_list_closure c; + int dofs, aofs, pofs, mofs; + int max_ofs; + + dofs = compute_alarm_offset (&ico->dalarm); + aofs = compute_alarm_offset (&ico->aalarm); + pofs = compute_alarm_offset (&ico->palarm); + mofs = compute_alarm_offset (&ico->malarm); + + max_ofs = MAX (dofs, MAX (aofs, MAX (pofs, mofs))); + if (max_ofs == -1) + return alarms; + + c.start = start; + c.end = end; + c.alarms = alarms; + + ical_object_generate_events (ico, start, end, build_alarm_list, &c); + return c.alarms; +} + +/* Get_alarms_in_range handler for the IMC backend */ +static GList * +cal_backend_imc_get_alarms_in_range (CalBackend *backend, time_t start, time_t end) +{ + CalBackendIMC *cbimc; + IMCPrivate *priv; + GList *l; + GList *alarms; + + cbimc = CAL_BACKEND_IMC (backend); + priv = cbimc->priv; + + g_return_val_if_fail (priv->loaded, NULL); + + g_return_val_if_fail (start != -1 && end != -1, NULL); + g_return_val_if_fail (start <= end, NULL); + + /* Only VEVENT and VTODO components can have alarms */ + + alarms = NULL; + + for (l = priv->events; l; l = l->next) + alarms = add_alarms_for_object (alarms, (iCalObject *) l->data, start, end); + + for (l = priv->todos; l; l = l->next) + alarms = add_alarms_for_object (alarms, (iCalObject *) l->data, start, end); + + alarms = g_list_sort (alarms, compare_instance_func); + return alarms; +} + /* Notifies a backend's clients that an object was updated */ static void notify_update (CalBackendIMC *cbimc, const char *uid) diff --git a/calendar/pcs/cal-backend-imc.h b/calendar/pcs/cal-backend-imc.h index b0fa93611e..954bbc52cc 100644 --- a/calendar/pcs/cal-backend-imc.h +++ b/calendar/pcs/cal-backend-imc.h @@ -4,6 +4,7 @@ * * Authors: Federico Mena-Quintero * Seth Alves + * Miguel de Icaza * * 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 diff --git a/calendar/pcs/cal-backend.c b/calendar/pcs/cal-backend.c index 335d7ff591..e9c18d6f86 100644 --- a/calendar/pcs/cal-backend.c +++ b/calendar/pcs/cal-backend.c @@ -244,6 +244,28 @@ cal_backend_get_events_in_range (CalBackend *backend, time_t start, time_t end) return (* CLASS (backend)->get_events_in_range) (backend, start, end); } +/** + * cal_backend_get_alarms_in_range: + * @backend: A calendar backend. + * @start: Start time for query. + * @end: End time for query. + * + * Builds a sorted list of the alarms that trigger in the specified time range. + * + * Return value: A list of #CalAlarmInstance structures, sorted by trigger time. + **/ +GList * +cal_backend_get_alarms_in_range (CalBackend *backend, time_t start, time_t end) +{ + g_return_val_if_fail (backend != NULL, NULL); + g_return_val_if_fail (IS_CAL_BACKEND (backend), NULL); + g_return_val_if_fail (start != -1 && end != -1, NULL); + g_return_val_if_fail (start <= end, NULL); + + g_assert (CLASS (backend)->get_alarms_in_range != NULL); + return (* CLASS (backend)->get_alarms_in_range) (backend, start, end); +} + /** * cal_backend_update_object: * @backend: A calendar backend. diff --git a/calendar/pcs/cal-backend.h b/calendar/pcs/cal-backend.h index d3697f3e14..5ce9027415 100644 --- a/calendar/pcs/cal-backend.h +++ b/calendar/pcs/cal-backend.h @@ -67,6 +67,7 @@ struct _CalBackendClass { char *(* get_object) (CalBackend *backend, const char *uid); GList *(* get_uids) (CalBackend *backend, CalObjType type); GList *(* get_events_in_range) (CalBackend *backend, time_t start, time_t end); + GList *(* get_alarms_in_range) (CalBackend *backend, time_t start, time_t end); gboolean (* update_object) (CalBackend *backend, const char *uid, const char *calobj); gboolean (* remove_object) (CalBackend *backend, const char *uid); }; @@ -87,6 +88,8 @@ GList *cal_backend_get_uids (CalBackend *backend, CalObjType type); GList *cal_backend_get_events_in_range (CalBackend *backend, time_t start, time_t end); +GList *cal_backend_get_alarms_in_range (CalBackend *backend, time_t start, time_t end); + gboolean cal_backend_update_object (CalBackend *backend, const char *uid, const char *calobj); gboolean cal_backend_remove_object (CalBackend *backend, const char *uid); diff --git a/calendar/pcs/cal.c b/calendar/pcs/cal.c index e1d3623ccf..8fc1de51b2 100644 --- a/calendar/pcs/cal.c +++ b/calendar/pcs/cal.c @@ -250,6 +250,40 @@ Cal_get_uids (PortableServer_Servant servant, return seq; } +/* Builds a CORBA sequence of calendar object instances from a CalObjInstance + * list. + */ +static Evolution_Calendar_CalObjInstanceSeq * +build_object_instance_seq (GList *list) +{ + GList *l; + int n, i; + Evolution_Calendar_CalObjInstanceSeq *seq; + + n = g_list_length (list); + + seq = Evolution_Calendar_CalObjInstanceSeq__alloc (); + CORBA_sequence_set_release (seq, TRUE); + seq->_length = n; + seq->_buffer = CORBA_sequence_Evolution_Calendar_CalObjInstance_allocbuf (n); + + /* Fill the sequence */ + + for (i = 0, l = list; l; i++, l = l->next) { + CalObjInstance *icoi; + Evolution_Calendar_CalObjInstance *corba_icoi; + + icoi = l->data; + corba_icoi = &seq->_buffer[i]; + + corba_icoi->uid = CORBA_string_dup (icoi->uid); + corba_icoi->start = icoi->start; + corba_icoi->end = icoi->end; + } + + return seq; +} + /* Cal::get_events_in_range method */ static Evolution_Calendar_CalObjInstanceSeq * Cal_get_events_in_range (PortableServer_Servant servant, @@ -261,8 +295,7 @@ Cal_get_events_in_range (PortableServer_Servant servant, CalPrivate *priv; time_t t_start, t_end; Evolution_Calendar_CalObjInstanceSeq *seq; - GList *elist, *l; - int n, i; + GList *elist; cal = CAL (bonobo_object_from_servant (servant)); priv = cal->priv; @@ -280,30 +313,99 @@ Cal_get_events_in_range (PortableServer_Servant servant, /* Figure out the list and allocate the sequence */ elist = cal_backend_get_events_in_range (priv->backend, t_start, t_end); - n = g_list_length (elist); + seq = build_object_instance_seq (elist); + cal_obj_instance_list_free (elist); - seq = Evolution_Calendar_CalObjInstanceSeq__alloc (); + return seq; +} + +/* Translates an enum AlarmType to its CORBA representation */ +static Evolution_Calendar_AlarmType +corba_alarm_type (enum AlarmType type) +{ + switch (type) { + case ALARM_MAIL: + return Evolution_Calendar_MAIL; + + case ALARM_PROGRAM: + return Evolution_Calendar_PROGRAM; + + case ALARM_DISPLAY: + return Evolution_Calendar_DISPLAY; + + case ALARM_AUDIO: + return Evolution_Calendar_AUDIO; + + default: + g_assert_not_reached (); + return Evolution_Calendar_DISPLAY; + } +} + +/* Builds a CORBA sequence of alarm instances from a CalAlarmInstance list. */ +static Evolution_Calendar_CalAlarmInstanceSeq * +build_alarm_instance_seq (GList *alarms) +{ + GList *l; + int n, i; + Evolution_Calendar_CalAlarmInstanceSeq *seq; + + n = g_list_length (alarms); + + seq = Evolution_Calendar_CalAlarmInstanceSeq__alloc (); CORBA_sequence_set_release (seq, TRUE); seq->_length = n; - seq->_buffer = CORBA_sequence_Evolution_Calendar_CalObjInstance_allocbuf (n); + seq->_buffer = CORBA_sequence_Evolution_Calendar_CalAlarmInstance_allocbuf (n); /* Fill the sequence */ - for (i = 0, l = elist; l; i++, l = l->next) { - CalObjInstance *icoi; - Evolution_Calendar_CalObjInstance *corba_icoi; + for (i = 0, l = alarms; l; i++, l = l->next) { + CalAlarmInstance *ai; + Evolution_Calendar_CalAlarmInstance *corba_ai; - icoi = l->data; - corba_icoi = &seq->_buffer[i]; + ai = l->data; + corba_ai = &seq->_buffer[i]; - corba_icoi->uid = CORBA_string_dup (icoi->uid); - corba_icoi->start = icoi->start; - corba_icoi->end = icoi->end; + corba_ai->uid = CORBA_string_dup (ai->uid); + corba_ai->type = corba_alarm_type (ai->type); + corba_ai->trigger = ai->trigger; + corba_ai->occur = ai->occur; } - /* Done */ + return seq; +} - cal_obj_instance_list_free (elist); +/* Cal::get_alarms_in_range method */ +static Evolution_Calendar_CalAlarmInstanceSeq * +Cal_get_alarms_in_range (PortableServer_Servant servant, + const Evolution_Calendar_Time_t start, + const Evolution_Calendar_Time_t end, + CORBA_Environment *ev) +{ + Cal *cal; + CalPrivate *priv; + time_t t_start, t_end; + Evolution_Calendar_CalAlarmInstanceSeq *seq; + GList *alarms; + + cal = CAL (bonobo_object_from_servant (servant)); + priv = cal->priv; + + t_start = (time_t) start; + t_end = (time_t) end; + + if (t_start > t_end || t_start == -1 || t_end == -1) { + CORBA_exception_set (ev, CORBA_USER_EXCEPTION, + ex_Evolution_Calendar_Cal_InvalidRange, + NULL); + return NULL; + } + + /* Figure out the list and allocate the sequence */ + + alarms = cal_backend_get_alarms_in_range (priv->backend, t_start, t_end); + seq = build_alarm_instance_seq (alarms); + cal_alarm_instance_list_free (alarms); return seq; } @@ -363,6 +465,7 @@ cal_get_epv (void) epv->get_object = Cal_get_object; epv->get_uids = Cal_get_uids; epv->get_events_in_range = Cal_get_events_in_range; + epv->get_alarms_in_range = Cal_get_alarms_in_range; epv->update_object = Cal_update_object; epv->remove_object = Cal_remove_object; -- cgit v1.2.3