/* * Evolution calendar - alarm notification dialog * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * Federico Mena-Quintero * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "alarm-notify-dialog.h" #include "config-data.h" #include "util.h" #include "e-util/e-util.h" #include "e-util/e-util-private.h" #include "misc/e-buffer-tagger.h" enum { ALARM_DISPLAY_COLUMN, ALARM_SUMMARY_COLUMN, ALARM_DESCRIPTION_COLUMN, ALARM_LOCATION_COLUMN, ALARM_START_COLUMN, ALARM_END_COLUMN, ALARM_FUNCINFO_COLUMN, N_ALARM_COLUMNS }; /* The useful contents of the alarm notify dialog */ typedef struct { AlarmNotifyFunc func; gpointer func_data; } AlarmFuncInfo; typedef struct { GtkBuilder *builder; GtkWidget *dialog; GtkWidget *snooze_time_min; GtkWidget *snooze_time_hrs; GtkWidget *snooze_time_days; GtkWidget *snooze_btn; GtkWidget *edit_btn; GtkWidget *print_btn; GtkWidget *dismiss_btn; GtkWidget *minutes_label; GtkWidget *hrs_label; GtkWidget *days_label; GtkWidget *description; GtkWidget *location; GtkWidget *treeview; AlarmFuncInfo *cur_funcinfo; } AlarmNotify; static void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data); static void fill_in_labels (AlarmNotify *an, const gchar *summary, const gchar *description, const gchar *location, time_t occur_start, time_t occur_end); static void edit_pressed_cb (GtkButton *button, gpointer user_data); static void snooze_pressed_cb (GtkButton *button, gpointer user_data); static void an_update_minutes_label (GtkSpinButton *sb, gpointer data) { AlarmNotify *an; gint snooze_timeout_min; an = (AlarmNotify *) data; snooze_timeout_min = gtk_spin_button_get_value_as_int (sb); gtk_label_set_text (GTK_LABEL (an->minutes_label), ngettext ("minute", "minutes", snooze_timeout_min)); } static void an_update_hrs_label (GtkSpinButton *sb, gpointer data) { AlarmNotify *an; gint snooze_timeout_hrs; an = (AlarmNotify *) data; snooze_timeout_hrs = gtk_spin_button_get_value_as_int (sb); gtk_label_set_text (GTK_LABEL (an->hrs_label), ngettext ("hour", "hours", snooze_timeout_hrs)); } static void an_update_days_label (GtkSpinButton *sb, gpointer data) { AlarmNotify *an; gint snooze_timeout_days; an = (AlarmNotify *) data; snooze_timeout_days = gtk_spin_button_get_value_as_int (sb); gtk_label_set_text (GTK_LABEL (an->days_label), ngettext ("day", "days", snooze_timeout_days)); } static void dialog_response_cb (GtkDialog *dialog, guint response_id, gpointer user_data) { AlarmNotify *an = user_data; GtkTreeIter iter; GtkTreeModel *model = NULL; AlarmFuncInfo *funcinfo = NULL; GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview)); if (gtk_tree_selection_get_selected (selection, &model, &iter)) { gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1); } if (!funcinfo) { GtkTreeModel *treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (an->treeview)); if (!gtk_tree_model_get_iter_first (treemodel, &iter)) return; gtk_tree_model_get (treemodel, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1); } g_return_if_fail (funcinfo); switch (response_id) { case GTK_RESPONSE_CLOSE: case GTK_RESPONSE_DELETE_EVENT: (* funcinfo->func) (ALARM_NOTIFY_CLOSE, -1, funcinfo->func_data); break; } } static void edit_pressed_cb (GtkButton *button, gpointer user_data) { AlarmNotify *an = user_data; AlarmFuncInfo *funcinfo = NULL; GtkTreeIter iter; GtkTreeModel *model = NULL; GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview)); if (gtk_tree_selection_get_selected (selection, &model, &iter)) gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1); g_return_if_fail (funcinfo); (* funcinfo->func) (ALARM_NOTIFY_EDIT, -1, funcinfo->func_data); } static void print_pressed_cb (GtkButton *button, gpointer user_data) { AlarmNotify *an = user_data; AlarmFuncInfo *funcinfo = NULL; GtkTreeIter iter; GtkTreeModel *model = NULL; GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview)); if (gtk_tree_selection_get_selected (selection, &model, &iter)) gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1); g_return_if_fail (funcinfo); (* funcinfo->func) (ALARM_NOTIFY_PRINT, -1, funcinfo->func_data); } #define DEFAULT_SNOOZE_MINS 5 static void snooze_pressed_cb (GtkButton *button, gpointer user_data) { gint snooze_timeout; AlarmNotify *an = user_data; GtkTreeIter iter; GtkTreeModel *model = NULL; AlarmFuncInfo *funcinfo = NULL; GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview)); gtk_widget_grab_focus ((GtkWidget *) button); if (gtk_tree_selection_get_selected (selection, &model, &iter)) gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1); g_return_if_fail (funcinfo); snooze_timeout = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (an->snooze_time_min)); snooze_timeout += 60 * (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (an->snooze_time_hrs))); snooze_timeout += 60 * 24 * (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (an->snooze_time_days))); if (!snooze_timeout) snooze_timeout = DEFAULT_SNOOZE_MINS; (* funcinfo->func) (ALARM_NOTIFY_SNOOZE, snooze_timeout, funcinfo->func_data); } static void dismiss_pressed_cb (GtkButton *button, gpointer user_data) { AlarmNotify *an = user_data; GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (an->treeview)); g_return_if_fail (model != NULL); if (gtk_tree_model_iter_n_children (model, NULL) <= 1) { gtk_dialog_response (GTK_DIALOG (an->dialog), GTK_RESPONSE_CLOSE); } else { GtkTreeIter iter; AlarmFuncInfo *funcinfo = NULL; GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview)); if (gtk_tree_selection_get_selected (selection, &model, &iter)) gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1); g_return_if_fail (funcinfo); (* funcinfo->func) (ALARM_NOTIFY_DISMISS, -1, funcinfo->func_data); } } static void dialog_destroyed_cb (GtkWidget *dialog, gpointer user_data) { AlarmNotify *an = user_data; g_object_unref (an->builder); g_free (an); } /** * notified_alarms_dialog_new: * * Return value: a new dialog in which you can add alarm notifications **/ AlarmNotificationsDialog * notified_alarms_dialog_new (void) { GtkWidget *container; GtkWidget *image; GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); AlarmNotificationsDialog *na = NULL; AlarmNotify *an = g_new0 (AlarmNotify, 1); GtkTreeViewColumn *column = NULL; GtkTreeSelection *selection = NULL; GtkTreeModel *model = GTK_TREE_MODEL (gtk_list_store_new ( N_ALARM_COLUMNS, G_TYPE_STRING, /* Display */ G_TYPE_STRING, /* Summary */ G_TYPE_STRING, /* Description */ G_TYPE_STRING, /* Location */ G_TYPE_POINTER, /* Start */ G_TYPE_POINTER, /* End */ G_TYPE_POINTER /* FuncInfo */)); an->builder = gtk_builder_new (); e_load_ui_builder_definition (an->builder, "alarm-notify.ui"); an->dialog = e_builder_get_widget (an->builder, "alarm-notify"); an->snooze_time_min = e_builder_get_widget (an->builder, "snooze-time-min"); an->minutes_label = e_builder_get_widget (an->builder, "minutes-label"); an->snooze_time_hrs = e_builder_get_widget (an->builder, "snooze-time-hrs"); an->hrs_label = e_builder_get_widget (an->builder, "hrs-label"); an->snooze_time_days = e_builder_get_widget (an->builder, "snooze-time-days"); an->days_label = e_builder_get_widget (an->builder, "days-label"); an->description = e_builder_get_widget (an->builder, "description-label"); an->location = e_builder_get_widget (an->builder, "location-label"); an->treeview = e_builder_get_widget (an->builder, "appointments-treeview"); an->snooze_btn = e_builder_get_widget (an->builder, "snooze-button"); an->edit_btn = e_builder_get_widget (an->builder, "edit-button"); an->print_btn = e_builder_get_widget (an->builder, "print-button"); an->dismiss_btn = e_builder_get_widget (an->builder, "dismiss-button"); if (!(an->dialog && an->treeview && an->snooze_time_min && an->snooze_time_hrs && an->snooze_time_days && an->description && an->location && an->edit_btn && an->print_btn && an->snooze_btn && an->dismiss_btn)) { g_warning ("alarm_notify_dialog(): Could not find all widgets in alarm-notify.ui file!"); g_object_unref (an->builder); g_free (an); return NULL; } e_buffer_tagger_connect (GTK_TEXT_VIEW (an->description)); gtk_tree_view_set_model (GTK_TREE_VIEW (an->treeview), model); gtk_window_set_keep_above (GTK_WINDOW (an->dialog), TRUE); column = gtk_tree_view_column_new_with_attributes (_("Start time"), renderer, "text", ALARM_DISPLAY_COLUMN, NULL); gtk_tree_view_column_set_attributes (column, renderer, "markup", ALARM_DISPLAY_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (an->treeview), column); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); g_signal_connect ( selection, "changed", G_CALLBACK (tree_selection_changed_cb), an); tree_selection_changed_cb (selection, an); gtk_widget_realize (an->dialog); container = gtk_dialog_get_action_area (GTK_DIALOG (an->dialog)); gtk_container_set_border_width (GTK_CONTAINER (container), 12); container = gtk_dialog_get_content_area (GTK_DIALOG (an->dialog)); gtk_container_set_border_width (GTK_CONTAINER (container), 0); image = e_builder_get_widget (an->builder, "alarm-image"); gtk_image_set_from_icon_name ( GTK_IMAGE (image), "stock_alarm", GTK_ICON_SIZE_DIALOG); g_signal_connect ( an->edit_btn, "clicked", G_CALLBACK (edit_pressed_cb), an); g_signal_connect ( an->print_btn, "clicked", G_CALLBACK (print_pressed_cb), an); g_signal_connect ( an->snooze_btn, "clicked", G_CALLBACK (snooze_pressed_cb), an); g_signal_connect ( an->dismiss_btn, "clicked", G_CALLBACK (dismiss_pressed_cb), an); g_signal_connect ( an->dialog, "response", G_CALLBACK (dialog_response_cb), an); g_signal_connect ( an->dialog, "destroy", G_CALLBACK (dialog_destroyed_cb), an); if (!gtk_widget_get_realized (an->dialog)) gtk_widget_realize (an->dialog); gtk_window_set_icon_name (GTK_WINDOW (an->dialog), "stock_alarm"); /* Set callback for updating the snooze "minutes" label */ g_signal_connect ( an->snooze_time_min, "value_changed", G_CALLBACK (an_update_minutes_label), an); /* Set callback for updating the snooze "hours" label */ g_signal_connect ( an->snooze_time_hrs, "value_changed", G_CALLBACK (an_update_hrs_label), an); /* Set callback for updating the snooze "days" label */ g_signal_connect ( an->snooze_time_days, "value_changed", G_CALLBACK (an_update_days_label), an); na = g_new0 (AlarmNotificationsDialog, 1); na->treeview = an->treeview; na->dialog = an->dialog; return na; } /** * add_alarm_to_notified_alarms_dialog: * @na: Pointer to the dialog-info * @trigger: Trigger time for the alarm. * @occur_start: Start of occurrence time for the event. * @occur_end: End of occurrence time for the event. * @vtype: Type of the component which corresponds to the alarm. * @summary: Short summary of the appointment * @description: Long description of the appointment * @location: Location of the appointment * @func: Function to be called when a dialog action is invoked. * @func_data: Closure data for @func. * * The specified @func will be used to notify the client about result of * the actions in the dialog. * * Return value: the iter in the treeview of the dialog **/ GtkTreeIter add_alarm_to_notified_alarms_dialog (AlarmNotificationsDialog *na, time_t trigger, time_t occur_start, time_t occur_end, ECalComponentVType vtype, const gchar *summary, const gchar *description, const gchar *location, AlarmNotifyFunc func, gpointer func_data) { GtkTreeIter iter; GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (na->treeview)); AlarmFuncInfo *funcinfo = NULL; gchar *to_display = NULL, *start, *end, *str_time; icaltimezone *current_zone; /* Iter is not yet defined but still we return it in all the g_return_val_if_fail() calls? */ g_return_val_if_fail (trigger != -1, iter); /* Only VEVENTs or VTODOs can have alarms */ g_return_val_if_fail (vtype == E_CAL_COMPONENT_EVENT || vtype == E_CAL_COMPONENT_TODO, iter); g_return_val_if_fail (summary != NULL, iter); g_return_val_if_fail (description != NULL, iter); g_return_val_if_fail (location != NULL, iter); g_return_val_if_fail (func != NULL, iter); funcinfo = g_new0 (AlarmFuncInfo, 1); funcinfo->func = func; funcinfo->func_data = func_data; gtk_list_store_append (GTK_LIST_STORE (model), &iter); current_zone = config_data_get_timezone (); start = timet_to_str_with_zone (occur_start, current_zone); end = timet_to_str_with_zone (occur_end, current_zone); str_time = calculate_time (occur_start, occur_end); to_display = g_markup_printf_escaped ("%s\n%s %s", summary, start, str_time); g_free (start); g_free (end); gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_DISPLAY_COLUMN, to_display, -1); g_free (to_display); g_free (str_time); gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_SUMMARY_COLUMN, summary, -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_DESCRIPTION_COLUMN, description, -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_LOCATION_COLUMN, location, -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_START_COLUMN, occur_start, -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_END_COLUMN, occur_end, -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_FUNCINFO_COLUMN, funcinfo, -1); return iter; } static void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer user_data) { GtkTreeModel *model; GtkTreeIter iter; AlarmNotify *an = user_data; if (gtk_tree_selection_get_selected (selection, &model, &iter)) { gchar *summary, *description, *location; time_t occur_start, occur_end; gtk_widget_set_sensitive (an->snooze_btn, TRUE); gtk_widget_set_sensitive (an->edit_btn, TRUE); gtk_widget_set_sensitive (an->print_btn, TRUE); gtk_widget_set_sensitive (an->dismiss_btn, TRUE); gtk_tree_model_get (model, &iter, ALARM_SUMMARY_COLUMN, &summary, -1); gtk_tree_model_get (model, &iter, ALARM_DESCRIPTION_COLUMN, &description, -1); gtk_tree_model_get (model, &iter, ALARM_LOCATION_COLUMN, &location, -1); gtk_tree_model_get (model, &iter, ALARM_START_COLUMN, &occur_start, -1); gtk_tree_model_get (model, &iter, ALARM_END_COLUMN, &occur_end, -1);\ gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &an->cur_funcinfo, -1); fill_in_labels (an, summary, description, location, occur_start, occur_end); g_free (summary); g_free (description); g_free (location); } else { gtk_widget_set_sensitive (an->snooze_btn, FALSE); gtk_widget_set_sensitive (an->edit_btn, FALSE); gtk_widget_set_sensitive (an->print_btn, FALSE); gtk_widget_set_sensitive (an->dismiss_btn, FALSE); fill_in_labels (an, "", "", "", -1, -1); } } static void fill_in_labels (AlarmNotify *an, const gchar *summary, const gchar *description, const gchar *location, time_t occur_start, time_t occur_end) { GtkTextTagTable *table = gtk_text_tag_table_new (); GtkTextBuffer *buffer = gtk_text_buffer_new (table); gtk_text_buffer_set_text (buffer, description, -1); e_buffer_tagger_disconnect (GTK_TEXT_VIEW (an->description)); gtk_text_view_set_buffer (GTK_TEXT_VIEW (an->description), buffer); gtk_label_set_text (GTK_LABEL (an->location), location); e_buffer_tagger_connect (GTK_TEXT_VIEW (an->description)); e_buffer_tagger_update_tags (GTK_TEXT_VIEW (an->description)); g_object_unref (table); g_object_unref (buffer); }