aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/alarm-notify/alarm.c
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2012-01-03 00:07:59 +0800
committerMilan Crha <mcrha@redhat.com>2012-01-03 00:07:59 +0800
commit4dc5558f19f96858ec2a97d82b23b6401ed74a0b (patch)
tree461df0416fabcaf872539cd650b0b3e8b7366b3a /calendar/alarm-notify/alarm.c
parent49ffbb973093f15e4d5e34f287445f66a8d64e6d (diff)
downloadgsoc2013-evolution-4dc5558f19f96858ec2a97d82b23b6401ed74a0b.tar
gsoc2013-evolution-4dc5558f19f96858ec2a97d82b23b6401ed74a0b.tar.gz
gsoc2013-evolution-4dc5558f19f96858ec2a97d82b23b6401ed74a0b.tar.bz2
gsoc2013-evolution-4dc5558f19f96858ec2a97d82b23b6401ed74a0b.tar.lz
gsoc2013-evolution-4dc5558f19f96858ec2a97d82b23b6401ed74a0b.tar.xz
gsoc2013-evolution-4dc5558f19f96858ec2a97d82b23b6401ed74a0b.tar.zst
gsoc2013-evolution-4dc5558f19f96858ec2a97d82b23b6401ed74a0b.zip
Bug #353743 - Add Print button to meeting notification dialog
Diffstat (limited to 'calendar/alarm-notify/alarm.c')
-rw-r--r--calendar/alarm-notify/alarm.c324
1 files changed, 324 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Miguel de Icaza <miguel@ximian.com>
+ * Federico Mena-Quintero <federico@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <gdk/gdk.h>
+#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;
+}