evolution-3.6.4/calendar/gui/comp-util.c

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 }