diff options
author | Milan Crha <mcrha@redhat.com> | 2012-01-03 00:07:59 +0800 |
---|---|---|
committer | Milan Crha <mcrha@redhat.com> | 2012-01-03 00:07:59 +0800 |
commit | 4dc5558f19f96858ec2a97d82b23b6401ed74a0b (patch) | |
tree | 461df0416fabcaf872539cd650b0b3e8b7366b3a /calendar/gui/alarm-notify/alarm-queue.c | |
parent | 49ffbb973093f15e4d5e34f287445f66a8d64e6d (diff) | |
download | gsoc2013-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/gui/alarm-notify/alarm-queue.c')
-rw-r--r-- | calendar/gui/alarm-notify/alarm-queue.c | 2400 |
1 files changed, 0 insertions, 2400 deletions
diff --git a/calendar/gui/alarm-notify/alarm-queue.c b/calendar/gui/alarm-notify/alarm-queue.c deleted file mode 100644 index 8a4ae2face..0000000000 --- a/calendar/gui/alarm-notify/alarm-queue.c +++ /dev/null @@ -1,2400 +0,0 @@ -/* - * Alarm queueing engine - * - * 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: - * Federico Mena-Quintero <federico@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> -#include <gtk/gtk.h> -#include <glib/gi18n.h> - -#ifdef HAVE_CANBERRA -#include <canberra-gtk.h> -#endif - -#include <libecal/e-cal-client-view.h> -#include <libecal/e-cal-time-util.h> -#include <libecal/e-cal-component.h> - -#ifdef HAVE_LIBNOTIFY -#include <libnotify/notify.h> -#endif - -#include "alarm.h" -#include "alarm-notify-dialog.h" -#include "alarm-queue.h" -#include "alarm-notify.h" -#include "config-data.h" -#include "util.h" - -/* The dialog with alarm nofications */ -static AlarmNotificationsDialog *alarm_notifications_dialog = NULL; - -/* Whether the queueing system has been initialized */ -static gboolean alarm_queue_inited = FALSE; - -/* Clients we are monitoring for alarms */ -static GHashTable *client_alarms_hash = NULL; - -/* List of tray icons being displayed */ -static GList *tray_icons_list = NULL; - -/* Top Tray Image */ -static GtkStatusIcon *tray_icon = NULL; -static gint tray_blink_id = -1; -static gint tray_blink_countdown = 0; -static AlarmNotify *an; - -/* Structure that stores a client we are monitoring */ -typedef struct { - /* Monitored client */ - ECalClient *cal_client; - - /* The live view to the calendar */ - ECalClientView *view; - - /* 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 ECalComponentAlarms and the mapping from queued alarm IDs to the - * actual alarm instance structures. - */ -typedef struct { - /* The parent client alarms structure */ - ClientAlarms *parent_client; - - /* The component's UID */ - ECalComponentId *id; - - /* The actual component and its alarm instances */ - ECalComponentAlarms *alarms; - - /* List of QueuedAlarm structures */ - GSList *queued_alarms; - - /* Flags */ - gboolean expecting_update; -} 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 CompQueuedAlarms->alarms->alarms list */ - ECalComponentAlarmInstance *instance; - - /* original trigger of the instance from component */ - time_t orig_trigger; - - /* Whether this is a snoozed queued alarm or a normal one */ - guint snooze : 1; -} QueuedAlarm; - -/* Alarm ID for the midnight refresh function */ -static gpointer midnight_refresh_id = NULL; -static time_t midnight = 0; - -static void remove_client_alarms (ClientAlarms *ca); -static void display_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id, - gboolean use_description); -static void audio_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id); -static void mail_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id); -static void procedure_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id); -#ifdef HAVE_LIBNOTIFY -static void popup_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id, - gboolean use_description); -#endif -static void query_objects_modified_cb (ECalClientView *view, - const GSList *objects, - gpointer data); -static void query_objects_removed_cb (ECalClientView *view, - const GSList *uids, - gpointer data); - -static void update_cqa (CompQueuedAlarms *cqa, - ECalComponent *comp); -static void update_qa (ECalComponentAlarms *alarms, - QueuedAlarm *qa); -static void tray_list_remove_cqa (CompQueuedAlarms *cqa); -static void on_dialog_objs_removed_cb (ECalClientView *view, - const GSList *uids, - gpointer data); - -/* Alarm queue engine */ - -static void load_alarms_for_today (ClientAlarms *ca); -static void midnight_refresh_cb (gpointer alarm_id, - time_t trigger, - gpointer data); - -/* Simple asynchronous message dispatcher */ - -typedef struct _Message Message; -typedef void (*MessageFunc) (Message *msg); - -struct _Message { - MessageFunc func; -}; - -/* -static void -message_proxy (Message *msg) -{ - g_return_if_fail (msg->func != NULL); - * - msg->func (msg); -} - * -static gpointer -create_thread_pool (void) -{ - return g_thread_pool_new ((GFunc) message_proxy, NULL, 1, FALSE, NULL); -}*/ - -static void -message_push (Message *msg) -{ - /* This used be pushed through the thread pool. This fix is made to - * work-around the crashers in dbus due to threading. The threading - * is not completely removed as its better to have alarm daemon - * running in a thread rather than blocking main thread. This is - * the reason the creation of thread pool is commented out. */ - msg->func (msg); -} - -/* - * use a static ring-buffer so we can call this twice - * in a printf without getting nonsense results. - */ -static const gchar * -e_ctime (const time_t *timep) -{ - static gchar *buffer[4] = { 0, }; - static gint next = 0; - const gchar *ret; - - g_free (buffer[next]); - ret = buffer[next++] = g_strdup (ctime (timep)); - if (buffer[next - 1] && *buffer[next - 1]) { - gint len = strlen (buffer[next - 1]); - while (len > 0 && (buffer[next - 1][len - 1] == '\n' || - buffer[next - 1][len - 1] == '\r' || - g_ascii_isspace (buffer[next - 1][len - 1]))) - len--; - - buffer[next - 1][len - 1] = 0; - } - - if (next >= G_N_ELEMENTS (buffer)) - next = 0; - - return ret; -} - -/* Queues an alarm trigger for midnight so that we can load the next - * day's worth of alarms. */ -static void -queue_midnight_refresh (void) -{ - icaltimezone *zone; - - if (midnight_refresh_id != NULL) { - alarm_remove (midnight_refresh_id); - midnight_refresh_id = NULL; - } - - zone = config_data_get_timezone (); - midnight = time_day_end_with_zone (time (NULL), zone); - - debug (("Refresh at %s", e_ctime (&midnight))); - - midnight_refresh_id = alarm_add ( - midnight, midnight_refresh_cb, NULL, NULL); - if (!midnight_refresh_id) { - debug (("Could not setup 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 = (ClientAlarms *) value; - - debug (("Adding %p", ca)); - - load_alarms_for_today (ca); -} - -struct _midnight_refresh_msg { - Message header; - gboolean remove; -}; - -/* Loads the alarms for the new day every midnight */ -static void -midnight_refresh_async (struct _midnight_refresh_msg *msg) -{ - debug (("...")); - - /* Re-load the alarms for all clients */ - g_hash_table_foreach (client_alarms_hash, add_client_alarms_cb, NULL); - - /* Re-schedule the midnight update */ - if (msg->remove && midnight_refresh_id != NULL) { - debug (("Reschedule the midnight update")); - alarm_remove (midnight_refresh_id); - midnight_refresh_id = NULL; - } - - queue_midnight_refresh (); - - g_slice_free (struct _midnight_refresh_msg, msg); -} - -static void -midnight_refresh_cb (gpointer alarm_id, - time_t trigger, - gpointer data) -{ - struct _midnight_refresh_msg *msg; - - msg = g_slice_new0 (struct _midnight_refresh_msg); - msg->header.func = (MessageFunc) midnight_refresh_async; - msg->remove = TRUE; - - message_push ((Message *) msg); -} - -/* Looks up a client in the client alarms hash table */ -static ClientAlarms * -lookup_client (ECalClient *cal_client) -{ - return g_hash_table_lookup (client_alarms_hash, cal_client); -} - -/* Looks up a queued alarm based on its alarm ID */ -static QueuedAlarm * -lookup_queued_alarm (CompQueuedAlarms *cqa, - gpointer alarm_id) -{ - GSList *l; - QueuedAlarm *qa; - - qa = NULL; - - for (l = cqa->queued_alarms; l; l = l->next) { - qa = l->data; - if (qa->alarm_id == alarm_id) - return qa; - } - - /* not found, might have been updated/removed */ - return NULL; -} - -/* Removes an alarm from the list of alarms of a component. If the alarm was - * the last one listed for the component, it removes the component itself. - */ -static gboolean -remove_queued_alarm (CompQueuedAlarms *cqa, - gpointer alarm_id, - gboolean free_object, - gboolean remove_alarm) -{ - QueuedAlarm *qa = NULL; - GSList *l; - - debug (("...")); - - for (l = cqa->queued_alarms; l; l = l->next) { - qa = l->data; - if (qa->alarm_id == alarm_id) - break; - } - - if (!l) - return FALSE; - - cqa->queued_alarms = g_slist_delete_link (cqa->queued_alarms, l); - - if (remove_alarm) { - GError *error = NULL; - ECalComponentId *id; - - id = e_cal_component_get_id (cqa->alarms->comp); - if (id) { - cqa->expecting_update = TRUE; - e_cal_client_discard_alarm_sync ( - cqa->parent_client->cal_client, id->uid, - id->rid, qa->instance->auid, NULL, &error); - cqa->expecting_update = FALSE; - - if (error) { - if (!g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED)) - g_warning ( - "%s: Failed to discard alarm: %s", - G_STRFUNC, error->message); - g_error_free (error); - } - e_cal_component_free_id (id); - } - } - - g_free (qa); - - /* If this was the last queued alarm for this component, remove the - * component itself. - */ - - if (cqa->queued_alarms != NULL) - return FALSE; - - debug (("Last Component. Removing CQA- Free=%d", free_object)); - if (free_object) { - cqa->id = NULL; - cqa->parent_client = NULL; - e_cal_component_alarms_free (cqa->alarms); - g_free (cqa); - } else { - e_cal_component_alarms_free (cqa->alarms); - cqa->alarms = NULL; - } - - return TRUE; -} - -/** - * has_known_notification: - * Test for notification method and returns if it knows it or not. - * @comp: Component with an alarm. - * @alarm_uid: ID of the alarm in the comp to test. - * - * Returns: %TRUE when we know the notification type, %FALSE otherwise. - */ -static gboolean -has_known_notification (ECalComponent *comp, - const gchar *alarm_uid) -{ - ECalComponentAlarm *alarm; - ECalComponentAlarmAction action; - - g_return_val_if_fail (comp != NULL, FALSE); - g_return_val_if_fail (alarm_uid != NULL, FALSE); - - alarm = e_cal_component_get_alarm (comp, alarm_uid); - if (!alarm) - return FALSE; - - e_cal_component_alarm_get_action (alarm, &action); - e_cal_component_alarm_free (alarm); - - switch (action) { - case E_CAL_COMPONENT_ALARM_AUDIO: - case E_CAL_COMPONENT_ALARM_DISPLAY: - case E_CAL_COMPONENT_ALARM_EMAIL: - case E_CAL_COMPONENT_ALARM_PROCEDURE: - return TRUE; - default: - break; - } - - return FALSE; -} - -/* Callback used when an alarm triggers */ -static void -alarm_trigger_cb (gpointer alarm_id, - time_t trigger, - gpointer data) -{ - CompQueuedAlarms *cqa; - ECalComponent *comp; - QueuedAlarm *qa; - ECalComponentAlarm *alarm; - ECalComponentAlarmAction action; - - cqa = data; - comp = cqa->alarms->comp; - - config_data_set_last_notification_time ( - cqa->parent_client->cal_client, trigger); - debug (("Setting Last notification time to %s", e_ctime (&trigger))); - - qa = lookup_queued_alarm (cqa, alarm_id); - if (!qa) - return; - - /* Decide what to do based on the alarm action. We use the trigger that - * is passed to us instead of the one from the instance structure - * because this may be a snoozed alarm instead of an original - * occurrence. - */ - - alarm = e_cal_component_get_alarm (comp, qa->instance->auid); - if (!alarm) - return; - - e_cal_component_alarm_get_action (alarm, &action); - e_cal_component_alarm_free (alarm); - - switch (action) { - case E_CAL_COMPONENT_ALARM_AUDIO: - audio_notification (trigger, cqa, alarm_id); - break; - - case E_CAL_COMPONENT_ALARM_DISPLAY: -#ifdef HAVE_LIBNOTIFY - popup_notification (trigger, cqa, alarm_id, TRUE); -#endif - display_notification (trigger, cqa, alarm_id, TRUE); - break; - - case E_CAL_COMPONENT_ALARM_EMAIL: - mail_notification (trigger, cqa, alarm_id); - break; - - case E_CAL_COMPONENT_ALARM_PROCEDURE: - procedure_notification (trigger, cqa, alarm_id); - break; - - default: - g_return_if_reached (); - } - debug (("Notification sent: %d", action)); -} - -/* Adds the alarms in a ECalComponentAlarms 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, - ECalComponentAlarms *alarms) -{ - ECalComponentId *id; - CompQueuedAlarms *cqa; - GSList *l; - - /* No alarms? */ - if (alarms == NULL || alarms->alarms == NULL) { - debug (("No alarms to add")); - if (alarms) - e_cal_component_alarms_free (alarms); - return; - } - - cqa = g_new (CompQueuedAlarms, 1); - cqa->parent_client = ca; - cqa->alarms = alarms; - cqa->expecting_update = FALSE; - - cqa->queued_alarms = NULL; - debug (("Creating CQA %p", cqa)); - - for (l = alarms->alarms; l; l = l->next) { - ECalComponentAlarmInstance *instance; - gpointer alarm_id; - QueuedAlarm *qa; - - instance = l->data; - - if (!has_known_notification (cqa->alarms->comp, instance->auid)) - continue; - - alarm_id = alarm_add ( - instance->trigger, alarm_trigger_cb, cqa, NULL); - if (!alarm_id) - continue; - - qa = g_new (QueuedAlarm, 1); - qa->alarm_id = alarm_id; - qa->instance = instance; - qa->orig_trigger = instance->trigger; - qa->snooze = FALSE; - - cqa->queued_alarms = g_slist_prepend (cqa->queued_alarms, qa); - } - - id = e_cal_component_get_id (alarms->comp); - - /* If we failed to add all the alarms, then we should get rid of the cqa */ - if (cqa->queued_alarms == NULL) { - e_cal_component_alarms_free (cqa->alarms); - cqa->alarms = NULL; - debug (("Failed to add all : %p", cqa)); - g_free (cqa); - return; - } - - cqa->queued_alarms = g_slist_reverse (cqa->queued_alarms); - cqa->id = id; - debug (("Alarm added for %s", id->uid)); - g_hash_table_insert (ca->uid_alarms_hash, cqa->id, cqa); -} - -/* Loads the alarms of a client for a given range of time */ -static void -load_alarms (ClientAlarms *ca, - time_t start, - time_t end) -{ - gchar *str_query, *iso_start, *iso_end; - GError *error = NULL; - - debug (("...")); - - iso_start = isodate_from_time_t (start); - if (!iso_start) - return; - - iso_end = isodate_from_time_t (end); - if (!iso_end) { - g_free (iso_start); - return; - } - - str_query = g_strdup_printf ( - "(has-alarms-in-range? (make-time \"%s\") " - "(make-time \"%s\"))", iso_start, iso_end); - g_free (iso_start); - g_free (iso_end); - - /* create the live query */ - if (ca->view) { - debug (("Disconnecting old queries")); - g_signal_handlers_disconnect_matched ( - ca->view, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, ca); - g_object_unref (ca->view); - ca->view = NULL; - } - - e_cal_client_get_view_sync ( - ca->cal_client, str_query, &ca->view, NULL, &error); - - if (error != NULL) { - g_warning ( - "%s: Could not get query for client: %s", - G_STRFUNC, error->message); - g_error_free (error); - } else { - debug (("Setting Call backs")); - - g_signal_connect ( - ca->view, "objects-added", - G_CALLBACK (query_objects_modified_cb), ca); - g_signal_connect ( - ca->view, "objects-modified", - G_CALLBACK (query_objects_modified_cb), ca); - g_signal_connect ( - ca->view, "objects-removed", - G_CALLBACK (query_objects_removed_cb), ca); - - e_cal_client_view_start (ca->view, &error); - - if (error != NULL) { - g_warning ( - "%s: Failed to start view: %s", - G_STRFUNC, error->message); - g_error_free (error); - } - } - - g_free (str_query); -} - -/* Loads today's remaining alarms for a client */ -static void -load_alarms_for_today (ClientAlarms *ca) -{ - time_t now, from, day_end, day_start; - icaltimezone *zone; - - now = time (NULL); - zone = config_data_get_timezone (); - day_start = time_day_begin_with_zone (now, zone); - - /* Make sure we don't miss some events from the last notification. - * We add 1 to the saved notification time to make the time ranges - * half-open; we do not want to display the "last" displayed alarm - * twice, once when it occurs and once when the alarm daemon restarts. - */ - from = config_data_get_last_notification_time (ca->cal_client) + 1; - if (from <= 0) - from = MAX (from, day_start); - - day_end = time_day_end_with_zone (now, zone); - debug (("From %s to %s", e_ctime (&from), e_ctime (&day_end))); - load_alarms (ca, from, day_end); -} - -/* Looks up a component's queued alarm structure in a client alarms structure */ -static CompQueuedAlarms * -lookup_comp_queued_alarms (ClientAlarms *ca, - const ECalComponentId *id) -{ - return g_hash_table_lookup (ca->uid_alarms_hash, id); -} - -static void -remove_alarms (CompQueuedAlarms *cqa, - gboolean free_object) -{ - GSList *l; - - debug (("Removing for %p", cqa)); - for (l = cqa->queued_alarms; l;) { - QueuedAlarm *qa; - - qa = l->data; - - /* Get the next element here because the list element will go - * away in remove_queued_alarm(). The qa will be freed there as - * well. - */ - l = l->next; - - alarm_remove (qa->alarm_id); - remove_queued_alarm (cqa, qa->alarm_id, free_object, FALSE); - } -} - -/* Removes a component an its alarms */ -static void -remove_comp (ClientAlarms *ca, - ECalComponentId *id) -{ - CompQueuedAlarms *cqa; - - debug (("Removing uid %s", id->uid)); - - if (id->rid && !(*(id->rid))) { - g_free (id->rid); - id->rid = NULL; - } - - cqa = lookup_comp_queued_alarms (ca, id); - if (!cqa) - return; - - /* If a component is present, then it means we must have alarms queued - * for it. - */ - g_return_if_fail (cqa->queued_alarms != NULL); - - debug (("Removing CQA %p", cqa)); - remove_alarms (cqa, TRUE); -} - -/* Called when a calendar component changes; we must reload its corresponding - * alarms. - */ -struct _query_msg { - Message header; - GSList *objects; - gpointer data; -}; - -static GSList * -duplicate_ical (const GSList *in_list) -{ - const GSList *l; - GSList *out_list = NULL; - for (l = in_list; l; l = l->next) { - out_list = g_slist_prepend ( - out_list, icalcomponent_new_clone (l->data)); - } - - return g_slist_reverse (out_list); -} - -static GSList * -duplicate_ecal (const GSList *in_list) -{ - const GSList *l; - GSList *out_list = NULL; - for (l = in_list; l; l = l->next) { - ECalComponentId *id, *old; - old = l->data; - id = g_new0 (ECalComponentId, 1); - id->uid = g_strdup (old->uid); - id->rid = g_strdup (old->rid); - out_list = g_slist_prepend (out_list, id); - } - - return g_slist_reverse (out_list); -} - -static gboolean -get_alarms_for_object (ECalClient *cal_client, - const ECalComponentId *id, - time_t start, - time_t end, - ECalComponentAlarms **alarms) -{ - icalcomponent *icalcomp; - ECalComponent *comp; - ECalComponentAlarmAction omit[] = {-1}; - - g_return_val_if_fail (cal_client != NULL, FALSE); - g_return_val_if_fail (id != NULL, FALSE); - g_return_val_if_fail (alarms != NULL, FALSE); - g_return_val_if_fail (start >= 0 && end >= 0, FALSE); - g_return_val_if_fail (start <= end, FALSE); - - if (!e_cal_client_get_object_sync ( - cal_client, id->uid, id->rid, &icalcomp, NULL, NULL)) - return FALSE; - - if (!icalcomp) - return FALSE; - - comp = e_cal_component_new (); - if (!e_cal_component_set_icalcomponent (comp, icalcomp)) { - icalcomponent_free (icalcomp); - g_object_unref (comp); - return FALSE; - } - - *alarms = e_cal_util_generate_alarms_for_comp ( - comp, start, end, omit, e_cal_client_resolve_tzid_cb, - cal_client, e_cal_client_get_default_timezone (cal_client)); - - g_object_unref (comp); - - return TRUE; -} - -static void -query_objects_changed_async (struct _query_msg *msg) -{ - ClientAlarms *ca; - time_t from, day_end; - ECalComponentAlarms *alarms; - gboolean found; - icaltimezone *zone; - CompQueuedAlarms *cqa; - GSList *l; - GSList *objects; - - ca = msg->data; - objects = msg->objects; - - from = config_data_get_last_notification_time (ca->cal_client); - if (from == -1) - from = time (NULL); - else - from += 1; /* we add 1 to make sure the alarm is not displayed twice */ - - zone = config_data_get_timezone (); - - day_end = time_day_end_with_zone (time (NULL), zone); - - for (l = objects; l != NULL; l = l->next) { - ECalComponentId *id; - GSList *sl; - ECalComponent *comp = e_cal_component_new (); - - e_cal_component_set_icalcomponent (comp, l->data); - - id = e_cal_component_get_id (comp); - found = get_alarms_for_object (ca->cal_client, id, from, day_end, &alarms); - - if (!found) { - debug (("No Alarm found for client %p", ca->cal_client)); - tray_list_remove_cqa (lookup_comp_queued_alarms (ca, id)); - remove_comp (ca, id); - g_hash_table_remove (ca->uid_alarms_hash, id); - e_cal_component_free_id (id); - g_object_unref (comp); - comp = NULL; - continue; - } - - cqa = lookup_comp_queued_alarms (ca, id); - if (!cqa) { - debug (("No currently queued alarms for %s", id->uid)); - add_component_alarms (ca, alarms); - g_object_unref (comp); - comp = NULL; - continue; - } - - debug (("Alarm Already Exist for %s", id->uid)); - /* If the alarms or the alarms list is empty, - * remove it after updating the cqa structure. */ - if (alarms == NULL || alarms->alarms == NULL) { - - /* Update the cqa and its queued alarms - * for changes in summary and alarm_uid. */ - update_cqa (cqa, comp); - - if (alarms) - e_cal_component_alarms_free (alarms); - continue; - } - - /* if already in the list, just update it */ - remove_alarms (cqa, FALSE); - cqa->alarms = alarms; - cqa->queued_alarms = NULL; - - /* add the new alarms */ - for (sl = cqa->alarms->alarms; sl; sl = sl->next) { - ECalComponentAlarmInstance *instance; - gpointer alarm_id; - QueuedAlarm *qa; - - instance = sl->data; - - if (!has_known_notification (cqa->alarms->comp, instance->auid)) - continue; - - alarm_id = alarm_add (instance->trigger, alarm_trigger_cb, cqa, NULL); - if (!alarm_id) - continue; - - qa = g_new (QueuedAlarm, 1); - qa->alarm_id = alarm_id; - qa->instance = instance; - qa->snooze = FALSE; - qa->orig_trigger = instance->trigger; - cqa->queued_alarms = g_slist_prepend (cqa->queued_alarms, qa); - debug (("Adding %p to queue", qa)); - } - - cqa->queued_alarms = g_slist_reverse (cqa->queued_alarms); - g_object_unref (comp); - comp = NULL; - } - g_slist_free (objects); - - g_slice_free (struct _query_msg, msg); -} - -static void -query_objects_modified_cb (ECalClientView *view, - const GSList *objects, - gpointer data) -{ - struct _query_msg *msg; - - msg = g_slice_new0 (struct _query_msg); - msg->header.func = (MessageFunc) query_objects_changed_async; - msg->objects = duplicate_ical (objects); - msg->data = data; - - message_push ((Message *) msg); -} - -/* Called when a calendar component is removed; we must delete its corresponding - * alarms. - */ -static void -query_objects_removed_async (struct _query_msg *msg) -{ - ClientAlarms *ca; - GSList *l; - GSList *objects; - - ca = msg->data; - objects = msg->objects; - - debug (("Removing %d objects", g_slist_length (objects))); - - for (l = objects; l != NULL; l = l->next) { - /* If the alarm is already triggered remove it. */ - tray_list_remove_cqa (lookup_comp_queued_alarms (ca, l->data)); - remove_comp (ca, l->data); - g_hash_table_remove (ca->uid_alarms_hash, l->data); - e_cal_component_free_id (l->data); - } - - g_slist_free (objects); - - g_slice_free (struct _query_msg, msg); -} - -static void -query_objects_removed_cb (ECalClientView *view, - const GSList *uids, - gpointer data) -{ - struct _query_msg *msg; - - msg = g_slice_new0 (struct _query_msg); - msg->header.func = (MessageFunc) query_objects_removed_async; - msg->objects = duplicate_ecal (uids); - msg->data = data; - - message_push ((Message *) msg); -} - -/* Notification functions */ - -/* Creates a snooze alarm based on an existing one. The snooze offset is - * compued with respect to the current time. - */ -static void -create_snooze (CompQueuedAlarms *cqa, - gpointer alarm_id, - gint snooze_mins) -{ - QueuedAlarm *orig_qa; - time_t t; - gpointer new_id; - - orig_qa = lookup_queued_alarm (cqa, alarm_id); - if (!orig_qa) - return; - - t = time (NULL); - t += snooze_mins * 60; - - new_id = alarm_add (t, alarm_trigger_cb, cqa, NULL); - if (!new_id) { - debug (("Unable to schedule trigger for %s", e_ctime (&t))); - return; - } - - orig_qa->instance->trigger = t; - orig_qa->alarm_id = new_id; - orig_qa->snooze = TRUE; - debug (("Adding a alarm at %s", e_ctime (&t))); -} - -/* Launches a component editor for a component */ -static void -edit_component (ECalClient *cal_client, - ECalComponent *comp) -{ - ESource *source; - gchar *command_line; - const gchar *scheme; - const gchar *comp_uid; - const gchar *source_uid; - GError *error = NULL; - - /* XXX Don't we have a function to construct these URIs? - * How are other apps expected to know this stuff? */ - - source = e_client_get_source (E_CLIENT (cal_client)); - source_uid = e_source_peek_uid (source); - - e_cal_component_get_uid (comp, &comp_uid); - - switch (e_cal_client_get_source_type (cal_client)) { - case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: - scheme = "calendar:"; - break; - case E_CAL_CLIENT_SOURCE_TYPE_TASKS: - scheme = "task:"; - break; - case E_CAL_CLIENT_SOURCE_TYPE_MEMOS: - scheme = "memo:"; - break; - default: - g_return_if_reached (); - } - - command_line = g_strdup_printf ( - "%s %s///?source-uid=%s&comp-uid=%s", - PACKAGE, scheme, source_uid, comp_uid); - - if (!g_spawn_command_line_async (command_line, &error)) { - g_critical ("%s", error->message); - g_error_free (error); - } - - g_free (command_line); -} - -typedef struct { - gchar *summary; - gchar *description; - gchar *location; - gboolean blink_state; - gboolean snooze_set; - gint blink_id; - time_t trigger; - CompQueuedAlarms *cqa; - gpointer alarm_id; - ECalComponent *comp; - ECalClient *cal_client; - ECalClientView *view; - GdkPixbuf *image; - GtkTreeIter iter; -} TrayIconData; - -static void -free_tray_icon_data (TrayIconData *tray_data) -{ - g_return_if_fail (tray_data != NULL); - - if (tray_data->summary) { - g_free (tray_data->summary); - tray_data->summary = NULL; - } - - if (tray_data->description) { - g_free (tray_data->description); - tray_data->description = NULL; - } - - if (tray_data->location) { - g_free (tray_data->location); - tray_data->location = NULL; - } - - g_object_unref (tray_data->cal_client); - tray_data->cal_client = NULL; - - g_signal_handlers_disconnect_matched ( - tray_data->view, G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, on_dialog_objs_removed_cb, NULL); - g_object_unref (tray_data->view); - tray_data->view = NULL; - - g_object_unref (tray_data->comp); - tray_data->comp = NULL; - - tray_data->cqa = NULL; - tray_data->alarm_id = NULL; - tray_data->image = NULL; - - g_free (tray_data); -} - -static void -on_dialog_objs_removed_async (struct _query_msg *msg) -{ - TrayIconData *tray_data; - GSList *l, *objects; - ECalComponentId *our_id; - - debug (("...")); - - tray_data = msg->data; - objects = msg->objects; - - our_id = e_cal_component_get_id (tray_data->comp); - g_return_if_fail (our_id); - - for (l = objects; l != NULL; l = l->next) { - ECalComponentId *id = l->data; - - if (!id) - continue; - - if (g_strcmp0 (id->uid, our_id->uid) == 0&& g_strcmp0 (id->rid, our_id->rid) == 0) { - tray_data->cqa = NULL; - tray_data->alarm_id = NULL; - tray_icons_list = g_list_remove (tray_icons_list, tray_data); - tray_data = NULL; - } - - e_cal_component_free_id (id); - } - - e_cal_component_free_id (our_id); - g_slist_free (objects); - g_slice_free (struct _query_msg, msg); -} - -static void -on_dialog_objs_removed_cb (ECalClientView *view, - const GSList *uids, - gpointer data) -{ - struct _query_msg *msg; - - msg = g_slice_new0 (struct _query_msg); - msg->header.func = (MessageFunc) on_dialog_objs_removed_async; - msg->objects = duplicate_ecal (uids); - msg->data = data; - - message_push ((Message *) msg); -} - -struct _tray_cqa_msg { - Message header; - CompQueuedAlarms *cqa; -}; - -static void -tray_list_remove_cqa_async (struct _tray_cqa_msg *msg) -{ - CompQueuedAlarms *cqa = msg->cqa; - GList *list = tray_icons_list; - - debug (("Removing CQA %p from tray list", cqa)); - - while (list) { - TrayIconData *tray_data = list->data; - GList *tmp = list; - GtkTreeModel *model; - - list = list->next; - if (tray_data->cqa == cqa) { - debug (("Found")); - tray_icons_list = g_list_delete_link (tray_icons_list, tmp); - if (alarm_notifications_dialog) { - model = gtk_tree_view_get_model ( - GTK_TREE_VIEW (alarm_notifications_dialog->treeview)); - gtk_list_store_remove (GTK_LIST_STORE (model), &(tray_data->iter)); - } - free_tray_icon_data (tray_data); - } - } - - debug (("%d alarms left", g_list_length (tray_icons_list))); - - if (alarm_notifications_dialog) { - if (!g_list_length (tray_icons_list)) { - gtk_widget_destroy (alarm_notifications_dialog->dialog); - g_free (alarm_notifications_dialog); - alarm_notifications_dialog = NULL; - } else { - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeSelection *sel; - - model = gtk_tree_view_get_model ( - GTK_TREE_VIEW (alarm_notifications_dialog->treeview)); - gtk_tree_model_get_iter_first (model, &iter); - sel = gtk_tree_view_get_selection ( - GTK_TREE_VIEW (alarm_notifications_dialog->treeview)); - gtk_tree_selection_select_iter (sel, &iter); - } - } - - g_slice_free (struct _tray_cqa_msg, msg); -} - -static void -tray_list_remove_cqa (CompQueuedAlarms *cqa) -{ - struct _tray_cqa_msg *msg; - - msg = g_slice_new0 (struct _tray_cqa_msg); - msg->header.func = (MessageFunc) tray_list_remove_cqa_async; - msg->cqa = cqa; - - message_push ((Message *) msg); -} - -/* Callback used from the alarm notify dialog */ -static void -tray_list_remove_async (Message *msg) -{ - GList *list = tray_icons_list; - - debug (("Removing %d alarms", g_list_length (list))); - while (list != NULL) { - - TrayIconData *tray_data = list->data; - - if (!tray_data->snooze_set) { - GList *temp = list->next; - gboolean status; - - tray_icons_list = g_list_remove_link (tray_icons_list, list); - status = remove_queued_alarm ( - tray_data->cqa, - tray_data->alarm_id, FALSE, TRUE); - if (status) { - g_hash_table_remove ( - tray_data->cqa->parent_client->uid_alarms_hash, - tray_data->cqa->id); - e_cal_component_free_id (tray_data->cqa->id); - g_free (tray_data->cqa); - } - free_tray_icon_data (tray_data); - tray_data = NULL; - g_list_free_1 (list); - if (tray_icons_list != list) /* List head is modified */ - list = tray_icons_list; - else - list = temp; - } else - list = list->next; - } - - g_slice_free (Message, msg); -} - -static void -tray_list_remove_icons (void) -{ - Message *msg; - - msg = g_slice_new0 (Message); - msg->func = tray_list_remove_async; - - message_push (msg); -} - -struct _tray_msg { - Message header; - TrayIconData *data; -}; - -static void -tray_list_remove_data_async (struct _tray_msg *msg) -{ - TrayIconData *tray_data = msg->data; - - debug (("Removing %p from tray list", tray_data)); - - tray_icons_list = g_list_remove_all (tray_icons_list, tray_data); - free_tray_icon_data (tray_data); - tray_data = NULL; - - g_slice_free (struct _tray_msg, msg); -} - -static void -tray_list_remove_data (TrayIconData *data) -{ - struct _tray_msg *msg; - - msg = g_slice_new0 (struct _tray_msg); - msg->header.func = (MessageFunc) tray_list_remove_data_async; - msg->data = data; - - message_push ((Message *) msg); -} - -static void -notify_dialog_cb (AlarmNotifyResult result, - gint snooze_mins, - gpointer data) -{ - TrayIconData *tray_data = data; - - debug (("Received from dialog")); - - g_signal_handlers_disconnect_matched ( - tray_data->view, G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, on_dialog_objs_removed_cb, NULL); - - switch (result) { - case ALARM_NOTIFY_SNOOZE: - debug (("Creating a snooze")); - create_snooze (tray_data->cqa, tray_data->alarm_id, snooze_mins); - tray_data->snooze_set = TRUE; - tray_list_remove_data (tray_data); - if (alarm_notifications_dialog) { - GtkTreeSelection *selection = - gtk_tree_view_get_selection ( - GTK_TREE_VIEW (alarm_notifications_dialog->treeview)); - GtkTreeIter iter; - GtkTreeModel *model = NULL; - - /* We can also use tray_data->iter */ - if (gtk_tree_selection_get_selected (selection, &model, &iter)) { - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - if (!gtk_tree_model_get_iter_first (model, &iter)) { - /* We removed the last one */ - gtk_widget_destroy (alarm_notifications_dialog->dialog); - g_free (alarm_notifications_dialog); - alarm_notifications_dialog = NULL; - } else { - /* Select the first */ - gtk_tree_selection_select_iter (selection, &iter); - } - } - - } - - break; - - case ALARM_NOTIFY_EDIT: - edit_component (tray_data->cal_client, tray_data->comp); - - break; - - case ALARM_NOTIFY_DISMISS: - if (alarm_notifications_dialog) { - GtkTreeModel *model; - - model = gtk_tree_view_get_model ( - GTK_TREE_VIEW (alarm_notifications_dialog->treeview)); - gtk_list_store_remove (GTK_LIST_STORE (model), &tray_data->iter); - } - break; - - case ALARM_NOTIFY_CLOSE: - debug (("Dialog close")); - if (alarm_notifications_dialog) { - GtkTreeIter iter; - GtkTreeModel *model = - gtk_tree_view_get_model ( - GTK_TREE_VIEW (alarm_notifications_dialog->treeview)); - gboolean valid = gtk_tree_model_get_iter_first (model, &iter); - - /* Maybe we should warn about this first? */ - while (valid) { - valid = gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - } - - gtk_widget_destroy (alarm_notifications_dialog->dialog); - g_free (alarm_notifications_dialog); - alarm_notifications_dialog = NULL; - - /* Task to remove the tray icons */ - tray_list_remove_icons (); - } - - break; - - default: - g_return_if_reached (); - } - - return; -} - -static void -remove_tray_icon (void) -{ - if (tray_blink_id > -1) - g_source_remove (tray_blink_id); - tray_blink_id = -1; - - if (tray_icon) { - gtk_status_icon_set_visible (tray_icon, FALSE); - g_object_unref (tray_icon); - tray_icon = NULL; - } -} - -/* Callbacks. */ -static gboolean -open_alarm_dialog (TrayIconData *tray_data) -{ - QueuedAlarm *qa; - - debug (("...")); - qa = lookup_queued_alarm (tray_data->cqa, tray_data->alarm_id); - if (qa) { - remove_tray_icon (); - - if (!alarm_notifications_dialog) - alarm_notifications_dialog = notified_alarms_dialog_new (); - - if (alarm_notifications_dialog) { - - GtkTreeSelection *selection = NULL; - - selection = gtk_tree_view_get_selection ( - GTK_TREE_VIEW (alarm_notifications_dialog->treeview)); - - tray_data->iter = add_alarm_to_notified_alarms_dialog ( - alarm_notifications_dialog, - tray_data->trigger, - qa->instance->occur_start, - qa->instance->occur_end, - e_cal_component_get_vtype (tray_data->comp), - tray_data->summary, - tray_data->description, - tray_data->location, - notify_dialog_cb, tray_data); - - gtk_tree_selection_select_iter ( - selection, &tray_data->iter); - - } - - } else { - remove_tray_icon (); - } - - return TRUE; -} - -static gint -tray_icon_clicked_cb (GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) -{ - if (event->type == GDK_BUTTON_PRESS) { - debug (("left click and %d alarms", g_list_length (tray_icons_list))); - if (event->button == 1 && g_list_length (tray_icons_list) > 0) { - GList *tmp; - for (tmp = tray_icons_list; tmp; tmp = tmp->next) { - open_alarm_dialog (tmp->data); - } - - return TRUE; - } else if (event->button == 3) { - debug (("right click")); - - remove_tray_icon (); - return TRUE; - } - } - - return FALSE; -} - -static void -icon_activated (GtkStatusIcon *icon) -{ - GdkEventButton event; - - event.type = GDK_BUTTON_PRESS; - event.button = 1; - event.time = gtk_get_current_event_time (); - - tray_icon_clicked_cb (NULL, &event, NULL); -} - -static void -popup_menu (GtkStatusIcon *icon, - guint button, - guint activate_time) -{ - if (button == 3) { - /* right click */ - GdkEventButton event; - - event.type = GDK_BUTTON_PRESS; - event.button = 3; - event.time = gtk_get_current_event_time (); - - tray_icon_clicked_cb (NULL, &event, NULL); - } -} - -static gboolean -tray_icon_blink_cb (gpointer data) -{ - static gboolean tray_blink_state = FALSE; - const gchar *icon_name; - - tray_blink_countdown--; - tray_blink_state = !tray_blink_state; - - if (tray_blink_state || tray_blink_countdown <= 0) - icon_name = "appointment-missed"; - else - icon_name = "appointment-soon"; - - if (tray_icon) - gtk_status_icon_set_from_icon_name (tray_icon, icon_name); - - if (tray_blink_countdown <= 0) - tray_blink_id = -1; - - return tray_blink_countdown > 0; -} - -/* Add a new data to tray list */ - -static void -tray_list_add_async (struct _tray_msg *msg) -{ - tray_icons_list = g_list_prepend (tray_icons_list, msg->data); - - g_slice_free (struct _tray_msg, msg); -} - -static void -tray_list_add_new (TrayIconData *data) -{ - struct _tray_msg *msg; - - msg = g_slice_new0 (struct _tray_msg); - msg->header.func = (MessageFunc) tray_list_add_async; - msg->data = data; - - message_push ((Message *) msg); -} - -/* Performs notification of a display alarm */ -static void -display_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id, - gboolean use_description) -{ - QueuedAlarm *qa; - ECalComponent *comp; - const gchar *summary, *description, *location; - TrayIconData *tray_data; - ECalComponentText text; - GSList *text_list; - gchar *str, *start_str, *end_str, *alarm_str, *time_str; - icaltimezone *current_zone; - ECalComponentOrganizer organiser; - - debug (("...")); - - comp = cqa->alarms->comp; - qa = lookup_queued_alarm (cqa, alarm_id); - if (!qa) - return; - - /* get a sensible description for the event */ - e_cal_component_get_summary (comp, &text); - e_cal_component_get_organizer (comp, &organiser); - - if (text.value) - summary = text.value; - else - summary = _("No summary available."); - - e_cal_component_get_description_list (comp, &text_list); - - if (text_list) { - text = *((ECalComponentText *) text_list->data); - if (text.value) - description = text.value; - else - description = _("No description available."); - } else { - description = _("No description available."); - } - - e_cal_component_free_text_list (text_list); - - e_cal_component_get_location (comp, &location); - - if (!location) - location = _("No location information available."); - - /* create the tray icon */ - if (tray_icon == NULL) { - tray_icon = gtk_status_icon_new (); - gtk_status_icon_set_from_icon_name ( - tray_icon, "appointment-soon"); - g_signal_connect ( - tray_icon, "activate", - G_CALLBACK (icon_activated), NULL); - g_signal_connect ( - tray_icon, "popup-menu", - G_CALLBACK (popup_menu), NULL); - } - - current_zone = config_data_get_timezone (); - alarm_str = timet_to_str_with_zone (trigger, current_zone); - start_str = timet_to_str_with_zone (qa->instance->occur_start, current_zone); - end_str = timet_to_str_with_zone (qa->instance->occur_end, current_zone); - time_str = calculate_time (qa->instance->occur_start, qa->instance->occur_end); - - str = g_strdup_printf ("%s\n%s %s", - summary, start_str, time_str); - - /* create the private structure */ - tray_data = g_new0 (TrayIconData, 1); - tray_data->summary = g_strdup (summary); - tray_data->description = g_strdup (description); - tray_data->location = g_strdup (location); - tray_data->trigger = trigger; - tray_data->cqa = cqa; - tray_data->alarm_id = alarm_id; - tray_data->comp = g_object_ref (e_cal_component_clone (comp)); - tray_data->cal_client = cqa->parent_client->cal_client; - tray_data->view = g_object_ref (cqa->parent_client->view); - tray_data->blink_state = FALSE; - tray_data->snooze_set = FALSE; - g_object_ref (tray_data->cal_client); - - /* Task to add tray_data to the global tray_icon_list */ - tray_list_add_new (tray_data); - - if (g_list_length (tray_icons_list) > 1) { - gchar *tip; - - tip = g_strdup_printf (ngettext ( - "You have %d reminder", "You have %d reminders", - g_list_length (tray_icons_list)), - g_list_length (tray_icons_list)); - gtk_status_icon_set_tooltip_text (tray_icon, tip); - } - else { - gtk_status_icon_set_tooltip_text (tray_icon, str); - } - - g_free (start_str); - g_free (end_str); - g_free (alarm_str); - g_free (time_str); - g_free (str); - - g_signal_connect ( - tray_data->view, "objects_removed", - G_CALLBACK (on_dialog_objs_removed_cb), tray_data); - - /* FIXME: We should remove this check */ - if (!config_data_get_notify_with_tray ()) { - tray_blink_id = -1; - open_alarm_dialog (tray_data); - if (alarm_notifications_dialog) - gtk_window_stick (GTK_WINDOW ( - alarm_notifications_dialog->dialog)); - } else { - if (tray_blink_id == -1) { - tray_blink_countdown = 30; - tray_blink_id = g_timeout_add (500, tray_icon_blink_cb, tray_data); - } - } -} - -#ifdef HAVE_LIBNOTIFY -static void -popup_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id, - gboolean use_description) -{ - QueuedAlarm *qa; - ECalComponent *comp; - const gchar *summary, *location; - ECalComponentText text; - gchar *str, *start_str, *end_str, *alarm_str, *time_str; - icaltimezone *current_zone; - ECalComponentOrganizer organiser; - NotifyNotification *n; - gchar *body; - - debug (("...")); - - comp = cqa->alarms->comp; - qa = lookup_queued_alarm (cqa, alarm_id); - if (!qa) - return; - if (!notify_is_initted ()) - notify_init("Evolution Alarm Notify"); - - /* get a sensible description for the event */ - e_cal_component_get_summary (comp, &text); - e_cal_component_get_organizer (comp, &organiser); - - if (text.value) - summary = text.value; - else - summary = _("No summary available."); - - e_cal_component_get_location (comp, &location); - - /* create the tray icon */ - - current_zone = config_data_get_timezone (); - alarm_str = timet_to_str_with_zone (trigger, current_zone); - start_str = timet_to_str_with_zone (qa->instance->occur_start, current_zone); - end_str = timet_to_str_with_zone (qa->instance->occur_end, current_zone); - time_str = calculate_time (qa->instance->occur_start, qa->instance->occur_end); - - str = g_strdup_printf ("%s %s", - start_str, time_str); - - if (organiser.cn) { - if (location) - body = g_strdup_printf ( - "<b>%s</b>\n%s %s\n%s %s", - organiser.cn, _("Location:"), - location, start_str, time_str); - else - body = g_strdup_printf ( - "<b>%s</b>\n%s %s", - organiser.cn, start_str, time_str); - } - else { - if (location) - body = g_strdup_printf ( - "%s %s\n%s %s", _("Location:"), - location, start_str, time_str); - else - body = g_strdup_printf ( - "%s %s", start_str, time_str); - } - -#ifdef HAVE_LIBNOTIFY_07 - n = notify_notification_new (summary, body, "appointment-soon"); -#else - n = notify_notification_new (summary, body, "appointment-soon", NULL); -#endif /* HAVE_LIBNOTIFY_07 */ - if (!notify_notification_show (n, NULL)) - g_warning ("Could not send notification to daemon\n"); - - /* create the private structure */ - g_free (start_str); - g_free (end_str); - g_free (alarm_str); - g_free (time_str); - g_free (str); - -} -#endif - -/* Performs notification of an audio alarm */ -static void -audio_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id) -{ - QueuedAlarm *qa; - ECalComponent *comp; - ECalComponentAlarm *alarm; - icalattach *attach; - gint flag = 0; - - debug (("...")); - - comp = cqa->alarms->comp; - qa = lookup_queued_alarm (cqa, alarm_id); - if (!qa) - return; - - alarm = e_cal_component_get_alarm (comp, qa->instance->auid); - g_return_if_fail (alarm != NULL); - - e_cal_component_alarm_get_attach (alarm, &attach); - e_cal_component_alarm_free (alarm); - - if (attach && icalattach_get_is_url (attach)) { - const gchar *url; - - url = icalattach_get_url (attach); - if (url && *url) { - gchar *filename; - GError *error = NULL; - - filename = g_filename_from_uri (url, NULL, &error); - - if (error != NULL) { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_error_free (error); - } else if (filename && g_file_test (filename, G_FILE_TEST_EXISTS)) { -#ifdef HAVE_CANBERRA - flag = 1; - ca_context_play ( - ca_gtk_context_get (), 0, - CA_PROP_MEDIA_FILENAME, filename, NULL); -#endif - } - - g_free (filename); - } - } - - if (!flag) - gdk_beep (); - - if (attach) - icalattach_unref (attach); - -} - -/* Performs notification of a mail alarm */ -static void -mail_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id) -{ - GtkWidget *container; - GtkWidget *dialog; - GtkWidget *label; - - /* FIXME */ - - debug (("...")); - - if (!e_client_check_capability ( - E_CLIENT (cqa->parent_client->cal_client), - CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS)) - return; - - dialog = gtk_dialog_new_with_buttons ( - _("Warning"), NULL, 0, - GTK_STOCK_OK, GTK_RESPONSE_CANCEL, - NULL); - label = gtk_label_new ( - _("Evolution does not support calendar reminders with\n" - "email notifications yet, but this reminder was\n" - "configured to send an email. Evolution will display\n" - "a normal reminder dialog box instead.")); - gtk_widget_show (label); - - container = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); - gtk_box_pack_start (GTK_BOX (container), label, TRUE, TRUE, 4); - - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); -} - -/* Performs notification of a procedure alarm */ -static gboolean -procedure_notification_dialog (const gchar *cmd, - const gchar *url) -{ - GtkWidget *container; - GtkWidget *dialog; - GtkWidget *label; - GtkWidget *checkbox; - gchar *str; - gint btn; - - debug (("...")); - - if (config_data_is_blessed_program (url)) - return TRUE; - - dialog = gtk_dialog_new_with_buttons ( - _("Warning"), NULL, 0, - GTK_STOCK_NO, GTK_RESPONSE_CANCEL, - GTK_STOCK_YES, GTK_RESPONSE_OK, - NULL); - - str = g_strdup_printf ( - _("An Evolution Calendar reminder is about to trigger. " - "This reminder is configured to run the following program:\n\n" - " %s\n\n" - "Are you sure you want to run this program?"), - cmd); - label = gtk_label_new (str); - gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); - gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); - gtk_widget_show (label); - - container = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); - gtk_box_pack_start (GTK_BOX (container), label, TRUE, TRUE, 4); - g_free (str); - - checkbox = gtk_check_button_new_with_label - (_("Do not ask me about this program again.")); - gtk_widget_show (checkbox); - gtk_box_pack_start (GTK_BOX (container), checkbox, TRUE, TRUE, 4); - - /* Run the dialog */ - btn = gtk_dialog_run (GTK_DIALOG (dialog)); - if (btn == GTK_RESPONSE_OK && - gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox))) - config_data_save_blessed_program (url); - gtk_widget_destroy (dialog); - - return (btn == GTK_RESPONSE_OK); -} - -static void -procedure_notification (time_t trigger, - CompQueuedAlarms *cqa, - gpointer alarm_id) -{ - QueuedAlarm *qa; - ECalComponent *comp; - ECalComponentAlarm *alarm; - ECalComponentText description; - icalattach *attach; - const gchar *url; - gchar *cmd; - gboolean result = TRUE; - - debug (("...")); - - comp = cqa->alarms->comp; - qa = lookup_queued_alarm (cqa, alarm_id); - if (!qa) - return; - - alarm = e_cal_component_get_alarm (comp, qa->instance->auid); - g_return_if_fail (alarm != NULL); - - e_cal_component_alarm_get_attach (alarm, &attach); - e_cal_component_alarm_get_description (alarm, &description); - e_cal_component_alarm_free (alarm); - - /* If the alarm has no attachment, simply display a notification dialog. */ - if (!attach) - goto fallback; - - if (!icalattach_get_is_url (attach)) { - icalattach_unref (attach); - goto fallback; - } - - url = icalattach_get_url (attach); - g_return_if_fail (url != NULL); - - /* Ask for confirmation before executing the stuff */ - if (description.value) - cmd = g_strconcat (url, " ", description.value, NULL); - else - cmd = (gchar *) url; - - if (procedure_notification_dialog (cmd, url)) - result = g_spawn_command_line_async (cmd, NULL); - - if (cmd != (gchar *) url) - g_free (cmd); - - icalattach_unref (attach); - - /* Fall back to display notification if we got an error */ - if (result == FALSE) - goto fallback; - - return; - - fallback: - - display_notification (trigger, cqa, alarm_id, FALSE); -} - -static gboolean -check_midnight_refresh (gpointer user_data) -{ - time_t new_midnight; - icaltimezone *zone; - - debug (("...")); - - zone = config_data_get_timezone (); - new_midnight = time_day_end_with_zone (time (NULL), zone); - - if (new_midnight > midnight) { - struct _midnight_refresh_msg *msg; - - msg = g_slice_new0 (struct _midnight_refresh_msg); - msg->header.func = (MessageFunc) midnight_refresh_async; - msg->remove = FALSE; - - message_push ((Message *) msg); - } - - return TRUE; -} - -/** - * alarm_queue_init: - * - * Initializes the alarm queueing system. This should be called near the - * beginning of the program. - **/ -void -alarm_queue_init (gpointer data) -{ - an = data; - g_return_if_fail (alarm_queue_inited == FALSE); - - debug (("...")); - - client_alarms_hash = g_hash_table_new (g_direct_hash, g_direct_equal); - queue_midnight_refresh (); - - if (config_data_get_last_notification_time (NULL) == -1) { - time_t tmval = time_day_begin (time (NULL)); - debug (("Setting last notification time to %s", e_ctime (&tmval))); - config_data_set_last_notification_time (NULL, tmval); - } - - /* install timeout handler (every 30 mins) for not missing the midnight refresh */ - g_timeout_add_seconds (1800, (GSourceFunc) check_midnight_refresh, NULL); - -#ifdef HAVE_LIBNOTIFY - notify_init("Evolution Alarms"); -#endif - - alarm_queue_inited = TRUE; -} - -static gboolean -free_client_alarms_cb (gpointer key, - gpointer value, - gpointer user_data) -{ - ClientAlarms *ca = value; - - debug (("ca=%p", ca)); - - if (ca) { - remove_client_alarms (ca); - if (ca->cal_client) { - debug (("Disconnecting Client")); - - g_signal_handlers_disconnect_matched (ca->cal_client, G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, ca); - g_object_unref (ca->cal_client); - } - - if (ca->view) { - debug (("Disconnecting Query")); - - g_signal_handlers_disconnect_matched (ca->view, G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, ca); - g_object_unref (ca->view); - } - - g_hash_table_destroy (ca->uid_alarms_hash); - - g_free (ca); - return TRUE; - } - - return FALSE; -} - -/** - * alarm_queue_done: - * - * Shuts down the alarm queueing system. This should be called near the end - * of the program. All the monitored calendar clients should already have been - * unregistered with alarm_queue_remove_client(). - **/ -void -alarm_queue_done (void) -{ - g_return_if_fail (alarm_queue_inited); - - /* All clients must be unregistered by now */ - g_return_if_fail (g_hash_table_size (client_alarms_hash) == 0); - - debug (("...")); - - g_hash_table_foreach_remove ( - client_alarms_hash, (GHRFunc) free_client_alarms_cb, NULL); - g_hash_table_destroy (client_alarms_hash); - client_alarms_hash = NULL; - - if (midnight_refresh_id != NULL) { - alarm_remove (midnight_refresh_id); - midnight_refresh_id = NULL; - } - - alarm_queue_inited = FALSE; -} - -static gboolean -compare_ids (gpointer a, - gpointer b) -{ - ECalComponentId *id, *id1; - - id = a; - id1 = b; - if (id->uid != NULL && id1->uid != NULL) { - if (g_str_equal (id->uid, id1->uid)) { - - if (id->rid && id1->rid) - return g_str_equal (id->rid, id1->rid); - else if (!(id->rid && id1->rid)) - return TRUE; - } - } - return FALSE; -} - -static guint -hash_ids (gpointer a) -{ - ECalComponentId *id =a; - - return g_str_hash (id->uid); -} - -struct _alarm_client_msg { - Message header; - ECalClient *cal_client; -}; - -static void -alarm_queue_add_async (struct _alarm_client_msg *msg) -{ - ClientAlarms *ca; - ECalClient *cal_client = msg->cal_client; - - g_return_if_fail (alarm_queue_inited); - g_return_if_fail (cal_client != NULL); - g_return_if_fail (E_IS_CAL_CLIENT (cal_client)); - - ca = lookup_client (cal_client); - if (ca) { - /* We already have it. Unref the passed one*/ - g_object_unref (cal_client); - return; - } - - debug (("client=%p", cal_client)); - - ca = g_new (ClientAlarms, 1); - - ca->cal_client = cal_client; - ca->view = NULL; - - g_hash_table_insert (client_alarms_hash, cal_client, ca); - - ca->uid_alarms_hash = g_hash_table_new ( - (GHashFunc) hash_ids, (GEqualFunc) compare_ids); - - load_alarms_for_today (ca); - - g_slice_free (struct _alarm_client_msg, msg); -} - -/** - * alarm_queue_add_client: - * @cal_client: A calendar client. - * - * Adds a calendar client to the alarm queueing system. Alarm trigger - * notifications will be presented at the appropriate times. The client should - * be removed with alarm_queue_remove_client() when receiving notifications - * from it is no longer desired. - * - * A client can be added any number of times to the alarm queueing 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 - * queueing system when it is no longer wanted. - **/ -void -alarm_queue_add_client (ECalClient *cal_client) -{ - struct _alarm_client_msg *msg; - - msg = g_slice_new0 (struct _alarm_client_msg); - msg->header.func = (MessageFunc) alarm_queue_add_async; - msg->cal_client = g_object_ref (cal_client); - - message_push ((Message *) msg); -} - -/* Removes a component an its alarms */ -static void -remove_cqa (ClientAlarms *ca, - ECalComponentId *id, - CompQueuedAlarms *cqa) -{ - - /* If a component is present, then it means we must have alarms queued - * for it. - */ - g_return_if_fail (cqa->queued_alarms != NULL); - - debug (("removing %d alarms", g_slist_length (cqa->queued_alarms))); - remove_alarms (cqa, TRUE); -} - -static gboolean -remove_comp_by_id (gpointer key, - gpointer value, - gpointer userdata) -{ - - ClientAlarms *ca = (ClientAlarms *) userdata; - - debug (("...")); - -/* if (!g_hash_table_size (ca->uid_alarms_hash)) */ -/* return; */ - - remove_cqa (ca, (ECalComponentId *) key, (CompQueuedAlarms *) value); - - return TRUE; -} - -/* Removes all the alarms queued for a particular calendar client */ -static void -remove_client_alarms (ClientAlarms *ca) -{ - debug (("size %d", g_hash_table_size (ca->uid_alarms_hash))); - - g_hash_table_foreach_remove ( - ca->uid_alarms_hash, (GHRFunc) remove_comp_by_id, ca); - - /* The hash table should be empty now */ - g_return_if_fail (g_hash_table_size (ca->uid_alarms_hash) == 0); -} - -/** - * alarm_queue_remove_client: - * @client: A calendar client. - * - * Removes a calendar client from the alarm queueing system. - **/ -static void -alarm_queue_remove_async (struct _alarm_client_msg *msg) -{ - ClientAlarms *ca; - ECalClient *cal_client = msg->cal_client; - - g_return_if_fail (alarm_queue_inited); - g_return_if_fail (cal_client != NULL); - g_return_if_fail (E_IS_CAL_CLIENT (cal_client)); - - ca = lookup_client (cal_client); - g_return_if_fail (ca != NULL); - - debug (("...")); - remove_client_alarms (ca); - - /* Clean up */ - if (ca->cal_client) { - debug (("Disconnecting Client")); - - g_signal_handlers_disconnect_matched (ca->cal_client, G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, ca); - g_object_unref (ca->cal_client); - ca->cal_client = NULL; - } - - if (ca->view) { - debug (("Disconnecting Query")); - - g_signal_handlers_disconnect_matched (ca->view, G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, ca); - g_object_unref (ca->view); - ca->view = NULL; - } - - g_hash_table_destroy (ca->uid_alarms_hash); - ca->uid_alarms_hash = NULL; - - g_free (ca); - - g_hash_table_remove (client_alarms_hash, cal_client); - - g_slice_free (struct _alarm_client_msg, msg); -} - -/** alarm_queue_remove_client - * - * asynchronously remove client from alarm queue. - * @cal_client: Client to remove. - * @immediately: Indicates whether use thread or do it right now. - */ - -void -alarm_queue_remove_client (ECalClient *cal_client, - gboolean immediately) -{ - struct _alarm_client_msg *msg; - - msg = g_slice_new0 (struct _alarm_client_msg); - msg->header.func = (MessageFunc) alarm_queue_remove_async; - msg->cal_client = cal_client; - - if (immediately) { - alarm_queue_remove_async (msg); - } else - message_push ((Message *) msg); -} - -/* Update non-time related variables for various structures on modification - * of an existing component to be called only from query_objects_changed_cb */ -static void -update_cqa (CompQueuedAlarms *cqa, - ECalComponent *newcomp) -{ - ECalComponent *oldcomp; - ECalComponentAlarms *alarms = NULL; - GSList *qa_list; /* List of current QueuedAlarms corresponding to cqa */ - time_t from, to; - icaltimezone *zone; - ECalComponentAlarmAction omit[] = {-1}; - - oldcomp = cqa->alarms->comp; - - zone = config_data_get_timezone (); - from = time_day_begin_with_zone (time (NULL), zone); - to = time_day_end_with_zone (time (NULL), zone); - - debug (("Generating alarms between %s and %s", e_ctime (&from), e_ctime (&to))); - alarms = e_cal_util_generate_alarms_for_comp ( - newcomp, from, to, omit, e_cal_client_resolve_tzid_cb, - cqa->parent_client->cal_client, zone); - - /* Update auids in Queued Alarms*/ - for (qa_list = cqa->queued_alarms; qa_list; qa_list = qa_list->next) { - QueuedAlarm *qa = qa_list->data; - gchar *check_auid = (gchar *) qa->instance->auid; - ECalComponentAlarm *alarm; - - alarm = e_cal_component_get_alarm (newcomp, check_auid); - if (alarm) { - e_cal_component_alarm_free (alarm); - continue; - } else { - alarm = e_cal_component_get_alarm (oldcomp, check_auid); - if (alarm) { /* Need to update QueuedAlarms */ - e_cal_component_alarm_free (alarm); - if (alarms == NULL) { - debug (("No alarms found in the modified component")); - break; - } - update_qa (alarms, qa); - } - else - g_warning ("Failed in auid lookup for old component also\n"); - } - } - - /* Update the actual component stored in CompQueuedAlarms structure */ - g_object_unref (cqa->alarms->comp); - cqa->alarms->comp = newcomp; - if (alarms != NULL ) - e_cal_component_alarms_free (alarms); -} - -static void -update_qa (ECalComponentAlarms *alarms, - QueuedAlarm *qa) -{ - ECalComponentAlarmInstance *al_inst; - GSList *instance_list; - - debug (("...")); - for (instance_list = alarms->alarms; - instance_list; - instance_list = instance_list->next) { - al_inst = instance_list->data; - /* FIXME If two or more alarm instances (audio, note) - * for same component have same trigger... */ - if (al_inst->trigger == qa->orig_trigger) { - g_free ((gchar *) qa->instance->auid); - qa->instance->auid = g_strdup (al_inst->auid); - break; - } - } -} |