No issues found
1 /*
2 * EAlarmList - list of calendar alarms with GtkTreeModel interface.
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 * Hans Petter Jansson <hpj@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 <string.h>
30 #include <glib/gi18n.h>
31
32 #include "calendar-config.h"
33 #include "e-alarm-list.h"
34
35 #define G_LIST(x) ((GList *) x)
36 #define E_ALARM_LIST_IS_SORTED(list) (E_ALARM_LIST (list)->sort_column_id != -2)
37 #define IS_VALID_ITER(dt_list, iter) (iter!= NULL && iter->user_data != NULL && \
38 dt_list->stamp == iter->stamp)
39
40 static GType column_types[E_ALARM_LIST_NUM_COLUMNS];
41
42 static void e_alarm_list_tree_model_init (GtkTreeModelIface *iface);
43 static GtkTreeModelFlags e_alarm_list_get_flags (GtkTreeModel *tree_model);
44 static gint e_alarm_list_get_n_columns (GtkTreeModel *tree_model);
45 static GType e_alarm_list_get_column_type (GtkTreeModel *tree_model,
46 gint index);
47 static gboolean e_alarm_list_get_iter (GtkTreeModel *tree_model,
48 GtkTreeIter *iter,
49 GtkTreePath *path);
50 static GtkTreePath *e_alarm_list_get_path (GtkTreeModel *tree_model,
51 GtkTreeIter *iter);
52 static void e_alarm_list_get_value (GtkTreeModel *tree_model,
53 GtkTreeIter *iter,
54 gint column,
55 GValue *value);
56 static gboolean e_alarm_list_iter_next (GtkTreeModel *tree_model,
57 GtkTreeIter *iter);
58 static gboolean e_alarm_list_iter_children (GtkTreeModel *tree_model,
59 GtkTreeIter *iter,
60 GtkTreeIter *parent);
61 static gboolean e_alarm_list_iter_has_child (GtkTreeModel *tree_model,
62 GtkTreeIter *iter);
63 static gint e_alarm_list_iter_n_children (GtkTreeModel *tree_model,
64 GtkTreeIter *iter);
65 static gboolean e_alarm_list_iter_nth_child (GtkTreeModel *tree_model,
66 GtkTreeIter *iter,
67 GtkTreeIter *parent,
68 gint n);
69 static gboolean e_alarm_list_iter_parent (GtkTreeModel *tree_model,
70 GtkTreeIter *iter,
71 GtkTreeIter *child);
72
73 G_DEFINE_TYPE_WITH_CODE (
74 EAlarmList,
75 e_alarm_list,
76 G_TYPE_OBJECT,
77 G_IMPLEMENT_INTERFACE (
78 GTK_TYPE_TREE_MODEL,
79 e_alarm_list_tree_model_init))
80
81 static void
82 e_alarm_list_class_init (EAlarmListClass *class)
83 {
84 column_types[E_ALARM_LIST_COLUMN_DESCRIPTION] = G_TYPE_STRING;
85 }
86
87 static void
88 e_alarm_list_tree_model_init (GtkTreeModelIface *iface)
89 {
90 iface->get_flags = e_alarm_list_get_flags;
91 iface->get_n_columns = e_alarm_list_get_n_columns;
92 iface->get_column_type = e_alarm_list_get_column_type;
93 iface->get_iter = e_alarm_list_get_iter;
94 iface->get_path = e_alarm_list_get_path;
95 iface->get_value = e_alarm_list_get_value;
96 iface->iter_next = e_alarm_list_iter_next;
97 iface->iter_children = e_alarm_list_iter_children;
98 iface->iter_has_child = e_alarm_list_iter_has_child;
99 iface->iter_n_children = e_alarm_list_iter_n_children;
100 iface->iter_nth_child = e_alarm_list_iter_nth_child;
101 iface->iter_parent = e_alarm_list_iter_parent;
102 }
103
104 static void
105 e_alarm_list_init (EAlarmList *alarm_list)
106 {
107 alarm_list->stamp = g_random_int ();
108 alarm_list->columns_dirty = FALSE;
109 alarm_list->list = NULL;
110 }
111
112 EAlarmList *
113 e_alarm_list_new (void)
114 {
115 EAlarmList *alarm_list;
116
117 alarm_list = E_ALARM_LIST (g_object_new (e_alarm_list_get_type (), NULL));
118
119 return alarm_list;
120 }
121
122 static void
123 all_rows_deleted (EAlarmList *alarm_list)
124 {
125 GtkTreePath *path;
126 gint i;
127
128 if (!alarm_list->list)
129 return;
130
131 path = gtk_tree_path_new ();
132 i = g_list_length (alarm_list->list);
133 gtk_tree_path_append_index (path, i);
134
135 for (; i >= 0; i--) {
136 gtk_tree_model_row_deleted (GTK_TREE_MODEL (alarm_list), path);
137 gtk_tree_path_prev (path);
138 }
139
140 gtk_tree_path_free (path);
141 }
142
143 static void
144 row_deleted (EAlarmList *alarm_list,
145 gint n)
146 {
147 GtkTreePath *path;
148
149 path = gtk_tree_path_new ();
150 gtk_tree_path_append_index (path, n);
151 gtk_tree_model_row_deleted (GTK_TREE_MODEL (alarm_list), path);
152 gtk_tree_path_free (path);
153 }
154
155 static void
156 row_added (EAlarmList *alarm_list,
157 gint n)
158 {
159 GtkTreePath *path;
160 GtkTreeIter iter;
161
162 path = gtk_tree_path_new ();
163 gtk_tree_path_append_index (path, n);
164
165 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (alarm_list), &iter, path))
166 gtk_tree_model_row_inserted (GTK_TREE_MODEL (alarm_list), path, &iter);
167
168 gtk_tree_path_free (path);
169 }
170
171 static void
172 row_updated (EAlarmList *alarm_list,
173 gint n)
174 {
175 GtkTreePath *path;
176 GtkTreeIter iter;
177
178 path = gtk_tree_path_new ();
179 gtk_tree_path_append_index (path, n);
180
181 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (alarm_list), &iter, path))
182 gtk_tree_model_row_changed (GTK_TREE_MODEL (alarm_list), path, &iter);
183
184 gtk_tree_path_free (path);
185 }
186
187 /* Fulfill the GtkTreeModel requirements */
188 static GtkTreeModelFlags
189 e_alarm_list_get_flags (GtkTreeModel *tree_model)
190 {
191 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), 0);
192
193 return GTK_TREE_MODEL_LIST_ONLY;
194 }
195
196 static gint
197 e_alarm_list_get_n_columns (GtkTreeModel *tree_model)
198 {
199 EAlarmList *alarm_list = (EAlarmList *) tree_model;
200
201 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), 0);
202
203 alarm_list->columns_dirty = TRUE;
204 return E_ALARM_LIST_NUM_COLUMNS;
205 }
206
207 static GType
208 e_alarm_list_get_column_type (GtkTreeModel *tree_model,
209 gint index)
210 {
211 EAlarmList *alarm_list = (EAlarmList *) tree_model;
212
213 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), G_TYPE_INVALID);
214 g_return_val_if_fail (index < E_ALARM_LIST_NUM_COLUMNS &&
215 index >= 0, G_TYPE_INVALID);
216
217 alarm_list->columns_dirty = TRUE;
218 return column_types[index];
219 }
220
221 const ECalComponentAlarm *
222 e_alarm_list_get_alarm (EAlarmList *alarm_list,
223 GtkTreeIter *iter)
224 {
225 g_return_val_if_fail (IS_VALID_ITER (alarm_list, iter), NULL);
226
227 return G_LIST (iter->user_data)->data;
228 }
229
230 static void
231 free_alarm (ECalComponentAlarm *alarm)
232 {
233 e_cal_component_alarm_free (alarm);
234 }
235
236 static ECalComponentAlarm *
237 copy_alarm (const ECalComponentAlarm *alarm)
238 {
239 return e_cal_component_alarm_clone ((ECalComponentAlarm *) alarm);
240 }
241
242 void
243 e_alarm_list_set_alarm (EAlarmList *alarm_list,
244 GtkTreeIter *iter,
245 const ECalComponentAlarm *alarm)
246 {
247 ECalComponentAlarm *alarm_old;
248
249 g_return_if_fail (IS_VALID_ITER (alarm_list, iter));
250
251 alarm_old = G_LIST (iter->user_data)->data;
252 free_alarm (alarm_old);
253 G_LIST (iter->user_data)->data = copy_alarm (alarm);
254 row_updated (alarm_list, g_list_position (alarm_list->list, G_LIST (iter->user_data)));
255 }
256
257 void
258 e_alarm_list_append (EAlarmList *alarm_list,
259 GtkTreeIter *iter,
260 const ECalComponentAlarm *alarm)
261 {
262 g_return_if_fail (alarm != NULL);
263
264 alarm_list->list = g_list_append (alarm_list->list, copy_alarm (alarm));
265 row_added (alarm_list, g_list_length (alarm_list->list) - 1);
266
267 if (iter) {
268 iter->user_data = g_list_last (alarm_list->list);
269 iter->stamp = alarm_list->stamp;
270 }
271 }
272
273 void
274 e_alarm_list_remove (EAlarmList *alarm_list,
275 GtkTreeIter *iter)
276 {
277 gint n;
278
279 g_return_if_fail (IS_VALID_ITER (alarm_list, iter));
280
281 n = g_list_position (alarm_list->list, G_LIST (iter->user_data));
282 free_alarm ((ECalComponentAlarm *) G_LIST (iter->user_data)->data);
283 alarm_list->list = g_list_delete_link (alarm_list->list, G_LIST (iter->user_data));
284 row_deleted (alarm_list, n);
285 }
286
287 void
288 e_alarm_list_clear (EAlarmList *alarm_list)
289 {
290 GList *l;
291
292 all_rows_deleted (alarm_list);
293
294 for (l = alarm_list->list; l; l = g_list_next (l)) {
295 free_alarm ((ECalComponentAlarm *) l->data);
296 }
297
298 g_list_free (alarm_list->list);
299 alarm_list->list = NULL;
300 }
301
302 static gboolean
303 e_alarm_list_get_iter (GtkTreeModel *tree_model,
304 GtkTreeIter *iter,
305 GtkTreePath *path)
306 {
307 EAlarmList *alarm_list = (EAlarmList *) tree_model;
308 GList *l;
309 gint i;
310
311 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), FALSE);
312 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
313
314 if (!alarm_list->list)
315 return FALSE;
316
317 alarm_list->columns_dirty = TRUE;
318
319 i = gtk_tree_path_get_indices (path)[0];
320 l = g_list_nth (alarm_list->list, i);
321 if (!l)
322 return FALSE;
323
324 iter->user_data = l;
325 iter->stamp = alarm_list->stamp;
326 return TRUE;
327 }
328
329 static GtkTreePath *
330 e_alarm_list_get_path (GtkTreeModel *tree_model,
331 GtkTreeIter *iter)
332 {
333 EAlarmList *alarm_list = (EAlarmList *) tree_model;
334 GtkTreePath *retval;
335 GList *l;
336
337 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), NULL);
338 g_return_val_if_fail (iter->stamp == E_ALARM_LIST (tree_model)->stamp, NULL);
339
340 l = iter->user_data;
341 retval = gtk_tree_path_new ();
342 gtk_tree_path_append_index (retval, g_list_position (alarm_list->list, l));
343 return retval;
344 }
345
346 /* Builds a string for the duration of the alarm. If the duration is zero, returns NULL. */
347 static gchar *
348 get_alarm_duration_string (struct icaldurationtype *duration)
349 {
350 GString *string = g_string_new (NULL);
351 gchar *ret;
352 gboolean have_something;
353
354 have_something = FALSE;
355
356 if (duration->days >= 1) {
357 /* Translator: Entire string is like "Pop up an alert %d days before start of appointment" */
358 g_string_printf (string, ngettext ("%d day", "%d days", duration->days), duration->days);
359 have_something = TRUE;
360 }
361
362 if (duration->weeks >= 1) {
363 /* Translator: Entire string is like "Pop up an alert %d weeks before start of appointment" */
364 g_string_printf (string, ngettext ("%d week","%d weeks", duration->weeks), duration->weeks);
365 have_something = TRUE;
366 }
367
368 if (duration->hours >= 1) {
369 /* Translator: Entire string is like "Pop up an alert %d hours before start of appointment" */
370 g_string_printf (string, ngettext ("%d hour", "%d hours", duration->hours), duration->hours);
371 have_something = TRUE;
372 }
373
374 if (duration->minutes >= 1) {
375 /* Translator: Entire string is like "Pop up an alert %d minutes before start of appointment" */
376 g_string_printf (string, ngettext ("%d minute", "%d minutes", duration->minutes), duration->minutes);
377 have_something = TRUE;
378 }
379
380 if (duration->seconds >= 1) {
381 /* Translator: Entire string is like "Pop up an alert %d seconds before start of appointment" */
382 g_string_printf (string, ngettext ("%d second", "%d seconds", duration->seconds), duration->seconds);
383 have_something = TRUE;
384 }
385
386 if (have_something) {
387 ret = string->str;
388 g_string_free (string, FALSE);
389 return ret;
390 } else {
391 g_string_free (string, TRUE);
392 return NULL;
393 }
394 }
395
396 static gchar *
397 get_alarm_string (ECalComponentAlarm *alarm)
398 {
399 ECalComponentAlarmAction action;
400 ECalComponentAlarmTrigger trigger;
401 gchar *base, *str = NULL, *dur;
402
403 e_cal_component_alarm_get_action (alarm, &action);
404 e_cal_component_alarm_get_trigger (alarm, &trigger);
405
406 switch (action) {
407 case E_CAL_COMPONENT_ALARM_AUDIO:
408 base = _("Play a sound");
409 break;
410
411 case E_CAL_COMPONENT_ALARM_DISPLAY:
412 base = _("Pop up an alert");
413 break;
414
415 case E_CAL_COMPONENT_ALARM_EMAIL:
416 base = _("Send an email");
417 break;
418
419 case E_CAL_COMPONENT_ALARM_PROCEDURE:
420 base = _("Run a program");
421 break;
422
423 case E_CAL_COMPONENT_ALARM_NONE:
424 case E_CAL_COMPONENT_ALARM_UNKNOWN:
425 default:
426 base = _("Unknown action to be performed");
427 break;
428 }
429
430 /* FIXME: This does not look like it will localize correctly. */
431
432 switch (trigger.type) {
433 case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START:
434 dur = get_alarm_duration_string (&trigger.u.rel_duration);
435
436 if (dur) {
437 if (trigger.u.rel_duration.is_neg)
438 /*Translator: The first %s refers to the base, which would be actions like
439 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes"*/
440 str = g_strdup_printf (
441 _("%s %s before the start of the appointment"),
442 base, dur);
443 else
444 /*Translator: The first %s refers to the base, which would be actions like
445 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes"*/
446 str = g_strdup_printf (
447 _("%s %s after the start of the appointment"),
448 base, dur);
449
450 g_free (dur);
451 } else
452 /*Translator: The %s refers to the base, which would be actions like
453 * "Play a sound" */
454 str = g_strdup_printf (_("%s at the start of the appointment"), base);
455
456 break;
457
458 case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END:
459 dur = get_alarm_duration_string (&trigger.u.rel_duration);
460
461 if (dur) {
462 if (trigger.u.rel_duration.is_neg)
463 /* Translator: The first %s refers to the base, which would be actions like
464 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" */
465 str = g_strdup_printf (
466 _("%s %s before the end of the appointment"),
467 base, dur);
468 else
469 /* Translator: The first %s refers to the base, which would be actions like
470 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" */
471 str = g_strdup_printf (
472 _("%s %s after the end of the appointment"),
473 base, dur);
474
475 g_free (dur);
476 } else
477 /* Translator: The %s refers to the base, which would be actions like
478 * "Play a sound" */
479 str = g_strdup_printf (_("%s at the end of the appointment"), base);
480
481 break;
482
483 case E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE: {
484 struct icaltimetype itt;
485 icaltimezone *utc_zone, *current_zone;
486 struct tm tm;
487 gchar buf[256];
488
489 /* Absolute triggers come in UTC, so convert them to the local timezone */
490
491 itt = trigger.u.abs_time;
492
493 utc_zone = icaltimezone_get_utc_timezone ();
494 current_zone = calendar_config_get_icaltimezone ();
495
496 tm = icaltimetype_to_tm_with_zone (&itt, utc_zone, current_zone);
497
498 e_time_format_date_and_time (&tm, calendar_config_get_24_hour_format (),
499 FALSE, FALSE, buf, sizeof (buf));
500
501 /* Translator: The first %s refers to the base, which would be actions like
502 * "Play a Sound". Second %s is an absolute time, e.g. "10:00AM" */
503 str = g_strdup_printf (_("%s at %s"), base, buf);
504
505 break; }
506
507 case E_CAL_COMPONENT_ALARM_TRIGGER_NONE:
508 default:
509 /* Translator: The %s refers to the base, which would be actions like
510 * "Play a sound". "Trigger types" are absolute or relative dates */
511 str = g_strdup_printf (_("%s for an unknown trigger type"), base);
512 break;
513 }
514
515 return str;
516 }
517
518 static void
519 e_alarm_list_get_value (GtkTreeModel *tree_model,
520 GtkTreeIter *iter,
521 gint column,
522 GValue *value)
523 {
524 EAlarmList *alarm_list = E_ALARM_LIST (tree_model);
525 ECalComponentAlarm *alarm;
526 GList *l;
527 gchar *str;
528
529 g_return_if_fail (E_IS_ALARM_LIST (tree_model));
530 g_return_if_fail (column < E_ALARM_LIST_NUM_COLUMNS);
531 g_return_if_fail (E_ALARM_LIST (tree_model)->stamp == iter->stamp);
532 g_return_if_fail (IS_VALID_ITER (alarm_list, iter));
533
534 g_value_init (value, column_types[column]);
535
536 if (!alarm_list->list)
537 return;
538
539 l = iter->user_data;
540 alarm = l->data;
541
542 if (!alarm)
543 return;
544
545 switch (column) {
546 case E_ALARM_LIST_COLUMN_DESCRIPTION:
547 str = get_alarm_string (alarm);
548 g_value_set_string (value, str);
549 g_free (str);
550 break;
551 }
552 }
553
554 static gboolean
555 e_alarm_list_iter_next (GtkTreeModel *tree_model,
556 GtkTreeIter *iter)
557 {
558 GList *l;
559
560 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), FALSE);
561 g_return_val_if_fail (IS_VALID_ITER (E_ALARM_LIST (tree_model), iter), FALSE);
562
563 if (!E_ALARM_LIST (tree_model)->list)
564 return FALSE;
565
566 l = iter->user_data;
567 l = g_list_next (l);
568 if (l) {
569 iter->user_data = l;
570 return TRUE;
571 }
572
573 return FALSE;
574 }
575
576 static gboolean
577 e_alarm_list_iter_children (GtkTreeModel *tree_model,
578 GtkTreeIter *iter,
579 GtkTreeIter *parent)
580 {
581 EAlarmList *alarm_list = E_ALARM_LIST (tree_model);
582
583 /* this is a list, nodes have no children */
584 if (parent)
585 return FALSE;
586
587 /* but if parent == NULL we return the list itself as children of the
588 * "root" */
589
590 if (!alarm_list->list)
591 return FALSE;
592
593 iter->stamp = E_ALARM_LIST (tree_model)->stamp;
594 iter->user_data = alarm_list->list;
595 return TRUE;
596 }
597
598 static gboolean
599 e_alarm_list_iter_has_child (GtkTreeModel *tree_model,
600 GtkTreeIter *iter)
601 {
602 g_return_val_if_fail (IS_VALID_ITER (E_ALARM_LIST (tree_model), iter), FALSE);
603 return FALSE;
604 }
605
606 static gint
607 e_alarm_list_iter_n_children (GtkTreeModel *tree_model,
608 GtkTreeIter *iter)
609 {
610 EAlarmList *alarm_list = E_ALARM_LIST (tree_model);
611
612 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), -1);
613
614 if (iter == NULL)
615 return g_list_length (alarm_list->list);
616
617 g_return_val_if_fail (E_ALARM_LIST (tree_model)->stamp == iter->stamp, -1);
618 return 0;
619 }
620
621 static gboolean
622 e_alarm_list_iter_nth_child (GtkTreeModel *tree_model,
623 GtkTreeIter *iter,
624 GtkTreeIter *parent,
625 gint n)
626 {
627 EAlarmList *alarm_list = E_ALARM_LIST (tree_model);
628
629 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), FALSE);
630
631 if (parent)
632 return FALSE;
633
634 if (alarm_list->list) {
635 GList *l;
636
637 l = g_list_nth (alarm_list->list, n);
638 if (!l)
639 return FALSE;
640
641 iter->stamp = alarm_list->stamp;
642 iter->user_data = l;
643 return TRUE;
644 }
645
646 return FALSE;
647 }
648
649 static gboolean
650 e_alarm_list_iter_parent (GtkTreeModel *tree_model,
651 GtkTreeIter *iter,
652 GtkTreeIter *child)
653 {
654 return FALSE;
655 }