1 /*
2  * This program is free software; you can redistribute it and/or modify it
3  * under the terms of the GNU Lesser General Public License as published by
4  * the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful, but
7  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
9  * for more details.
10  *
11  * You should have received a copy of the GNU Lesser General Public License
12  * along with this program; if not, see <http://www.gnu.org/licenses/>.
13  *
14  *
15  * Authors:
16  *		JP Rosevear <jpr@ximian.com>
17  *
18  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19  *
20  */
21 
22 #include "evolution-config.h"
23 
24 #include <stdlib.h>
25 #include <gtk/gtk.h>
26 #include "e-meeting-utils.h"
27 #include "e-meeting-attendee.h"
28 
29 #define E_MEETING_ATTENDEE_GET_PRIVATE(obj) \
30 	(G_TYPE_INSTANCE_GET_PRIVATE \
31 	((obj), E_TYPE_MEETING_ATTENDEE, EMeetingAttendeePrivate))
32 
33 struct _EMeetingAttendeePrivate {
34 	gchar *address;
35 	gchar *member;
36 	gchar *fburi;
37 
38 	ICalParameterCutype cutype;
39 	ICalParameterRole role;
40 
41 	gboolean rsvp;
42 
43 	gchar *delto;
44 	gchar *delfrom;
45 
46 	ICalParameterPartstat partstat;
47 
48 	gchar *sentby;
49 	gchar *cn;
50 	gchar *language;
51 
52 	ECalComponentParameterBag *parameter_bag;
53 
54 	EMeetingAttendeeEditLevel edit_level;
55 
56 	gboolean show_address;
57 	gboolean has_calendar_info;
58 
59 	GArray *busy_periods;
60 	gboolean busy_periods_sorted;
61 
62 	EMeetingTime busy_periods_start;
63 	EMeetingTime busy_periods_end;
64 	gboolean start_busy_range_set;
65 	gboolean end_busy_range_set;
66 
67 	gint longest_period_in_days;
68 };
69 
70 enum {
71 	CHANGED,
72 	LAST_SIGNAL
73 };
74 static guint signals[LAST_SIGNAL];
75 
76 static void e_meeting_attendee_finalize	(GObject *obj);
77 
G_DEFINE_TYPE(EMeetingAttendee,e_meeting_attendee,G_TYPE_OBJECT)78 G_DEFINE_TYPE (EMeetingAttendee, e_meeting_attendee, G_TYPE_OBJECT)
79 
80 static gchar *
81 string_test (const gchar *string)
82 {
83 	return g_strdup (string ? string : "");
84 }
85 
86 static gboolean
string_is_set(const gchar * string)87 string_is_set (const gchar *string)
88 {
89 	if (string != NULL && *string != '\0')
90 		return TRUE;
91 
92 	return FALSE;
93 }
94 
95 static void
busy_periods_array_clear_func(gpointer data)96 busy_periods_array_clear_func (gpointer data)
97 {
98 	EMeetingFreeBusyPeriod *period = (EMeetingFreeBusyPeriod *) data;
99 
100 	/* We're expected to clear the data segment,
101 	 * but not deallocate the segment itself. The
102 	 * XFB data possibly attached to the
103 	 * EMeetingFreeBusyPeriod requires special
104 	 * care when removing elements from the GArray
105 	 */
106 	e_meeting_xfb_data_clear (&(period->xfb));
107 }
108 
109 static void
notify_changed(EMeetingAttendee * ia)110 notify_changed (EMeetingAttendee *ia)
111 {
112 	g_signal_emit_by_name (ia, "changed");
113 }
114 
115 static void
set_string_value(EMeetingAttendee * ia,gchar ** member,const gchar * value)116 set_string_value (EMeetingAttendee *ia,
117 		  gchar **member,
118 		  const gchar *value)
119 {
120 	if (!string_is_set (*member) && !string_is_set (value))
121 		return;
122 
123 	if (g_strcmp0 (*member, value) == 0)
124 		return;
125 
126 	g_free (*member);
127 	*member = string_test (value);
128 
129 	notify_changed (ia);
130 }
131 
132 static void
e_meeting_attendee_finalize(GObject * object)133 e_meeting_attendee_finalize (GObject *object)
134 {
135 	EMeetingAttendee *ia;
136 
137 	ia = E_MEETING_ATTENDEE (object);
138 
139 	g_free (ia->priv->address);
140 	g_free (ia->priv->member);
141 	g_free (ia->priv->fburi);
142 
143 	g_free (ia->priv->delto);
144 	g_free (ia->priv->delfrom);
145 
146 	g_free (ia->priv->sentby);
147 	g_free (ia->priv->cn);
148 	g_free (ia->priv->language);
149 
150 	e_cal_component_parameter_bag_free (ia->priv->parameter_bag);
151 
152 	g_array_free (ia->priv->busy_periods, TRUE);
153 
154 	/* Chain up to parent's finalize() method. */
155 	G_OBJECT_CLASS (e_meeting_attendee_parent_class)->finalize (object);
156 }
157 
158 static void
e_meeting_attendee_class_init(EMeetingAttendeeClass * class)159 e_meeting_attendee_class_init (EMeetingAttendeeClass *class)
160 {
161 	GObjectClass *object_class;
162 
163 	g_type_class_add_private (class, sizeof (EMeetingAttendeePrivate));
164 
165 	object_class = G_OBJECT_CLASS (class);
166 	object_class->finalize = e_meeting_attendee_finalize;
167 
168 	signals[CHANGED] = g_signal_new (
169 		"changed",
170 		G_TYPE_FROM_CLASS (class),
171 		G_SIGNAL_RUN_FIRST,
172 		G_STRUCT_OFFSET (EMeetingAttendeeClass, changed),
173 		NULL, NULL,
174 		g_cclosure_marshal_VOID__VOID,
175 		G_TYPE_NONE, 0);
176 }
177 
178 static void
e_meeting_attendee_init(EMeetingAttendee * ia)179 e_meeting_attendee_init (EMeetingAttendee *ia)
180 {
181 	ia->priv = E_MEETING_ATTENDEE_GET_PRIVATE (ia);
182 
183 	ia->priv->address = string_test (NULL);
184 	ia->priv->member = string_test (NULL);
185 
186 	ia->priv->cutype = I_CAL_CUTYPE_NONE;
187 	ia->priv->role = I_CAL_ROLE_NONE;
188 
189 	ia->priv->rsvp = FALSE;
190 
191 	ia->priv->delto = string_test (NULL);
192 	ia->priv->delfrom = string_test (NULL);
193 
194 	ia->priv->partstat = I_CAL_PARTSTAT_NONE;
195 
196 	ia->priv->sentby = string_test (NULL);
197 	ia->priv->cn = string_test (NULL);
198 	ia->priv->language = string_test (NULL);
199 
200 	ia->priv->parameter_bag = e_cal_component_parameter_bag_new ();
201 
202 	ia->priv->edit_level = E_MEETING_ATTENDEE_EDIT_FULL;
203 	ia->priv->show_address = FALSE;
204 	ia->priv->has_calendar_info = FALSE;
205 
206 	ia->priv->busy_periods = g_array_new (FALSE, FALSE, sizeof (EMeetingFreeBusyPeriod));
207 	g_array_set_clear_func (ia->priv->busy_periods, busy_periods_array_clear_func);
208 	ia->priv->busy_periods_sorted = FALSE;
209 
210 	g_date_clear (&ia->priv->busy_periods_start.date, 1);
211 	ia->priv->busy_periods_start.hour = 0;
212 	ia->priv->busy_periods_start.minute = 0;
213 
214 	g_date_clear (&ia->priv->busy_periods_end.date, 1);
215 	ia->priv->busy_periods_end.hour = 0;
216 	ia->priv->busy_periods_end.minute = 0;
217 
218 	ia->priv->start_busy_range_set = FALSE;
219 	ia->priv->end_busy_range_set = FALSE;
220 
221 	ia->priv->longest_period_in_days = 0;
222 }
223 
224 GObject *
e_meeting_attendee_new(void)225 e_meeting_attendee_new (void)
226 {
227 	return g_object_new (E_TYPE_MEETING_ATTENDEE, NULL);
228 }
229 
230 GObject *
e_meeting_attendee_new_from_e_cal_component_attendee(const ECalComponentAttendee * ca)231 e_meeting_attendee_new_from_e_cal_component_attendee (const ECalComponentAttendee *ca)
232 {
233 	EMeetingAttendee *ia;
234 
235 	g_return_val_if_fail (ca != NULL, NULL);
236 
237 	ia = E_MEETING_ATTENDEE (g_object_new (E_TYPE_MEETING_ATTENDEE, NULL));
238 
239 	e_meeting_attendee_set_address (ia, e_cal_component_attendee_get_value (ca));
240 	e_meeting_attendee_set_member (ia, e_cal_component_attendee_get_member (ca));
241 	e_meeting_attendee_set_cutype (ia, e_cal_component_attendee_get_cutype (ca));
242 	e_meeting_attendee_set_role (ia, e_cal_component_attendee_get_role (ca));
243 	e_meeting_attendee_set_partstat (ia, e_cal_component_attendee_get_partstat (ca));
244 	e_meeting_attendee_set_rsvp (ia, e_cal_component_attendee_get_rsvp (ca));
245 	e_meeting_attendee_set_delto (ia, e_cal_component_attendee_get_delegatedto (ca));
246 	e_meeting_attendee_set_delfrom (ia, e_cal_component_attendee_get_delegatedfrom (ca));
247 	e_meeting_attendee_set_sentby (ia, e_cal_component_attendee_get_sentby (ca));
248 	e_meeting_attendee_set_cn (ia, e_cal_component_attendee_get_cn (ca));
249 	e_meeting_attendee_set_language (ia, e_cal_component_attendee_get_language (ca));
250 	e_cal_component_parameter_bag_assign (ia->priv->parameter_bag,
251 		e_cal_component_attendee_get_parameter_bag (ca));
252 
253 	return G_OBJECT (ia);
254 }
255 
256 ECalComponentAttendee *
e_meeting_attendee_as_e_cal_component_attendee(const EMeetingAttendee * ia)257 e_meeting_attendee_as_e_cal_component_attendee (const EMeetingAttendee *ia)
258 {
259 	ECalComponentAttendee *attendee;
260 
261 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
262 
263 	attendee = e_cal_component_attendee_new_full (
264 		ia->priv->address,
265 		string_is_set (ia->priv->member) ? ia->priv->member : NULL,
266 		ia->priv->cutype,
267 		ia->priv->role,
268 		ia->priv->partstat,
269 		ia->priv->rsvp,
270 		string_is_set (ia->priv->delfrom) ? ia->priv->delfrom : NULL,
271 		string_is_set (ia->priv->delto) ? ia->priv->delto : NULL,
272 		string_is_set (ia->priv->sentby) ? ia->priv->sentby : NULL,
273 		string_is_set (ia->priv->cn) ? ia->priv->cn : NULL,
274 		string_is_set (ia->priv->language) ? ia->priv->language : NULL);
275 
276 	e_cal_component_parameter_bag_assign (e_cal_component_attendee_get_parameter_bag (attendee),
277 		ia->priv->parameter_bag);
278 
279 	return attendee;
280 }
281 
282 const gchar *
e_meeting_attendee_get_fburi(const EMeetingAttendee * ia)283 e_meeting_attendee_get_fburi (const EMeetingAttendee *ia)
284 {
285 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
286 
287 	return ia->priv->fburi;
288 }
289 
290 void
e_meeting_attendee_set_fburi(EMeetingAttendee * ia,const gchar * fburi)291 e_meeting_attendee_set_fburi (EMeetingAttendee *ia,
292 			      const gchar *fburi)
293 {
294 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
295 
296 	set_string_value (ia, &ia->priv->fburi, fburi);
297 }
298 
299 const gchar *
e_meeting_attendee_get_address(const EMeetingAttendee * ia)300 e_meeting_attendee_get_address (const EMeetingAttendee *ia)
301 {
302 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
303 
304 	return ia->priv->address;
305 }
306 
307 void
e_meeting_attendee_set_address(EMeetingAttendee * ia,const gchar * address)308 e_meeting_attendee_set_address (EMeetingAttendee *ia,
309 				const gchar *address)
310 {
311 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
312 
313 	set_string_value (ia, &ia->priv->address, address);
314 }
315 
316 gboolean
e_meeting_attendee_is_set_address(const EMeetingAttendee * ia)317 e_meeting_attendee_is_set_address (const EMeetingAttendee *ia)
318 {
319 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
320 
321 	return string_is_set (ia->priv->address);
322 }
323 
324 const gchar *
e_meeting_attendee_get_member(const EMeetingAttendee * ia)325 e_meeting_attendee_get_member (const EMeetingAttendee *ia)
326 {
327 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
328 
329 	return ia->priv->member;
330 }
331 
332 void
e_meeting_attendee_set_member(EMeetingAttendee * ia,const gchar * member)333 e_meeting_attendee_set_member (EMeetingAttendee *ia,
334 			       const gchar *member)
335 {
336 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
337 
338 	set_string_value (ia, &ia->priv->member, member);
339 }
340 
341 gboolean
e_meeting_attendee_is_set_member(const EMeetingAttendee * ia)342 e_meeting_attendee_is_set_member (const EMeetingAttendee *ia)
343 {
344 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
345 
346 	return string_is_set (ia->priv->member);
347 }
348 
349 ICalParameterCutype
e_meeting_attendee_get_cutype(const EMeetingAttendee * ia)350 e_meeting_attendee_get_cutype (const EMeetingAttendee *ia)
351 {
352 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), I_CAL_CUTYPE_NONE);
353 
354 	return ia->priv->cutype;
355 }
356 
357 void
e_meeting_attendee_set_cutype(EMeetingAttendee * ia,ICalParameterCutype cutype)358 e_meeting_attendee_set_cutype (EMeetingAttendee *ia,
359 			       ICalParameterCutype cutype)
360 {
361 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
362 
363 	if (ia->priv->cutype != cutype) {
364 		ia->priv->cutype = cutype;
365 		notify_changed (ia);
366 	}
367 }
368 
369 ICalParameterRole
e_meeting_attendee_get_role(const EMeetingAttendee * ia)370 e_meeting_attendee_get_role (const EMeetingAttendee *ia)
371 {
372 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), I_CAL_ROLE_NONE);
373 
374 	return ia->priv->role;
375 }
376 
377 void
e_meeting_attendee_set_role(EMeetingAttendee * ia,ICalParameterRole role)378 e_meeting_attendee_set_role (EMeetingAttendee *ia,
379 			     ICalParameterRole role)
380 {
381 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
382 
383 	if (ia->priv->role != role) {
384 		ia->priv->role = role;
385 		notify_changed (ia);
386 	}
387 }
388 
389 gboolean
e_meeting_attendee_get_rsvp(const EMeetingAttendee * ia)390 e_meeting_attendee_get_rsvp (const EMeetingAttendee *ia)
391 {
392 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
393 
394 	return ia->priv->rsvp;
395 }
396 
397 void
e_meeting_attendee_set_rsvp(EMeetingAttendee * ia,gboolean rsvp)398 e_meeting_attendee_set_rsvp (EMeetingAttendee *ia,
399                              gboolean rsvp)
400 {
401 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
402 
403 	if ((ia->priv->rsvp ? 1 : 0) != (rsvp ? 1 : 0)) {
404 		ia->priv->rsvp = rsvp;
405 		notify_changed (ia);
406 	}
407 }
408 
409 const gchar *
e_meeting_attendee_get_delto(const EMeetingAttendee * ia)410 e_meeting_attendee_get_delto (const EMeetingAttendee *ia)
411 {
412 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
413 
414 	return ia->priv->delto;
415 }
416 
417 void
e_meeting_attendee_set_delto(EMeetingAttendee * ia,const gchar * delto)418 e_meeting_attendee_set_delto (EMeetingAttendee *ia,
419 			      const gchar *delto)
420 {
421 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
422 
423 	set_string_value (ia, &ia->priv->delto, delto);
424 }
425 
426 gboolean
e_meeting_attendee_is_set_delto(const EMeetingAttendee * ia)427 e_meeting_attendee_is_set_delto (const EMeetingAttendee *ia)
428 {
429 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
430 
431 	return string_is_set (ia->priv->delto);
432 }
433 
434 const gchar *
e_meeting_attendee_get_delfrom(const EMeetingAttendee * ia)435 e_meeting_attendee_get_delfrom (const EMeetingAttendee *ia)
436 {
437 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
438 
439 	return ia->priv->delfrom;
440 }
441 
442 void
e_meeting_attendee_set_delfrom(EMeetingAttendee * ia,const gchar * delfrom)443 e_meeting_attendee_set_delfrom (EMeetingAttendee *ia,
444 				const gchar *delfrom)
445 {
446 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
447 
448 	set_string_value (ia, &ia->priv->delfrom, delfrom);
449 }
450 
451 gboolean
e_meeting_attendee_is_set_delfrom(const EMeetingAttendee * ia)452 e_meeting_attendee_is_set_delfrom (const EMeetingAttendee *ia)
453 {
454 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
455 
456 	return string_is_set (ia->priv->delfrom);
457 }
458 
459 ICalParameterPartstat
e_meeting_attendee_get_partstat(const EMeetingAttendee * ia)460 e_meeting_attendee_get_partstat (const EMeetingAttendee *ia)
461 {
462 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), I_CAL_PARTSTAT_NONE);
463 
464 	return ia->priv->partstat;
465 }
466 
467 void
e_meeting_attendee_set_partstat(EMeetingAttendee * ia,ICalParameterPartstat partstat)468 e_meeting_attendee_set_partstat (EMeetingAttendee *ia,
469 				 ICalParameterPartstat partstat)
470 {
471 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
472 
473 	if (ia->priv->partstat != partstat) {
474 		ia->priv->partstat = partstat;
475 		notify_changed (ia);
476 	}
477 }
478 
479 const gchar *
e_meeting_attendee_get_sentby(const EMeetingAttendee * ia)480 e_meeting_attendee_get_sentby (const EMeetingAttendee *ia)
481 {
482 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
483 
484 	return ia->priv->sentby;
485 }
486 
487 void
e_meeting_attendee_set_sentby(EMeetingAttendee * ia,const gchar * sentby)488 e_meeting_attendee_set_sentby (EMeetingAttendee *ia,
489 			       const gchar *sentby)
490 {
491 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
492 
493 	set_string_value (ia, &ia->priv->sentby, sentby);
494 }
495 
496 gboolean
e_meeting_attendee_is_set_sentby(const EMeetingAttendee * ia)497 e_meeting_attendee_is_set_sentby (const EMeetingAttendee *ia)
498 {
499 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
500 
501 	return string_is_set (ia->priv->sentby);
502 }
503 
504 const gchar *
e_meeting_attendee_get_cn(const EMeetingAttendee * ia)505 e_meeting_attendee_get_cn (const EMeetingAttendee *ia)
506 {
507 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
508 
509 	return ia->priv->cn;
510 }
511 
512 void
e_meeting_attendee_set_cn(EMeetingAttendee * ia,const gchar * cn)513 e_meeting_attendee_set_cn (EMeetingAttendee *ia,
514 			   const gchar *cn)
515 {
516 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
517 
518 	set_string_value (ia, &ia->priv->cn, cn);
519 }
520 
521 gboolean
e_meeting_attendee_is_set_cn(const EMeetingAttendee * ia)522 e_meeting_attendee_is_set_cn (const EMeetingAttendee *ia)
523 {
524 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
525 
526 	return string_is_set (ia->priv->cn);
527 }
528 
529 const gchar *
e_meeting_attendee_get_language(const EMeetingAttendee * ia)530 e_meeting_attendee_get_language (const EMeetingAttendee *ia)
531 {
532 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
533 
534 	return ia->priv->language;
535 }
536 
537 void
e_meeting_attendee_set_language(EMeetingAttendee * ia,const gchar * language)538 e_meeting_attendee_set_language (EMeetingAttendee *ia,
539 				 const gchar *language)
540 {
541 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
542 
543 	set_string_value (ia, &ia->priv->language, language);
544 }
545 
546 gboolean
e_meeting_attendee_is_set_language(const EMeetingAttendee * ia)547 e_meeting_attendee_is_set_language (const EMeetingAttendee *ia)
548 {
549 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
550 
551 	return string_is_set (ia->priv->language);
552 }
553 
554 ECalComponentParameterBag *
e_meeting_attendee_get_parameter_bag(const EMeetingAttendee * ia)555 e_meeting_attendee_get_parameter_bag (const EMeetingAttendee *ia)
556 {
557 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
558 
559 	return ia->priv->parameter_bag;
560 }
561 
562 EMeetingAttendeeType
e_meeting_attendee_get_atype(const EMeetingAttendee * ia)563 e_meeting_attendee_get_atype (const EMeetingAttendee *ia)
564 {
565 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), E_MEETING_ATTENDEE_RESOURCE_UNKNOWN);
566 
567 	if (ia->priv->cutype == I_CAL_CUTYPE_ROOM ||
568 	    ia->priv->cutype == I_CAL_CUTYPE_RESOURCE)
569 		return E_MEETING_ATTENDEE_RESOURCE;
570 
571 	if (ia->priv->role == I_CAL_ROLE_CHAIR ||
572 	    ia->priv->role == I_CAL_ROLE_REQPARTICIPANT)
573 		return E_MEETING_ATTENDEE_REQUIRED_PERSON;
574 
575 	return E_MEETING_ATTENDEE_OPTIONAL_PERSON;
576 }
577 
578 EMeetingAttendeeEditLevel
e_meeting_attendee_get_edit_level(const EMeetingAttendee * ia)579 e_meeting_attendee_get_edit_level (const EMeetingAttendee *ia)
580 {
581 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), E_MEETING_ATTENDEE_EDIT_NONE);
582 
583 	return ia->priv->edit_level;
584 }
585 
586 void
e_meeting_attendee_set_edit_level(EMeetingAttendee * ia,EMeetingAttendeeEditLevel level)587 e_meeting_attendee_set_edit_level (EMeetingAttendee *ia,
588                                    EMeetingAttendeeEditLevel level)
589 {
590 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
591 
592 	ia->priv->edit_level = level;
593 }
594 
595 static gint
compare_times(EMeetingTime * time1,EMeetingTime * time2)596 compare_times (EMeetingTime *time1,
597                EMeetingTime *time2)
598 {
599 	gint day_comparison;
600 
601 	day_comparison = g_date_compare (
602 		&time1->date,
603 		&time2->date);
604 	if (day_comparison != 0)
605 		return day_comparison;
606 
607 	if (time1->hour < time2->hour)
608 		return -1;
609 	if (time1->hour > time2->hour)
610 		return 1;
611 
612 	if (time1->minute < time2->minute)
613 		return -1;
614 	if (time1->minute > time2->minute)
615 		return 1;
616 
617 	/* The start times are exactly the same. */
618 	return 0;
619 }
620 
621 static gint
compare_period_starts(gconstpointer arg1,gconstpointer arg2)622 compare_period_starts (gconstpointer arg1,
623                        gconstpointer arg2)
624 {
625 	EMeetingFreeBusyPeriod *period1, *period2;
626 
627 	period1 = (EMeetingFreeBusyPeriod *) arg1;
628 	period2 = (EMeetingFreeBusyPeriod *) arg2;
629 
630 	return compare_times (&period1->start, &period2->start);
631 }
632 
633 static void
ensure_periods_sorted(EMeetingAttendee * ia)634 ensure_periods_sorted (EMeetingAttendee *ia)
635 {
636 	if (ia->priv->busy_periods_sorted)
637 		return;
638 
639 	qsort (
640 		ia->priv->busy_periods->data, ia->priv->busy_periods->len,
641 		sizeof (EMeetingFreeBusyPeriod),
642 		compare_period_starts);
643 
644 	ia->priv->busy_periods_sorted = TRUE;
645 }
646 
647 gboolean
e_meeting_attendee_get_show_address(const EMeetingAttendee * ia)648 e_meeting_attendee_get_show_address (const EMeetingAttendee *ia)
649 {
650 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
651 
652 	return ia->priv->show_address;
653 }
654 
655 void
e_meeting_attendee_set_show_address(EMeetingAttendee * ia,gboolean show_address)656 e_meeting_attendee_set_show_address (EMeetingAttendee *ia,
657 				     gboolean show_address)
658 {
659 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
660 
661 	if ((ia->priv->show_address ? 1 : 0) == (show_address ? 1 : 0))
662 		return;
663 
664 	ia->priv->show_address = show_address;
665 
666 	notify_changed (ia);
667 }
668 
669 gboolean
e_meeting_attendee_get_has_calendar_info(const EMeetingAttendee * ia)670 e_meeting_attendee_get_has_calendar_info (const EMeetingAttendee *ia)
671 {
672 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
673 
674 	return ia->priv->has_calendar_info;
675 }
676 
677 void
e_meeting_attendee_set_has_calendar_info(EMeetingAttendee * ia,gboolean has_calendar_info)678 e_meeting_attendee_set_has_calendar_info (EMeetingAttendee *ia,
679                                           gboolean has_calendar_info)
680 {
681 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
682 
683 	ia->priv->has_calendar_info = has_calendar_info;
684 }
685 
686 const GArray *
e_meeting_attendee_get_busy_periods(EMeetingAttendee * ia)687 e_meeting_attendee_get_busy_periods (EMeetingAttendee *ia)
688 {
689 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), NULL);
690 
691 	ensure_periods_sorted (ia);
692 
693 	return ia->priv->busy_periods;
694 }
695 
696 gint
e_meeting_attendee_find_first_busy_period(EMeetingAttendee * ia,const GDate * date)697 e_meeting_attendee_find_first_busy_period (EMeetingAttendee *ia,
698 					   const GDate *date)
699 {
700 	EMeetingFreeBusyPeriod *period;
701 	gint lower, upper, middle = 0, cmp = 0;
702 	GDate tmp_date;
703 
704 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), -1);
705 
706 	/* Make sure the busy periods have been sorted. */
707 	ensure_periods_sorted (ia);
708 
709 	/* Calculate the first day which could have a busy period which
710 	 * continues onto our given date. */
711 	tmp_date = *date;
712 	g_date_subtract_days (&tmp_date, ia->priv->longest_period_in_days);
713 
714 	/* We want the first busy period which starts on tmp_date. */
715 	lower = 0;
716 	upper = ia->priv->busy_periods->len;
717 
718 	if (upper == 0)
719 		return -1;
720 
721 	while (lower < upper) {
722 		middle = (lower + upper) >> 1;
723 
724 		period = &g_array_index (ia->priv->busy_periods,
725 					 EMeetingFreeBusyPeriod, middle);
726 
727 		cmp = g_date_compare (&tmp_date, &period->start.date);
728 
729 		if (cmp == 0)
730 			break;
731 		else if (cmp < 0)
732 			upper = middle;
733 		else
734 			lower = middle + 1;
735 	}
736 
737 	/* There may be several busy periods on the same day so we step
738 	 * backwards to the first one. */
739 	if (cmp == 0) {
740 		while (middle > 0) {
741 			period = &g_array_index (ia->priv->busy_periods,
742 						 EMeetingFreeBusyPeriod, middle - 1);
743 			if (g_date_compare (&tmp_date, &period->start.date) != 0)
744 				break;
745 			middle--;
746 		}
747 	} else if (cmp > 0) {
748 		/* This means we couldn't find a period on the given day, and
749 		 * the last one we looked at was before it, so if there are
750 		 * any more periods after this one we return it. */
751 		middle++;
752 		if (ia->priv->busy_periods->len <= middle)
753 			return -1;
754 	}
755 
756 	return middle;
757 }
758 
759 gboolean
e_meeting_attendee_add_busy_period(EMeetingAttendee * ia,gint start_year,gint start_month,gint start_day,gint start_hour,gint start_minute,gint end_year,gint end_month,gint end_day,gint end_hour,gint end_minute,EMeetingFreeBusyType busy_type,const gchar * summary,const gchar * location)760 e_meeting_attendee_add_busy_period (EMeetingAttendee *ia,
761                                     gint start_year,
762                                     gint start_month,
763                                     gint start_day,
764                                     gint start_hour,
765                                     gint start_minute,
766                                     gint end_year,
767                                     gint end_month,
768                                     gint end_day,
769                                     gint end_hour,
770                                     gint end_minute,
771                                     EMeetingFreeBusyType busy_type,
772                                     const gchar *summary,
773                                     const gchar *location)
774 {
775 	EMeetingFreeBusyPeriod period;
776 	gint period_in_days;
777 
778 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
779 	g_return_val_if_fail (busy_type < E_MEETING_FREE_BUSY_LAST, FALSE);
780 	/* summary may be NULL (optional XFB data)  */
781 	/* location may be NULL (optional XFB data) */
782 
783 	/* Check the dates are valid. */
784 	if (!g_date_valid_dmy (start_day, start_month, start_year))
785 		return FALSE;
786 	if (!g_date_valid_dmy (end_day, end_month, end_year))
787 		return FALSE;
788 	if (start_hour < 0 || start_hour > 23)
789 		return FALSE;
790 	if (end_hour < 0 || end_hour > 23)
791 		return FALSE;
792 	if (start_minute < 0 || start_minute > 59)
793 		return FALSE;
794 	if (end_minute < 0 || end_minute > 59)
795 		return FALSE;
796 
797 	g_date_clear (&period.start.date, 1);
798 	g_date_clear (&period.end.date, 1);
799 	g_date_set_dmy (&period.start.date, start_day, start_month, start_year);
800 	g_date_set_dmy (&period.end.date, end_day, end_month, end_year);
801 	period.start.hour = start_hour;
802 	period.start.minute = start_minute;
803 	period.end.hour = end_hour;
804 	period.end.minute = end_minute;
805 	period.busy_type = busy_type;
806 
807 	/* Check that the start time is before or equal to the end time. */
808 	if (compare_times (&period.start, &period.end) > 0)
809 		return FALSE;
810 
811 	/* If the busy_type is FREE, then there is no need to render it in UI */
812 	if (busy_type == E_MEETING_FREE_BUSY_FREE)
813 		goto done;
814 
815 	/* If the busy range is not set elsewhere, track it as best we can */
816 	if (!ia->priv->start_busy_range_set) {
817 		if (!g_date_valid (&ia->priv->busy_periods_start.date)) {
818 			ia->priv->busy_periods_start.date = period.start.date;
819 			ia->priv->busy_periods_start.hour = period.start.hour;
820 			ia->priv->busy_periods_start.minute = period.start.minute;
821 		} else {
822 			gint compare;
823 
824 			compare = g_date_compare (
825 				&period.start.date,
826 				&ia->priv->busy_periods_start.date);
827 
828 			switch (compare) {
829 			case -1:
830 				ia->priv->busy_periods_start.date = period.start.date;
831 				ia->priv->busy_periods_start.hour = period.start.hour;
832 				ia->priv->busy_periods_start.minute = period.start.minute;
833 				break;
834 			case 0:
835 				if (period.start.hour < ia->priv->busy_periods_start.hour
836 				    || (period.start.hour == ia->priv->busy_periods_start.hour
837 					&& period.start.minute < ia->priv->busy_periods_start.minute)) {
838 					ia->priv->busy_periods_start.date = period.start.date;
839 					ia->priv->busy_periods_start.hour = period.start.hour;
840 					ia->priv->busy_periods_start.minute = period.start.minute;
841 					break;
842 				}
843 				break;
844 			}
845 		}
846 	}
847 
848 	if (!ia->priv->end_busy_range_set) {
849 		if (!g_date_valid (&ia->priv->busy_periods_end.date)) {
850 			ia->priv->busy_periods_end.date = period.end.date;
851 			ia->priv->busy_periods_end.hour = period.end.hour;
852 			ia->priv->busy_periods_end.minute = period.end.minute;
853 		} else {
854 			gint compare;
855 
856 			compare = g_date_compare (
857 				&period.end.date,
858 				&ia->priv->busy_periods_end.date);
859 
860 			switch (compare) {
861 			case 0:
862 				if (period.end.hour > ia->priv->busy_periods_end.hour
863 				    || (period.end.hour == ia->priv->busy_periods_end.hour
864 					&& period.end.minute > ia->priv->busy_periods_end.minute)) {
865 					ia->priv->busy_periods_end.date = period.end.date;
866 					ia->priv->busy_periods_end.hour = period.end.hour;
867 					ia->priv->busy_periods_end.minute = period.end.minute;
868 					break;
869 				}
870 				break;
871 			case 1:
872 				ia->priv->busy_periods_end.date = period.end.date;
873 				ia->priv->busy_periods_end.hour = period.end.hour;
874 				ia->priv->busy_periods_end.minute = period.end.minute;
875 				break;
876 			}
877 		}
878 	}
879 
880 	/* Setting of extended free/busy (XFB) data, if we have any. */
881 	e_meeting_xfb_data_init (&(period.xfb));
882 	e_meeting_xfb_data_set (&(period.xfb), summary, location);
883 
884 	g_array_append_val (ia->priv->busy_periods, period);
885 
886 	period_in_days =
887 		g_date_get_julian (&period.end.date) -
888 		g_date_get_julian (&period.start.date) + 1;
889 	ia->priv->longest_period_in_days =
890 		MAX (ia->priv->longest_period_in_days, period_in_days);
891 
892 done:
893 	ia->priv->has_calendar_info = TRUE;
894 	ia->priv->busy_periods_sorted = FALSE;
895 
896 	return TRUE;
897 }
898 
899 EMeetingTime
e_meeting_attendee_get_start_busy_range(const EMeetingAttendee * ia)900 e_meeting_attendee_get_start_busy_range (const EMeetingAttendee *ia)
901 {
902 	EMeetingTime mt;
903 
904 	g_date_clear (&mt.date, 1);
905 	mt.hour = 0;
906 	mt.minute = 0;
907 
908 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), mt);
909 
910 	return ia->priv->busy_periods_start;
911 }
912 
913 EMeetingTime
e_meeting_attendee_get_end_busy_range(const EMeetingAttendee * ia)914 e_meeting_attendee_get_end_busy_range (const EMeetingAttendee *ia)
915 {
916 	EMeetingTime mt;
917 
918 	g_date_clear (&mt.date, 1);
919 	mt.hour = 0;
920 	mt.minute = 0;
921 
922 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), mt);
923 
924 	return ia->priv->busy_periods_end;
925 }
926 
927 gboolean
e_meeting_attendee_set_start_busy_range(EMeetingAttendee * ia,gint start_year,gint start_month,gint start_day,gint start_hour,gint start_minute)928 e_meeting_attendee_set_start_busy_range (EMeetingAttendee *ia,
929                                          gint start_year,
930                                          gint start_month,
931                                          gint start_day,
932                                          gint start_hour,
933                                          gint start_minute)
934 {
935 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
936 
937 	/* Check the dates are valid. */
938 	if (!g_date_valid_dmy (start_day, start_month, start_year))
939 		return FALSE;
940 	if (start_hour < 0 || start_hour > 23)
941 		return FALSE;
942 	if (start_minute < 0 || start_minute > 59)
943 		return FALSE;
944 
945 	g_date_clear (&ia->priv->busy_periods_start.date, 1);
946 	g_date_set_dmy (
947 		&ia->priv->busy_periods_start.date,
948 		start_day, start_month, start_year);
949 	ia->priv->busy_periods_start.hour = start_hour;
950 	ia->priv->busy_periods_start.minute = start_minute;
951 
952 	ia->priv->start_busy_range_set = TRUE;
953 
954 	return TRUE;
955 }
956 
957 gboolean
e_meeting_attendee_set_end_busy_range(EMeetingAttendee * ia,gint end_year,gint end_month,gint end_day,gint end_hour,gint end_minute)958 e_meeting_attendee_set_end_busy_range (EMeetingAttendee *ia,
959                                        gint end_year,
960                                        gint end_month,
961                                        gint end_day,
962                                        gint end_hour,
963                                        gint end_minute)
964 {
965 	g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE);
966 
967 	/* Check the dates are valid. */
968 	if (!g_date_valid_dmy (end_day, end_month, end_year))
969 		return FALSE;
970 	if (end_hour < 0 || end_hour > 23)
971 		return FALSE;
972 	if (end_minute < 0 || end_minute > 59)
973 		return FALSE;
974 
975 	g_date_clear (&ia->priv->busy_periods_end.date, 1);
976 	g_date_set_dmy (
977 		&ia->priv->busy_periods_end.date,
978 		end_day, end_month, end_year);
979 	ia->priv->busy_periods_end.hour = end_hour;
980 	ia->priv->busy_periods_end.minute = end_minute;
981 
982 	ia->priv->end_busy_range_set = TRUE;
983 
984 	return TRUE;
985 }
986 
987 /* Clears all busy times for the given attendee. */
988 void
e_meeting_attendee_clear_busy_periods(EMeetingAttendee * ia)989 e_meeting_attendee_clear_busy_periods (EMeetingAttendee *ia)
990 {
991 	g_return_if_fail (E_IS_MEETING_ATTENDEE (ia));
992 
993 	g_array_set_size (ia->priv->busy_periods, 0);
994 	ia->priv->busy_periods_sorted = TRUE;
995 
996 	g_date_clear (&ia->priv->busy_periods_start.date, 1);
997 	ia->priv->busy_periods_start.hour = 0;
998 	ia->priv->busy_periods_start.minute = 0;
999 
1000 	g_date_clear (&ia->priv->busy_periods_end.date, 1);
1001 	ia->priv->busy_periods_end.hour = 0;
1002 	ia->priv->busy_periods_end.minute = 0;
1003 
1004 	ia->priv->longest_period_in_days = 0;
1005 }
1006