No issues found
1 /*
2 * Evolution calendar - alarm notification dialog
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Authors:
19 * Federico Mena-Quintero <federico@ximian.com>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <glib/gi18n.h>
32
33 #include "alarm-notify-dialog.h"
34 #include "config-data.h"
35 #include "util.h"
36
37 #include "e-util/e-util.h"
38 #include "e-util/e-util-private.h"
39 #include "misc/e-buffer-tagger.h"
40
41 enum {
42 ALARM_DISPLAY_COLUMN,
43 ALARM_SUMMARY_COLUMN,
44 ALARM_DESCRIPTION_COLUMN,
45 ALARM_LOCATION_COLUMN,
46
47 ALARM_START_COLUMN,
48 ALARM_END_COLUMN,
49
50 ALARM_FUNCINFO_COLUMN,
51
52 N_ALARM_COLUMNS
53 };
54
55 /* The useful contents of the alarm notify dialog */
56
57 typedef struct {
58 AlarmNotifyFunc func;
59 gpointer func_data;
60 } AlarmFuncInfo;
61
62 typedef struct {
63 GtkBuilder *builder;
64
65 GtkWidget *dialog;
66 GtkWidget *snooze_time_min;
67 GtkWidget *snooze_time_hrs;
68 GtkWidget *snooze_time_days;
69 GtkWidget *snooze_btn;
70 GtkWidget *edit_btn;
71 GtkWidget *print_btn;
72 GtkWidget *dismiss_btn;
73 GtkWidget *minutes_label;
74 GtkWidget *hrs_label;
75 GtkWidget *days_label;
76 GtkWidget *description;
77 GtkWidget *location;
78 GtkWidget *treeview;
79
80 AlarmFuncInfo *cur_funcinfo;
81
82 } AlarmNotify;
83
84 static void tree_selection_changed_cb (GtkTreeSelection *selection,
85 gpointer data);
86 static void fill_in_labels (AlarmNotify *an,
87 const gchar *summary,
88 const gchar *description,
89 const gchar *location,
90 time_t occur_start,
91 time_t occur_end);
92
93 static void edit_pressed_cb (GtkButton *button,
94 gpointer user_data);
95 static void snooze_pressed_cb (GtkButton *button,
96 gpointer user_data);
97
98 static void
99 an_update_minutes_label (GtkSpinButton *sb,
100 gpointer data)
101 {
102 AlarmNotify *an;
103 gint snooze_timeout_min;
104
105 an = (AlarmNotify *) data;
106
107 snooze_timeout_min = gtk_spin_button_get_value_as_int (sb);
108 gtk_label_set_text (GTK_LABEL (an->minutes_label), ngettext ("minute", "minutes", snooze_timeout_min));
109 }
110
111 static void
112 an_update_hrs_label (GtkSpinButton *sb,
113 gpointer data)
114 {
115 AlarmNotify *an;
116 gint snooze_timeout_hrs;
117
118 an = (AlarmNotify *) data;
119
120 snooze_timeout_hrs = gtk_spin_button_get_value_as_int (sb);
121 gtk_label_set_text (GTK_LABEL (an->hrs_label), ngettext ("hour", "hours", snooze_timeout_hrs));
122 }
123
124 static void
125 an_update_days_label (GtkSpinButton *sb,
126 gpointer data)
127 {
128 AlarmNotify *an;
129 gint snooze_timeout_days;
130
131 an = (AlarmNotify *) data;
132
133 snooze_timeout_days = gtk_spin_button_get_value_as_int (sb);
134 gtk_label_set_text (GTK_LABEL (an->days_label), ngettext ("day", "days", snooze_timeout_days));
135 }
136
137 static void
138 dialog_response_cb (GtkDialog *dialog,
139 guint response_id,
140 gpointer user_data)
141 {
142 AlarmNotify *an = user_data;
143 GtkTreeIter iter;
144 GtkTreeModel *model = NULL;
145 AlarmFuncInfo *funcinfo = NULL;
146 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview));
147
148 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
149 gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1);
150 }
151
152 if (!funcinfo) {
153 GtkTreeModel *treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (an->treeview));
154 if (!gtk_tree_model_get_iter_first (treemodel, &iter))
155 return;
156
157 gtk_tree_model_get (treemodel, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1);
158 }
159
160 g_return_if_fail (funcinfo);
161
162 switch (response_id) {
163 case GTK_RESPONSE_CLOSE:
164 case GTK_RESPONSE_DELETE_EVENT:
165 (* funcinfo->func) (ALARM_NOTIFY_CLOSE, -1, funcinfo->func_data);
166 break;
167 }
168 }
169
170 static void
171 edit_pressed_cb (GtkButton *button,
172 gpointer user_data)
173 {
174 AlarmNotify *an = user_data;
175 AlarmFuncInfo *funcinfo = NULL;
176 GtkTreeIter iter;
177 GtkTreeModel *model = NULL;
178 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview));
179
180 if (gtk_tree_selection_get_selected (selection, &model, &iter))
181 gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1);
182
183 g_return_if_fail (funcinfo);
184
185 (* funcinfo->func) (ALARM_NOTIFY_EDIT, -1, funcinfo->func_data);
186 }
187
188 static void
189 print_pressed_cb (GtkButton *button,
190 gpointer user_data)
191 {
192 AlarmNotify *an = user_data;
193 AlarmFuncInfo *funcinfo = NULL;
194 GtkTreeIter iter;
195 GtkTreeModel *model = NULL;
196 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview));
197
198 if (gtk_tree_selection_get_selected (selection, &model, &iter))
199 gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1);
200
201 g_return_if_fail (funcinfo);
202
203 (* funcinfo->func) (ALARM_NOTIFY_PRINT, -1, funcinfo->func_data);
204 }
205
206 #define DEFAULT_SNOOZE_MINS 5
207
208 static void
209 snooze_pressed_cb (GtkButton *button,
210 gpointer user_data)
211 {
212 gint snooze_timeout;
213 AlarmNotify *an = user_data;
214 GtkTreeIter iter;
215 GtkTreeModel *model = NULL;
216 AlarmFuncInfo *funcinfo = NULL;
217 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview));
218
219 gtk_widget_grab_focus ((GtkWidget *) button);
220
221 if (gtk_tree_selection_get_selected (selection, &model, &iter))
222 gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1);
223
224 g_return_if_fail (funcinfo);
225
226 snooze_timeout = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (an->snooze_time_min));
227 snooze_timeout += 60 * (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (an->snooze_time_hrs)));
228 snooze_timeout += 60 * 24 * (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (an->snooze_time_days)));
229 if (!snooze_timeout)
230 snooze_timeout = DEFAULT_SNOOZE_MINS;
231 (* funcinfo->func) (ALARM_NOTIFY_SNOOZE, snooze_timeout, funcinfo->func_data);
232 }
233
234 static void
235 dismiss_pressed_cb (GtkButton *button,
236 gpointer user_data)
237 {
238 AlarmNotify *an = user_data;
239 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (an->treeview));
240
241 g_return_if_fail (model != NULL);
242
243 if (gtk_tree_model_iter_n_children (model, NULL) <= 1) {
244 gtk_dialog_response (GTK_DIALOG (an->dialog), GTK_RESPONSE_CLOSE);
245 } else {
246 GtkTreeIter iter;
247 AlarmFuncInfo *funcinfo = NULL;
248 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview));
249
250 if (gtk_tree_selection_get_selected (selection, &model, &iter))
251 gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &funcinfo, -1);
252
253 g_return_if_fail (funcinfo);
254
255 (* funcinfo->func) (ALARM_NOTIFY_DISMISS, -1, funcinfo->func_data);
256 }
257 }
258
259 static void
260 dialog_destroyed_cb (GtkWidget *dialog,
261 gpointer user_data)
262 {
263 AlarmNotify *an = user_data;
264
265 g_object_unref (an->builder);
266 g_free (an);
267 }
268
269 /**
270 * notified_alarms_dialog_new:
271 *
272 * Return value: a new dialog in which you can add alarm notifications
273 **/
274 AlarmNotificationsDialog *
275 notified_alarms_dialog_new (void)
276 {
277 GtkWidget *container;
278 GtkWidget *image;
279 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
280 AlarmNotificationsDialog *na = NULL;
281 AlarmNotify *an = g_new0 (AlarmNotify, 1);
282 GtkTreeViewColumn *column = NULL;
283 GtkTreeSelection *selection = NULL;
284 GtkTreeModel *model = GTK_TREE_MODEL (gtk_list_store_new (
285 N_ALARM_COLUMNS,
286
287 G_TYPE_STRING, /* Display */
288 G_TYPE_STRING, /* Summary */
289 G_TYPE_STRING, /* Description */
290 G_TYPE_STRING, /* Location */
291
292 G_TYPE_POINTER, /* Start */
293 G_TYPE_POINTER, /* End */
294
295 G_TYPE_POINTER /* FuncInfo */));
296
297 an->builder = gtk_builder_new ();
298 e_load_ui_builder_definition (an->builder, "alarm-notify.ui");
299
300 an->dialog = e_builder_get_widget (an->builder, "alarm-notify");
301 an->snooze_time_min = e_builder_get_widget (an->builder, "snooze-time-min");
302 an->minutes_label = e_builder_get_widget (an->builder, "minutes-label");
303 an->snooze_time_hrs = e_builder_get_widget (an->builder, "snooze-time-hrs");
304 an->hrs_label = e_builder_get_widget (an->builder, "hrs-label");
305 an->snooze_time_days = e_builder_get_widget (an->builder, "snooze-time-days");
306 an->days_label = e_builder_get_widget (an->builder, "days-label");
307 an->description = e_builder_get_widget (an->builder, "description-label");
308 an->location = e_builder_get_widget (an->builder, "location-label");
309 an->treeview = e_builder_get_widget (an->builder, "appointments-treeview");
310 an->snooze_btn = e_builder_get_widget (an->builder, "snooze-button");
311 an->edit_btn = e_builder_get_widget (an->builder, "edit-button");
312 an->print_btn = e_builder_get_widget (an->builder, "print-button");
313 an->dismiss_btn = e_builder_get_widget (an->builder, "dismiss-button");
314
315 if (!(an->dialog && an->treeview
316 && an->snooze_time_min && an->snooze_time_hrs && an->snooze_time_days
317 && an->description && an->location && an->edit_btn && an->print_btn && an->snooze_btn && an->dismiss_btn)) {
318 g_warning ("alarm_notify_dialog(): Could not find all widgets in alarm-notify.ui file!");
319 g_object_unref (an->builder);
320 g_free (an);
321 return NULL;
322 }
323
324 e_buffer_tagger_connect (GTK_TEXT_VIEW (an->description));
325
326 gtk_tree_view_set_model (GTK_TREE_VIEW (an->treeview), model);
327
328 gtk_window_set_keep_above (GTK_WINDOW (an->dialog), TRUE);
329
330 column = gtk_tree_view_column_new_with_attributes (
331 _("Start time"),
332 renderer, "text", ALARM_DISPLAY_COLUMN, NULL);
333
334 gtk_tree_view_column_set_attributes (
335 column, renderer,
336 "markup", ALARM_DISPLAY_COLUMN, NULL);
337
338 gtk_tree_view_append_column (GTK_TREE_VIEW (an->treeview), column);
339
340 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (an->treeview));
341 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
342 g_signal_connect (
343 selection, "changed",
344 G_CALLBACK (tree_selection_changed_cb), an);
345 tree_selection_changed_cb (selection, an);
346
347 gtk_widget_realize (an->dialog);
348
349 container = gtk_dialog_get_action_area (GTK_DIALOG (an->dialog));
350 gtk_container_set_border_width (GTK_CONTAINER (container), 12);
351
352 container = gtk_dialog_get_content_area (GTK_DIALOG (an->dialog));
353 gtk_container_set_border_width (GTK_CONTAINER (container), 0);
354
355 image = e_builder_get_widget (an->builder, "alarm-image");
356 gtk_image_set_from_icon_name (
357 GTK_IMAGE (image), "stock_alarm", GTK_ICON_SIZE_DIALOG);
358
359 g_signal_connect (
360 an->edit_btn, "clicked",
361 G_CALLBACK (edit_pressed_cb), an);
362 g_signal_connect (
363 an->print_btn, "clicked",
364 G_CALLBACK (print_pressed_cb), an);
365 g_signal_connect (
366 an->snooze_btn, "clicked",
367 G_CALLBACK (snooze_pressed_cb), an);
368 g_signal_connect (
369 an->dismiss_btn, "clicked",
370 G_CALLBACK (dismiss_pressed_cb), an);
371 g_signal_connect (
372 an->dialog, "response",
373 G_CALLBACK (dialog_response_cb), an);
374 g_signal_connect (
375 an->dialog, "destroy",
376 G_CALLBACK (dialog_destroyed_cb), an);
377
378 if (!gtk_widget_get_realized (an->dialog))
379 gtk_widget_realize (an->dialog);
380
381 gtk_window_set_icon_name (GTK_WINDOW (an->dialog), "stock_alarm");
382
383 /* Set callback for updating the snooze "minutes" label */
384 g_signal_connect (
385 an->snooze_time_min, "value_changed",
386 G_CALLBACK (an_update_minutes_label), an);
387
388 /* Set callback for updating the snooze "hours" label */
389 g_signal_connect (
390 an->snooze_time_hrs, "value_changed",
391 G_CALLBACK (an_update_hrs_label), an);
392
393 /* Set callback for updating the snooze "days" label */
394 g_signal_connect (
395 an->snooze_time_days, "value_changed",
396 G_CALLBACK (an_update_days_label), an);
397
398 na = g_new0 (AlarmNotificationsDialog, 1);
399
400 na->treeview = an->treeview;
401 na->dialog = an->dialog;
402
403 return na;
404 }
405
406 /**
407 * add_alarm_to_notified_alarms_dialog:
408 * @na: Pointer to the dialog-info
409 * @trigger: Trigger time for the alarm.
410 * @occur_start: Start of occurrence time for the event.
411 * @occur_end: End of occurrence time for the event.
412 * @vtype: Type of the component which corresponds to the alarm.
413 * @summary: Short summary of the appointment
414 * @description: Long description of the appointment
415 * @location: Location of the appointment
416 * @func: Function to be called when a dialog action is invoked.
417 * @func_data: Closure data for @func.
418 *
419 * The specified @func will be used to notify the client about result of
420 * the actions in the dialog.
421 *
422 * Return value: the iter in the treeview of the dialog
423 **/
424
425 GtkTreeIter
426 add_alarm_to_notified_alarms_dialog (AlarmNotificationsDialog *na,
427 time_t trigger,
428 time_t occur_start,
429 time_t occur_end,
430 ECalComponentVType vtype,
431 const gchar *summary,
432 const gchar *description,
433 const gchar *location,
434 AlarmNotifyFunc func,
435 gpointer func_data)
436 {
437 GtkTreeIter iter;
438 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (na->treeview));
439 AlarmFuncInfo *funcinfo = NULL;
440 gchar *to_display = NULL, *start, *end, *str_time;
441 icaltimezone *current_zone;
442
443 /* Iter is not yet defined but still we return it in all the g_return_val_if_fail() calls? */
444 g_return_val_if_fail (trigger != -1, iter);
445
446 /* Only VEVENTs or VTODOs can have alarms */
447 g_return_val_if_fail (vtype == E_CAL_COMPONENT_EVENT || vtype == E_CAL_COMPONENT_TODO, iter);
448 g_return_val_if_fail (summary != NULL, iter);
449 g_return_val_if_fail (description != NULL, iter);
450 g_return_val_if_fail (location != NULL, iter);
451 g_return_val_if_fail (func != NULL, iter);
452
453 funcinfo = g_new0 (AlarmFuncInfo, 1);
454 funcinfo->func = func;
455 funcinfo->func_data = func_data;
456
457 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
458
459 current_zone = config_data_get_timezone ();
460 start = timet_to_str_with_zone (occur_start, current_zone);
461 end = timet_to_str_with_zone (occur_end, current_zone);
462 str_time = calculate_time (occur_start, occur_end);
463 to_display = g_markup_printf_escaped (
464 "<big><b>%s</b></big>\n%s %s",
465 summary, start, str_time);
466 g_free (start);
467 g_free (end);
468 gtk_list_store_set (
469 GTK_LIST_STORE (model), &iter,
470 ALARM_DISPLAY_COLUMN, to_display, -1);
471 g_free (to_display);
472 g_free (str_time);
473
474 gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_SUMMARY_COLUMN, summary, -1);
475 gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_DESCRIPTION_COLUMN, description, -1);
476 gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_LOCATION_COLUMN, location, -1);
477 gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_START_COLUMN, occur_start, -1);
478 gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_END_COLUMN, occur_end, -1);
479 gtk_list_store_set (GTK_LIST_STORE (model), &iter, ALARM_FUNCINFO_COLUMN, funcinfo, -1);
480
481 return iter;
482 }
483
484 static void
485 tree_selection_changed_cb (GtkTreeSelection *selection,
486 gpointer user_data)
487 {
488 GtkTreeModel *model;
489 GtkTreeIter iter;
490 AlarmNotify *an = user_data;
491
492 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
493 gchar *summary, *description, *location;
494 time_t occur_start, occur_end;
495
496 gtk_widget_set_sensitive (an->snooze_btn, TRUE);
497 gtk_widget_set_sensitive (an->edit_btn, TRUE);
498 gtk_widget_set_sensitive (an->print_btn, TRUE);
499 gtk_widget_set_sensitive (an->dismiss_btn, TRUE);
500 gtk_tree_model_get (model, &iter, ALARM_SUMMARY_COLUMN, &summary, -1);
501 gtk_tree_model_get (model, &iter, ALARM_DESCRIPTION_COLUMN, &description, -1);
502 gtk_tree_model_get (model, &iter, ALARM_LOCATION_COLUMN, &location, -1);
503 gtk_tree_model_get (model, &iter, ALARM_START_COLUMN, &occur_start, -1);
504 gtk_tree_model_get (model, &iter, ALARM_END_COLUMN, &occur_end, -1);\
505 gtk_tree_model_get (model, &iter, ALARM_FUNCINFO_COLUMN, &an->cur_funcinfo, -1);
506
507 fill_in_labels (an, summary, description, location, occur_start, occur_end);
508
509 g_free (summary);
510 g_free (description);
511 g_free (location);
512 } else {
513 gtk_widget_set_sensitive (an->snooze_btn, FALSE);
514 gtk_widget_set_sensitive (an->edit_btn, FALSE);
515 gtk_widget_set_sensitive (an->print_btn, FALSE);
516 gtk_widget_set_sensitive (an->dismiss_btn, FALSE);
517
518 fill_in_labels (an, "", "", "", -1, -1);
519 }
520 }
521
522 static void
523 fill_in_labels (AlarmNotify *an,
524 const gchar *summary,
525 const gchar *description,
526 const gchar *location,
527 time_t occur_start,
528 time_t occur_end)
529 {
530 GtkTextTagTable *table = gtk_text_tag_table_new ();
531 GtkTextBuffer *buffer = gtk_text_buffer_new (table);
532 gtk_text_buffer_set_text (buffer, description, -1);
533 e_buffer_tagger_disconnect (GTK_TEXT_VIEW (an->description));
534 gtk_text_view_set_buffer (GTK_TEXT_VIEW (an->description), buffer);
535 gtk_label_set_text (GTK_LABEL (an->location), location);
536 e_buffer_tagger_connect (GTK_TEXT_VIEW (an->description));
537 e_buffer_tagger_update_tags (GTK_TEXT_VIEW (an->description));
538 g_object_unref (table);
539 g_object_unref (buffer);
540 }