diff options
-rw-r--r-- | calendar/ChangeLog | 37 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/alarm-notify.c | 24 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/alarm-notify.h | 2 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/alarm-queue.c | 556 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/alarm-queue.h | 2 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/config-data.c | 78 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/config-data.h | 3 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/util.c | 49 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/util.h | 2 |
9 files changed, 631 insertions, 122 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 8e259fd42c..b87657764f 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,40 @@ +2005-11-24 Srinivasa Ragavan and P.S.Chakravarthi <sragavan@novell.com>, <pchakravarthi@novell.com> + + (simultaneously a patch for configure.in in evolution source dir is + applied to include libnotify, if present) + * gui/alarm-notify/alarm-notify.c (alarm_notify_get_selected_calendars): + added a field in _AlarmNotifyPrivate called selected_calendars and relevant + code elsewhere for use in disabling and enabling alarms for particular calendars. + * gui/alarm-notify/alarm-queue.c (free_tray_icon_data):added + (notify_dialog_cb): modified to free the non snoozed alarms on close. + (open_alarm_dialog): code added to set up tooltip on the panel icon if there + are alarms present. + (alarm_quit): added. Call back when "Quit" option is chosen on right click of the + panel icon. + (alarms_configure): added. The call back function for generating the "Configure Alarms" + dialog on right click on the panel icon. + (menu_item_toggle_cb): added. Call back when a particular calendar is selected + or deselected in the "Configure Alarms" dialog. + (populate): added. Function to populate the view in the dialog that appears when + "Configure Alarms" option is chosen on right click of the panel icon. + (alarm_preference_response):added. Called when the "Configure Alarms" dialog is + closed. + (tray_icon_clicked_cb): modified to get tooltip, giving alarm summary on mouse over + the panel icon, to have the panel icon blink in case of active alarms. + (display_notification): modified the code to change the strings shown in the alarm + dialog. + (popup_notification): added. Compiles if there is libnotify. If present, gives + a small notification bubble on alarm trigger giving necesary information on the + appointment. + (alarm_queue_init): modified to add the panel icon that is being used in above + functions. + * gui/alarm-notify/config-data.[ch](config_data_get_calendars):added. Gets calendar + information for notification from a gconf key. + (config_data_replace_string_list): added. To replace an old gconf key with a new one. + * gui/alarm-notify/util.[ch] (calculate_time):added. An utility function which + gives a time difference between two time_t instances in hours, minutes and seconds. + + 2005-11-24 Tor Lillqvist <tml@novell.com> * gui/e-alarm-list.c diff --git a/calendar/gui/alarm-notify/alarm-notify.c b/calendar/gui/alarm-notify/alarm-notify.c index a74908bb2c..edef6d34bb 100644 --- a/calendar/gui/alarm-notify/alarm-notify.c +++ b/calendar/gui/alarm-notify/alarm-notify.c @@ -33,7 +33,6 @@ #include "config-data.h" #include "common/authentication.h" - /* Private part of the AlarmNotify structure */ struct _AlarmNotifyPrivate { /* Mapping from EUri's to LoadedClient structures */ @@ -41,6 +40,7 @@ struct _AlarmNotifyPrivate { just need to hash based on source */ GHashTable *uri_client_hash [E_CAL_SOURCE_TYPE_LAST]; ESourceList *source_lists [E_CAL_SOURCE_TYPE_LAST]; + ESourceList *selected_calendars; GMutex *mutex; }; @@ -54,7 +54,6 @@ static void alarm_notify_finalize (GObject *object); static BonoboObjectClass *parent_class; - BONOBO_TYPE_FUNC_FULL(AlarmNotify, GNOME_Evolution_Calendar_AlarmNotify, BONOBO_TYPE_OBJECT, alarm_notify) /* Class initialization function for the alarm notify service */ @@ -140,6 +139,10 @@ list_changed_cb (ESourceList *source_list, gpointer data) for (q = sources; q != NULL; q = q->next) { ESource *source = E_SOURCE (q->data); char *uri; + const char *uid = e_source_peek_uid (source); + + if (!e_source_list_peek_source_by_uid (priv->selected_calendars, uid)) + continue; uri = e_source_get_uri (source); if (!g_hash_table_lookup (priv->uri_client_hash[source_type], uri)) { @@ -163,6 +166,11 @@ list_changed_cb (ESourceList *source_list, gpointer data) g_list_free (prd.removals); } +ESourceList * +alarm_notify_get_selected_calendars (AlarmNotify *an) +{ + return an->priv->selected_calendars; +} static void load_calendars (AlarmNotify *an, ECalSourceType source_type) { @@ -187,6 +195,10 @@ load_calendars (AlarmNotify *an, ECalSourceType source_type) for (q = sources; q != NULL; q = q->next) { ESource *source = E_SOURCE (q->data); char *uri; + const char *uid = e_source_peek_uid (source); + + if (!e_source_list_peek_source_by_uid (priv->selected_calendars, uid)) + continue; uri = e_source_get_uri (source); g_message ("Loading %s", uri); @@ -224,11 +236,13 @@ alarm_notify_init (AlarmNotify *an, AlarmNotifyClass *klass) priv = g_new0 (AlarmNotifyPrivate, 1); an->priv = priv; priv->mutex = g_mutex_new (); + priv->selected_calendars = config_data_get_calendars ("/apps/evolution/calendar/notify/calendars"); + for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++) priv->uri_client_hash[i] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - alarm_queue_init (); + alarm_queue_init (an); for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++) load_calendars (an, i); @@ -344,6 +358,7 @@ alarm_notify_add_calendar (AlarmNotify *an, ECalSourceType source_type, ESource if (g_hash_table_lookup (priv->uri_client_hash[source_type], str_uri)) { g_mutex_unlock (an->priv->mutex); g_free (str_uri); + g_free (pass_key); return; } /* if loading of this requires password and password is not currently availble in e-password @@ -356,6 +371,7 @@ alarm_notify_add_calendar (AlarmNotify *an, ECalSourceType source_type, ESource g_free (pass_key); return; } + client = auth_new_cal_from_source (source, source_type); if (client) { @@ -363,8 +379,8 @@ alarm_notify_add_calendar (AlarmNotify *an, ECalSourceType source_type, ESource g_signal_connect (G_OBJECT (client), "cal_opened", G_CALLBACK (cal_opened_cb), an); e_cal_open_async (client, FALSE); } - g_free (pass_key); g_free (str_uri); + g_free (pass_key); g_mutex_unlock (an->priv->mutex); } diff --git a/calendar/gui/alarm-notify/alarm-notify.h b/calendar/gui/alarm-notify/alarm-notify.h index 4d8b5f194f..beb58acc1e 100644 --- a/calendar/gui/alarm-notify/alarm-notify.h +++ b/calendar/gui/alarm-notify/alarm-notify.h @@ -26,7 +26,6 @@ #include "evolution-calendar.h" - #define TYPE_ALARM_NOTIFY (alarm_notify_get_type ()) #define ALARM_NOTIFY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_ALARM_NOTIFY, AlarmNotify)) #define ALARM_NOTIFY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_ALARM_NOTIFY, \ @@ -58,6 +57,7 @@ AlarmNotify *alarm_notify_new (void); void alarm_notify_add_calendar (AlarmNotify *an, ECalSourceType source_type, ESource *source, gboolean load_afterwards); void alarm_notify_remove_calendar (AlarmNotify *an, ECalSourceType source_type, const char *str_uri); +ESourceList *alarm_notify_get_selected_calendars (AlarmNotify *); diff --git a/calendar/gui/alarm-notify/alarm-queue.c b/calendar/gui/alarm-notify/alarm-queue.c index 40a43b674a..83372f886f 100644 --- a/calendar/gui/alarm-notify/alarm-queue.c +++ b/calendar/gui/alarm-notify/alarm-queue.c @@ -27,14 +27,19 @@ #include <bonobo-activation/bonobo-activation.h> #include <bonobo/bonobo-object.h> #include <bonobo/bonobo-exception.h> +#include <bonobo/bonobo-main.h> #include <gtk/gtksignal.h> #include <gtk/gtkbox.h> +#include <gtk/gtkvbox.h> +#include <gtk/gtkhbox.h> #include <gtk/gtkdialog.h> #include <gtk/gtkeventbox.h> #include <gtk/gtkimage.h> +#include <gtk/gtkframe.h> #include <gtk/gtkimagemenuitem.h> #include <gtk/gtklabel.h> #include <gtk/gtkcheckbutton.h> +#include <gtk/gtkcheckmenuitem.h> #include <gtk/gtkstock.h> #include <gtk/gtktooltips.h> #include <libgnome/gnome-i18n.h> @@ -45,10 +50,18 @@ #include <e-util/eggtrayicon.h> #include <e-util/e-icon-factory.h> #include <libecal/e-cal-time-util.h> +#include <libecal/e-cal-component.h> + + +#ifdef HAVE_LIBNOTIFY +#include <libnotify/notify.h> +#endif + #include "evolution-calendar.h" #include "alarm.h" #include "alarm-notify-dialog.h" #include "alarm-queue.h" +#include "alarm-notify.h" #include "config-data.h" #include "util.h" #include "e-util/e-popup.h" @@ -73,6 +86,13 @@ static GHashTable *client_alarms_hash = NULL; /* List of tray icons being displayed */ static GList *tray_icons_list = NULL; +/* Top Tray Image */ +static GtkWidget *tray_image = NULL; +static GtkWidget *tray_event_box = NULL; +static int tray_blink_id = -1; +static int tray_blink_state = FALSE; +static AlarmNotify *an; + /* Structure that stores a client we are monitoring */ typedef struct { /* Monitored client */ @@ -136,7 +156,10 @@ static void display_notification (time_t trigger, CompQueuedAlarms *cqa, 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_changed_cb (ECal *client, GList *objects, gpointer data); static void query_objects_removed_cb (ECal *client, GList *objects, gpointer data); @@ -309,6 +332,9 @@ alarm_trigger_cb (gpointer alarm_id, time_t trigger, gpointer data) alarm = e_cal_component_get_alarm (comp, qa->instance->auid); g_assert (alarm != NULL); + /* Show it independent of what the notification is?*/ + display_notification (trigger, cqa, alarm_id, TRUE); + e_cal_component_alarm_get_action (alarm, &action); e_cal_component_alarm_free (alarm); @@ -318,7 +344,9 @@ alarm_trigger_cb (gpointer alarm_id, time_t trigger, gpointer data) break; case E_CAL_COMPONENT_ALARM_DISPLAY: - display_notification (trigger, cqa, alarm_id, TRUE); +#ifdef HAVE_LIBNOTIFY + popup_notification (trigger, cqa, alarm_id, TRUE); +#endif break; case E_CAL_COMPONENT_ALARM_EMAIL: @@ -735,6 +763,7 @@ typedef struct { char *description; char *location; gboolean blink_state; + gboolean snooze_set; gint blink_id; time_t trigger; CompQueuedAlarms *cqa; @@ -748,6 +777,43 @@ typedef struct { } 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->description); + tray_data->location = NULL; + } + + g_object_unref (tray_data->client); + tray_data->client = NULL; + + g_object_unref (tray_data->query); + tray_data->query = NULL; + + g_object_unref (tray_data->comp); + tray_data->comp = NULL; + + tray_data->cqa = NULL; + tray_data->alarm_id = NULL; + tray_data->tray_icon = NULL; + tray_data->image = NULL; + + g_free (tray_data); +} + +static void on_dialog_objs_removed_cb (ECal *client, GList *objects, gpointer data) { const char *our_uid; @@ -766,8 +832,8 @@ on_dialog_objs_removed_cb (ECal *client, GList *objects, gpointer data) if (!strcmp (uid, our_uid)) { tray_data->cqa = NULL; tray_data->alarm_id = NULL; - - gtk_widget_destroy (tray_data->tray_icon); + tray_icons_list = g_list_remove (tray_icons_list, tray_data); + tray_data = NULL; } } } @@ -784,8 +850,7 @@ notify_dialog_cb (AlarmNotifyResult result, int snooze_mins, gpointer data) switch (result) { case ALARM_NOTIFY_SNOOZE: create_snooze (tray_data->cqa, tray_data->alarm_id, snooze_mins); - tray_data->cqa = NULL; - + tray_data->snooze_set = TRUE; if (alarm_notifications_dialog) { GtkTreeSelection *selection = gtk_tree_view_get_selection ( @@ -793,7 +858,7 @@ notify_dialog_cb (AlarmNotifyResult result, int snooze_mins, gpointer data) GtkTreeIter iter; GtkTreeModel *model = NULL; - /* We can also use tray_data->iter */ + /* 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)) { @@ -814,18 +879,12 @@ notify_dialog_cb (AlarmNotifyResult result, int snooze_mins, gpointer data) case ALARM_NOTIFY_EDIT: edit_component (tray_data->client, tray_data->comp); - gtk_widget_destroy (alarm_notifications_dialog->dialog); - g_free (alarm_notifications_dialog); - alarm_notifications_dialog = NULL; - - gtk_widget_destroy (tray_data->tray_icon); - break; case ALARM_NOTIFY_CLOSE: - if (alarm_notifications_dialog) { + GList *list; GtkTreeIter iter; GtkTreeModel *model = gtk_tree_view_get_model ( @@ -841,56 +900,37 @@ notify_dialog_cb (AlarmNotifyResult result, int snooze_mins, gpointer data) gtk_widget_destroy (alarm_notifications_dialog->dialog); g_free (alarm_notifications_dialog); alarm_notifications_dialog = NULL; - } - - gtk_widget_destroy (tray_data->tray_icon); - break; + /* FIXME tray_icons_list is a global data structure - make this thread safe */ - default: - g_assert_not_reached (); - } + list = tray_icons_list; + while (list != NULL) { - return; -} - -static gint -tray_icon_destroyed_cb (GtkWidget *tray, gpointer user_data) -{ - TrayIconData *tray_data = user_data; + tray_data = list->data; - g_signal_handlers_disconnect_matched (tray_data->query, G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, on_dialog_objs_removed_cb, NULL); - - if (tray_data->cqa != NULL) + if (!tray_data->snooze_set){ + GList *temp = list->next; + tray_icons_list = g_list_remove_link (tray_icons_list, list); remove_queued_alarm (tray_data->cqa, tray_data->alarm_id, TRUE, TRUE); - - if (tray_data->summary != NULL) { - g_free (tray_data->summary); - tray_data->summary = NULL; + 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; } - - if (tray_data->description != NULL) { - g_free (tray_data->description); - tray_data->description = NULL; - } - - if (tray_data->location != NULL) { - g_free (tray_data->location); - tray_data->location = NULL; } - if (tray_data->blink_id) - g_source_remove (tray_data->blink_id); - - g_object_unref (tray_data->comp); - g_object_unref (tray_data->client); - g_object_unref (tray_data->query); + break; - tray_icons_list = g_list_remove (tray_icons_list, tray_data); - g_free (tray_data); + default: + g_assert_not_reached (); + } - return TRUE; + return; } /* Callbacks. */ @@ -901,8 +941,19 @@ open_alarm_dialog (TrayIconData *tray_data) qa = lookup_queued_alarm (tray_data->cqa, tray_data->alarm_id); if (qa) { + GdkPixbuf *pixbuf; + GtkTooltips *tooltips = gtk_tooltips_new (); + + pixbuf = e_icon_factory_get_icon ("stock_appointment-reminder", E_ICON_SIZE_LARGE_TOOLBAR); + + gtk_image_set_from_pixbuf (GTK_IMAGE (tray_image), pixbuf); + g_object_unref (pixbuf); + + if (tray_blink_id > -1) + g_source_remove (tray_blink_id); + tray_blink_id = -1; - gtk_widget_hide (tray_data->tray_icon); + gtk_tooltips_set_tip (tooltips, tray_event_box, NULL, NULL); if (!alarm_notifications_dialog) alarm_notifications_dialog = notified_alarms_dialog_new (); @@ -938,37 +989,193 @@ open_alarm_dialog (TrayIconData *tray_data) } static void -popup_dismiss_cb (EPopup *ep, EPopupItem *pitem, void *data) +alarm_quit (EPopup *ep, EPopupItem *pitem, void *data) { - TrayIconData *tray_data = data; - - gtk_widget_destroy (tray_data->tray_icon); + bonobo_main_quit (); } static void -popup_dismiss_all_cb (EPopup *ep, EPopupItem *pitem, void *data) +menu_item_toggle_callback (GtkToggleButton *item, void *data) +{ + gboolean state = gtk_toggle_button_get_active (item); + ESource *source = e_source_copy ((ESource *) data); + GSList *groups, *p; + + if (e_source_get_uri ((ESource *)data)) { + g_free (e_source_get_uri (source)); + e_source_set_absolute_uri (source, g_strdup (e_source_get_uri ((ESource *)data))); + } + + if (state) { + const char *uid = e_source_peek_uid (source); + ESourceList *selected_cal = alarm_notify_get_selected_calendars (an); + ESourceList *all_cal; + ESourceGroup *sel_group = NULL; + const char *grp_name=NULL; + ESourceGroup *group; + ESource *del_source; + + e_cal_get_sources (&all_cal, E_CAL_SOURCE_TYPE_EVENT, NULL); + + alarm_notify_add_calendar (an, E_CAL_SOURCE_TYPE_EVENT, source, FALSE); + + /* Browse the list of calendars for the newly added calendar*/ + groups = e_source_list_peek_groups (all_cal); + for (p = groups; p != NULL; p = p->next) { + ESourceGroup *group = E_SOURCE_GROUP (p->data); + ESource *sel_source = e_source_group_peek_source_by_uid (group, uid); + + if (sel_source) { + sel_group = group; + grp_name = e_source_group_peek_name (sel_group); + /* You have got the group name*/ + break; + } +} + + /* Add the source the the group name in the alarms calendar list*/ + group = e_source_list_peek_group_by_name (selected_cal, grp_name); + del_source = e_source_group_peek_source_by_uid (group, uid); + + if (!del_source) { + char *xml, *old_xml; + + old_xml = e_source_group_to_xml (group); + e_source_group_add_source (group, source, -1); + xml = e_source_group_to_xml (group); + config_data_replace_string_list ("/apps/evolution/calendar/notify/calendars", old_xml, xml); + + g_free (xml); + g_free (old_xml); + } + + g_object_unref (all_cal); + + } else { + const char *uid = e_source_peek_uid (source); + ESourceList *selected_cal = alarm_notify_get_selected_calendars (an); + alarm_notify_remove_calendar (an, E_CAL_SOURCE_TYPE_EVENT, e_source_get_uri (source)); + + /* Browse the calendar for alarms and remove the source */ + groups = e_source_list_peek_groups (selected_cal); + for (p = groups; p != NULL; p = p->next) { + ESourceGroup *group = E_SOURCE_GROUP (p->data); + ESource *del_source; + + del_source = e_source_group_peek_source_by_uid (group, uid); + if (del_source) { + char *xml, *old_xml; + + old_xml = e_source_group_to_xml (group); + + e_source_group_remove_source_by_uid (group, uid); + + xml = e_source_group_to_xml (group); + + config_data_replace_string_list ("/apps/evolution/calendar/notify/calendars", old_xml, xml); + + g_free (xml); + g_free (old_xml); + break; + } + } + } + +} + +static GtkWidget * +populate () { - while (tray_icons_list != NULL) { - TrayIconData *tray_data = tray_icons_list->data; + GtkWidget *frame = gtk_frame_new (NULL); + GtkWidget *label1 = gtk_label_new (NULL); + GtkWidget *box = gtk_vbox_new(FALSE, 0); + ESourceList *selected_cal = alarm_notify_get_selected_calendars (an); + GSList *groups; + GSList *p; + ESourceList *source_list; + + gtk_label_set_markup (GTK_LABEL(label1), _("<b>Calendars</b>")); + gtk_frame_set_label_widget (GTK_FRAME(frame), label1); + + if (!e_cal_get_sources (&source_list, E_CAL_SOURCE_TYPE_EVENT, NULL)) { + g_message (G_STRLOC ": Could not get the list of sources to load"); + + return NULL; + } + + groups = e_source_list_peek_groups (source_list); + + for (p = groups; p != NULL; p = p->next) { + ESourceGroup *group = E_SOURCE_GROUP (p->data); + char *txt = g_strdup_printf ("<b>%s</b>", e_source_group_peek_name (group)); + GtkWidget *item = gtk_label_new (NULL); + GSList *q; + GtkWidget *hbox, *image; + + hbox = gtk_hbox_new (FALSE, 0); + image = e_icon_factory_get_image ("stock_appointment-reminder", E_ICON_SIZE_SMALL_TOOLBAR); + + gtk_box_pack_start ((GtkBox *)hbox, image, FALSE, FALSE, 2); + gtk_box_pack_start ((GtkBox *)hbox, item, FALSE, FALSE, 2); + + gtk_label_set_markup (GTK_LABEL(item), txt); + gtk_label_set_justify (GTK_LABEL(item), GTK_JUSTIFY_LEFT); + g_free (txt); + + gtk_box_pack_start (GTK_BOX(box), hbox, TRUE, TRUE, 4); + gtk_widget_show_all (hbox); - gtk_widget_destroy (tray_data->tray_icon); + for (q = e_source_group_peek_sources (group); q != NULL; q = q->next) { + ESource *source = E_SOURCE (q->data); + GtkWidget *hbox = gtk_hbox_new (FALSE, 0); + GtkWidget *item = gtk_check_button_new_with_label (e_source_peek_name (source)); - tray_icons_list = g_list_remove (tray_icons_list, tray_icons_list); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(item), e_source_list_peek_source_by_uid (selected_cal, e_source_peek_uid (source)) ? TRUE:FALSE); + + gtk_box_pack_start ((GtkBox *)hbox, item, FALSE, FALSE, 24); + gtk_object_set_data_full (GTK_OBJECT (item), "ESourceMenu", source, + (GtkDestroyNotify) g_object_unref); + + g_signal_connect (item, "toggled", G_CALLBACK (menu_item_toggle_callback), source); + + gtk_box_pack_start (GTK_BOX(box), hbox, FALSE, FALSE, 2); + gtk_widget_show_all (hbox); + } } + + gtk_container_add (GTK_CONTAINER(frame), box); + gtk_container_set_border_width (GTK_CONTAINER(frame), 6); + return frame; } static void -popup_open_cb (EPopup *ep, EPopupItem *pitem, void *data) +alarm_pref_response (GtkWidget *widget, int response, gpointer dummy) { - TrayIconData *tray_data = data; + gtk_widget_destroy (widget); +} - open_alarm_dialog (tray_data); +static void +alarms_configure (EPopup *ep, EPopupItem *pitem, void *data) +{ + GtkWidget *box = populate (); + GtkWidget *dialog; + + dialog = gtk_dialog_new_with_buttons (_("Preferences"), + NULL,0, + GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT, + GTK_STOCK_HELP, GTK_RESPONSE_HELP, + NULL); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), box); + gtk_dialog_set_has_separator (GTK_DIALOG(dialog), FALSE); + g_signal_connect (dialog, "response", G_CALLBACK (alarm_pref_response), NULL); + gtk_widget_show_all (dialog); } static EPopupItem tray_items[] = { - { E_POPUP_ITEM, "00.open", N_("Open"), popup_open_cb, NULL, GTK_STOCK_OPEN }, - { E_POPUP_ITEM, "10.dismiss", N_("Dismiss"), popup_dismiss_cb, NULL, NULL }, - { E_POPUP_ITEM, "20.dismissall", N_("Dismiss All"), popup_dismiss_all_cb, NULL, NULL }, + { E_POPUP_ITEM, "00.configure", N_("_Configure Alarms"), alarms_configure, NULL, GTK_STOCK_PREFERENCES }, + { E_POPUP_BAR , "10.bar" }, + { E_POPUP_ITEM, "10.quit", N_("_Quit"), alarm_quit, NULL, GTK_STOCK_QUIT }, }; static void @@ -983,13 +1190,35 @@ tray_icon_clicked_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_da TrayIconData *tray_data = user_data; if (event->type == GDK_BUTTON_PRESS) { - if (event->button == 1) { - return open_alarm_dialog (tray_data); + 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) { GtkMenu *menu; GSList *menus = NULL; EPopup *ep; int i; + GdkPixbuf *pixbuf; + GtkTooltips *tooltips = gtk_tooltips_new (); + + tray_blink_state = FALSE; + pixbuf = e_icon_factory_get_icon (tray_blink_state == TRUE ? + "stock_appointment-reminder-excl" : + "stock_appointment-reminder", + E_ICON_SIZE_LARGE_TOOLBAR); + + gtk_image_set_from_pixbuf (GTK_IMAGE (tray_image), pixbuf); + g_object_unref (pixbuf); + + if (tray_blink_id > -1) + g_source_remove (tray_blink_id); + tray_blink_id = -1; + + gtk_tooltips_set_tip (tooltips, tray_event_box, NULL, NULL); ep = e_popup_new("org.gnome.evolution.alarmNotify.popup"); for (i=0;i<sizeof(tray_items)/sizeof(tray_items[0]);i++) @@ -1008,16 +1237,16 @@ tray_icon_clicked_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_da static gboolean tray_icon_blink_cb (gpointer data) { - TrayIconData *tray_data = data; GdkPixbuf *pixbuf; - tray_data->blink_state = tray_data->blink_state == TRUE ? FALSE : TRUE; - pixbuf = e_icon_factory_get_icon (tray_data->blink_state == TRUE ? + tray_blink_state = tray_blink_state == TRUE ? FALSE: TRUE; + + pixbuf = e_icon_factory_get_icon (tray_blink_state == TRUE? "stock_appointment-reminder-excl" : "stock_appointment-reminder", E_ICON_SIZE_LARGE_TOOLBAR); - gtk_image_set_from_pixbuf (GTK_IMAGE (tray_data->image), pixbuf); + gtk_image_set_from_pixbuf (GTK_IMAGE (tray_image), pixbuf); g_object_unref (pixbuf); return TRUE; @@ -1031,13 +1260,14 @@ display_notification (time_t trigger, CompQueuedAlarms *cqa, QueuedAlarm *qa; ECalComponent *comp; const char *summary, *description, *location; - GtkWidget *tray_icon, *image, *ebox; + GtkWidget *tray_icon=NULL, *image=NULL; GtkTooltips *tooltips; TrayIconData *tray_data; ECalComponentText text; GSList *text_list; - char *str, *start_str, *end_str, *alarm_str; + char *str, *start_str, *end_str, *alarm_str, *time_str; icaltimezone *current_zone; + ECalComponentOrganizer organiser; comp = cqa->alarms->comp; qa = lookup_queued_alarm (cqa, alarm_id); @@ -1046,6 +1276,7 @@ display_notification (time_t trigger, CompQueuedAlarms *cqa, /* 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; @@ -1074,30 +1305,14 @@ display_notification (time_t trigger, CompQueuedAlarms *cqa, /* create the tray icon */ tooltips = gtk_tooltips_new (); - tray_icon = GTK_WIDGET (egg_tray_icon_new (qa->instance->auid)); - image = e_icon_factory_get_image ("stock_appointment-reminder", E_ICON_SIZE_LARGE_TOOLBAR); - ebox = gtk_event_box_new (); - - gtk_widget_show (image); - gtk_widget_show (ebox); - 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); - str = g_strdup_printf (_("Alarm on %s\n%s\nStarting at %s\nEnding at %s"), - alarm_str, summary, start_str, end_str); - gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltips), ebox, str, str); - g_free (start_str); - g_free (end_str); - g_free (alarm_str); - g_free (str); - - g_object_set_data (G_OBJECT (tray_icon), "image", image); - g_object_set_data (G_OBJECT (tray_icon), "available", GINT_TO_POINTER (1)); + time_str = calculate_time (qa->instance->occur_start, qa->instance->occur_end); - gtk_container_add (GTK_CONTAINER (ebox), image); - gtk_container_add (GTK_CONTAINER (tray_icon), ebox); + str = g_strdup_printf ("%s\n%s %s", + summary, start_str, time_str); /* create the private structure */ tray_data = g_new0 (TrayIconData, 1); @@ -1107,32 +1322,132 @@ display_notification (time_t trigger, CompQueuedAlarms *cqa, tray_data->trigger = trigger; tray_data->cqa = cqa; tray_data->alarm_id = alarm_id; - tray_data->comp = e_cal_component_clone (comp); + tray_data->comp = g_object_ref (e_cal_component_clone (comp)); tray_data->client = cqa->parent_client->client; tray_data->query = g_object_ref (cqa->parent_client->query); tray_data->image = image; tray_data->blink_state = FALSE; + tray_data->snooze_set = FALSE; g_object_ref (tray_data->client); tray_data->tray_icon = tray_icon; tray_icons_list = g_list_prepend (tray_icons_list, tray_data); - g_signal_connect (G_OBJECT (tray_icon), "destroy", - G_CALLBACK (tray_icon_destroyed_cb), tray_data); - g_signal_connect (G_OBJECT (ebox), "button_press_event", - G_CALLBACK (tray_icon_clicked_cb), tray_data); + if (g_list_length (tray_icons_list) > 1) { + char *tip; + + tip = g_strdup_printf (_("You have %d alarms"), g_list_length (tray_icons_list)); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltips), tray_event_box, tip, NULL); + g_free (tip); + } + else + gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltips), tray_event_box, str, NULL); + + g_free (start_str); + g_free (end_str); + g_free (alarm_str); + g_free (time_str); + g_free (str); + g_signal_connect (G_OBJECT (tray_data->query), "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_data->blink_id = -1; + tray_blink_id = -1; open_alarm_dialog (tray_data); gtk_window_stick (GTK_WINDOW (alarm_notifications_dialog->dialog)); } else { - tray_data->blink_id = g_timeout_add (500, tray_icon_blink_cb, tray_data); - gtk_widget_show (tray_icon); + if (tray_blink_id == -1) + 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 char *summary, *location; + GtkTooltips *tooltips; + ECalComponentText text; + char *str, *start_str, *end_str, *alarm_str, *time_str; + icaltimezone *current_zone; + ECalComponentOrganizer organiser; + NotifyIcon *icon; + char *filename; + char *body; + + comp = cqa->alarms->comp; + qa = lookup_queued_alarm (cqa, alarm_id); + if (!qa) + return; + if (!notify_is_initted ()) + notify_init("Evolution Alarm Notify"); + filename = e_icon_factory_get_icon_filename ("stock_appointment-reminder", E_ICON_SIZE_DIALOG); + icon = notify_icon_new_from_uri (filename); + g_free (filename); + + /* 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 */ + tooltips = gtk_tooltips_new (); + + 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); +} + + if (!notify_send_notification ( + NULL, "device", NOTIFY_URGENCY_NORMAL, + summary, + body, /* body text */ + icon, /* icon */ + TRUE, 0, /* expiry, server default */ + NULL, /* hints */ + NULL, /* no user_data */ + 0)) /* no actions */ + 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 @@ -1173,8 +1488,6 @@ audio_notification (time_t trigger, CompQueuedAlarms *cqa, if (attach) icalattach_unref (attach); - /* We present a notification message in addition to playing the sound */ - display_notification (trigger, cqa, alarm_id, FALSE); } /* Performs notification of a mail alarm */ @@ -1186,8 +1499,6 @@ mail_notification (time_t trigger, CompQueuedAlarms *cqa, gpointer alarm_id) /* FIXME */ - display_notification (trigger, cqa, alarm_id, FALSE); - dialog = gtk_dialog_new_with_buttons (_("Warning"), NULL, 0, GTK_STOCK_OK, GTK_RESPONSE_CANCEL, @@ -1301,7 +1612,6 @@ procedure_notification (time_t trigger, CompQueuedAlarms *cqa, gpointer alarm_id if (result < 0) goto fallback; - remove_queued_alarm (cqa, alarm_id, TRUE, TRUE); return; fallback: @@ -1337,8 +1647,10 @@ check_midnight_refresh (gpointer user_data) * beginning of the program. **/ void -alarm_queue_init (void) +alarm_queue_init (gpointer data) { + GtkWidget *tray_icon; + an = data; g_return_if_fail (alarm_queue_inited == FALSE); client_alarms_hash = g_hash_table_new (g_direct_hash, g_direct_equal); @@ -1353,6 +1665,19 @@ alarm_queue_init (void) /* install timeout handler (every 30 mins) for not missing the midnight refresh */ g_timeout_add (1800000, (GSourceFunc) check_midnight_refresh, NULL); + tray_icon = GTK_WIDGET (egg_tray_icon_new ("Evolution Alarm")); + tray_image = e_icon_factory_get_image ("stock_appointment-reminder", E_ICON_SIZE_LARGE_TOOLBAR); + tray_event_box = gtk_event_box_new (); + gtk_container_add (GTK_CONTAINER (tray_event_box), tray_image); + gtk_container_add (GTK_CONTAINER (tray_icon), tray_event_box); + g_signal_connect (G_OBJECT (tray_event_box), "button_press_event", + G_CALLBACK (tray_icon_clicked_cb), NULL); + gtk_widget_show_all (tray_icon); + +#ifdef HAVE_LIBNOTIFY + notify_init("Evolution Alarms"); +#endif + alarm_queue_inited = TRUE; } @@ -1479,11 +1804,12 @@ alarm_queue_add_client (ECal *client) static void add_id_cb (gpointer key, gpointer value, gpointer data) { - GSList **ids; - const ECalComponentId *id; + GSList **ids = (GSList **) data; + ECalComponentId *id = g_new0 (ECalComponentId, 1); + ECalComponentId *temp = (ECalComponentId *)key; - ids = data; - id = key; + id->uid = g_strdup (temp->uid); + id->rid = g_strdup (temp->rid); *ids = g_slist_prepend (*ids, (ECalComponentId *) id); } diff --git a/calendar/gui/alarm-notify/alarm-queue.h b/calendar/gui/alarm-notify/alarm-queue.h index 12802750ec..60e1abde46 100644 --- a/calendar/gui/alarm-notify/alarm-queue.h +++ b/calendar/gui/alarm-notify/alarm-queue.h @@ -24,7 +24,7 @@ #include <libecal/e-cal.h> -void alarm_queue_init (void); +void alarm_queue_init (gpointer); void alarm_queue_done (void); void alarm_queue_add_client (ECal *client); diff --git a/calendar/gui/alarm-notify/config-data.c b/calendar/gui/alarm-notify/config-data.c index cd3f7ed885..c8a35fada5 100644 --- a/calendar/gui/alarm-notify/config-data.c +++ b/calendar/gui/alarm-notify/config-data.c @@ -95,6 +95,84 @@ ensure_inited (void) } +ESourceList * +config_data_get_calendars (const char *key) +{ + ESourceList *cal_sources; + gboolean state; + GSList *gconf_list; + + if (!inited) + conf_client = gconf_client_get_default (); + + gconf_list = gconf_client_get_list (conf_client, + key, + GCONF_VALUE_STRING, + NULL); + cal_sources = e_source_list_new_for_gconf (conf_client, key); + + if (cal_sources && g_slist_length (gconf_list)) + return cal_sources; + + state = gconf_client_get_bool (conf_client, + "/apps/evolution/calendar/notify/notify_with_tray", + NULL); + if (!state) /* Should be old client*/ { + GSList *source; + gconf_client_set_bool (conf_client, + "/apps/evolution/calendar/notify/notify_with_tray", + TRUE, + NULL); + source = gconf_client_get_list (conf_client, + "/apps/evolution/calendar/sources", + GCONF_VALUE_STRING, + NULL); + gconf_client_set_list (conf_client, + key, + GCONF_VALUE_STRING, + source, + NULL); + cal_sources = e_source_list_new_for_gconf (conf_client, key); + } + + + return cal_sources; + +} + +void +config_data_replace_string_list (const char *key, + const char *old, + const char *new) +{ + GSList *source, *tmp; + + if (!inited) + conf_client = gconf_client_get_default (); + + source = gconf_client_get_list (conf_client, + key, + GCONF_VALUE_STRING, + NULL); + + for (tmp = source; tmp; tmp = tmp->next) { + + if (strcmp (tmp->data, old) == 0) { + gboolean state; + + g_free (tmp->data); + tmp->data = g_strdup ((gchar *) new); + + state = gconf_client_set_list (conf_client, + key, + GCONF_VALUE_STRING, + source, + NULL); + break; + } + } +} + GConfClient * config_data_get_conf_client (void) { diff --git a/calendar/gui/alarm-notify/config-data.h b/calendar/gui/alarm-notify/config-data.h index e5b2c3d689..d8274ad7e7 100644 --- a/calendar/gui/alarm-notify/config-data.h +++ b/calendar/gui/alarm-notify/config-data.h @@ -24,6 +24,7 @@ #include <glib.h> #include <libical/ical.h> #include <gconf/gconf-client.h> +#include <libedataserver/e-source-list.h> GConfClient *config_data_get_conf_client (void); @@ -34,5 +35,7 @@ void config_data_set_last_notification_time (time_t t); time_t config_data_get_last_notification_time (void); void config_data_save_blessed_program (const char *program); gboolean config_data_is_blessed_program (const char *program); +ESourceList *config_data_get_calendars (const char *); +void config_data_replace_string_list (const char *, const char *, const char *); #endif diff --git a/calendar/gui/alarm-notify/util.c b/calendar/gui/alarm-notify/util.c index ac24bbb56d..900d20e67d 100644 --- a/calendar/gui/alarm-notify/util.c +++ b/calendar/gui/alarm-notify/util.c @@ -47,3 +47,52 @@ timet_to_str_with_zone (time_t t, icaltimezone *zone) FALSE, FALSE, buf, sizeof (buf)); return g_strdup (buf); } + +char * +calculate_time (time_t start, time_t end) +{ + time_t difference = end - start; + char *str; + + if (difference < 60) {/* Can't be zero */ + str = g_strdup_printf (_("(%d seconds)"), difference); + } else if (difference > 60 && difference < 3600) { /* It will be x minutes y seconds*/ + int minutes, seconds; + minutes = difference / 60; + seconds = difference % 60; + if (seconds) + str = g_strdup_printf (_("(%d %s %d %s)"), minutes, ngettext(_("minute"), _("minutes"), minutes), seconds, ngettext(_("second"), _("seconds"), seconds)); + else + str = g_strdup_printf (_("(%d %s)"), minutes, ngettext(_("minute"), _("minutes"), minutes)); + } else { + guint hours, minutes, seconds; + char *s_hours = NULL, *s_minutes = NULL, *s_seconds = NULL; + + hours = difference / 3600; + minutes = (difference % 3600)/60; + seconds = difference % 60; + + + if (seconds) + s_seconds = g_strdup_printf (ngettext(_(" %u second"), _(" %u seconds"), seconds), seconds); + if (minutes) + s_minutes = g_strdup_printf (ngettext(_(" %u minute"), _(" %u minutes"), minutes), minutes); + if (hours) + s_hours = g_strdup_printf (ngettext(_("%u hour"),_("%u hours"), hours), hours); + + if (s_minutes && s_seconds) + str = g_strconcat ("(", s_hours, s_minutes, s_seconds, ")", NULL); + else if (s_minutes) + str = g_strconcat ("(", s_hours, s_minutes, ")", NULL); + else if (s_seconds) + str = g_strconcat ("(", s_hours, s_seconds, ")", NULL); + else + str = g_strconcat ("(", s_hours, ")", NULL); + + g_free (s_hours); + g_free (s_minutes); + g_free (s_seconds); + } + + return g_strchug(str); +} diff --git a/calendar/gui/alarm-notify/util.h b/calendar/gui/alarm-notify/util.h index 7dae3dd7fe..59abd072be 100644 --- a/calendar/gui/alarm-notify/util.h +++ b/calendar/gui/alarm-notify/util.h @@ -25,5 +25,5 @@ #include <libecal/e-cal-component.h> char *timet_to_str_with_zone (time_t t, icaltimezone *zone); - +char *calculate_time (time_t start, time_t end); #endif |