diff options
-rw-r--r-- | docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in | 4 | ||||
-rw-r--r-- | src/calendar/libecal/e-cal-component-alarm.c | 44 | ||||
-rw-r--r-- | src/calendar/libecal/e-cal-component-text.c | 181 | ||||
-rw-r--r-- | src/calendar/libecal/e-cal-component-text.h | 15 | ||||
-rw-r--r-- | src/calendar/libecal/e-cal-component.c | 205 | ||||
-rw-r--r-- | src/calendar/libecal/e-cal-component.h | 15 | ||||
-rw-r--r-- | src/calendar/libecal/e-cal-util.c | 126 | ||||
-rw-r--r-- | src/calendar/libecal/e-cal-util.h | 4 | ||||
-rw-r--r-- | tests/libecal/test-cal-component.c | 328 |
9 files changed, 834 insertions, 88 deletions
diff --git a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in index 30a95ea97..3cdabb3dd 100644 --- a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in +++ b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in @@ -362,6 +362,10 @@ <title>Index of deprecated symbols</title> <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include> </index> + <index id="api-index-3-46" role="3.46"> + <title>Index of new symbols in 3.46</title> + <xi:include href="xml/api-index-3.46.xml"><xi:fallback /></xi:include> + </index> <index id="api-index-3-44" role="3.44"> <title>Index of new symbols in 3.44</title> <xi:include href="xml/api-index-3.44.xml"><xi:fallback /></xi:include> diff --git a/src/calendar/libecal/e-cal-component-alarm.c b/src/calendar/libecal/e-cal-component-alarm.c index 6a1d0c073..1f146978c 100644 --- a/src/calendar/libecal/e-cal-component-alarm.c +++ b/src/calendar/libecal/e-cal-component-alarm.c @@ -309,27 +309,13 @@ e_cal_component_alarm_set_from_component (ECalComponentAlarm *alarm, break; case I_CAL_SUMMARY_PROPERTY: - if (i_cal_property_get_summary (prop)) { - ICalParameter *param; - - param = i_cal_property_get_first_parameter (prop, I_CAL_ALTREP_PARAMETER); - e_cal_component_alarm_take_summary (alarm, - e_cal_component_text_new (i_cal_property_get_summary (prop), - param ? i_cal_parameter_get_altrep (param) : NULL)); - g_clear_object (¶m); - } + if (i_cal_property_get_summary (prop)) + e_cal_component_alarm_take_summary (alarm, e_cal_component_text_new_from_property (prop)); break; case I_CAL_DESCRIPTION_PROPERTY: - if (i_cal_property_get_description (prop)) { - ICalParameter *param; - - param = i_cal_property_get_first_parameter (prop, I_CAL_ALTREP_PARAMETER); - e_cal_component_alarm_take_description (alarm, - e_cal_component_text_new (i_cal_property_get_description (prop), - param ? i_cal_parameter_get_altrep (param) : NULL)); - g_clear_object (¶m); - } + if (i_cal_property_get_description (prop)) + e_cal_component_alarm_take_description (alarm, e_cal_component_text_new_from_property (prop)); break; case I_CAL_DURATION_PROPERTY: @@ -524,16 +510,7 @@ e_cal_component_alarm_fill_component (ECalComponentAlarm *alarm, prop = i_cal_property_new_summary (e_cal_component_text_get_value (alarm->summary)); if (prop) { - const gchar *altrep = e_cal_component_text_get_altrep (alarm->summary); - - if (altrep && *altrep) { - ICalParameter *param; - - param = i_cal_parameter_new_altrep (altrep); - if (param) - i_cal_property_take_parameter (prop, param); - } - + e_cal_component_text_fill_property (alarm->summary, prop); i_cal_component_take_property (component, prop); } } @@ -542,16 +519,7 @@ e_cal_component_alarm_fill_component (ECalComponentAlarm *alarm, prop = i_cal_property_new_description (e_cal_component_text_get_value (alarm->description)); if (prop) { - const gchar *altrep = e_cal_component_text_get_altrep (alarm->description); - - if (altrep && *altrep) { - ICalParameter *param; - - param = i_cal_parameter_new_altrep (altrep); - if (param) - i_cal_property_take_parameter (prop, param); - } - + e_cal_component_text_fill_property (alarm->description, prop); i_cal_component_take_property (component, prop); } } diff --git a/src/calendar/libecal/e-cal-component-text.c b/src/calendar/libecal/e-cal-component-text.c index 332468604..c5006815c 100644 --- a/src/calendar/libecal/e-cal-component-text.c +++ b/src/calendar/libecal/e-cal-component-text.c @@ -33,6 +33,7 @@ G_DEFINE_BOXED_TYPE (ECalComponentText, e_cal_component_text, e_cal_component_te struct _ECalComponentText { gchar *value; gchar *altrep; + gchar *language; }; /** @@ -62,6 +63,31 @@ e_cal_component_text_new (const gchar *value, } /** + * e_cal_component_text_new_from_property: + * @property: an #ICalProperty + * + * Created a new #ECalComponentText filled with values from the @property. + * The @property should hold a text value. + * + * Returns: (transfer full): a newly allocated #ECalComponentText + * + * Since: 3.46 + **/ +ECalComponentText * +e_cal_component_text_new_from_property (const ICalProperty *property) +{ + ECalComponentText *text; + + g_return_val_if_fail (I_CAL_IS_PROPERTY (property), NULL); + + text = e_cal_component_text_new (NULL, NULL); + + e_cal_component_text_set_from_property (text, property); + + return text; +} + +/** * e_cal_component_text_copy: * @text: (not nullable): an #ECalComponentText to copy * @@ -74,9 +100,14 @@ e_cal_component_text_new (const gchar *value, ECalComponentText * e_cal_component_text_copy (const ECalComponentText *text) { + ECalComponentText *copy; + g_return_val_if_fail (text != NULL, NULL); - return e_cal_component_text_new (text->value, text->altrep); + copy = e_cal_component_text_new (text->value, text->altrep); + e_cal_component_text_set_language (copy, text->language); + + return copy; } /** @@ -96,11 +127,118 @@ e_cal_component_text_free (gpointer text) if (te) { g_free (te->value); g_free (te->altrep); + g_free (te->language); g_slice_free (ECalComponentText, te); } } /** + * e_cal_component_text_set_from_property: + * @text: an #ECalComponentText + * @property: an #ICalProperty + * + * Fill the @text structure with the information from the @property. + * The @property should hold a text value. + * + * Since: 3.46 + **/ +void +e_cal_component_text_set_from_property (ECalComponentText *text, + const ICalProperty *property) +{ + ICalValue *value; + ICalParameter *param; + + g_return_if_fail (text != NULL); + g_return_if_fail (I_CAL_IS_PROPERTY (property)); + + value = i_cal_property_get_value (property); + + if (value && i_cal_value_isa (value) == I_CAL_TEXT_VALUE) { + e_cal_component_text_set_value (text, i_cal_value_get_text (value)); + } else { + e_cal_component_text_set_value (text, NULL); + } + g_clear_object (&value); + + param = i_cal_property_get_first_parameter ((ICalProperty *) property, I_CAL_ALTREP_PARAMETER); + e_cal_component_text_set_altrep (text, param ? i_cal_parameter_get_altrep (param) : NULL); + g_clear_object (¶m); + + param = i_cal_property_get_first_parameter ((ICalProperty *) property, I_CAL_LANGUAGE_PARAMETER); + e_cal_component_text_set_language (text, param ? i_cal_parameter_get_language (param) : NULL); + g_clear_object (¶m); +} + +/** + * e_cal_component_text_fill_property: + * @text: an #ECalComponentText + * @property: an #ICalProperty + * + * Fills the @property with the content of the @text. + * + * Since: 3.46 + **/ +void +e_cal_component_text_fill_property (const ECalComponentText *text, + ICalProperty *property) +{ + ICalValue *value; + ICalParameter *param; + const gchar *str; + + g_return_if_fail (text != NULL); + g_return_if_fail (I_CAL_IS_PROPERTY (property)); + + str = e_cal_component_text_get_value (text) ? e_cal_component_text_get_value (text) : ""; + value = i_cal_property_get_value (property); + + if (value && i_cal_value_isa (value) == I_CAL_TEXT_VALUE) { + i_cal_value_set_text (value, str); + e_cal_component_text_get_value (text); + } else { + value = i_cal_value_new_text (str); + i_cal_property_set_value (property, value); + } + + g_clear_object (&value); + + str = e_cal_component_text_get_altrep (text); + param = i_cal_property_get_first_parameter (property, I_CAL_ALTREP_PARAMETER); + + if (str && *str) { + if (param) { + i_cal_parameter_set_altrep (param, str); + } else { + param = i_cal_parameter_new_altrep (str); + i_cal_property_take_parameter (property, param); + param = NULL; + } + } else if (param) { + i_cal_property_remove_parameter_by_kind (property, I_CAL_ALTREP_PARAMETER); + } + + g_clear_object (¶m); + + str = e_cal_component_text_get_language (text); + param = i_cal_property_get_first_parameter (property, I_CAL_LANGUAGE_PARAMETER); + + if (str && *str) { + if (param) { + i_cal_parameter_set_language (param, str); + } else { + param = i_cal_parameter_new_language (str); + i_cal_property_take_parameter (property, param); + param = NULL; + } + } else if (param) { + i_cal_property_remove_parameter_by_kind (property, I_CAL_LANGUAGE_PARAMETER); + } + + g_clear_object (¶m); +} + +/** * e_cal_component_text_get_value: * @text: an #ECalComponentText * @@ -173,3 +311,44 @@ e_cal_component_text_set_altrep (ECalComponentText *text, text->altrep = g_strdup (altrep); } } + +/** + * e_cal_component_text_get_language: + * @text: an #ECalComponentText + * + * Returns: the language of the @text + * + * Since: 3.46 + **/ +const gchar * +e_cal_component_text_get_language (const ECalComponentText *text) +{ + g_return_val_if_fail (text != NULL, NULL); + + return text->language; +} + +/** + * e_cal_component_text_set_language: + * @text: an #ECalComponentText + * @language: (nullable): language of the @text + * + * Set the @language as the language of the @text. The language tag + * is defined in RFC 5646. For example `en-US`, not `en_US`. + * + * Since: 3.46 + **/ +void +e_cal_component_text_set_language (ECalComponentText *text, + const gchar *language) +{ + g_return_if_fail (text != NULL); + + if (language && !*language) + language = NULL; + + if (g_strcmp0 (text->language, language) != 0) { + g_free (text->language); + text->language = g_strdup (language); + } +} diff --git a/src/calendar/libecal/e-cal-component-text.h b/src/calendar/libecal/e-cal-component-text.h index 44044f37e..4d229e87e 100644 --- a/src/calendar/libecal/e-cal-component-text.h +++ b/src/calendar/libecal/e-cal-component-text.h @@ -24,6 +24,7 @@ #define E_CAL_COMPONENT_TEXT_H #include <glib-object.h> +#include <libical-glib/libical-glib.h> G_BEGIN_DECLS @@ -40,14 +41,28 @@ ECalComponentText * e_cal_component_text_new (const gchar *value, const gchar *altrep); ECalComponentText * + e_cal_component_text_new_from_property + (const ICalProperty *property); +ECalComponentText * e_cal_component_text_copy (const ECalComponentText *text); void e_cal_component_text_free (gpointer text); /* ECalComponentText * */ +void e_cal_component_text_set_from_property + (ECalComponentText *text, + const ICalProperty *property); +void e_cal_component_text_fill_property + (const ECalComponentText *text, + ICalProperty *property); const gchar * e_cal_component_text_get_value (const ECalComponentText *text); void e_cal_component_text_set_value (ECalComponentText *text, const gchar *value); const gchar * e_cal_component_text_get_altrep (const ECalComponentText *text); void e_cal_component_text_set_altrep (ECalComponentText *text, const gchar *altrep); +const gchar * e_cal_component_text_get_language + (const ECalComponentText *text); +void e_cal_component_text_set_language + (ECalComponentText *text, + const gchar *language); G_END_DECLS diff --git a/src/calendar/libecal/e-cal-component.c b/src/calendar/libecal/e-cal-component.c index dd709d2f9..90a7eee5a 100644 --- a/src/calendar/libecal/e-cal-component.c +++ b/src/calendar/libecal/e-cal-component.c @@ -182,8 +182,7 @@ static ECalComponentText * get_text_from_prop (ICalProperty *prop, const gchar *(* get_prop_func) (ICalProperty *prop)) { - ICalParameter *altrep_param; - const gchar *value, *altrep; + const gchar *value; g_return_val_if_fail (prop != NULL, NULL); g_return_val_if_fail (get_prop_func != NULL, NULL); @@ -194,45 +193,27 @@ get_text_from_prop (ICalProperty *prop, if (!value || !*value) return NULL; - altrep_param = i_cal_property_get_first_parameter (prop, I_CAL_ALTREP_PARAMETER); - altrep = altrep_param ? i_cal_parameter_get_altrep (altrep_param) : NULL; - g_clear_object (&altrep_param); - - if (altrep && !*altrep) - altrep = NULL; - - return e_cal_component_text_new (value, altrep); + return e_cal_component_text_new_from_property (prop); } -static void -set_text_altrep_on_prop (ICalProperty *prop, - const ECalComponentText *text) +static ECalComponentText * +get_text_for_locale (ICalComponent *icalcomp, + ICalPropertyKind prop_kind, + const gchar *locale) { - ICalParameter *param; - const gchar *altrep; - - g_return_if_fail (prop != NULL); - g_return_if_fail (text != NULL); + ECalComponentText *result = NULL; + ICalProperty *prop; - altrep = e_cal_component_text_get_altrep (text); - param = i_cal_property_get_first_parameter (prop, I_CAL_ALTREP_PARAMETER); + prop = e_cal_util_component_find_property_for_locale (icalcomp, prop_kind, locale); - if (altrep && *altrep) { - if (param) { - i_cal_parameter_set_altrep (param, (gchar *) altrep); - } else { - param = i_cal_parameter_new_altrep ((gchar *) altrep); - i_cal_property_take_parameter (prop, param); - param = NULL; - } - } else if (param) { - i_cal_property_remove_parameter_by_kind (prop, I_CAL_ALTREP_PARAMETER); + if (prop) { + result = e_cal_component_text_new_from_property (prop); + g_clear_object (&prop); } - g_clear_object (¶m); + return result; } - static void cal_component_finalize (GObject *object) { @@ -1291,7 +1272,7 @@ set_text_list (ICalComponent *icalcomp, prop = new_prop_func ((gchar *) e_cal_component_text_get_value (text)); - set_text_altrep_on_prop (prop, text); + e_cal_component_text_fill_property (text, prop); i_cal_component_take_property (icalcomp, prop); } @@ -1343,6 +1324,33 @@ e_cal_component_set_comments (ECalComponent *comp, } /** + * e_cal_component_dup_comment_for_locale: + * @comp: A calendar component object. + * @locale: (nullable): a locale identifier, or %NULL + * + * Returns a comment for the given @locale. When @locale is %NULL, + * the current locale is assumed. If no such comment for the locale + * exists either a comment with no language parameter or the first + * found is returned. + * + * Free the returned non-NULL #ECalComponentText with e_cal_component_text_free(), + * when no longer needed. + * + * Returns: (transfer full) (nullable): comment for the @locale, %NULL + * if no comment is set on the @comp. + * + * Since: 3.46 + **/ +ECalComponentText * +e_cal_component_dup_comment_for_locale (ECalComponent *comp, + const gchar *locale) +{ + g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL); + + return get_text_for_locale (comp->priv->icalcomp, I_CAL_COMMENT_PROPERTY, locale); +} + +/** * e_cal_component_get_contacts: * @comp: A calendar component object. * @@ -1549,7 +1557,7 @@ e_cal_component_set_created (ECalComponent *comp, * Queries the description of a calendar component object. Journal components * may have more than one description, and as such this function returns a list * of #ECalComponentText structures. All other types of components can have at - * most one description. Free the returned #GSList with + * most one description for a single language. Free the returned #GSList with * g_slist_free_full (slist, e_cal_component_text_free);, when no longer needed. * * Returns: (transfer full) (element-type ECalComponentText) (nullable): the description @@ -1588,6 +1596,33 @@ e_cal_component_set_descriptions (ECalComponent *comp, set_text_list (comp->priv->icalcomp, I_CAL_DESCRIPTION_PROPERTY, i_cal_property_new_description, text_list); } +/** + * e_cal_component_dup_description_for_locale: + * @comp: A calendar component object. + * @locale: (nullable): a locale identifier, or %NULL + * + * Returns a description for the given @locale. When @locale is %NULL, + * the current locale is assumed. If no such description for the locale + * exists either a description with no language parameter or the first + * found is returned. + * + * Free the returned non-NULL #ECalComponentText with e_cal_component_text_free(), + * when no longer needed. + * + * Returns: (transfer full) (nullable): description for the @locale, %NULL + * if no description is set on the @comp. + * + * Since: 3.46 + **/ +ECalComponentText * +e_cal_component_dup_description_for_locale (ECalComponent *comp, + const gchar *locale) +{ + g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL); + + return get_text_for_locale (comp->priv->icalcomp, I_CAL_DESCRIPTION_PROPERTY, locale); +} + /* Gets a date/time and timezone pair */ static ECalComponentDateTime * get_datetime (ICalComponent *icalcomp, @@ -3446,7 +3481,10 @@ e_cal_component_set_status (ECalComponent *comp, * e_cal_component_get_summary: * @comp: A calendar component object. * - * Queries the summary of a calendar component object. + * Queries the summary of a calendar component object. It returns the first + * found summary property of the component. To get a summary suitable for a specific + * locale use e_cal_component_dup_summary_for_locale(). + * * Free the returned pointer withe_cal_component_text_free(), * when no longer needed. * @@ -3537,6 +3575,8 @@ set_alarm_description_cb (ICalComponent *icalcomp, * * Sets the summary of a calendar component object. * + * This also updates any alarm subcomponent descriptions, if needed. + * * Since: 3.34 **/ void @@ -3544,23 +3584,29 @@ e_cal_component_set_summary (ECalComponent *comp, const ECalComponentText *summary) { ICalProperty *prop; + GSList *to_remove, *link; SetAlarmDescriptionData sadd; g_return_if_fail (E_IS_CAL_COMPONENT (comp)); g_return_if_fail (comp->priv->icalcomp != NULL); - prop = i_cal_component_get_first_property (comp->priv->icalcomp, I_CAL_SUMMARY_PROPERTY); + to_remove = gather_all_properties (comp->priv->icalcomp, I_CAL_SUMMARY_PROPERTY, FALSE); + + prop = to_remove ? to_remove->data : NULL; if (!summary) { - if (prop) { + for (link = to_remove; link; link = g_slist_next (link)) { + prop = link->data; + i_cal_component_remove_property (comp->priv->icalcomp, prop); - g_object_unref (prop); } + g_slist_free_full (to_remove, g_object_unref); return; } if (!e_cal_component_text_get_value (summary)) { + g_slist_free_full (to_remove, g_object_unref); g_clear_object (&prop); return; } @@ -3569,16 +3615,22 @@ e_cal_component_set_summary (ECalComponent *comp, /* Make a copy, to avoid use-after-free */ sadd.old_summary = g_strdup (i_cal_property_get_summary (prop)); i_cal_property_set_summary (prop, (gchar *) e_cal_component_text_get_value (summary)); - set_text_altrep_on_prop (prop, summary); + e_cal_component_text_fill_property (summary, prop); } else { sadd.old_summary = NULL; prop = i_cal_property_new_summary ((gchar *) e_cal_component_text_get_value (summary)); - set_text_altrep_on_prop (prop, summary); + e_cal_component_text_fill_property (summary, prop); i_cal_component_take_property (comp->priv->icalcomp, prop); prop = NULL; } - g_clear_object (&prop); + /* Skip the first, because it had been re-used */ + for (link = to_remove ? to_remove->next : NULL; link; link = g_slist_next (link)) { + prop = link->data; + i_cal_component_remove_property (comp->priv->icalcomp, prop); + } + + g_slist_free_full (to_remove, g_object_unref); /* look for alarms that need a description */ sadd.new_summary = e_cal_component_text_get_value (summary); @@ -3589,6 +3641,77 @@ e_cal_component_set_summary (ECalComponent *comp, } /** + * e_cal_component_dup_summaries: + * @comp: A calendar component object. + * + * Queries the summary of a calendar component object. There can be one summary + * property per locale. Free the returned #GSList with + * g_slist_free_full (slist, e_cal_component_text_free);, when no longer needed. + * + * Returns: (transfer full) (element-type ECalComponentText) (nullable): the summary + * properties and their parameters, as a #GSList of #ECalComponentText structures. + * + * Since: 3.46 + **/ +GSList * +e_cal_component_dup_summaries (ECalComponent *comp) +{ + g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL); + g_return_val_if_fail (comp->priv->icalcomp != NULL, NULL); + + return get_text_list (comp->priv->icalcomp, I_CAL_SUMMARY_PROPERTY, i_cal_property_get_summary); +} + +/** + * e_cal_component_set_summaries: + * @comp: A calendar component object. + * @text_list: (element-type ECalComponentText): List of #ECalComponentText structures. + * + * Sets the summary of a calendar component object. The summaries can have each + * different language, otherwise it's not allowed to have more than one summary property. + * + * This does not update any alarm subcomponent description. + * + * Since: 3.46 + **/ +void +e_cal_component_set_summaries (ECalComponent *comp, + const GSList *text_list) +{ + g_return_if_fail (E_IS_CAL_COMPONENT (comp)); + g_return_if_fail (comp->priv->icalcomp != NULL); + + set_text_list (comp->priv->icalcomp, I_CAL_SUMMARY_PROPERTY, i_cal_property_new_summary, text_list); +} + +/** + * e_cal_component_dup_summary_for_locale: + * @comp: A calendar component object. + * @locale: (nullable): a locale identifier, or %NULL + * + * Returns a summary for the given @locale. When @locale is %NULL, + * the current locale is assumed. If no such summary for the locale + * exists either a summary with no language parameter or the first + * found is returned. + * + * Free the returned non-NULL #ECalComponentText with e_cal_component_text_free(), + * when no longer needed. + * + * Returns: (transfer full) (nullable): summary for the @locale, %NULL + * if no summary is set on the @comp. + * + * Since: 3.46 + **/ +ECalComponentText * +e_cal_component_dup_summary_for_locale (ECalComponent *comp, + const gchar *locale) +{ + g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL); + + return get_text_for_locale (comp->priv->icalcomp, I_CAL_SUMMARY_PROPERTY, locale); +} + +/** * e_cal_component_get_transparency: * @comp: A calendar component object. * diff --git a/src/calendar/libecal/e-cal-component.h b/src/calendar/libecal/e-cal-component.h index 2ef22ecea..2a8ad6160 100644 --- a/src/calendar/libecal/e-cal-component.h +++ b/src/calendar/libecal/e-cal-component.h @@ -131,6 +131,10 @@ void e_cal_component_set_classification GSList * e_cal_component_get_comments (ECalComponent *comp); /* ECalComponentText * */ void e_cal_component_set_comments (ECalComponent *comp, const GSList *text_list); /* ECalComponentText * */ +ECalComponentText * + e_cal_component_dup_comment_for_locale + (ECalComponent *comp, + const gchar *locale); ICalTime * e_cal_component_get_completed (ECalComponent *comp); void e_cal_component_set_completed (ECalComponent *comp, @@ -147,6 +151,10 @@ void e_cal_component_set_created (ECalComponent *comp, GSList * e_cal_component_get_descriptions(ECalComponent *comp); /* ECalComponentText * */ void e_cal_component_set_descriptions(ECalComponent *comp, const GSList *text_list); /* ECalComponentText * */ +ECalComponentText * + e_cal_component_dup_description_for_locale + (ECalComponent *comp, + const gchar *locale); ECalComponentDateTime * e_cal_component_get_dtend (ECalComponent *comp); @@ -244,6 +252,13 @@ ECalComponentText * e_cal_component_get_summary (ECalComponent *comp); void e_cal_component_set_summary (ECalComponent *comp, const ECalComponentText *summary); +GSList * e_cal_component_dup_summaries (ECalComponent *comp); +void e_cal_component_set_summaries (ECalComponent *comp, + const GSList *text_list); +ECalComponentText * + e_cal_component_dup_summary_for_locale + (ECalComponent *comp, + const gchar *locale); ECalComponentTransparency e_cal_component_get_transparency(ECalComponent *comp); diff --git a/src/calendar/libecal/e-cal-util.c b/src/calendar/libecal/e-cal-util.c index fed67ffae..6903069e4 100644 --- a/src/calendar/libecal/e-cal-util.c +++ b/src/calendar/libecal/e-cal-util.c @@ -3190,3 +3190,129 @@ e_cal_util_clamp_vtimezone_by_component (ICalComponent *vtimezone, g_clear_object (&dtstart); g_clear_object (&dtend); } + +static gboolean +locale_equals_language (const gchar *locale, + const gchar *language) +{ + guint ii; + + for (ii = 0; locale[ii] && language[ii]; ii++) { + if ((locale[ii] == '-' || locale[ii] == '_') && + (language[ii] == '-' || language[ii] == '_')) { + continue; + } + + if (g_ascii_tolower (locale[ii]) != g_ascii_tolower (language[ii])) + break; + } + + return !locale[ii] && !language[ii]; +} + +/** + * e_cal_util_component_find_property_for_locale: + * @icalcomp: an #ICalComponent + * @prop_kind: an #ICalPropertyKind to traverse + * @locale: (nullable): a locale identifier, or %NULL + * + * Searches properties of kind @prop_kind in the @icalcomp and returns + * one, which is usable for the @locale. When @locale is %NULL, + * the current locale is assumed. If no such property for the locale + * exists either the one with no language parameter or the first + * found is returned. + * + * Free the returned non-NULL #ICalProperty with g_object_unref(), + * when no longer needed. + * + * Returns: (transfer full) (nullable): a property of kind @prop_kind for the @locale, + * %NULL if no such property is set on the @comp. + * + * Since: 3.46 + + **/ +ICalProperty * +e_cal_util_component_find_property_for_locale (ICalComponent *icalcomp, + ICalPropertyKind prop_kind, + const gchar *locale) +{ + ICalProperty *prop; + ICalProperty *result = NULL; + ICalProperty *first = NULL; + ICalProperty *nolang = NULL; + ICalProperty *best = NULL; + gint best_index = -1; + gchar **locale_variants = NULL; + const gchar *const *locales = NULL; + + g_return_val_if_fail (I_CAL_IS_COMPONENT (icalcomp), NULL); + + if (locale) { + locale_variants = g_get_locale_variants (locale); + locales = (const gchar * const *) locale_variants; + } + + if (!locales) + locales = g_get_language_names (); + + for (prop = i_cal_component_get_first_property (icalcomp, prop_kind); + prop; + g_object_unref (prop), prop = i_cal_component_get_next_property (icalcomp, prop_kind)) { + ICalParameter *param; + + param = i_cal_property_get_first_parameter (prop, I_CAL_LANGUAGE_PARAMETER); + if (param) { + const gchar *language = i_cal_parameter_get_language (param); + + if (!language || !*language) { + if (!best) { + if (!first) + first = g_object_ref (prop); + if (!nolang) + nolang = g_object_ref (prop); + } + } else { + guint ii; + + for (ii = 0; locales && locales[ii] && (best_index == -1 || ii < best_index); ii++) { + if (locale_equals_language (locales[ii], language)) { + g_clear_object (&best); + best = g_object_ref (prop); + best_index = ii; + break; + } + } + + if (!ii && best) { + g_clear_object (¶m); + g_clear_object (&prop); + break; + } + + if (!best && !first) + first = g_object_ref (prop); + } + + g_clear_object (¶m); + } else if (!best) { + if (!first) + first = g_object_ref (prop); + if (!nolang) + nolang = g_object_ref (prop); + } + } + + if (best) + result = g_steal_pointer (&best); + else if (nolang) + result = g_steal_pointer (&nolang); + else if (first) + result = g_steal_pointer (&first); + + g_clear_object (&first); + g_clear_object (&nolang); + g_clear_object (&best); + g_clear_pointer (&locale_variants, g_strfreev); + + return result; +} diff --git a/src/calendar/libecal/e-cal-util.h b/src/calendar/libecal/e-cal-util.h index 540812263..bf79bf41d 100644 --- a/src/calendar/libecal/e-cal-util.h +++ b/src/calendar/libecal/e-cal-util.h @@ -378,6 +378,10 @@ void e_cal_util_clamp_vtimezone (ICalComponent *vtimezone, void e_cal_util_clamp_vtimezone_by_component (ICalComponent *vtimezone, ICalComponent *component); +ICalProperty * e_cal_util_component_find_property_for_locale + (ICalComponent *icalcomp, + ICalPropertyKind prop_kind, + const gchar *locale); G_END_DECLS diff --git a/tests/libecal/test-cal-component.c b/tests/libecal/test-cal-component.c index 2b8146662..0220d4f5b 100644 --- a/tests/libecal/test-cal-component.c +++ b/tests/libecal/test-cal-component.c @@ -348,6 +348,7 @@ verify_struct_text_equal (const ECalComponentText *expected, g_assert_cmpstr (e_cal_component_text_get_value (expected), ==, e_cal_component_text_get_value (received)); g_assert_cmpstr (e_cal_component_text_get_altrep (expected), ==, e_cal_component_text_get_altrep (received)); + g_assert_cmpstr (e_cal_component_text_get_language (expected), ==, e_cal_component_text_get_language (received)); } static void @@ -1524,7 +1525,7 @@ static void test_component_struct_parameter_bag (void) { const gchar *prop_str = - "ATTENDEE;CHARSET=utf-8;CN=User;CUTYPE=INDIVIDUAL;" X_PARAM_NAME "=" X_PARAM_VALUE ";LANGUAGE=en_US:mailto:user@no.where"; + "ATTENDEE;CHARSET=utf-8;CN=User;CUTYPE=INDIVIDUAL;" X_PARAM_NAME "=" X_PARAM_VALUE ";LANGUAGE=en-US:mailto:user@no.where"; ICalParameterKind expected_unfiltered[] = { I_CAL_CHARSET_PARAMETER, I_CAL_CN_PARAMETER, @@ -2078,17 +2079,24 @@ test_component_struct_text (void) struct _values { const gchar *value; const gchar *altrep; + const gchar *language; } values[] = { - { "value1", NULL }, - { "value2", "altrep1" }, - { "value3", "altrep2" }, - { "value4", NULL } + { "value1", NULL, NULL }, + { "value2", "altrep1", NULL }, + { "value3", "altrep2", NULL }, + { "value4", NULL, NULL }, + { "value1", NULL, "en_US" }, + { "value2", "altrep1", "en" }, + { "value3", "altrep2", "en_GB" }, + { "value4", NULL, "en" } }; + ICalProperty *prop; + ECalComponentText *expected, *received; gint ii, set_kind; for (set_kind = 0; set_kind < 3; set_kind++) { for (ii = 0; ii < G_N_ELEMENTS (values); ii++) { - ECalComponentText *expected = NULL, *received; + expected = NULL; if (set_kind == 1) { expected = e_cal_component_text_new ("non-empty", NULL); @@ -2099,13 +2107,16 @@ test_component_struct_text (void) if (expected) { e_cal_component_text_set_value (expected, values[ii].value); e_cal_component_text_set_altrep (expected, values[ii].altrep); + e_cal_component_text_set_language (expected, values[ii].language); } else { expected = e_cal_component_text_new (values[ii].value, values[ii].altrep); + e_cal_component_text_set_language (expected, values[ii].language); } g_assert_nonnull (expected); g_assert_cmpstr (e_cal_component_text_get_value (expected), ==, values[ii].value); g_assert_cmpstr (e_cal_component_text_get_altrep (expected), ==, values[ii].altrep); + g_assert_cmpstr (e_cal_component_text_get_language (expected), ==, values[ii].language); received = e_cal_component_text_copy (expected); verify_struct_text_equal (expected, received); @@ -2114,6 +2125,64 @@ test_component_struct_text (void) e_cal_component_text_free (expected); } } + + prop = i_cal_property_new_summary ("summary1"); + + expected = e_cal_component_text_new ("summary1", NULL); + + received = e_cal_component_text_new_from_property (prop); + g_assert_nonnull (received); + g_assert_cmpstr (e_cal_component_text_get_value (received), ==, "summary1"); + g_assert_null (e_cal_component_text_get_altrep (received)); + g_assert_null (e_cal_component_text_get_language (received)); + verify_struct_text_equal (expected, received); + + e_cal_component_text_set_value (expected, "summary2"); + e_cal_component_text_set_altrep (expected, "altrep"); + e_cal_component_text_fill_property (expected, prop); + + e_cal_component_text_set_from_property (received, prop); + g_assert_cmpstr (e_cal_component_text_get_value (received), ==, "summary2"); + g_assert_cmpstr (e_cal_component_text_get_altrep (received), ==, "altrep"); + g_assert_null (e_cal_component_text_get_language (received)); + verify_struct_text_equal (expected, received); + + e_cal_component_text_set_value (expected, "summary3"); + e_cal_component_text_set_altrep (expected, NULL); + e_cal_component_text_set_language (expected, "en"); + e_cal_component_text_fill_property (expected, prop); + + e_cal_component_text_set_from_property (received, prop); + g_assert_cmpstr (e_cal_component_text_get_value (received), ==, "summary3"); + g_assert_null (e_cal_component_text_get_altrep (received)); + g_assert_cmpstr (e_cal_component_text_get_language (received), ==, "en"); + verify_struct_text_equal (expected, received); + + e_cal_component_text_set_value (expected, "summary4"); + e_cal_component_text_set_altrep (expected, "altrep2"); + e_cal_component_text_set_language (expected, "en_GB"); + e_cal_component_text_fill_property (expected, prop); + + e_cal_component_text_set_from_property (received, prop); + g_assert_cmpstr (e_cal_component_text_get_value (received), ==, "summary4"); + g_assert_cmpstr (e_cal_component_text_get_altrep (received), ==, "altrep2"); + g_assert_cmpstr (e_cal_component_text_get_language (received), ==, "en_GB"); + verify_struct_text_equal (expected, received); + + e_cal_component_text_set_value (expected, "summary5"); + e_cal_component_text_set_altrep (expected, NULL); + e_cal_component_text_set_language (expected, NULL); + e_cal_component_text_fill_property (expected, prop); + + e_cal_component_text_set_from_property (received, prop); + g_assert_cmpstr (e_cal_component_text_get_value (received), ==, "summary5"); + g_assert_null (e_cal_component_text_get_altrep (received)); + g_assert_null (e_cal_component_text_get_language (received)); + verify_struct_text_equal (expected, received); + + e_cal_component_text_free (received); + e_cal_component_text_free (expected); + g_clear_object (&prop); } static void @@ -2364,16 +2433,30 @@ test_split_texts (const gchar *value) if (textsv) { for (ii = 0; textsv[ii]; ii++) { ECalComponentText *comptext; - gchar *altrep; + gchar *altrep, *language = NULL; altrep = strchr (textsv[ii], '|'); if (altrep) { *altrep = '\0'; altrep++; + + language = strchr (altrep, '|'); + if (language) { + *language = '\0'; + language++; + + if (!*language) + language = NULL; + } + + if (!*altrep) + altrep = NULL; } comptext = e_cal_component_text_new (textsv[ii], altrep); g_assert_nonnull (comptext); + if (language) + e_cal_component_text_set_language (comptext, language); result = g_slist_prepend (result, comptext); } @@ -2424,6 +2507,10 @@ test_component_text_list (void (* set_func) (ECalComponent *comp, "line1\nline2|altrep", "text|altrep", "text1:text2|altrep2:text3a\ntext3b|altrep3", + "text||en", + "line1\nline2|altrep|en_US", + "text|altrep|en_GB", + "text1:text2|altrep2|en_GB:text3a\ntext3b|altrep3|en", NULL }; ECalComponent *comp; @@ -2727,6 +2814,64 @@ test_component_comments (void) } static void +test_component_comments_locale (void) +{ + const gchar *comp_str = + "BEGIN:VEVENT\r\n" + "UID:1\r\n" + "COMMENT;LANGUAGE=en-US:desc-en-US\r\n" + "COMMENT;LANGUAGE=en:desc-en\r\n" + "COMMENT;LANGUAGE=en-GB:desc-en-GB\r\n" + "COMMENT:desc\r\n" + "END:VEVENT\r\n"; + ECalComponent *comp; + ECalComponentText *text; + + comp = e_cal_component_new_from_string (comp_str); + g_assert_nonnull (comp); + + text = e_cal_component_dup_comment_for_locale (comp, NULL); + g_assert_nonnull (text); + e_cal_component_text_free (text); + + text = e_cal_component_dup_comment_for_locale (comp, "en_US"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc-en-US"); + e_cal_component_text_free (text); + + text = e_cal_component_dup_comment_for_locale (comp, "en_GB"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc-en-GB"); + e_cal_component_text_free (text); + + text = e_cal_component_dup_comment_for_locale (comp, "en"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc-en"); + e_cal_component_text_free (text); + + text = e_cal_component_dup_comment_for_locale (comp, "xxx"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc"); + e_cal_component_text_free (text); + + g_clear_object (&comp); + + comp_str = + "BEGIN:VEVENT\r\n" + "UID:1\r\n" + "COMMENT;LANGUAGE=en-US:desc-en-US\r\n" + "COMMENT;LANGUAGE=en:desc-en\r\n" + "COMMENT;LANGUAGE=en-GB:desc-en-GB\r\n" + "END:VEVENT\r\n"; + + comp = e_cal_component_new_from_string (comp_str); + g_assert_nonnull (comp); + + text = e_cal_component_dup_comment_for_locale (comp, "xxx"); + g_assert_nonnull (text); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc-en-US"); + e_cal_component_text_free (text); + + g_clear_object (&comp); +} + +static void verify_component_completed (ECalComponent *comp, gpointer user_data) { @@ -2779,6 +2924,64 @@ test_component_descriptions (void) } static void +test_component_descriptions_locale (void) +{ + const gchar *comp_str = + "BEGIN:VEVENT\r\n" + "UID:1\r\n" + "DESCRIPTION;LANGUAGE=en-US:desc-en-US\r\n" + "DESCRIPTION;LANGUAGE=en:desc-en\r\n" + "DESCRIPTION;LANGUAGE=en-GB:desc-en-GB\r\n" + "DESCRIPTION:desc\r\n" + "END:VEVENT\r\n"; + ECalComponent *comp; + ECalComponentText *text; + + comp = e_cal_component_new_from_string (comp_str); + g_assert_nonnull (comp); + + text = e_cal_component_dup_description_for_locale (comp, NULL); + g_assert_nonnull (text); + e_cal_component_text_free (text); + + text = e_cal_component_dup_description_for_locale (comp, "en_US"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc-en-US"); + e_cal_component_text_free (text); + + text = e_cal_component_dup_description_for_locale (comp, "en_GB"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc-en-GB"); + e_cal_component_text_free (text); + + text = e_cal_component_dup_description_for_locale (comp, "en"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc-en"); + e_cal_component_text_free (text); + + text = e_cal_component_dup_description_for_locale (comp, "xxx"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc"); + e_cal_component_text_free (text); + + g_clear_object (&comp); + + comp_str = + "BEGIN:VEVENT\r\n" + "UID:1\r\n" + "DESCRIPTION;LANGUAGE=en-US:desc-en-US\r\n" + "DESCRIPTION;LANGUAGE=en:desc-en\r\n" + "DESCRIPTION;LANGUAGE=en-GB:desc-en-GB\r\n" + "END:VEVENT\r\n"; + + comp = e_cal_component_new_from_string (comp_str); + g_assert_nonnull (comp); + + text = e_cal_component_dup_description_for_locale (comp, "xxx"); + g_assert_nonnull (text); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "desc-en-US"); + e_cal_component_text_free (text); + + g_clear_object (&comp); +} + +static void verify_component_dtend (ECalComponent *comp, gpointer user_data) { @@ -3420,7 +3623,11 @@ test_component_summary (void) "line1\nline2|altrep", "text|altrep", NULL, - "text1:text2|altrep2:text3a\ntext3b|altrep3" + "text1:text2|altrep2:text3a\ntext3b|altrep3", + "text||en", + "line1\nline2|altrep|en_US", + "text|altrep|en_GB", + "text1:text2|altrep2|en:text3a\ntext3b|altrep3|en_US" }; ECalComponent *comp; gint ii; @@ -3450,6 +3657,107 @@ test_component_summary (void) } static void +test_component_summary_locale (void) +{ + const gchar *comp_str = + "BEGIN:VEVENT\r\n" + "UID:1\r\n" + "SUMMARY;LANGUAGE=en-US:summ-en-US\r\n" + "SUMMARY;LANGUAGE=en:summ-en\r\n" + "SUMMARY;LANGUAGE=en-GB:summ-en-GB\r\n" + "SUMMARY:summ\r\n" + "END:VEVENT\r\n"; + ECalComponent *comp; + ECalComponentText *text; + + comp = e_cal_component_new_from_string (comp_str); + g_assert_nonnull (comp); + + text = e_cal_component_dup_summary_for_locale (comp, NULL); + g_assert_nonnull (text); + e_cal_component_text_free (text); + + text = e_cal_component_dup_summary_for_locale (comp, "en_US"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "summ-en-US"); + e_cal_component_text_free (text); + + text = e_cal_component_dup_summary_for_locale (comp, "en_GB"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "summ-en-GB"); + e_cal_component_text_free (text); + + text = e_cal_component_dup_summary_for_locale (comp, "en"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "summ-en"); + e_cal_component_text_free (text); + + text = e_cal_component_dup_summary_for_locale (comp, "xxx"); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "summ"); + e_cal_component_text_free (text); + + g_clear_object (&comp); + + comp_str = + "BEGIN:VEVENT\r\n" + "UID:1\r\n" + "SUMMARY;LANGUAGE=en-US:summ-en-US\r\n" + "SUMMARY;LANGUAGE=en:summ-en\r\n" + "SUMMARY;LANGUAGE=en-GB:summ-en-GB\r\n" + "END:VEVENT\r\n"; + + comp = e_cal_component_new_from_string (comp_str); + g_assert_nonnull (comp); + + text = e_cal_component_dup_summary_for_locale (comp, "xxx"); + g_assert_nonnull (text); + g_assert_cmpstr (e_cal_component_text_get_value (text), ==, "summ-en-US"); + e_cal_component_text_free (text); + + g_clear_object (&comp); +} + +static void +verify_component_summaries (ECalComponent *comp, + gpointer user_data) +{ + verify_component_text_list (e_cal_component_dup_summaries, comp, user_data); +} + +static void +test_component_summaries (void) +{ + const gchar *comp_str = + "BEGIN:VEVENT\r\n" + "UID:1\r\n" + "SUMMARY;LANGUAGE=en-US:summ-en-US\r\n" + "SUMMARY;LANGUAGE=en:summ-en\r\n" + "SUMMARY;LANGUAGE=en-GB:summ-en-GB\r\n" + "SUMMARY:summ\r\n" + "END:VEVENT\r\n"; + ECalComponent *comp; + ECalComponentText *text; + GSList *slist1, *slist2; + + comp = e_cal_component_new_from_string (comp_str); + g_assert_nonnull (comp); + + slist1 = e_cal_component_dup_summaries (comp); + g_assert_cmpint (g_slist_length (slist1), ==, 4); + + text = e_cal_component_text_new ("summary", NULL); + e_cal_component_set_summary (comp, text); + slist2 = e_cal_component_dup_summaries (comp); + g_assert_cmpint (g_slist_length (slist2), ==, 1); + verify_struct_text_equal (text, slist2->data); + g_slist_free_full (slist2, e_cal_component_text_free); + e_cal_component_text_free (text); + + e_cal_component_set_summaries (comp, slist1); + verify_changes (comp, verify_component_summaries, slist1); + + g_slist_free_full (slist1, e_cal_component_text_free); + g_clear_object (&comp); +} + +static void verify_component_transparency (ECalComponent *comp, gpointer user_data) { @@ -4019,10 +4327,12 @@ main (gint argc, g_test_add_func ("/ECalComponent/categories", test_component_categories); g_test_add_func ("/ECalComponent/classification", test_component_classification); g_test_add_func ("/ECalComponent/comments", test_component_comments); + g_test_add_func ("/ECalComponent/comments-locale", test_component_comments_locale); g_test_add_func ("/ECalComponent/completed", test_component_completed); g_test_add_func ("/ECalComponent/contacts", test_component_contacts); g_test_add_func ("/ECalComponent/created", test_component_created); g_test_add_func ("/ECalComponent/descriptions", test_component_descriptions); + g_test_add_func ("/ECalComponent/descriptions-locale", test_component_descriptions_locale); g_test_add_func ("/ECalComponent/dtend", test_component_dtend); g_test_add_func ("/ECalComponent/dtstamp", test_component_dtstamp); g_test_add_func ("/ECalComponent/dtstart", test_component_dtstart); @@ -4040,6 +4350,8 @@ main (gint argc, g_test_add_func ("/ECalComponent/sequence", test_component_sequence); g_test_add_func ("/ECalComponent/status", test_component_status); g_test_add_func ("/ECalComponent/summary", test_component_summary); + g_test_add_func ("/ECalComponent/summaries", test_component_summaries); + g_test_add_func ("/ECalComponent/summary-locale", test_component_summary_locale); g_test_add_func ("/ECalComponent/transparency", test_component_transparency); g_test_add_func ("/ECalComponent/url", test_component_url); g_test_add_func ("/ECalComponent/attendees", test_component_attendees); |