No issues found
1 /*
2 * Evolution calendar - Utilities for manipulating ECalComponent objects
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Authors:
19 * Federico Mena-Quintero <federico@ximian.com>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30 #include <time.h>
31 #include <libedataserverui/libedataserverui.h>
32
33 #include "calendar-config.h"
34 #include "comp-util.h"
35 #include "dialogs/delete-comp.h"
36 #include "e-util/e-categories-config.h"
37
38 #include "gnome-cal.h"
39 #include "shell/e-shell-window.h"
40 #include "shell/e-shell-view.h"
41
42 /**
43 * cal_comp_util_add_exdate:
44 * @comp: A calendar component object.
45 * @itt: Time for the exception.
46 *
47 * Adds an exception date to the current list of EXDATE properties in a calendar
48 * component object.
49 **/
50 void
51 cal_comp_util_add_exdate (ECalComponent *comp,
52 time_t t,
53 icaltimezone *zone)
54 {
55 GSList *list;
56 ECalComponentDateTime *cdt;
57
58 g_return_if_fail (comp != NULL);
59 g_return_if_fail (E_IS_CAL_COMPONENT (comp));
60
61 e_cal_component_get_exdate_list (comp, &list);
62
63 cdt = g_new (ECalComponentDateTime, 1);
64 cdt->value = g_new (struct icaltimetype, 1);
65 *cdt->value = icaltime_from_timet_with_zone (t, FALSE, zone);
66 cdt->tzid = g_strdup (icaltimezone_get_tzid (zone));
67
68 list = g_slist_append (list, cdt);
69 e_cal_component_set_exdate_list (comp, list);
70 e_cal_component_free_exdate_list (list);
71 }
72
73 /* Returns TRUE if the TZIDs are equivalent, i.e. both NULL or the same. */
74 static gboolean
75 e_cal_component_compare_tzid (const gchar *tzid1,
76 const gchar *tzid2)
77 {
78 gboolean retval = TRUE;
79
80 if (tzid1) {
81 if (!tzid2 || strcmp (tzid1, tzid2))
82 retval = FALSE;
83 } else {
84 if (tzid2)
85 retval = FALSE;
86 }
87
88 return retval;
89 }
90
91 /**
92 * cal_comp_util_compare_event_timezones:
93 * @comp: A calendar component object.
94 * @client: A #ECalClient.
95 *
96 * Checks if the component uses the given timezone for both the start and
97 * the end time, or if the UTC offsets of the start and end times are the same
98 * as in the given zone.
99 *
100 * Returns: TRUE if the component's start and end time are at the same UTC
101 * offset in the given timezone.
102 **/
103 gboolean
104 cal_comp_util_compare_event_timezones (ECalComponent *comp,
105 ECalClient *client,
106 icaltimezone *zone)
107 {
108 ECalComponentDateTime start_datetime, end_datetime;
109 const gchar *tzid;
110 gboolean retval = FALSE;
111 icaltimezone *start_zone, *end_zone;
112 gint offset1, offset2;
113
114 tzid = icaltimezone_get_tzid (zone);
115
116 e_cal_component_get_dtstart (comp, &start_datetime);
117 e_cal_component_get_dtend (comp, &end_datetime);
118
119 /* If either the DTSTART or the DTEND is a DATE value, we return TRUE.
120 * Maybe if one was a DATE-TIME we should check that, but that should
121 * not happen often. */
122 if ((start_datetime.value && start_datetime.value->is_date)
123 || (end_datetime.value && end_datetime.value->is_date)) {
124 retval = TRUE;
125 goto out;
126 }
127
128 /* If the event uses UTC for DTSTART & DTEND, return TRUE. Outlook
129 * will send single events as UTC, so we don't want to mark all of
130 * these. */
131 if ((!start_datetime.value || start_datetime.value->is_utc)
132 && (!end_datetime.value || end_datetime.value->is_utc)) {
133 retval = TRUE;
134 goto out;
135 }
136
137 /* If the event uses floating time for DTSTART & DTEND, return TRUE.
138 * Imported vCalendar files will use floating times, so we don't want
139 * to mark all of these. */
140 if (!start_datetime.tzid && !end_datetime.tzid) {
141 retval = TRUE;
142 goto out;
143 }
144
145 /* FIXME: DURATION may be used instead. */
146 if (e_cal_component_compare_tzid (tzid, start_datetime.tzid)
147 && e_cal_component_compare_tzid (tzid, end_datetime.tzid)) {
148 /* If both TZIDs are the same as the given zone's TZID, then
149 * we know the timezones are the same so we return TRUE. */
150 retval = TRUE;
151 } else {
152 /* If the TZIDs differ, we have to compare the UTC offsets
153 * of the start and end times, using their own timezones and
154 * the given timezone. */
155 if (!e_cal_client_get_timezone_sync (client, start_datetime.tzid,
156 &start_zone, NULL, NULL))
157 goto out;
158
159 if (start_datetime.value) {
160 offset1 = icaltimezone_get_utc_offset (
161 start_zone,
162 start_datetime.value,
163 NULL);
164 offset2 = icaltimezone_get_utc_offset (
165 zone,
166 start_datetime.value,
167 NULL);
168 if (offset1 != offset2)
169 goto out;
170 }
171
172 if (!e_cal_client_get_timezone_sync (client, end_datetime.tzid,
173 &end_zone, NULL, NULL))
174 goto out;
175
176 if (end_datetime.value) {
177 offset1 = icaltimezone_get_utc_offset (
178 end_zone,
179 end_datetime.value,
180 NULL);
181 offset2 = icaltimezone_get_utc_offset (
182 zone,
183 end_datetime.value,
184 NULL);
185 if (offset1 != offset2)
186 goto out;
187 }
188
189 retval = TRUE;
190 }
191
192 out:
193
194 e_cal_component_free_datetime (&start_datetime);
195 e_cal_component_free_datetime (&end_datetime);
196
197 return retval;
198 }
199
200 /**
201 * cal_comp_confirm_delete_empty_comp:
202 * @comp: A calendar component.
203 * @client: Calendar client where the component purportedly lives.
204 * @widget: Widget to be used as the basis for UTF8 conversion.
205 *
206 * Assumming a calendar component with an empty SUMMARY property (as per
207 * string_is_empty()), asks whether the user wants to delete it based on
208 * whether the appointment is on the calendar server or not. If the
209 * component is on the server, this function will present a confirmation
210 * dialog and delete the component if the user tells it to. If the component
211 * is not on the server it will just return TRUE.
212 *
213 * Return value: A result code indicating whether the component
214 * was not on the server and is to be deleted locally, whether it
215 * was on the server and the user deleted it, or whether the
216 * user cancelled the deletion.
217 **/
218 gboolean
219 cal_comp_is_on_server (ECalComponent *comp,
220 ECalClient *client)
221 {
222 const gchar *uid;
223 gchar *rid = NULL;
224 icalcomponent *icalcomp;
225 GError *error = NULL;
226
227 g_return_val_if_fail (comp != NULL, FALSE);
228 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
229 g_return_val_if_fail (client != NULL, FALSE);
230 g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
231
232 /* See if the component is on the server. If it is not, then it likely
233 * means that the appointment is new, only in the day view, and we
234 * haven't added it yet to the server. In that case, we don't need to
235 * confirm and we can just delete the event. Otherwise, we ask
236 * the user.
237 */
238 e_cal_component_get_uid (comp, &uid);
239
240 /* TODO We should not be checking for this here. But since
241 * e_cal_util_construct_instance does not create the instances
242 * of all day events, so we default to old behaviour. */
243 if (e_cal_client_check_recurrences_no_master (client)) {
244 rid = e_cal_component_get_recurid_as_string (comp);
245 }
246
247 if (e_cal_client_get_object_sync (client, uid, rid, &icalcomp, NULL, &error)) {
248 icalcomponent_free (icalcomp);
249 g_free (rid);
250
251 return TRUE;
252 }
253
254 if (!g_error_matches (error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND))
255 g_warning (G_STRLOC ": %s", error->message);
256
257 g_clear_error (&error);
258 g_free (rid);
259
260 return FALSE;
261 }
262
263 /**
264 * is_icalcomp_on_the_server:
265 * same as @cal_comp_is_on_server, only the component parameter is
266 * icalcomponent, not the ECalComponent.
267 **/
268 gboolean
269 is_icalcomp_on_the_server (icalcomponent *icalcomp,
270 ECalClient *client)
271 {
272 gboolean on_server;
273 ECalComponent *comp;
274
275 if (!icalcomp || !client || !icalcomponent_get_uid (icalcomp))
276 return FALSE;
277
278 comp = e_cal_component_new ();
279 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
280
281 on_server = cal_comp_is_on_server (comp, client);
282
283 g_object_unref (comp);
284
285 return on_server;
286 }
287
288 /**
289 * cal_comp_event_new_with_defaults:
290 *
291 * Creates a new VEVENT component and adds any default alarms to it as set in
292 * the program's configuration values, but only if not the all_day event.
293 *
294 * Return value: A newly-created calendar component.
295 **/
296 ECalComponent *
297 cal_comp_event_new_with_defaults (ECalClient *client,
298 gboolean all_day,
299 gboolean use_default_reminder,
300 gint default_reminder_interval,
301 EDurationType default_reminder_units)
302 {
303 icalcomponent *icalcomp;
304 ECalComponent *comp;
305 ECalComponentAlarm *alarm;
306 icalproperty *icalprop;
307 ECalComponentAlarmTrigger trigger;
308
309 if (!e_cal_client_get_default_object_sync (client, &icalcomp, NULL, NULL))
310 icalcomp = icalcomponent_new (ICAL_VEVENT_COMPONENT);
311
312 comp = e_cal_component_new ();
313 if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
314 icalcomponent_free (icalcomp);
315
316 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
317 }
318
319 if (all_day || !use_default_reminder)
320 return comp;
321
322 alarm = e_cal_component_alarm_new ();
323
324 /* We don't set the description of the alarm; we'll copy it from the
325 * summary when it gets committed to the server. For that, we add a
326 * X-EVOLUTION-NEEDS-DESCRIPTION property to the alarm's component.
327 */
328 icalcomp = e_cal_component_alarm_get_icalcomponent (alarm);
329 icalprop = icalproperty_new_x ("1");
330 icalproperty_set_x_name (icalprop, "X-EVOLUTION-NEEDS-DESCRIPTION");
331 icalcomponent_add_property (icalcomp, icalprop);
332
333 e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
334
335 trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
336
337 memset (&trigger.u.rel_duration, 0, sizeof (trigger.u.rel_duration));
338
339 trigger.u.rel_duration.is_neg = TRUE;
340
341 switch (default_reminder_units) {
342 case E_DURATION_MINUTES:
343 trigger.u.rel_duration.minutes = default_reminder_interval;
344 break;
345
346 case E_DURATION_HOURS:
347 trigger.u.rel_duration.hours = default_reminder_interval;
348 break;
349
350 case E_DURATION_DAYS:
351 trigger.u.rel_duration.days = default_reminder_interval;
352 break;
353
354 default:
355 g_warning ("wrong units %d\n", default_reminder_units);
356 }
357
358 e_cal_component_alarm_set_trigger (alarm, trigger);
359
360 e_cal_component_add_alarm (comp, alarm);
361 e_cal_component_alarm_free (alarm);
362
363 return comp;
364 }
365
366 ECalComponent *
367 cal_comp_event_new_with_current_time (ECalClient *client,
368 gboolean all_day,
369 icaltimezone *zone,
370 gboolean use_default_reminder,
371 gint default_reminder_interval,
372 EDurationType default_reminder_units)
373 {
374 ECalComponent *comp;
375 struct icaltimetype itt;
376 ECalComponentDateTime dt;
377
378 comp = cal_comp_event_new_with_defaults (
379 client, all_day, use_default_reminder,
380 default_reminder_interval, default_reminder_units);
381 g_return_val_if_fail (comp != NULL, NULL);
382
383 if (all_day) {
384 itt = icaltime_from_timet_with_zone (time (NULL), 1, zone);
385
386 dt.value = &itt;
387 dt.tzid = icaltimezone_get_tzid (zone);
388
389 e_cal_component_set_dtstart (comp, &dt);
390 e_cal_component_set_dtend (comp, &dt);
391 } else {
392 itt = icaltime_current_time_with_zone (zone);
393 icaltime_adjust (&itt, 0, 1, -itt.minute, -itt.second);
394
395 dt.value = &itt;
396 dt.tzid = icaltimezone_get_tzid (zone);
397
398 e_cal_component_set_dtstart (comp, &dt);
399 icaltime_adjust (&itt, 0, 1, 0, 0);
400 e_cal_component_set_dtend (comp, &dt);
401 }
402
403 return comp;
404 }
405
406 ECalComponent *
407 cal_comp_task_new_with_defaults (ECalClient *client)
408 {
409 ECalComponent *comp;
410 icalcomponent *icalcomp;
411
412 if (!e_cal_client_get_default_object_sync (client, &icalcomp, NULL, NULL))
413 icalcomp = icalcomponent_new (ICAL_VTODO_COMPONENT);
414
415 comp = e_cal_component_new ();
416 if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
417 icalcomponent_free (icalcomp);
418
419 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
420 }
421
422 return comp;
423 }
424
425 ECalComponent *
426 cal_comp_memo_new_with_defaults (ECalClient *client)
427 {
428 ECalComponent *comp;
429 icalcomponent *icalcomp;
430
431 if (!e_cal_client_get_default_object_sync (client, &icalcomp, NULL, NULL))
432 icalcomp = icalcomponent_new (ICAL_VJOURNAL_COMPONENT);
433
434 comp = e_cal_component_new ();
435 if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
436 icalcomponent_free (icalcomp);
437
438 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
439 }
440
441 return comp;
442 }
443
444 void
445 cal_comp_update_time_by_active_window (ECalComponent *comp,
446 EShell *shell)
447 {
448 GtkWindow *window;
449
450 g_return_if_fail (comp != NULL);
451 g_return_if_fail (shell != NULL);
452
453 window = e_shell_get_active_window (shell);
454
455 if (E_IS_SHELL_WINDOW (window)) {
456 EShellWindow *shell_window;
457 const gchar *active_view;
458
459 shell_window = E_SHELL_WINDOW (window);
460 active_view = e_shell_window_get_active_view (shell_window);
461
462 if (g_strcmp0 (active_view, "calendar") == 0) {
463 EShellContent *shell_content;
464 EShellView *shell_view;
465 GnomeCalendar *gnome_cal;
466 time_t start = 0, end = 0;
467 icaltimezone *zone;
468 struct icaltimetype itt;
469 icalcomponent *icalcomp;
470 icalproperty *prop;
471
472 shell_view = e_shell_window_peek_shell_view (
473 shell_window, "calendar");
474 g_return_if_fail (shell_view != NULL);
475
476 gnome_cal = NULL;
477 shell_content = e_shell_view_get_shell_content (shell_view);
478 g_object_get (shell_content, "calendar", &gnome_cal, NULL);
479 g_return_if_fail (gnome_cal != NULL);
480
481 gnome_calendar_get_current_time_range (gnome_cal, &start, &end);
482 g_return_if_fail (start != 0);
483
484 zone = e_cal_model_get_timezone (gnome_calendar_get_model (gnome_cal));
485 itt = icaltime_from_timet_with_zone (start, FALSE, zone);
486
487 icalcomp = e_cal_component_get_icalcomponent (comp);
488 prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY);
489 if (prop) {
490 icalproperty_set_dtstart (prop, itt);
491 } else {
492 prop = icalproperty_new_dtstart (itt);
493 icalcomponent_add_property (icalcomp, prop);
494 }
495
496 e_cal_component_rescan (comp);
497 }
498 }
499 }
500
501 /**
502 * cal_comp_util_get_n_icons:
503 * @comp: A calendar component object.
504 * @pixbufs: List of pixbufs to use. Can be NULL.
505 *
506 * Get the number of icons owned by the component.
507 * Each member of pixmaps should be freed with g_object_unref
508 * and the list itself should be freed too.
509 *
510 * Returns: the number of icons owned by the component.
511 **/
512 gint
513 cal_comp_util_get_n_icons (ECalComponent *comp,
514 GSList **pixbufs)
515 {
516 GSList *categories_list, *elem;
517 gint num_icons = 0;
518
519 g_return_val_if_fail (comp != NULL, 0);
520 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), 0);
521
522 e_cal_component_get_categories_list (comp, &categories_list);
523 for (elem = categories_list; elem; elem = elem->next) {
524 const gchar *category;
525 GdkPixbuf *pixbuf = NULL;
526
527 category = elem->data;
528 if (e_categories_config_get_icon_for (category, &pixbuf)) {
529 if (!pixbuf)
530 continue;
531
532 num_icons++;
533
534 if (pixbufs) {
535 *pixbufs = g_slist_append (*pixbufs, pixbuf);
536 } else {
537 g_object_unref (pixbuf);
538 }
539 }
540 }
541 e_cal_component_free_categories_list (categories_list);
542
543 return num_icons;
544 }
545
546 /**
547 * cal_comp_selection_set_string_list
548 * @data: Selection data, where to put list of strings.
549 * @str_list: List of strings. (Each element is of type const gchar *.)
550 *
551 * Stores list of strings into selection target data. Use
552 * cal_comp_selection_get_string_list() to get this list from target data.
553 **/
554 void
555 cal_comp_selection_set_string_list (GtkSelectionData *data,
556 GSList *str_list)
557 {
558 /* format is "str1\0str2\0...strN\0" */
559 GSList *p;
560 GByteArray *array;
561 GdkAtom target;
562
563 g_return_if_fail (data != NULL);
564
565 if (!str_list)
566 return;
567
568 array = g_byte_array_new ();
569 for (p = str_list; p; p = p->next) {
570 const guint8 *c = p->data;
571
572 if (c)
573 g_byte_array_append (array, c, strlen ((const gchar *) c) + 1);
574 }
575
576 target = gtk_selection_data_get_target (data);
577 gtk_selection_data_set (data, target, 8, array->data, array->len);
578 g_byte_array_free (array, TRUE);
579 }
580
581 /**
582 * cal_comp_selection_get_string_list
583 * @data: Selection data, where to put list of strings.
584 *
585 * Converts data from selection to list of strings. Data should be assigned
586 * to selection data with cal_comp_selection_set_string_list().
587 * Each string in newly created list should be freed by g_free().
588 * List itself should be freed by g_slist_free().
589 *
590 * Returns: Newly allocated #GSList of strings.
591 **/
592 GSList *
593 cal_comp_selection_get_string_list (GtkSelectionData *selection_data)
594 {
595 /* format is "str1\0str2\0...strN\0" */
596 gchar *inptr, *inend;
597 GSList *list;
598 const guchar *data;
599 gint length;
600
601 g_return_val_if_fail (selection_data != NULL, NULL);
602
603 data = gtk_selection_data_get_data (selection_data);
604 length = gtk_selection_data_get_length (selection_data);
605
606 list = NULL;
607 inptr = (gchar *) data;
608 inend = (gchar *) (data + length);
609
610 while (inptr < inend) {
611 gchar *start = inptr;
612
613 while (inptr < inend && *inptr)
614 inptr++;
615
616 list = g_slist_prepend (list, g_strndup (start, inptr - start));
617
618 inptr++;
619 }
620
621 return list;
622 }
623
624 static void
625 datetime_to_zone (ECalClient *client,
626 ECalComponentDateTime *date,
627 const gchar *tzid)
628 {
629 icaltimezone *from, *to;
630
631 g_return_if_fail (date != NULL);
632
633 if (date->tzid == NULL || tzid == NULL ||
634 date->tzid == tzid || g_str_equal (date->tzid, tzid))
635 return;
636
637 from = icaltimezone_get_builtin_timezone_from_tzid (date->tzid);
638 if (!from) {
639 GError *error = NULL;
640
641 e_cal_client_get_timezone_sync (
642 client, date->tzid, &from, NULL, &error);
643
644 if (error != NULL) {
645 g_warning (
646 "%s: Could not get timezone '%s' from server: %s",
647 G_STRFUNC, date->tzid ? date->tzid : "",
648 error->message);
649 g_error_free (error);
650 }
651 }
652
653 to = icaltimezone_get_builtin_timezone_from_tzid (tzid);
654 if (!to) {
655 /* do not check failure here, maybe the zone is not available there */
656 e_cal_client_get_timezone_sync (client, tzid, &to, NULL, NULL);
657 }
658
659 icaltimezone_convert_time (date->value, from, to);
660 date->tzid = tzid;
661 }
662
663 /**
664 * cal_comp_set_dtstart_with_oldzone:
665 * @client: ECalClient structure, to retrieve timezone from, when required.
666 * @comp: Component, where make the change.
667 * @pdate: Value, to change to.
668 *
669 * Changes 'dtstart' of the component, but converts time to the old timezone.
670 **/
671 void
672 cal_comp_set_dtstart_with_oldzone (ECalClient *client,
673 ECalComponent *comp,
674 const ECalComponentDateTime *pdate)
675 {
676 ECalComponentDateTime olddate, date;
677
678 g_return_if_fail (comp != NULL);
679 g_return_if_fail (pdate != NULL);
680
681 e_cal_component_get_dtstart (comp, &olddate);
682
683 date = *pdate;
684
685 datetime_to_zone (client, &date, olddate.tzid);
686 e_cal_component_set_dtstart (comp, &date);
687
688 e_cal_component_free_datetime (&olddate);
689 }
690
691 /**
692 * cal_comp_set_dtend_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.
696 *
697 * Changes 'dtend' of the component, but converts time to the old timezone.
698 **/
699 void
700 cal_comp_set_dtend_with_oldzone (ECalClient *client,
701 ECalComponent *comp,
702 const ECalComponentDateTime *pdate)
703 {
704 ECalComponentDateTime olddate, date;
705
706 g_return_if_fail (comp != NULL);
707 g_return_if_fail (pdate != NULL);
708
709 e_cal_component_get_dtend (comp, &olddate);
710
711 date = *pdate;
712
713 datetime_to_zone (client, &date, olddate.tzid);
714 e_cal_component_set_dtend (comp, &date);
715
716 e_cal_component_free_datetime (&olddate);
717 }
718
719 void
720 comp_util_sanitize_recurrence_master (ECalComponent *comp,
721 ECalClient *client)
722 {
723 ECalComponent *master = NULL;
724 icalcomponent *icalcomp = NULL;
725 ECalComponentRange rid;
726 ECalComponentDateTime sdt;
727 const gchar *uid;
728 GError *error = NULL;
729
730 /* Get the master component */
731 e_cal_component_get_uid (comp, &uid);
732
733 e_cal_client_get_object_sync (
734 client, uid, NULL, &icalcomp, NULL, &error);
735
736 if (error != NULL) {
737 g_warning (
738 "Unable to get the master component: %s",
739 error->message);
740 g_error_free (error);
741 return;
742 }
743
744 master = e_cal_component_new ();
745 if (!e_cal_component_set_icalcomponent (master, icalcomp)) {
746 icalcomponent_free (icalcomp);
747 g_object_unref (master);
748 g_return_if_reached ();
749 return;
750 }
751
752 /* Compare recur id and start date */
753 e_cal_component_get_recurid (comp, &rid);
754 e_cal_component_get_dtstart (comp, &sdt);
755
756 if (rid.datetime.value && sdt.value &&
757 icaltime_compare_date_only (
758 *rid.datetime.value, *sdt.value) == 0) {
759 ECalComponentDateTime msdt, medt, edt;
760 gint *sequence;
761
762 e_cal_component_get_dtstart (master, &msdt);
763 e_cal_component_get_dtend (master, &medt);
764
765 e_cal_component_get_dtend (comp, &edt);
766
767 g_return_if_fail (msdt.value != NULL);
768 g_return_if_fail (medt.value != NULL);
769 g_return_if_fail (edt.value != NULL);
770
771 sdt.value->year = msdt.value->year;
772 sdt.value->month = msdt.value->month;
773 sdt.value->day = msdt.value->day;
774
775 edt.value->year = medt.value->year;
776 edt.value->month = medt.value->month;
777 edt.value->day = medt.value->day;
778
779 e_cal_component_set_dtstart (comp, &sdt);
780 e_cal_component_set_dtend (comp, &edt);
781
782 e_cal_component_get_sequence (master, &sequence);
783 e_cal_component_set_sequence (comp, sequence);
784
785 e_cal_component_free_datetime (&msdt);
786 e_cal_component_free_datetime (&medt);
787 e_cal_component_free_datetime (&edt);
788 }
789
790 e_cal_component_free_datetime (&sdt);
791 e_cal_component_free_range (&rid);
792 e_cal_component_set_recurid (comp, NULL);
793
794 g_object_unref (master);
795 }
796
797 gchar *
798 icalcomp_suggest_filename (icalcomponent *icalcomp,
799 const gchar *default_name)
800 {
801 icalproperty *prop;
802 const gchar *summary = NULL;
803
804 if (!icalcomp)
805 return g_strconcat (default_name, ".ics", NULL);
806
807 prop = icalcomponent_get_first_property (icalcomp, ICAL_SUMMARY_PROPERTY);
808 if (prop)
809 summary = icalproperty_get_summary (prop);
810
811 if (!summary || !*summary)
812 summary = default_name;
813
814 return g_strconcat (summary, ".ics", NULL);
815 }