diff options
Diffstat (limited to 'calendar/cal-util/cal-util.c')
-rw-r--r-- | calendar/cal-util/cal-util.c | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/calendar/cal-util/cal-util.c b/calendar/cal-util/cal-util.c index bb1b8a2f51..1df57bacec 100644 --- a/calendar/cal-util/cal-util.c +++ b/calendar/cal-util/cal-util.c @@ -98,3 +98,305 @@ cal_util_new_top_level (void) return icalcomp; } + +/* Computes the range of time in which recurrences should be generated for a + * component in order to compute alarm trigger times. + */ +static void +compute_alarm_range (CalComponent *comp, GList *alarm_uids, time_t start, time_t end, + time_t *alarm_start, time_t *alarm_end) +{ + GList *l; + + *alarm_start = start; + *alarm_end = end; + + for (l = alarm_uids; l; l = l->next) { + const char *auid; + CalComponentAlarm *alarm; + CalAlarmTrigger trigger; + struct icaldurationtype *dur; + time_t dur_time; + + auid = l->data; + alarm = cal_component_get_alarm (comp, auid); + g_assert (alarm != NULL); + + cal_component_alarm_get_trigger (alarm, &trigger); + cal_component_alarm_free (alarm); + + switch (trigger.type) { + case CAL_ALARM_TRIGGER_NONE: + case CAL_ALARM_TRIGGER_ABSOLUTE: + continue; + + case CAL_ALARM_TRIGGER_RELATIVE_START: + case CAL_ALARM_TRIGGER_RELATIVE_END: + dur = &trigger.u.rel_duration; + dur_time = icaldurationtype_as_int (*dur); + + if (dur->is_neg) + /* If the duration is negative then dur_time + * will be negative as well; that is why we + * subtract to expand the range. + */ + *alarm_end = MAX (*alarm_end, end - dur_time); + else + *alarm_start = MIN (*alarm_start, start - dur_time); + + break; + + default: + g_assert_not_reached (); + } + } + + g_assert (*alarm_start <= *alarm_end); +} + +/* Closure data to generate alarm occurrences */ +struct alarm_occurrence_data { + /* These are the info we have */ + GList *alarm_uids; + time_t start; + time_t end; + + /* This is what we compute */ + GSList *triggers; + int n_triggers; +}; + +/* Callback used from cal_recur_generate_instances(); generates triggers for all + * of a component's RELATIVE alarms. + */ +static gboolean +add_alarm_occurrences_cb (CalComponent *comp, time_t start, time_t end, gpointer data) +{ + struct alarm_occurrence_data *aod; + GList *l; + + aod = data; + + for (l = aod->alarm_uids; l; l = l->next) { + const char *auid; + CalComponentAlarm *alarm; + CalAlarmTrigger trigger; + struct icaldurationtype *dur; + time_t dur_time; + time_t occur_time, trigger_time; + CalAlarmInstance *instance; + + auid = l->data; + alarm = cal_component_get_alarm (comp, auid); + g_assert (alarm != NULL); + + cal_component_alarm_get_trigger (alarm, &trigger); + cal_component_alarm_free (alarm); + + if (trigger.type != CAL_ALARM_TRIGGER_RELATIVE_START + && trigger.type != CAL_ALARM_TRIGGER_RELATIVE_END) + continue; + + dur = &trigger.u.rel_duration; + dur_time = icaldurationtype_as_int (*dur); + + if (trigger.type == CAL_ALARM_TRIGGER_RELATIVE_START) + occur_time = start; + else + occur_time = end; + + /* If dur->is_neg is true then dur_time will already be + * negative. So we do not need to test for dur->is_neg here; we + * can simply add the dur_time value to the occur_time and get + * the correct result. + */ + + trigger_time = occur_time + dur_time; + + if (trigger_time < aod->start || trigger_time >= aod->end) + continue; + + instance = g_new (CalAlarmInstance, 1); + instance->auid = auid; + instance->trigger = trigger_time; + instance->occur_start = start; + instance->occur_end = end; + + aod->triggers = g_slist_prepend (aod->triggers, instance); + aod->n_triggers++; + } + + return TRUE; +} + +/* Generates the absolute triggers for a component */ +static void +generate_absolute_triggers (CalComponent *comp, struct alarm_occurrence_data *aod) +{ + GList *l; + CalComponentDateTime dt_start, dt_end; + + cal_component_get_dtstart (comp, &dt_start); + cal_component_get_dtend (comp, &dt_end); + + for (l = aod->alarm_uids; l; l = l->next) { + const char *auid; + CalComponentAlarm *alarm; + CalAlarmTrigger trigger; + time_t abs_time; + CalAlarmInstance *instance; + + auid = l->data; + alarm = cal_component_get_alarm (comp, auid); + g_assert (alarm != NULL); + + cal_component_alarm_get_trigger (alarm, &trigger); + cal_component_alarm_free (alarm); + + if (trigger.type != CAL_ALARM_TRIGGER_ABSOLUTE) + continue; + + abs_time = icaltime_as_timet (trigger.u.abs_time); + + if (abs_time < aod->start || abs_time >= aod->end) + continue; + + instance = g_new (CalAlarmInstance, 1); + instance->auid = auid; + instance->trigger = abs_time; + + /* No particular occurrence, so just use the times from the component */ + + if (dt_start.value) + instance->occur_start = icaltime_as_timet (*dt_start.value); + else + instance->occur_start = -1; + + if (dt_end.value) + instance->occur_end = icaltime_as_timet (*dt_end.value); + else + instance->occur_end = -1; + + aod->triggers = g_slist_prepend (aod->triggers, instance); + aod->n_triggers++; + } + + cal_component_free_datetime (&dt_start); + cal_component_free_datetime (&dt_end); +} + +/* Compares two alarm instances; called from g_slist_sort() */ +static gint +compare_alarm_instance (gconstpointer a, gconstpointer b) +{ + const CalAlarmInstance *aia, *aib; + + aia = a; + aib = b; + + if (aia->trigger < aib->trigger) + return -1; + else if (aia->trigger > aib->trigger) + return 1; + else + return 0; +} + +/** + * cal_util_generate_alarms_for_comp + * @comp: the CalComponent to generate alarms from + * @start: start time + * @end: end time + * @resolve_tzid: callback for resolving timezones + * @user_data: data to be passed to the resolve_tzid callback + * + * Generates alarm instances for a calendar component. Returns the instances + * structure, or NULL if no alarm instances occurred in the specified time + * range. + */ +CalComponentAlarms * +cal_util_generate_alarms_for_comp (CalComponent *comp, + time_t start, + time_t end, + CalRecurResolveTimezoneFn resolve_tzid, + gpointer user_data) +{ + GList *alarm_uids; + time_t alarm_start, alarm_end; + struct alarm_occurrence_data aod; + CalComponentAlarms *alarms; + + if (!cal_component_has_alarms (comp)) + return NULL; + + alarm_uids = cal_component_get_alarm_uids (comp); + compute_alarm_range (comp, alarm_uids, start, end, &alarm_start, &alarm_end); + + aod.alarm_uids = alarm_uids; + aod.start = start; + aod.end = end; + aod.triggers = NULL; + aod.n_triggers = 0; + + cal_recur_generate_instances (comp, alarm_start, alarm_end, + add_alarm_occurrences_cb, &aod, + resolve_tzid, user_data); + + /* We add the ABSOLUTE triggers separately */ + generate_absolute_triggers (comp, &aod); + + if (aod.n_triggers == 0) + return NULL; + + /* Create the component alarm instances structure */ + + alarms = g_new (CalComponentAlarms, 1); + alarms->comp = comp; + gtk_object_ref (GTK_OBJECT (alarms->comp)); + alarms->alarms = g_slist_sort (aod.triggers, compare_alarm_instance); + + return alarms; +} + +/** + * cal_util_generate_alarms_for_list + * @comps: list of CalComponent's + * @start: start time + * @end: end time + * @comp_alarms: list to be returned + * @resolve_tzid: callback for resolving timezones + * @user_data: data to be passed to the resolve_tzid callback + * + * Iterates through all the components in the comps list and generates alarm + * instances for them; putting them in the comp_alarms list. + * + * Returns: the number of elements it added to that list. + */ +int +cal_util_generate_alarms_for_list (GList *comps, + time_t start, + time_t end, + GSList **comp_alarms, + CalRecurResolveTimezoneFn resolve_tzid, + gpointer user_data) +{ + GList *l; + int n; + + n = 0; + + for (l = comps; l; l = l->next) { + CalComponent *comp; + CalComponentAlarms *alarms; + + comp = CAL_COMPONENT (l->data); + alarms = cal_util_generate_alarms_for_comp (comp, start, end, resolve_tzid, user_data); + + if (alarms) { + *comp_alarms = g_slist_prepend (*comp_alarms, alarms); + n++; + } + } + + return n; +} |