2 * Evolution calendar - Utilities for manipulating ECalComponent objects
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 * Federico Mena-Quintero <federico@ximian.com>
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
24 #include "evolution-config.h"
26 #include <glib/gi18n-lib.h>
31 #include "calendar-config.h"
32 #include "comp-util.h"
33 #include "e-calendar-view.h"
34 #include "itip-utils.h"
36 #include "shell/e-shell-window.h"
37 #include "shell/e-shell-view.h"
40 * cal_comp_util_add_exdate:
41 * @comp: A calendar component object.
42 * @itt: Time for the exception.
44 * Adds an exception date to the current list of EXDATE properties in a calendar
48 cal_comp_util_add_exdate (ECalComponent
*comp
,
53 ECalComponentDateTime
*cdt
;
55 g_return_if_fail (comp
!= NULL
);
56 g_return_if_fail (E_IS_CAL_COMPONENT (comp
));
58 e_cal_component_get_exdate_list (comp
, &list
);
60 cdt
= g_new (ECalComponentDateTime
, 1);
61 cdt
->value
= g_new (struct icaltimetype
, 1);
62 *cdt
->value
= icaltime_from_timet_with_zone (t
, FALSE
, zone
);
63 cdt
->tzid
= g_strdup (icaltimezone_get_tzid (zone
));
65 list
= g_slist_append (list
, cdt
);
66 e_cal_component_set_exdate_list (comp
, list
);
67 e_cal_component_free_exdate_list (list
);
70 /* Returns TRUE if the TZIDs are equivalent, i.e. both NULL or the same. */
72 e_cal_component_compare_tzid (const gchar
*tzid1
,
75 gboolean retval
= TRUE
;
78 if (!tzid2
|| strcmp (tzid1
, tzid2
))
89 * cal_comp_util_compare_event_timezones:
90 * @comp: A calendar component object.
91 * @client: A #ECalClient.
93 * Checks if the component uses the given timezone for both the start and
94 * the end time, or if the UTC offsets of the start and end times are the same
95 * as in the given zone.
97 * Returns: TRUE if the component's start and end time are at the same UTC
98 * offset in the given timezone.
101 cal_comp_util_compare_event_timezones (ECalComponent
*comp
,
105 ECalComponentDateTime start_datetime
, end_datetime
;
107 gboolean retval
= FALSE
;
108 icaltimezone
*start_zone
= NULL
;
109 icaltimezone
*end_zone
= NULL
;
110 gint offset1
, offset2
;
112 tzid
= icaltimezone_get_tzid (zone
);
114 e_cal_component_get_dtstart (comp
, &start_datetime
);
115 e_cal_component_get_dtend (comp
, &end_datetime
);
117 /* If either the DTSTART or the DTEND is a DATE value, we return TRUE.
118 * Maybe if one was a DATE-TIME we should check that, but that should
119 * not happen often. */
120 if ((start_datetime
.value
&& start_datetime
.value
->is_date
)
121 || (end_datetime
.value
&& end_datetime
.value
->is_date
)) {
126 /* If the event uses UTC for DTSTART & DTEND, return TRUE. Outlook
127 * will send single events as UTC, so we don't want to mark all of
129 if ((!start_datetime
.value
|| icaltime_is_utc (*start_datetime
.value
))
130 && (!end_datetime
.value
|| icaltime_is_utc (*end_datetime
.value
))) {
135 /* If the event uses floating time for DTSTART & DTEND, return TRUE.
136 * Imported vCalendar files will use floating times, so we don't want
137 * to mark all of these. */
138 if (!start_datetime
.tzid
&& !end_datetime
.tzid
) {
143 /* FIXME: DURATION may be used instead. */
144 if (e_cal_component_compare_tzid (tzid
, start_datetime
.tzid
)
145 && e_cal_component_compare_tzid (tzid
, end_datetime
.tzid
)) {
146 /* If both TZIDs are the same as the given zone's TZID, then
147 * we know the timezones are the same so we return TRUE. */
150 /* If the TZIDs differ, we have to compare the UTC offsets
151 * of the start and end times, using their own timezones and
152 * the given timezone. */
153 if (start_datetime
.tzid
)
154 e_cal_client_get_timezone_sync (client
, start_datetime
.tzid
, &start_zone
, NULL
, NULL
);
158 if (start_zone
== NULL
)
161 if (start_datetime
.value
) {
162 offset1
= icaltimezone_get_utc_offset (
164 start_datetime
.value
,
166 offset2
= icaltimezone_get_utc_offset (
168 start_datetime
.value
,
170 if (offset1
!= offset2
)
174 if (end_datetime
.tzid
)
175 e_cal_client_get_timezone_sync (client
, end_datetime
.tzid
, &end_zone
, NULL
, NULL
);
179 if (end_zone
== NULL
)
182 if (end_datetime
.value
) {
183 offset1
= icaltimezone_get_utc_offset (
187 offset2
= icaltimezone_get_utc_offset (
191 if (offset1
!= offset2
)
200 e_cal_component_free_datetime (&start_datetime
);
201 e_cal_component_free_datetime (&end_datetime
);
207 * cal_comp_is_on_server_sync:
208 * @comp: an #ECalComponent
209 * @client: an #ECalClient
210 * @cancellable: (allow none): a #GCancellable
211 * @error: (out): (allow none): a #GError
213 * Checks whether @client contains @comp. A "No Such Object" error is not
214 * propagated to the caller, any other errors are.
216 * Returns: #TRUE, when the @client contains @comp, #FALSE when not or on error.
217 * The @error is not set when the @client doesn't contain the @comp.
220 cal_comp_is_on_server_sync (ECalComponent
*comp
,
222 GCancellable
*cancellable
,
227 icalcomponent
*icalcomp
= NULL
;
228 GError
*local_error
= NULL
;
230 g_return_val_if_fail (comp
!= NULL
, FALSE
);
231 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp
), FALSE
);
232 g_return_val_if_fail (client
!= NULL
, FALSE
);
233 g_return_val_if_fail (E_IS_CAL_CLIENT (client
), FALSE
);
235 /* See if the component is on the server. If it is not, then it likely
236 * means that the appointment is new, only in the day view, and we
237 * haven't added it yet to the server. In that case, we don't need to
238 * confirm and we can just delete the event. Otherwise, we ask
241 e_cal_component_get_uid (comp
, &uid
);
243 /* TODO We should not be checking for this here. But since
244 * e_cal_util_construct_instance does not create the instances
245 * of all day events, so we default to old behaviour. */
246 if (e_cal_client_check_recurrences_no_master (client
)) {
247 rid
= e_cal_component_get_recurid_as_string (comp
);
250 if (e_cal_client_get_object_sync (client
, uid
, rid
, &icalcomp
, cancellable
, &local_error
) &&
252 icalcomponent_free (icalcomp
);
258 if (g_error_matches (local_error
, E_CAL_CLIENT_ERROR
, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND
))
259 g_clear_error (&local_error
);
261 g_propagate_error (error
, local_error
);
269 * cal_comp_is_icalcomp_on_server_sync:
270 * The same as cal_comp_is_on_server_sync(), only the component parameter is
271 * icalcomponent, not the ECalComponent.
274 cal_comp_is_icalcomp_on_server_sync (icalcomponent
*icalcomp
,
276 GCancellable
*cancellable
,
282 if (!icalcomp
|| !client
|| !icalcomponent_get_uid (icalcomp
))
285 comp
= e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp
));
289 on_server
= cal_comp_is_on_server_sync (comp
, client
, cancellable
, error
);
291 g_object_unref (comp
);
297 * cal_comp_event_new_with_defaults_sync:
299 * Creates a new VEVENT component and adds any default alarms to it as set in
300 * the program's configuration values, but only if not the all_day event.
302 * Return value: A newly-created calendar component.
305 cal_comp_event_new_with_defaults_sync (ECalClient
*client
,
307 gboolean use_default_reminder
,
308 gint default_reminder_interval
,
309 EDurationType default_reminder_units
,
310 GCancellable
*cancellable
,
313 icalcomponent
*icalcomp
= NULL
;
315 ECalComponentAlarm
*alarm
;
316 icalproperty
*icalprop
;
317 ECalComponentAlarmTrigger trigger
;
319 if (client
&& !e_cal_client_get_default_object_sync (client
, &icalcomp
, cancellable
, error
))
322 if (icalcomp
== NULL
)
323 icalcomp
= icalcomponent_new (ICAL_VEVENT_COMPONENT
);
325 comp
= e_cal_component_new ();
326 if (!e_cal_component_set_icalcomponent (comp
, icalcomp
)) {
327 icalcomponent_free (icalcomp
);
329 e_cal_component_set_new_vtype (comp
, E_CAL_COMPONENT_EVENT
);
332 if (all_day
|| !use_default_reminder
)
335 alarm
= e_cal_component_alarm_new ();
337 /* We don't set the description of the alarm; we'll copy it from the
338 * summary when it gets committed to the server. For that, we add a
339 * X-EVOLUTION-NEEDS-DESCRIPTION property to the alarm's component.
341 icalcomp
= e_cal_component_alarm_get_icalcomponent (alarm
);
342 icalprop
= icalproperty_new_x ("1");
343 icalproperty_set_x_name (icalprop
, "X-EVOLUTION-NEEDS-DESCRIPTION");
344 icalcomponent_add_property (icalcomp
, icalprop
);
346 e_cal_component_alarm_set_action (alarm
, E_CAL_COMPONENT_ALARM_DISPLAY
);
348 trigger
.type
= E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START
;
350 memset (&trigger
.u
.rel_duration
, 0, sizeof (trigger
.u
.rel_duration
));
352 trigger
.u
.rel_duration
.is_neg
= TRUE
;
354 switch (default_reminder_units
) {
355 case E_DURATION_MINUTES
:
356 trigger
.u
.rel_duration
.minutes
= default_reminder_interval
;
359 case E_DURATION_HOURS
:
360 trigger
.u
.rel_duration
.hours
= default_reminder_interval
;
363 case E_DURATION_DAYS
:
364 trigger
.u
.rel_duration
.days
= default_reminder_interval
;
368 g_warning ("wrong units %d\n", default_reminder_units
);
371 e_cal_component_alarm_set_trigger (alarm
, trigger
);
373 e_cal_component_add_alarm (comp
, alarm
);
374 e_cal_component_alarm_free (alarm
);
380 cal_comp_event_new_with_current_time_sync (ECalClient
*client
,
382 gboolean use_default_reminder
,
383 gint default_reminder_interval
,
384 EDurationType default_reminder_units
,
385 GCancellable
*cancellable
,
389 struct icaltimetype itt
;
390 ECalComponentDateTime dt
;
393 comp
= cal_comp_event_new_with_defaults_sync (
394 client
, all_day
, use_default_reminder
,
395 default_reminder_interval
, default_reminder_units
,
400 zone
= calendar_config_get_icaltimezone ();
403 itt
= icaltime_from_timet_with_zone (time (NULL
), 1, zone
);
406 dt
.tzid
= icaltimezone_get_tzid (zone
);
408 e_cal_component_set_dtstart (comp
, &dt
);
409 e_cal_component_set_dtend (comp
, &dt
);
411 itt
= icaltime_current_time_with_zone (zone
);
412 icaltime_adjust (&itt
, 0, 1, -itt
.minute
, -itt
.second
);
415 dt
.tzid
= icaltimezone_get_tzid (zone
);
417 e_cal_component_set_dtstart (comp
, &dt
);
418 icaltime_adjust (&itt
, 0, 1, 0, 0);
419 e_cal_component_set_dtend (comp
, &dt
);
426 cal_comp_task_new_with_defaults_sync (ECalClient
*client
,
427 GCancellable
*cancellable
,
431 icalcomponent
*icalcomp
= NULL
;
433 if (client
&& !e_cal_client_get_default_object_sync (client
, &icalcomp
, cancellable
, error
))
436 if (icalcomp
== NULL
)
437 icalcomp
= icalcomponent_new (ICAL_VTODO_COMPONENT
);
439 comp
= e_cal_component_new ();
440 if (!e_cal_component_set_icalcomponent (comp
, icalcomp
)) {
441 icalcomponent_free (icalcomp
);
443 e_cal_component_set_new_vtype (comp
, E_CAL_COMPONENT_TODO
);
450 cal_comp_memo_new_with_defaults_sync (ECalClient
*client
,
451 GCancellable
*cancellable
,
455 icalcomponent
*icalcomp
= NULL
;
457 if (client
&& !e_cal_client_get_default_object_sync (client
, &icalcomp
, cancellable
, error
))
460 if (icalcomp
== NULL
)
461 icalcomp
= icalcomponent_new (ICAL_VJOURNAL_COMPONENT
);
463 comp
= e_cal_component_new ();
464 if (!e_cal_component_set_icalcomponent (comp
, icalcomp
)) {
465 icalcomponent_free (icalcomp
);
467 e_cal_component_set_new_vtype (comp
, E_CAL_COMPONENT_JOURNAL
);
474 cal_comp_update_time_by_active_window (ECalComponent
*comp
,
479 g_return_if_fail (comp
!= NULL
);
480 g_return_if_fail (shell
!= NULL
);
482 window
= e_shell_get_active_window (shell
);
484 if (E_IS_SHELL_WINDOW (window
)) {
485 EShellWindow
*shell_window
;
486 const gchar
*active_view
;
488 shell_window
= E_SHELL_WINDOW (window
);
489 active_view
= e_shell_window_get_active_view (shell_window
);
491 if (g_strcmp0 (active_view
, "calendar") == 0) {
492 EShellContent
*shell_content
;
493 EShellView
*shell_view
;
494 ECalendarView
*cal_view
;
495 time_t start
= 0, end
= 0;
497 struct icaltimetype itt
;
498 icalcomponent
*icalcomp
;
501 shell_view
= e_shell_window_peek_shell_view (shell_window
, "calendar");
502 g_return_if_fail (shell_view
!= NULL
);
505 shell_content
= e_shell_view_get_shell_content (shell_view
);
506 g_object_get (shell_content
, "current-view", &cal_view
, NULL
);
507 g_return_if_fail (cal_view
!= NULL
);
508 g_return_if_fail (e_calendar_view_get_visible_time_range (cal_view
, &start
, &end
));
510 zone
= e_cal_model_get_timezone (e_calendar_view_get_model (cal_view
));
511 itt
= icaltime_from_timet_with_zone (start
, FALSE
, zone
);
513 icalcomp
= e_cal_component_get_icalcomponent (comp
);
514 prop
= icalcomponent_get_first_property (icalcomp
, ICAL_DTSTART_PROPERTY
);
516 icalproperty_set_dtstart (prop
, itt
);
518 prop
= icalproperty_new_dtstart (itt
);
519 icalcomponent_add_property (icalcomp
, prop
);
522 e_cal_component_rescan (comp
);
524 g_clear_object (&cal_view
);
530 * cal_comp_util_get_n_icons:
531 * @comp: A calendar component object.
532 * @pixbufs: List of pixbufs to use. Can be NULL.
534 * Get the number of icons owned by the component.
535 * Each member of pixmaps should be freed with g_object_unref
536 * and the list itself should be freed too.
538 * Returns: the number of icons owned by the component.
541 cal_comp_util_get_n_icons (ECalComponent
*comp
,
544 GSList
*categories_list
, *elem
;
547 g_return_val_if_fail (comp
!= NULL
, 0);
548 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp
), 0);
550 e_cal_component_get_categories_list (comp
, &categories_list
);
551 for (elem
= categories_list
; elem
; elem
= elem
->next
) {
552 const gchar
*category
;
553 GdkPixbuf
*pixbuf
= NULL
;
555 category
= elem
->data
;
556 if (e_categories_config_get_icon_for (category
, &pixbuf
)) {
563 *pixbufs
= g_slist_append (*pixbufs
, pixbuf
);
565 g_object_unref (pixbuf
);
569 e_cal_component_free_categories_list (categories_list
);
575 * cal_comp_selection_set_string_list
576 * @data: Selection data, where to put list of strings.
577 * @str_list: List of strings. (Each element is of type const gchar *.)
579 * Stores list of strings into selection target data. Use
580 * cal_comp_selection_get_string_list() to get this list from target data.
583 cal_comp_selection_set_string_list (GtkSelectionData
*data
,
586 /* format is "str1\0str2\0...strN\0" */
591 g_return_if_fail (data
!= NULL
);
596 array
= g_byte_array_new ();
597 for (p
= str_list
; p
; p
= p
->next
) {
598 const guint8
*c
= p
->data
;
601 g_byte_array_append (array
, c
, strlen ((const gchar
*) c
) + 1);
604 target
= gtk_selection_data_get_target (data
);
605 gtk_selection_data_set (data
, target
, 8, array
->data
, array
->len
);
606 g_byte_array_free (array
, TRUE
);
610 * cal_comp_selection_get_string_list
611 * @data: Selection data, where to put list of strings.
613 * Converts data from selection to list of strings. Data should be assigned
614 * to selection data with cal_comp_selection_set_string_list().
615 * Each string in newly created list should be freed by g_free().
616 * List itself should be freed by g_slist_free().
618 * Returns: Newly allocated #GSList of strings.
621 cal_comp_selection_get_string_list (GtkSelectionData
*selection_data
)
623 /* format is "str1\0str2\0...strN\0" */
624 gchar
*inptr
, *inend
;
629 g_return_val_if_fail (selection_data
!= NULL
, NULL
);
631 data
= gtk_selection_data_get_data (selection_data
);
632 length
= gtk_selection_data_get_length (selection_data
);
635 inptr
= (gchar
*) data
;
636 inend
= (gchar
*) (data
+ length
);
638 while (inptr
< inend
) {
639 gchar
*start
= inptr
;
641 while (inptr
< inend
&& *inptr
)
644 list
= g_slist_prepend (list
, g_strndup (start
, inptr
- start
));
653 datetime_to_zone (ECalClient
*client
,
654 ECalComponentDateTime
*date
,
657 icaltimezone
*from
, *to
;
659 g_return_if_fail (date
!= NULL
);
661 if (date
->tzid
== NULL
|| tzid
== NULL
||
662 date
->tzid
== tzid
|| g_str_equal (date
->tzid
, tzid
))
665 from
= icaltimezone_get_builtin_timezone_from_tzid (date
->tzid
);
667 GError
*error
= NULL
;
669 e_cal_client_get_timezone_sync (
670 client
, date
->tzid
, &from
, NULL
, &error
);
674 "%s: Could not get timezone '%s' from server: %s",
675 G_STRFUNC
, date
->tzid
? date
->tzid
: "",
677 g_error_free (error
);
681 to
= icaltimezone_get_builtin_timezone_from_tzid (tzid
);
683 /* do not check failure here, maybe the zone is not available there */
684 e_cal_client_get_timezone_sync (client
, tzid
, &to
, NULL
, NULL
);
687 icaltimezone_convert_time (date
->value
, from
, to
);
692 * cal_comp_set_dtstart_with_oldzone:
693 * @client: ECalClient structure, to retrieve timezone from, when required.
694 * @comp: Component, where make the change.
695 * @pdate: Value, to change to.
697 * Changes 'dtstart' of the component, but converts time to the old timezone.
700 cal_comp_set_dtstart_with_oldzone (ECalClient
*client
,
702 const ECalComponentDateTime
*pdate
)
704 ECalComponentDateTime olddate
, date
;
706 g_return_if_fail (comp
!= NULL
);
707 g_return_if_fail (pdate
!= NULL
);
709 e_cal_component_get_dtstart (comp
, &olddate
);
713 datetime_to_zone (client
, &date
, olddate
.tzid
);
714 e_cal_component_set_dtstart (comp
, &date
);
716 e_cal_component_free_datetime (&olddate
);
720 * cal_comp_set_dtend_with_oldzone:
721 * @client: ECalClient structure, to retrieve timezone from, when required.
722 * @comp: Component, where make the change.
723 * @pdate: Value, to change to.
725 * Changes 'dtend' of the component, but converts time to the old timezone.
728 cal_comp_set_dtend_with_oldzone (ECalClient
*client
,
730 const ECalComponentDateTime
*pdate
)
732 ECalComponentDateTime olddate
, date
;
734 g_return_if_fail (comp
!= NULL
);
735 g_return_if_fail (pdate
!= NULL
);
737 e_cal_component_get_dtend (comp
, &olddate
);
741 datetime_to_zone (client
, &date
, olddate
.tzid
);
742 e_cal_component_set_dtend (comp
, &date
);
744 e_cal_component_free_datetime (&olddate
);
748 comp_util_sanitize_recurrence_master_sync (ECalComponent
*comp
,
750 GCancellable
*cancellable
,
753 ECalComponent
*master
= NULL
;
754 icalcomponent
*icalcomp
= NULL
;
755 ECalComponentRange rid
;
756 ECalComponentDateTime sdt
;
759 /* Get the master component */
760 e_cal_component_get_uid (comp
, &uid
);
762 if (!e_cal_client_get_object_sync (client
, uid
, NULL
, &icalcomp
, cancellable
, error
))
765 master
= e_cal_component_new_from_icalcomponent (icalcomp
);
767 g_warn_if_reached ();
771 /* Compare recur id and start date */
772 e_cal_component_get_recurid (comp
, &rid
);
773 e_cal_component_get_dtstart (comp
, &sdt
);
775 if (rid
.datetime
.value
&& sdt
.value
&&
776 icaltime_compare_date_only (
777 *rid
.datetime
.value
, *sdt
.value
) == 0) {
778 ECalComponentDateTime msdt
, medt
, edt
;
781 e_cal_component_get_dtstart (master
, &msdt
);
782 e_cal_component_get_dtend (master
, &medt
);
784 e_cal_component_get_dtend (comp
, &edt
);
786 if (!msdt
.value
|| !medt
.value
|| !edt
.value
) {
787 g_warn_if_reached ();
788 e_cal_component_free_datetime (&msdt
);
789 e_cal_component_free_datetime (&medt
);
790 e_cal_component_free_datetime (&edt
);
791 e_cal_component_free_datetime (&sdt
);
792 e_cal_component_free_range (&rid
);
793 g_object_unref (master
);
797 sdt
.value
->year
= msdt
.value
->year
;
798 sdt
.value
->month
= msdt
.value
->month
;
799 sdt
.value
->day
= msdt
.value
->day
;
801 edt
.value
->year
= medt
.value
->year
;
802 edt
.value
->month
= medt
.value
->month
;
803 edt
.value
->day
= medt
.value
->day
;
805 e_cal_component_set_dtstart (comp
, &sdt
);
806 e_cal_component_set_dtend (comp
, &edt
);
808 e_cal_component_get_sequence (master
, &sequence
);
809 e_cal_component_set_sequence (comp
, sequence
);
811 e_cal_component_free_datetime (&msdt
);
812 e_cal_component_free_datetime (&medt
);
813 e_cal_component_free_datetime (&edt
);
816 e_cal_component_free_datetime (&sdt
);
817 e_cal_component_free_range (&rid
);
818 e_cal_component_set_recurid (comp
, NULL
);
820 g_object_unref (master
);
826 icalcomp_suggest_filename (icalcomponent
*icalcomp
,
827 const gchar
*default_name
)
830 const gchar
*summary
= NULL
;
833 return g_strconcat (default_name
, ".ics", NULL
);
835 prop
= icalcomponent_get_first_property (icalcomp
, ICAL_SUMMARY_PROPERTY
);
837 summary
= icalproperty_get_summary (prop
);
839 if (!summary
|| !*summary
)
840 summary
= default_name
;
842 return g_strconcat (summary
, ".ics", NULL
);
846 cal_comp_get_instance_times (ECalClient
*client
,
847 icalcomponent
*icalcomp
,
848 const icaltimezone
*default_zone
,
849 time_t *instance_start
,
850 gboolean
*start_is_date
,
851 time_t *instance_end
,
852 gboolean
*end_is_date
,
853 GCancellable
*cancellable
)
855 struct icaltimetype start_time
, end_time
;
856 const icaltimezone
*zone
= default_zone
;
858 g_return_if_fail (E_IS_CAL_CLIENT (client
));
859 g_return_if_fail (icalcomp
!= NULL
);
860 g_return_if_fail (instance_start
!= NULL
);
861 g_return_if_fail (instance_end
!= NULL
);
863 start_time
= icalcomponent_get_dtstart (icalcomp
);
864 end_time
= icalcomponent_get_dtend (icalcomp
);
866 /* Some event can have missing DTEND, then use the start_time for them */
867 if (icaltime_is_null_time (end_time
))
868 end_time
= start_time
;
870 if (start_time
.zone
) {
871 zone
= start_time
.zone
;
873 icalparameter
*param
= NULL
;
874 icalproperty
*prop
= icalcomponent_get_first_property (icalcomp
, ICAL_DTSTART_PROPERTY
);
877 param
= icalproperty_get_first_parameter (prop
, ICAL_TZID_PARAMETER
);
880 const gchar
*tzid
= NULL
;
881 icaltimezone
*st_zone
= NULL
;
883 tzid
= icalparameter_get_tzid (param
);
885 e_cal_client_get_timezone_sync (client
, tzid
, &st_zone
, cancellable
, NULL
);
893 *instance_start
= icaltime_as_timet_with_zone (start_time
, zone
);
895 *start_is_date
= start_time
.is_date
;
898 zone
= end_time
.zone
;
900 icalparameter
*param
= NULL
;
901 icalproperty
*prop
= icalcomponent_get_first_property (icalcomp
, ICAL_DTSTART_PROPERTY
);
904 param
= icalproperty_get_first_parameter (prop
, ICAL_TZID_PARAMETER
);
907 const gchar
*tzid
= NULL
;
908 icaltimezone
*end_zone
= NULL
;
910 tzid
= icalparameter_get_tzid (param
);
912 e_cal_client_get_timezone_sync (client
, tzid
, &end_zone
, cancellable
, NULL
);
920 *instance_end
= icaltime_as_timet_with_zone (end_time
, zone
);
922 *end_is_date
= end_time
.is_date
;
926 cal_comp_gdate_to_timet (const GDate
*date
,
927 const icaltimezone
*with_zone
)
930 struct icaltimetype tt
;
932 g_return_val_if_fail (date
!= NULL
, (time_t) -1);
933 g_return_val_if_fail (g_date_valid (date
), (time_t) -1);
935 g_date_to_struct_tm (date
, &tm
);
937 tt
= tm_to_icaltimetype (&tm
, TRUE
);
939 return icaltime_as_timet_with_zone (tt
, with_zone
);
941 return icaltime_as_timet (tt
);
944 typedef struct _AsyncContext
{
945 ECalClient
*src_client
;
946 icalcomponent
*icalcomp_clone
;
950 struct ForeachTzidData
952 ECalClient
*source_client
;
953 ECalClient
*destination_client
;
954 GCancellable
*cancellable
;
960 async_context_free (AsyncContext
*async_context
)
962 if (async_context
->src_client
)
963 g_object_unref (async_context
->src_client
);
965 if (async_context
->icalcomp_clone
)
966 icalcomponent_free (async_context
->icalcomp_clone
);
968 g_slice_free (AsyncContext
, async_context
);
972 add_timezone_to_cal_cb (icalparameter
*param
,
975 struct ForeachTzidData
*ftd
= data
;
976 icaltimezone
*tz
= NULL
;
979 g_return_if_fail (ftd
!= NULL
);
980 g_return_if_fail (ftd
->source_client
!= NULL
);
981 g_return_if_fail (ftd
->destination_client
!= NULL
);
986 if (ftd
->cancellable
&& g_cancellable_is_cancelled (ftd
->cancellable
)) {
987 ftd
->success
= FALSE
;
991 tzid
= icalparameter_get_tzid (param
);
995 if (e_cal_client_get_timezone_sync (ftd
->source_client
, tzid
, &tz
, ftd
->cancellable
, NULL
) && tz
)
996 ftd
->success
= e_cal_client_add_timezone_sync (
997 ftd
->destination_client
, tz
, ftd
->cancellable
, ftd
->error
);
1000 /* Helper for cal_comp_transfer_item_to() */
1002 cal_comp_transfer_item_to_thread (GSimpleAsyncResult
*simple
,
1003 GObject
*source_object
,
1004 GCancellable
*cancellable
)
1006 AsyncContext
*async_context
;
1007 GError
*local_error
= NULL
;
1009 async_context
= g_simple_async_result_get_op_res_gpointer (simple
);
1011 cal_comp_transfer_item_to_sync (
1012 async_context
->src_client
,
1013 E_CAL_CLIENT (source_object
),
1014 async_context
->icalcomp_clone
,
1015 async_context
->do_copy
,
1016 cancellable
, &local_error
);
1018 if (local_error
!= NULL
)
1019 g_simple_async_result_take_error (simple
, local_error
);
1023 cal_comp_transfer_item_to (ECalClient
*src_client
,
1024 ECalClient
*dest_client
,
1025 icalcomponent
*icalcomp_vcal
,
1027 GCancellable
*cancellable
,
1028 GAsyncReadyCallback callback
,
1031 GSimpleAsyncResult
*simple
;
1032 AsyncContext
*async_context
;
1034 g_return_if_fail (E_IS_CAL_CLIENT (src_client
));
1035 g_return_if_fail (E_IS_CAL_CLIENT (dest_client
));
1036 g_return_if_fail (icalcomp_vcal
!= NULL
);
1038 async_context
= g_slice_new0 (AsyncContext
);
1039 async_context
->src_client
= g_object_ref (src_client
);
1040 async_context
->icalcomp_clone
= icalcomponent_new_clone (icalcomp_vcal
);
1041 async_context
->do_copy
= do_copy
;
1043 simple
= g_simple_async_result_new (
1044 G_OBJECT (dest_client
), callback
, user_data
,
1045 cal_comp_transfer_item_to
);
1047 g_simple_async_result_set_check_cancellable (simple
, cancellable
);
1049 g_simple_async_result_set_op_res_gpointer (
1050 simple
, async_context
, (GDestroyNotify
) async_context_free
);
1052 g_simple_async_result_run_in_thread (
1053 simple
, cal_comp_transfer_item_to_thread
,
1054 G_PRIORITY_DEFAULT
, cancellable
);
1056 g_object_unref (simple
);
1060 cal_comp_transfer_item_to_finish (ECalClient
*client
,
1061 GAsyncResult
*result
,
1064 GSimpleAsyncResult
*simple
;
1066 g_return_val_if_fail (
1067 g_simple_async_result_is_valid (result
, G_OBJECT (client
), cal_comp_transfer_item_to
),
1070 simple
= G_SIMPLE_ASYNC_RESULT (result
);
1072 if (g_simple_async_result_propagate_error (simple
, error
))
1079 cal_comp_transfer_item_to_sync (ECalClient
*src_client
,
1080 ECalClient
*dest_client
,
1081 icalcomponent
*icalcomp_vcal
,
1083 GCancellable
*cancellable
,
1086 icalcomponent
*icalcomp
;
1087 icalcomponent
*icalcomp_event
, *subcomp
;
1088 icalcomponent_kind icalcomp_kind
;
1090 gchar
*new_uid
= NULL
;
1091 struct ForeachTzidData ftd
;
1092 ECalClientSourceType source_type
;
1093 GHashTable
*processed_uids
;
1094 gboolean same_client
;
1095 gboolean success
= FALSE
;
1097 g_return_val_if_fail (E_IS_CAL_CLIENT (src_client
), FALSE
);
1098 g_return_val_if_fail (E_IS_CAL_CLIENT (dest_client
), FALSE
);
1099 g_return_val_if_fail (icalcomp_vcal
!= NULL
, FALSE
);
1101 icalcomp_event
= icalcomponent_get_inner (icalcomp_vcal
);
1102 g_return_val_if_fail (icalcomp_event
!= NULL
, FALSE
);
1104 source_type
= e_cal_client_get_source_type (src_client
);
1105 switch (source_type
) {
1106 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS
:
1107 icalcomp_kind
= ICAL_VEVENT_COMPONENT
;
1109 case E_CAL_CLIENT_SOURCE_TYPE_TASKS
:
1110 icalcomp_kind
= ICAL_VTODO_COMPONENT
;
1112 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS
:
1113 icalcomp_kind
= ICAL_VJOURNAL_COMPONENT
;
1116 g_return_val_if_reached (FALSE
);
1119 same_client
= src_client
== dest_client
|| e_source_equal (
1120 e_client_get_source (E_CLIENT (src_client
)), e_client_get_source (E_CLIENT (dest_client
)));
1121 processed_uids
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
1123 icalcomp_event
= icalcomponent_get_first_component (icalcomp_vcal
, icalcomp_kind
);
1125 * This check should be removed in the near future.
1126 * We should be able to work properly with multiselection, which means that we always
1127 * will receive a component with subcomponents.
1129 if (icalcomp_event
== NULL
)
1130 icalcomp_event
= icalcomp_vcal
;
1133 icalcomp_event
= icalcomponent_get_next_component (icalcomp_vcal
, icalcomp_kind
)) {
1134 GError
*local_error
= NULL
;
1136 uid
= icalcomponent_get_uid (icalcomp_event
);
1138 if (g_hash_table_lookup (processed_uids
, uid
))
1141 if (do_copy
&& same_client
)
1144 success
= e_cal_client_get_object_sync (dest_client
, uid
, NULL
, &icalcomp
, cancellable
, &local_error
);
1146 success
= e_cal_client_modify_object_sync (
1147 dest_client
, icalcomp_event
, E_CAL_OBJ_MOD_ALL
, cancellable
, error
);
1149 icalcomponent_free (icalcomp
);
1154 ECalObjModType mod_type
= E_CAL_OBJ_MOD_THIS
;
1156 /* Remove the item from the source calendar. */
1157 if (e_cal_util_component_is_instance (icalcomp_event
) ||
1158 e_cal_util_component_has_recurrences (icalcomp_event
))
1159 mod_type
= E_CAL_OBJ_MOD_ALL
;
1161 success
= e_cal_client_remove_object_sync (
1162 src_client
, uid
, NULL
, mod_type
, cancellable
, error
);
1168 } else if (local_error
!= NULL
&& !g_error_matches (
1169 local_error
, E_CAL_CLIENT_ERROR
, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND
)) {
1170 g_propagate_error (error
, local_error
);
1173 g_clear_error (&local_error
);
1176 if (e_cal_util_component_is_instance (icalcomp_event
)) {
1177 GSList
*ecalcomps
= NULL
, *eiter
;
1178 ECalComponent
*comp
;
1180 success
= e_cal_client_get_objects_for_uid_sync (src_client
, uid
, &ecalcomps
, cancellable
, error
);
1184 if (ecalcomps
&& !ecalcomps
->next
) {
1185 /* only one component, no need for a vCalendar list */
1186 comp
= ecalcomps
->data
;
1187 icalcomp
= icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp
));
1189 icalcomp
= icalcomponent_new (ICAL_VCALENDAR_COMPONENT
);
1190 for (eiter
= ecalcomps
; eiter
; eiter
= g_slist_next (eiter
)) {
1193 icalcomponent_add_component (
1195 icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp
)));
1199 e_cal_client_free_ecalcomp_slist (ecalcomps
);
1201 icalcomp
= icalcomponent_new_clone (icalcomp_event
);
1205 /* Change the UID to avoid problems with duplicated UID */
1206 new_uid
= e_util_generate_uid ();
1207 if (icalcomponent_isa (icalcomp
) == ICAL_VCALENDAR_COMPONENT
) {
1208 /* in case of a vCalendar, the component might have detached instances,
1209 * thus change the UID on all of the subcomponents of it */
1210 for (subcomp
= icalcomponent_get_first_component (icalcomp
, icalcomp_kind
);
1212 subcomp
= icalcomponent_get_next_component (icalcomp
, icalcomp_kind
)) {
1213 icalcomponent_set_uid (subcomp
, new_uid
);
1216 icalcomponent_set_uid (icalcomp
, new_uid
);
1222 ftd
.source_client
= src_client
;
1223 ftd
.destination_client
= dest_client
;
1224 ftd
.cancellable
= cancellable
;
1228 if (icalcomponent_isa (icalcomp
) == ICAL_VCALENDAR_COMPONENT
) {
1229 /* in case of a vCalendar, the component might have detached instances,
1230 * thus check timezones on all of the subcomponents of it */
1231 for (subcomp
= icalcomponent_get_first_component (icalcomp
, icalcomp_kind
);
1232 subcomp
&& ftd
.success
;
1233 subcomp
= icalcomponent_get_next_component (icalcomp
, icalcomp_kind
)) {
1234 icalcomponent_foreach_tzid (subcomp
, add_timezone_to_cal_cb
, &ftd
);
1237 icalcomponent_foreach_tzid (icalcomp
, add_timezone_to_cal_cb
, &ftd
);
1245 if (icalcomponent_isa (icalcomp
) == ICAL_VCALENDAR_COMPONENT
) {
1246 gboolean did_add
= FALSE
;
1248 /* in case of a vCalendar, the component might have detached instances,
1249 * thus add the master object first, and then all of the subcomponents of it */
1250 for (subcomp
= icalcomponent_get_first_component (icalcomp
, icalcomp_kind
);
1251 subcomp
&& !did_add
;
1252 subcomp
= icalcomponent_get_next_component (icalcomp
, icalcomp_kind
)) {
1253 if (icaltime_is_null_time (icalcomponent_get_recurrenceid (subcomp
))) {
1255 success
= e_cal_client_create_object_sync (
1256 dest_client
, subcomp
,
1257 &new_uid
, cancellable
, error
);
1263 icalcomponent_free (icalcomp
);
1267 /* deal with detached instances */
1268 for (subcomp
= icalcomponent_get_first_component (icalcomp
, icalcomp_kind
);
1270 subcomp
= icalcomponent_get_next_component (icalcomp
, icalcomp_kind
)) {
1271 if (!icaltime_is_null_time (icalcomponent_get_recurrenceid (subcomp
))) {
1273 success
= e_cal_client_modify_object_sync (
1274 dest_client
, subcomp
,
1275 E_CAL_OBJ_MOD_THIS
, cancellable
, error
);
1277 /* just in case there are only detached instances and no master object */
1279 success
= e_cal_client_create_object_sync (
1280 dest_client
, subcomp
,
1281 &new_uid
, cancellable
, error
);
1287 success
= e_cal_client_create_object_sync (dest_client
, icalcomp
, &new_uid
, cancellable
, error
);
1291 icalcomponent_free (icalcomp
);
1296 ECalObjModType mod_type
= E_CAL_OBJ_MOD_THIS
;
1298 /* Remove the item from the source calendar. */
1299 if (e_cal_util_component_is_instance (icalcomp_event
) ||
1300 e_cal_util_component_has_recurrences (icalcomp_event
))
1301 mod_type
= E_CAL_OBJ_MOD_ALL
;
1303 success
= e_cal_client_remove_object_sync (src_client
, uid
, NULL
, mod_type
, cancellable
, error
);
1308 g_hash_table_insert (processed_uids
, g_strdup (uid
), GINT_TO_POINTER (1));
1312 g_hash_table_destroy (processed_uids
);
1318 cal_comp_util_update_tzid_parameter (icalproperty
*prop
,
1319 const struct icaltimetype tt
)
1321 icalparameter
*param
;
1322 const gchar
*tzid
= NULL
;
1324 g_return_if_fail (prop
!= NULL
);
1326 if (!icaltime_is_valid_time (tt
) ||
1327 icaltime_is_null_time (tt
))
1330 param
= icalproperty_get_first_parameter (prop
, ICAL_TZID_PARAMETER
);
1332 tzid
= icaltimezone_get_tzid ((icaltimezone
*) tt
.zone
);
1334 if (tt
.zone
&& tzid
&& *tzid
&& !icaltime_is_utc (tt
) && !tt
.is_date
) {
1336 icalparameter_set_tzid (param
, (gchar
*) tzid
);
1338 param
= icalparameter_new_tzid ((gchar
*) tzid
);
1339 icalproperty_add_parameter (prop
, param
);
1342 icalproperty_remove_parameter_by_kind (prop
, ICAL_TZID_PARAMETER
);
1346 /* Returns <0 for time before today, 0 for today, >0 for after today (future) */
1348 cal_comp_util_compare_time_with_today (const struct icaltimetype time_tt
)
1350 struct icaltimetype now_tt
;
1352 if (icaltime_is_null_time (time_tt
))
1355 if (time_tt
.is_date
) {
1356 now_tt
= icaltime_today ();
1357 return icaltime_compare_date_only (time_tt
, now_tt
);
1359 now_tt
= icaltime_current_time_with_zone (time_tt
.zone
);
1360 now_tt
.zone
= time_tt
.zone
;
1363 return icaltime_compare (time_tt
, now_tt
);
1366 /* Returns whether removed any */
1368 cal_comp_util_remove_all_properties (icalcomponent
*component
,
1369 icalproperty_kind kind
)
1372 gboolean removed_any
= FALSE
;
1374 g_return_val_if_fail (component
!= NULL
, FALSE
);
1376 while (prop
= icalcomponent_get_first_property (component
, kind
), prop
) {
1377 icalcomponent_remove_property (component
, prop
);
1378 icalproperty_free (prop
);
1387 cal_comp_util_have_in_new_attendees (const GSList
*new_attendees_mails
,
1395 for (link
= new_attendees_mails
; link
; link
= g_slist_next (link
)) {
1396 if (link
->data
&& g_ascii_strcasecmp (eml
, link
->data
) == 0)
1404 free_slist_strs (gpointer data
)
1409 g_slist_foreach (lst
, (GFunc
) g_free
, NULL
);
1415 * cal_comp_util_copy_new_attendees:
1416 * @des: Component, to copy to.
1417 * @src: Component, to copy from.
1419 * Copies "new-attendees" information from @src to @des component.
1422 cal_comp_util_copy_new_attendees (ECalComponent
*des
,
1425 GSList
*copy
= NULL
, *l
;
1427 g_return_if_fail (src
!= NULL
);
1428 g_return_if_fail (des
!= NULL
);
1430 for (l
= g_object_get_data (G_OBJECT (src
), "new-attendees"); l
; l
= l
->next
) {
1431 copy
= g_slist_append (copy
, g_strdup (l
->data
));
1434 g_object_set_data_full (G_OBJECT (des
), "new-attendees", copy
, free_slist_strs
);
1437 /* Takes ownership of the 'emails' */
1439 cal_comp_util_set_added_attendees_mails (ECalComponent
*comp
,
1442 g_return_if_fail (E_IS_CAL_COMPONENT (comp
));
1444 g_object_set_data_full (G_OBJECT (comp
), "new-attendees", emails
, free_slist_strs
);
1448 cal_comp_util_find_parameter_xvalue (icalproperty
*prop
,
1451 icalparameter
*param
;
1453 if (!prop
|| !name
|| !*name
)
1456 for (param
= icalproperty_get_first_parameter (prop
, ICAL_X_PARAMETER
);
1458 param
= icalproperty_get_next_parameter (prop
, ICAL_X_PARAMETER
)) {
1459 const gchar
*xname
= icalparameter_get_xname (param
);
1461 if (xname
&& g_ascii_strcasecmp (xname
, name
) == 0)
1462 return icalparameter_get_xvalue (param
);
1469 cal_comp_util_get_attendee_comments (icalcomponent
*icalcomp
)
1471 GString
*comments
= NULL
;
1474 g_return_val_if_fail (icalcomp
!= NULL
, NULL
);
1476 for (prop
= icalcomponent_get_first_property (icalcomp
, ICAL_ATTENDEE_PROPERTY
);
1478 prop
= icalcomponent_get_next_property (icalcomp
, ICAL_ATTENDEE_PROPERTY
)) {
1479 gchar
*guests_str
= NULL
;
1480 guint32 num_guests
= 0;
1483 value
= cal_comp_util_find_parameter_xvalue (prop
, "X-NUM-GUESTS");
1484 if (value
&& *value
)
1485 num_guests
= atoi (value
);
1487 value
= cal_comp_util_find_parameter_xvalue (prop
, "X-RESPONSE-COMMENT");
1490 guests_str
= g_strdup_printf (g_dngettext (GETTEXT_PACKAGE
, "with one guest", "with %d guests", num_guests
), num_guests
);
1492 if (guests_str
|| (value
&& *value
)) {
1493 const gchar
*email
= icalproperty_get_attendee (prop
);
1494 const gchar
*cn
= NULL
;
1495 icalparameter
*cnparam
;
1497 cnparam
= icalproperty_get_first_parameter (prop
, ICAL_CN_PARAMETER
);
1499 cn
= icalparameter_get_cn (cnparam
);
1504 email
= itip_strip_mailto (email
);
1506 if ((email
&& *email
) || (cn
&& *cn
)) {
1508 comments
= g_string_new ("");
1510 g_string_append (comments
, "\n ");
1513 g_string_append (comments
, cn
);
1515 if (g_strcmp0 (email
, cn
) == 0)
1519 if (email
&& *email
) {
1521 g_string_append_printf (comments
, " <%s>", email
);
1523 g_string_append (comments
, email
);
1526 g_string_append (comments
, ": ");
1529 g_string_append (comments
, guests_str
);
1531 if (value
&& *value
)
1532 g_string_append (comments
, "; ");
1535 if (value
&& *value
)
1536 g_string_append (comments
, value
);
1540 g_free (guests_str
);
1546 str
= g_strdup_printf (_("Comments: %s"), comments
->str
);
1547 g_string_free (comments
, TRUE
);