2 * EAlarmList - list of calendar alarms with GtkTreeModel interface.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 * Hans Petter Jansson <hpj@ximian.com>
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
24 #include "evolution-config.h"
27 #include <glib/gi18n.h>
29 #include "calendar-config.h"
30 #include "e-alarm-list.h"
32 #define G_LIST(x) ((GList *) x)
33 #define E_ALARM_LIST_IS_SORTED(list) (E_ALARM_LIST (list)->sort_column_id != -2)
34 #define IS_VALID_ITER(dt_list, iter) (iter!= NULL && iter->user_data != NULL && \
35 dt_list->stamp == iter->stamp)
37 static GType column_types
[E_ALARM_LIST_NUM_COLUMNS
];
39 static void e_alarm_list_tree_model_init (GtkTreeModelIface
*iface
);
40 static GtkTreeModelFlags
e_alarm_list_get_flags (GtkTreeModel
*tree_model
);
41 static gint
e_alarm_list_get_n_columns (GtkTreeModel
*tree_model
);
42 static GType
e_alarm_list_get_column_type (GtkTreeModel
*tree_model
,
44 static gboolean
e_alarm_list_get_iter (GtkTreeModel
*tree_model
,
47 static GtkTreePath
*e_alarm_list_get_path (GtkTreeModel
*tree_model
,
49 static void e_alarm_list_get_value (GtkTreeModel
*tree_model
,
53 static gboolean
e_alarm_list_iter_next (GtkTreeModel
*tree_model
,
55 static gboolean
e_alarm_list_iter_children (GtkTreeModel
*tree_model
,
58 static gboolean
e_alarm_list_iter_has_child (GtkTreeModel
*tree_model
,
60 static gint
e_alarm_list_iter_n_children (GtkTreeModel
*tree_model
,
62 static gboolean
e_alarm_list_iter_nth_child (GtkTreeModel
*tree_model
,
66 static gboolean
e_alarm_list_iter_parent (GtkTreeModel
*tree_model
,
70 G_DEFINE_TYPE_WITH_CODE (
74 G_IMPLEMENT_INTERFACE (
76 e_alarm_list_tree_model_init
))
79 alarm_list_dispose (GObject
*object
)
81 e_alarm_list_clear (E_ALARM_LIST (object
));
83 G_OBJECT_CLASS (e_alarm_list_parent_class
)->dispose (object
);
87 e_alarm_list_class_init (EAlarmListClass
*class)
89 GObjectClass
*object_class
;
91 column_types
[E_ALARM_LIST_COLUMN_DESCRIPTION
] = G_TYPE_STRING
;
93 object_class
= G_OBJECT_CLASS (class);
94 object_class
->dispose
= alarm_list_dispose
;
98 e_alarm_list_tree_model_init (GtkTreeModelIface
*iface
)
100 iface
->get_flags
= e_alarm_list_get_flags
;
101 iface
->get_n_columns
= e_alarm_list_get_n_columns
;
102 iface
->get_column_type
= e_alarm_list_get_column_type
;
103 iface
->get_iter
= e_alarm_list_get_iter
;
104 iface
->get_path
= e_alarm_list_get_path
;
105 iface
->get_value
= e_alarm_list_get_value
;
106 iface
->iter_next
= e_alarm_list_iter_next
;
107 iface
->iter_children
= e_alarm_list_iter_children
;
108 iface
->iter_has_child
= e_alarm_list_iter_has_child
;
109 iface
->iter_n_children
= e_alarm_list_iter_n_children
;
110 iface
->iter_nth_child
= e_alarm_list_iter_nth_child
;
111 iface
->iter_parent
= e_alarm_list_iter_parent
;
115 e_alarm_list_init (EAlarmList
*alarm_list
)
117 alarm_list
->stamp
= g_random_int ();
118 alarm_list
->columns_dirty
= FALSE
;
119 alarm_list
->list
= NULL
;
123 e_alarm_list_new (void)
125 EAlarmList
*alarm_list
;
127 alarm_list
= E_ALARM_LIST (g_object_new (e_alarm_list_get_type (), NULL
));
133 all_rows_deleted (EAlarmList
*alarm_list
)
138 if (!alarm_list
->list
)
141 path
= gtk_tree_path_new ();
142 i
= g_list_length (alarm_list
->list
);
143 gtk_tree_path_append_index (path
, i
);
145 for (; i
>= 0; i
--) {
146 gtk_tree_model_row_deleted (GTK_TREE_MODEL (alarm_list
), path
);
147 gtk_tree_path_prev (path
);
150 gtk_tree_path_free (path
);
154 row_deleted (EAlarmList
*alarm_list
,
159 path
= gtk_tree_path_new ();
160 gtk_tree_path_append_index (path
, n
);
161 gtk_tree_model_row_deleted (GTK_TREE_MODEL (alarm_list
), path
);
162 gtk_tree_path_free (path
);
166 row_added (EAlarmList
*alarm_list
,
172 path
= gtk_tree_path_new ();
173 gtk_tree_path_append_index (path
, n
);
175 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (alarm_list
), &iter
, path
))
176 gtk_tree_model_row_inserted (GTK_TREE_MODEL (alarm_list
), path
, &iter
);
178 gtk_tree_path_free (path
);
182 row_updated (EAlarmList
*alarm_list
,
188 path
= gtk_tree_path_new ();
189 gtk_tree_path_append_index (path
, n
);
191 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (alarm_list
), &iter
, path
))
192 gtk_tree_model_row_changed (GTK_TREE_MODEL (alarm_list
), path
, &iter
);
194 gtk_tree_path_free (path
);
197 /* Fulfill the GtkTreeModel requirements */
198 static GtkTreeModelFlags
199 e_alarm_list_get_flags (GtkTreeModel
*tree_model
)
201 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model
), 0);
203 return GTK_TREE_MODEL_LIST_ONLY
;
207 e_alarm_list_get_n_columns (GtkTreeModel
*tree_model
)
209 EAlarmList
*alarm_list
= (EAlarmList
*) tree_model
;
211 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model
), 0);
213 alarm_list
->columns_dirty
= TRUE
;
214 return E_ALARM_LIST_NUM_COLUMNS
;
218 e_alarm_list_get_column_type (GtkTreeModel
*tree_model
,
221 EAlarmList
*alarm_list
= (EAlarmList
*) tree_model
;
223 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model
), G_TYPE_INVALID
);
224 g_return_val_if_fail (index
< E_ALARM_LIST_NUM_COLUMNS
&&
225 index
>= 0, G_TYPE_INVALID
);
227 alarm_list
->columns_dirty
= TRUE
;
228 return column_types
[index
];
231 const ECalComponentAlarm
*
232 e_alarm_list_get_alarm (EAlarmList
*alarm_list
,
235 g_return_val_if_fail (IS_VALID_ITER (alarm_list
, iter
), NULL
);
237 return G_LIST (iter
->user_data
)->data
;
241 free_alarm (ECalComponentAlarm
*alarm
)
243 e_cal_component_alarm_free (alarm
);
246 static ECalComponentAlarm
*
247 copy_alarm (const ECalComponentAlarm
*alarm
)
249 return e_cal_component_alarm_clone ((ECalComponentAlarm
*) alarm
);
253 e_alarm_list_set_alarm (EAlarmList
*alarm_list
,
255 const ECalComponentAlarm
*alarm
)
257 ECalComponentAlarm
*alarm_old
;
259 g_return_if_fail (IS_VALID_ITER (alarm_list
, iter
));
261 alarm_old
= G_LIST (iter
->user_data
)->data
;
262 free_alarm (alarm_old
);
263 G_LIST (iter
->user_data
)->data
= copy_alarm (alarm
);
264 row_updated (alarm_list
, g_list_position (alarm_list
->list
, G_LIST (iter
->user_data
)));
268 e_alarm_list_append (EAlarmList
*alarm_list
,
270 const ECalComponentAlarm
*alarm
)
272 g_return_if_fail (alarm
!= NULL
);
274 alarm_list
->list
= g_list_append (alarm_list
->list
, copy_alarm (alarm
));
275 row_added (alarm_list
, g_list_length (alarm_list
->list
) - 1);
278 iter
->user_data
= g_list_last (alarm_list
->list
);
279 iter
->stamp
= alarm_list
->stamp
;
284 e_alarm_list_remove (EAlarmList
*alarm_list
,
289 g_return_if_fail (IS_VALID_ITER (alarm_list
, iter
));
291 n
= g_list_position (alarm_list
->list
, G_LIST (iter
->user_data
));
292 free_alarm ((ECalComponentAlarm
*) G_LIST (iter
->user_data
)->data
);
293 alarm_list
->list
= g_list_delete_link (alarm_list
->list
, G_LIST (iter
->user_data
));
294 row_deleted (alarm_list
, n
);
298 e_alarm_list_clear (EAlarmList
*alarm_list
)
302 all_rows_deleted (alarm_list
);
304 for (l
= alarm_list
->list
; l
; l
= g_list_next (l
)) {
305 free_alarm ((ECalComponentAlarm
*) l
->data
);
308 g_list_free (alarm_list
->list
);
309 alarm_list
->list
= NULL
;
313 e_alarm_list_get_iter (GtkTreeModel
*tree_model
,
317 EAlarmList
*alarm_list
= (EAlarmList
*) tree_model
;
321 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model
), FALSE
);
322 g_return_val_if_fail (gtk_tree_path_get_depth (path
) > 0, FALSE
);
324 if (!alarm_list
->list
)
327 alarm_list
->columns_dirty
= TRUE
;
329 i
= gtk_tree_path_get_indices (path
)[0];
330 l
= g_list_nth (alarm_list
->list
, i
);
335 iter
->stamp
= alarm_list
->stamp
;
340 e_alarm_list_get_path (GtkTreeModel
*tree_model
,
343 EAlarmList
*alarm_list
= (EAlarmList
*) tree_model
;
347 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model
), NULL
);
348 g_return_val_if_fail (iter
->stamp
== E_ALARM_LIST (tree_model
)->stamp
, NULL
);
351 retval
= gtk_tree_path_new ();
352 gtk_tree_path_append_index (retval
, g_list_position (alarm_list
->list
, l
));
356 /* Builds a string for the duration of the alarm. If the duration is zero, returns NULL. */
358 get_alarm_duration_string (struct icaldurationtype
*duration
)
360 GString
*string
= g_string_new (NULL
);
362 gboolean have_something
;
364 have_something
= FALSE
;
366 if (duration
->days
>= 1) {
367 /* Translator: Entire string is like "Pop up an alert %d days before start" */
368 g_string_printf (string
, ngettext ("%d day", "%d days", duration
->days
), duration
->days
);
369 have_something
= TRUE
;
372 if (duration
->weeks
>= 1) {
373 /* Translator: Entire string is like "Pop up an alert %d weeks before start" */
374 g_string_printf (string
, ngettext ("%d week","%d weeks", duration
->weeks
), duration
->weeks
);
375 have_something
= TRUE
;
378 if (duration
->hours
>= 1) {
379 /* Translator: Entire string is like "Pop up an alert %d hours before start" */
380 g_string_printf (string
, ngettext ("%d hour", "%d hours", duration
->hours
), duration
->hours
);
381 have_something
= TRUE
;
384 if (duration
->minutes
>= 1) {
385 /* Translator: Entire string is like "Pop up an alert %d minutes before start" */
386 g_string_printf (string
, ngettext ("%d minute", "%d minutes", duration
->minutes
), duration
->minutes
);
387 have_something
= TRUE
;
390 if (duration
->seconds
>= 1) {
391 /* Translator: Entire string is like "Pop up an alert %d seconds before start" */
392 g_string_printf (string
, ngettext ("%d second", "%d seconds", duration
->seconds
), duration
->seconds
);
393 have_something
= TRUE
;
396 if (have_something
) {
398 g_string_free (string
, FALSE
);
401 g_string_free (string
, TRUE
);
407 get_alarm_string (ECalComponentAlarm
*alarm
)
409 ECalComponentAlarmAction action
;
410 ECalComponentAlarmTrigger trigger
;
412 gchar
*str
= NULL
, *dur
;
414 e_cal_component_alarm_get_action (alarm
, &action
);
415 e_cal_component_alarm_get_trigger (alarm
, &trigger
);
418 case E_CAL_COMPONENT_ALARM_AUDIO
:
419 base
= C_("cal-reminders", "Play a sound");
422 case E_CAL_COMPONENT_ALARM_DISPLAY
:
423 base
= C_("cal-reminders", "Pop up an alert");
426 case E_CAL_COMPONENT_ALARM_EMAIL
:
427 base
= C_("cal-reminders", "Send an email");
430 case E_CAL_COMPONENT_ALARM_PROCEDURE
:
431 base
= C_("cal-reminders", "Run a program");
434 case E_CAL_COMPONENT_ALARM_NONE
:
435 case E_CAL_COMPONENT_ALARM_UNKNOWN
:
437 base
= C_("cal-reminders", "Unknown action to be performed");
441 /* FIXME: This does not look like it will localize correctly. */
443 switch (trigger
.type
) {
444 case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START
:
445 dur
= get_alarm_duration_string (&trigger
.u
.rel_duration
);
448 if (trigger
.u
.rel_duration
.is_neg
)
449 str
= g_strdup_printf (
450 /*Translator: The first %s refers to the base, which would be actions like
451 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes"*/
452 C_("cal-reminders", "%s %s before the start"),
455 str
= g_strdup_printf (
456 /*Translator: The first %s refers to the base, which would be actions like
457 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes"*/
458 C_("cal-reminders", "%s %s after the start"),
463 /*Translator: The %s refers to the base, which would be actions like
465 str
= g_strdup_printf (C_("cal-reminders", "%s at the start"), base
);
469 case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END
:
470 dur
= get_alarm_duration_string (&trigger
.u
.rel_duration
);
473 if (trigger
.u
.rel_duration
.is_neg
)
474 str
= g_strdup_printf (
475 /* Translator: The first %s refers to the base, which would be actions like
476 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" */
477 C_("cal-reminders", "%s %s before the end"),
480 str
= g_strdup_printf (
481 /* Translator: The first %s refers to the base, which would be actions like
482 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" */
483 C_("cal-reminders", "%s %s after the end"),
488 /* Translator: The %s refers to the base, which would be actions like
490 str
= g_strdup_printf (C_("cal-reminders", "%s at the end"), base
);
494 case E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE
: {
495 struct icaltimetype itt
;
496 icaltimezone
*utc_zone
, *current_zone
;
500 /* Absolute triggers come in UTC, so convert them to the local timezone */
502 itt
= trigger
.u
.abs_time
;
504 utc_zone
= icaltimezone_get_utc_timezone ();
505 current_zone
= calendar_config_get_icaltimezone ();
507 tm
= icaltimetype_to_tm_with_zone (&itt
, utc_zone
, current_zone
);
509 e_time_format_date_and_time (&tm
, calendar_config_get_24_hour_format (),
510 FALSE
, FALSE
, buf
, sizeof (buf
));
512 /* Translator: The first %s refers to the base, which would be actions like
513 * "Play a Sound". Second %s is an absolute time, e.g. "10:00AM" */
514 str
= g_strdup_printf (C_("cal-reminders", "%s at %s"), base
, buf
);
518 case E_CAL_COMPONENT_ALARM_TRIGGER_NONE
:
520 /* Translator: The %s refers to the base, which would be actions like
521 * "Play a sound". "Trigger types" are absolute or relative dates */
522 str
= g_strdup_printf (C_("cal-reminders", "%s for an unknown trigger type"), base
);
530 e_alarm_list_get_value (GtkTreeModel
*tree_model
,
535 EAlarmList
*alarm_list
= E_ALARM_LIST (tree_model
);
536 ECalComponentAlarm
*alarm
;
540 g_return_if_fail (E_IS_ALARM_LIST (tree_model
));
541 g_return_if_fail (column
< E_ALARM_LIST_NUM_COLUMNS
);
542 g_return_if_fail (E_ALARM_LIST (tree_model
)->stamp
== iter
->stamp
);
543 g_return_if_fail (IS_VALID_ITER (alarm_list
, iter
));
545 g_value_init (value
, column_types
[column
]);
547 if (!alarm_list
->list
)
557 case E_ALARM_LIST_COLUMN_DESCRIPTION
:
558 str
= get_alarm_string (alarm
);
559 g_value_set_string (value
, str
);
566 e_alarm_list_iter_next (GtkTreeModel
*tree_model
,
571 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model
), FALSE
);
572 g_return_val_if_fail (IS_VALID_ITER (E_ALARM_LIST (tree_model
), iter
), FALSE
);
574 if (!E_ALARM_LIST (tree_model
)->list
)
588 e_alarm_list_iter_children (GtkTreeModel
*tree_model
,
592 EAlarmList
*alarm_list
= E_ALARM_LIST (tree_model
);
594 /* this is a list, nodes have no children */
598 /* but if parent == NULL we return the list itself as children of the
601 if (!alarm_list
->list
)
604 iter
->stamp
= E_ALARM_LIST (tree_model
)->stamp
;
605 iter
->user_data
= alarm_list
->list
;
610 e_alarm_list_iter_has_child (GtkTreeModel
*tree_model
,
613 g_return_val_if_fail (IS_VALID_ITER (E_ALARM_LIST (tree_model
), iter
), FALSE
);
618 e_alarm_list_iter_n_children (GtkTreeModel
*tree_model
,
621 EAlarmList
*alarm_list
= E_ALARM_LIST (tree_model
);
623 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model
), -1);
626 return g_list_length (alarm_list
->list
);
628 g_return_val_if_fail (E_ALARM_LIST (tree_model
)->stamp
== iter
->stamp
, -1);
633 e_alarm_list_iter_nth_child (GtkTreeModel
*tree_model
,
638 EAlarmList
*alarm_list
= E_ALARM_LIST (tree_model
);
640 g_return_val_if_fail (E_IS_ALARM_LIST (tree_model
), FALSE
);
645 if (alarm_list
->list
) {
648 l
= g_list_nth (alarm_list
->list
, n
);
652 iter
->stamp
= alarm_list
->stamp
;
661 e_alarm_list_iter_parent (GtkTreeModel
*tree_model
,