From 4dc5558f19f96858ec2a97d82b23b6401ed74a0b Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 2 Jan 2012 17:07:59 +0100 Subject: Bug #353743 - Add Print button to meeting notification dialog --- calendar/alarm-notify/alarm.c | 324 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 calendar/alarm-notify/alarm.c (limited to 'calendar/alarm-notify/alarm.c') diff --git a/calendar/alarm-notify/alarm.c b/calendar/alarm-notify/alarm.c new file mode 100644 index 0000000000..2797d44b0a --- /dev/null +++ b/calendar/alarm-notify/alarm.c @@ -0,0 +1,324 @@ +/* + * Evolution calendar - Low-level alarm timer mechanism + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + * + * Authors: + * Miguel de Icaza + * Federico Mena-Quintero + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "alarm.h" +#include "config-data.h" + +/* Our glib timeout */ +static guint timeout_id; + +/* The list of pending alarms */ +static GList *alarms = NULL; + +/* A queued alarm structure */ +typedef struct { + time_t trigger; + AlarmFunction alarm_fn; + gpointer data; + AlarmDestroyNotify destroy_notify_fn; +} AlarmRecord; + +static void setup_timeout (void); + +/* Removes the head alarm from the queue. Does not touch the timeout_id. */ +static void +pop_alarm (void) +{ + AlarmRecord *ar; + GList *l; + + if (!alarms) { + g_warning ("Nothing to pop from the alarm queue"); + return; + } + + ar = alarms->data; + + l = alarms; + alarms = g_list_delete_link (alarms, l); + + g_free (ar); +} + +/* Callback from the alarm timeout */ +static gboolean +alarm_ready_cb (gpointer data) +{ + time_t now; + + if (!alarms) { + g_warning ("Alarm triggered, but no alarm present\n"); + return FALSE; + } + + timeout_id = 0; + + now = time (NULL); + + debug (("Alarm callback!")); + while (alarms) { + AlarmRecord *notify_id, *ar; + AlarmRecord ar_copy; + + ar = alarms->data; + + if (ar->trigger > now) + break; + + debug (("Process alarm with trigger %lu", ar->trigger)); + notify_id = ar; + + ar_copy = *ar; + ar = &ar_copy; + + /* This will free the original AlarmRecord; + * that's why we copy it. */ + pop_alarm (); + + (* ar->alarm_fn) (notify_id, ar->trigger, ar->data); + + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (notify_id, ar->data); + } + + /* We need this check because one of the alarm_fn above may have + * re-entered and added an alarm of its own, so the timer will + * already be set up. + */ + if (alarms) + setup_timeout (); + + return FALSE; +} + +/* Sets up a timeout for the next minute. We do not need to be concerned with + * timezones here, as this is just a periodic check on the alarm queue. + */ +static void +setup_timeout (void) +{ + const AlarmRecord *ar; + guint diff; + time_t now; + + if (!alarms) { + g_warning ("No alarm to setup\n"); + return; + } + + ar = alarms->data; + + /* Remove the existing time out */ + if (timeout_id != 0) { + g_source_remove (timeout_id); + timeout_id = 0; + } + + /* Ensure that if the trigger managed to get behind the + * current time we timeout immediately */ + diff = MAX (0, ar->trigger - time (NULL)); + now = time (NULL); + + /* Add the time out */ + debug (("Setting timeout for %d.%2d (from now) %lu %lu", + diff / 60, diff % 60, ar->trigger, now)); + debug ((" %s", ctime (&ar->trigger))); + debug ((" %s", ctime (&now))); + timeout_id = g_timeout_add_seconds (diff, alarm_ready_cb, NULL); + +} + +/* Used from g_list_insert_sorted(); compares the + * trigger times of two AlarmRecord structures. */ +static gint +compare_alarm_by_time (gconstpointer a, + gconstpointer b) +{ + const AlarmRecord *ara = a; + const AlarmRecord *arb = b; + time_t diff; + + 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 void +queue_alarm (AlarmRecord *ar) +{ + GList *old_head; + + /* Track the current head of the list in case there are changes */ + old_head = alarms; + + /* Insert the new alarm in order if the alarm's trigger time is + * after the current time */ + alarms = g_list_insert_sorted (alarms, ar, compare_alarm_by_time); + + /* If there first item on the list didn't change, the time out is fine */ + if (old_head == alarms) + return; + + /* Set the timer for removal upon activation */ + setup_timeout (); +} + +/** + * alarm_add: + * @trigger: Time at which alarm will trigger. + * @alarm_fn: Callback for trigger. + * @data: Closure data for callback. + * @destroy_notify_fn: destroy notification callback. + * + * 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. + * + * 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) +{ + AlarmRecord *ar; + + g_return_val_if_fail (trigger != -1, NULL); + g_return_val_if_fail (alarm_fn != NULL, NULL); + + ar = g_new (AlarmRecord, 1); + ar->trigger = trigger; + ar->alarm_fn = alarm_fn; + ar->data = data; + ar->destroy_notify_fn = destroy_notify_fn; + + queue_alarm (ar); + + return ar; +} + +/** + * alarm_remove: + * @alarm: A queued alarm identifier. + * + * Removes an alarm from the alarm queue. + **/ +void +alarm_remove (gpointer alarm) +{ + AlarmRecord *notify_id, *ar; + AlarmRecord ar_copy; + AlarmRecord *old_head; + GList *l; + + g_return_if_fail (alarm != NULL); + + ar = alarm; + + l = g_list_find (alarms, ar); + if (!l) { + g_warning (G_STRLOC ": Requested removal of nonexistent alarm!"); + return; + } + + old_head = alarms->data; + + notify_id = ar; + + if (old_head == ar) { + ar_copy = *ar; + ar = &ar_copy; + + /* This will free the original AlarmRecord; + * that's why we copy it. */ + pop_alarm (); + } else { + alarms = g_list_delete_link (alarms, l); + } + + /* Reset the timeout */ + if (!alarms) { + g_source_remove (timeout_id); + timeout_id = 0; + } + + /* Notify about destructiono of the alarm */ + + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (notify_id, ar->data); + +} + +/** + * alarm_done: + * + * Terminates the alarm timer mechanism. This should be called at the end of + * the program. + **/ +void +alarm_done (void) +{ + GList *l; + + if (timeout_id == 0) { + if (alarms) + g_warning ("No timeout, but queue is not NULL\n"); + return; + } + + g_source_remove (timeout_id); + timeout_id = 0; + + if (!alarms) { + g_warning ("timeout present, freed, but no alarms active\n"); + return; + } + + 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; +} -- cgit v1.2.3