2 * Copyright (C) 2014 Red Hat, Inc. (www.redhat.com)
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 Lesser 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/>.
16 * Authors: Milan Crha <mcrha@redhat.com>
19 #include "evolution-config.h"
22 #include <glib/gi18n-lib.h>
24 #include "comp-util.h"
25 #include "e-cal-data-model.h"
27 #define LOCK_PROPS() g_rec_mutex_lock (&data_model->priv->props_lock)
28 #define UNLOCK_PROPS() g_rec_mutex_unlock (&data_model->priv->props_lock)
30 struct _ECalDataModelPrivate
{
32 ECalDataModelSubmitThreadJobFunc submit_thread_job_func
;
33 GWeakRef
*submit_thread_job_responder
;
34 GThreadPool
*thread_pool
;
36 GRecMutex props_lock
; /* to guard all the below members */
39 gboolean expand_recurrences
;
41 gchar
*full_filter
; /* to be used with views */
46 GHashTable
*clients
; /* ESource::uid ~> ECalClient */
47 GHashTable
*views
; /* ECalClient ~> ViewData */
48 GSList
*subscribers
; /* ~> SubscriberData */
50 guint32 views_update_freeze
;
51 gboolean views_update_required
;
56 PROP_EXPAND_RECURRENCES
,
65 static guint signals
[LAST_SIGNAL
];
67 G_DEFINE_TYPE (ECalDataModel
, e_cal_data_model
, G_TYPE_OBJECT
)
69 typedef struct _ComponentData
{
70 ECalComponent
*component
;
71 time_t instance_start
;
76 typedef struct _ViewData
{
83 gulong objects_added_id
;
84 gulong objects_modified_id
;
85 gulong objects_removed_id
;
89 GHashTable
*components
; /* ECalComponentId ~> ComponentData */
90 GHashTable
*lost_components
; /* ECalComponentId ~> ComponentData; when re-running view, valid till 'complete' is received */
91 gboolean received_complete
;
92 GSList
*to_expand_recurrences
; /* icalcomponent */
93 GSList
*expanded_recurrences
; /* ComponentData */
94 gint pending_expand_recurrences
; /* how many is waiting to be processed */
96 GCancellable
*cancellable
;
99 typedef struct _SubscriberData
{
100 ECalDataModelSubscriber
*subscriber
;
105 static ComponentData
*
106 component_data_new (ECalComponent
*comp
,
107 time_t instance_start
,
109 gboolean is_detached
)
111 ComponentData
*comp_data
;
113 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp
), NULL
);
115 comp_data
= g_new0 (ComponentData
, 1);
116 comp_data
->component
= g_object_ref (comp
);
117 comp_data
->instance_start
= instance_start
;
118 comp_data
->instance_end
= instance_end
;
119 comp_data
->is_detached
= is_detached
;
125 component_data_free (gpointer ptr
)
127 ComponentData
*comp_data
= ptr
;
130 g_object_unref (comp_data
->component
);
136 component_data_equal (ComponentData
*comp_data1
,
137 ComponentData
*comp_data2
)
139 icalcomponent
*icomp1
, *icomp2
;
140 struct icaltimetype tt1
, tt2
;
141 gchar
*as_str1
, *as_str2
;
144 if (comp_data1
== comp_data2
)
147 if (!comp_data1
|| !comp_data2
|| !comp_data1
->component
|| !comp_data2
->component
)
150 if (comp_data1
->instance_start
!= comp_data2
->instance_start
||
151 comp_data1
->instance_end
!= comp_data2
->instance_end
)
154 icomp1
= e_cal_component_get_icalcomponent (comp_data1
->component
);
155 icomp2
= e_cal_component_get_icalcomponent (comp_data2
->component
);
157 if (!icomp1
|| !icomp2
||
158 icalcomponent_get_sequence (icomp1
) != icalcomponent_get_sequence (icomp2
) ||
159 g_strcmp0 (icalcomponent_get_uid (icomp1
), icalcomponent_get_uid (icomp2
)) != 0)
162 tt1
= icalcomponent_get_recurrenceid (icomp1
);
163 tt2
= icalcomponent_get_recurrenceid (icomp2
);
164 if ((icaltime_is_valid_time (tt1
) ? 1 : 0) != (icaltime_is_valid_time (tt2
) ? 1 : 0) ||
165 (icaltime_is_null_time (tt1
) ? 1 : 0) != (icaltime_is_null_time (tt2
) ? 1 : 0) ||
166 icaltime_compare (tt1
, tt2
) != 0)
169 tt1
= icalcomponent_get_dtstamp (icomp1
);
170 tt2
= icalcomponent_get_dtstamp (icomp2
);
171 if ((icaltime_is_valid_time (tt1
) ? 1 : 0) != (icaltime_is_valid_time (tt2
) ? 1 : 0) ||
172 (icaltime_is_null_time (tt1
) ? 1 : 0) != (icaltime_is_null_time (tt2
) ? 1 : 0) ||
173 icaltime_compare (tt1
, tt2
) != 0)
176 /* Maybe not so effective compare, but might be still more effective
177 than updating whole UI with false notifications */
178 as_str1
= icalcomponent_as_ical_string_r (icomp1
);
179 as_str2
= icalcomponent_as_ical_string_r (icomp2
);
181 equal
= g_strcmp0 (as_str1
, as_str2
) == 0;
190 view_data_new (ECalClient
*client
)
194 g_return_val_if_fail (E_IS_CAL_CLIENT (client
), NULL
);
196 view_data
= g_new0 (ViewData
, 1);
197 view_data
->ref_count
= 1;
198 g_rec_mutex_init (&view_data
->lock
);
199 view_data
->is_used
= TRUE
;
200 view_data
->client
= g_object_ref (client
);
201 view_data
->components
= g_hash_table_new_full (
202 (GHashFunc
) e_cal_component_id_hash
, (GEqualFunc
) e_cal_component_id_equal
,
203 (GDestroyNotify
) e_cal_component_free_id
, component_data_free
);
209 view_data_disconnect_view (ViewData
*view_data
)
211 if (view_data
&& view_data
->view
) {
212 #define disconnect(x) G_STMT_START { \
213 if (view_data->x) { \
214 g_signal_handler_disconnect (view_data->view, view_data->x); \
219 disconnect (objects_added_id
);
220 disconnect (objects_modified_id
);
221 disconnect (objects_removed_id
);
222 disconnect (progress_id
);
223 disconnect (complete_id
);
230 view_data_ref (ViewData
*view_data
)
232 g_return_val_if_fail (view_data
!= NULL
, NULL
);
234 g_atomic_int_inc (&view_data
->ref_count
);
240 view_data_unref (gpointer ptr
)
242 ViewData
*view_data
= ptr
;
245 if (g_atomic_int_dec_and_test (&view_data
->ref_count
)) {
246 view_data_disconnect_view (view_data
);
247 if (view_data
->cancellable
)
248 g_cancellable_cancel (view_data
->cancellable
);
249 g_clear_object (&view_data
->cancellable
);
250 g_clear_object (&view_data
->client
);
251 g_clear_object (&view_data
->view
);
252 g_hash_table_destroy (view_data
->components
);
253 if (view_data
->lost_components
)
254 g_hash_table_destroy (view_data
->lost_components
);
255 g_slist_free_full (view_data
->to_expand_recurrences
, (GDestroyNotify
) icalcomponent_free
);
256 g_slist_free_full (view_data
->expanded_recurrences
, component_data_free
);
257 g_rec_mutex_clear (&view_data
->lock
);
264 view_data_lock (ViewData
*view_data
)
266 g_return_if_fail (view_data
!= NULL
);
268 g_rec_mutex_lock (&view_data
->lock
);
272 view_data_unlock (ViewData
*view_data
)
274 g_return_if_fail (view_data
!= NULL
);
276 g_rec_mutex_unlock (&view_data
->lock
);
279 static SubscriberData
*
280 subscriber_data_new (ECalDataModelSubscriber
*subscriber
,
284 SubscriberData
*subs_data
;
286 g_return_val_if_fail (E_IS_CAL_DATA_MODEL_SUBSCRIBER (subscriber
), NULL
);
288 subs_data
= g_new0 (SubscriberData
, 1);
289 subs_data
->subscriber
= g_object_ref (subscriber
);
290 subs_data
->range_start
= range_start
;
291 subs_data
->range_end
= range_end
;
297 subscriber_data_free (gpointer ptr
)
299 SubscriberData
*subs_data
= ptr
;
302 g_clear_object (&subs_data
->subscriber
);
307 typedef struct _ViewStateChangedData
{
308 ECalDataModel
*data_model
;
309 ECalClientView
*view
;
310 ECalDataModelViewState state
;
314 } ViewStateChangedData
;
317 view_state_changed_data_free (gpointer ptr
)
319 ViewStateChangedData
*vscd
= ptr
;
322 g_clear_object (&vscd
->data_model
);
323 g_clear_object (&vscd
->view
);
324 g_clear_error (&vscd
->error
);
325 g_free (vscd
->message
);
331 cal_data_model_emit_view_state_changed_timeout_cb (gpointer user_data
)
333 ViewStateChangedData
*vscd
= user_data
;
335 g_return_val_if_fail (vscd
!= NULL
, FALSE
);
336 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (vscd
->data_model
), FALSE
);
337 g_return_val_if_fail (E_IS_CAL_CLIENT_VIEW (vscd
->view
), FALSE
);
339 g_signal_emit (vscd
->data_model
, signals
[VIEW_STATE_CHANGED
], 0,
340 vscd
->view
, vscd
->state
, vscd
->percent
, vscd
->message
, vscd
->error
);
346 cal_data_model_emit_view_state_changed (ECalDataModel
*data_model
,
347 ECalClientView
*view
,
348 ECalDataModelViewState state
,
350 const gchar
*message
,
353 ViewStateChangedData
*vscd
;
355 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
356 g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view
));
358 if (e_cal_data_model_get_disposing (data_model
))
361 vscd
= g_new0 (ViewStateChangedData
, 1);
362 vscd
->data_model
= g_object_ref (data_model
);
363 vscd
->view
= g_object_ref (view
);
365 vscd
->percent
= percent
;
366 vscd
->message
= g_strdup (message
);
367 vscd
->error
= error
? g_error_copy (error
) : NULL
;
369 g_timeout_add_full (G_PRIORITY_DEFAULT
, 1,
370 cal_data_model_emit_view_state_changed_timeout_cb
,
371 vscd
, view_state_changed_data_free
);
374 typedef void (* InternalThreadJobFunc
) (ECalDataModel
*data_model
, gpointer user_data
);
376 typedef struct _InternalThreadJobData
{
377 InternalThreadJobFunc func
;
379 } InternalThreadJobData
;
382 cal_data_model_internal_thread_job_func (gpointer data
,
385 ECalDataModel
*data_model
= user_data
;
386 InternalThreadJobData
*job_data
= data
;
388 g_return_if_fail (job_data
!= NULL
);
389 g_return_if_fail (job_data
->func
!= NULL
);
391 job_data
->func (data_model
, job_data
->user_data
);
397 cal_data_model_submit_internal_thread_job (ECalDataModel
*data_model
,
398 InternalThreadJobFunc func
,
401 InternalThreadJobData
*job_data
;
403 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
404 g_return_if_fail (func
!= NULL
);
406 job_data
= g_new0 (InternalThreadJobData
, 1);
407 job_data
->func
= func
;
408 job_data
->user_data
= user_data
;
410 g_thread_pool_push (data_model
->priv
->thread_pool
, job_data
, NULL
);
413 typedef struct _SubmitThreadJobData
{
414 ECalDataModel
*data_model
;
415 const gchar
*description
;
416 const gchar
*alert_ident
;
417 const gchar
*alert_arg_0
;
418 EAlertSinkThreadJobFunc func
;
420 GDestroyNotify free_user_data
;
422 GCancellable
*cancellable
;
426 } SubmitThreadJobData
;
429 cal_data_model_call_submit_thread_job (gpointer user_data
)
431 SubmitThreadJobData
*stj_data
= user_data
;
434 g_return_val_if_fail (stj_data
!= NULL
, FALSE
);
436 g_mutex_lock (&stj_data
->mutex
);
437 responder
= g_weak_ref_get (stj_data
->data_model
->priv
->submit_thread_job_responder
);
439 stj_data
->cancellable
= stj_data
->data_model
->priv
->submit_thread_job_func (
440 responder
, stj_data
->description
, stj_data
->alert_ident
, stj_data
->alert_arg_0
,
441 stj_data
->func
, stj_data
->user_data
, stj_data
->free_user_data
);
443 g_clear_object (&responder
);
445 stj_data
->finished
= TRUE
;
446 g_cond_signal (&stj_data
->cond
);
447 g_mutex_unlock (&stj_data
->mutex
);
453 * e_cal_data_model_submit_thread_job:
454 * @data_model: an #ECalDataModel
455 * @description: user-friendly description of the job, to be shown in UI
456 * @alert_ident: in case of an error, this alert identificator is used
457 * for EAlert construction
458 * @alert_arg_0: (allow-none): in case of an error, use this string as
459 * the first argument to the EAlert construction; the second argument
460 * is the actual error message; can be #NULL, in which case only
461 * the error message is passed to the EAlert construction
462 * @func: function to be run in a dedicated thread
463 * @user_data: (allow-none): custom data passed into @func; can be #NULL
464 * @free_user_data: (allow-none): function to be called on @user_data,
465 * when the job is over; can be #NULL
467 * Runs the @func in a dedicated thread. Any error is propagated to UI.
468 * The cancellable passed into the @func is a #CamelOperation, thus
469 * the caller can overwrite progress and description message on it.
471 * Returns: (transfer full): Newly created #GCancellable on success.
472 * The caller is responsible to g_object_unref() it when done with it.
474 * Note: The @free_user_data, if set, is called in the main thread.
476 * Note: This is a blocking call, it waits until the thread job is submitted.
481 e_cal_data_model_submit_thread_job (ECalDataModel
*data_model
,
482 const gchar
*description
,
483 const gchar
*alert_ident
,
484 const gchar
*alert_arg_0
,
485 EAlertSinkThreadJobFunc func
,
487 GDestroyNotify free_user_data
)
489 SubmitThreadJobData stj_data
;
491 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), NULL
);
492 g_return_val_if_fail (data_model
->priv
->submit_thread_job_func
!= NULL
, NULL
);
494 if (g_thread_self () == data_model
->priv
->main_thread
) {
495 GCancellable
*cancellable
;
498 responder
= g_weak_ref_get (data_model
->priv
->submit_thread_job_responder
);
499 cancellable
= data_model
->priv
->submit_thread_job_func (
500 responder
, description
, alert_ident
, alert_arg_0
,
501 func
, user_data
, free_user_data
);
502 g_clear_object (&responder
);
507 stj_data
.data_model
= data_model
;
508 stj_data
.description
= description
;
509 stj_data
.alert_ident
= alert_ident
;
510 stj_data
.alert_arg_0
= alert_arg_0
;
511 stj_data
.func
= func
;
512 stj_data
.user_data
= user_data
;
513 stj_data
.free_user_data
= free_user_data
;
514 stj_data
.cancellable
= NULL
;
515 stj_data
.finished
= FALSE
;
516 g_mutex_init (&stj_data
.mutex
);
517 g_cond_init (&stj_data
.cond
);
519 g_timeout_add (1, cal_data_model_call_submit_thread_job
, &stj_data
);
521 g_mutex_lock (&stj_data
.mutex
);
522 while (!stj_data
.finished
) {
523 g_cond_wait (&stj_data
.cond
, &stj_data
.mutex
);
525 g_mutex_unlock (&stj_data
.mutex
);
527 g_cond_clear (&stj_data
.cond
);
528 g_mutex_clear (&stj_data
.mutex
);
530 return stj_data
.cancellable
;
533 typedef void (* ECalDataModelForeachSubscriberFunc
) (ECalDataModel
*data_model
,
535 ECalDataModelSubscriber
*subscriber
,
539 cal_data_model_foreach_subscriber_in_range (ECalDataModel
*data_model
,
541 time_t in_range_start
,
543 ECalDataModelForeachSubscriberFunc func
,
548 g_return_if_fail (func
!= NULL
);
552 if (in_range_end
== (time_t) 0) {
553 in_range_end
= in_range_start
;
556 for (link
= data_model
->priv
->subscribers
; link
; link
= g_slist_next (link
)) {
557 SubscriberData
*subs_data
= link
->data
;
559 if ((in_range_start
== (time_t) 0 && in_range_end
== (time_t) 0) ||
560 (subs_data
->range_start
== (time_t) 0 && subs_data
->range_end
== (time_t) 0) ||
561 (subs_data
->range_start
<= in_range_end
&& subs_data
->range_end
>= in_range_start
))
562 func (data_model
, client
, subs_data
->subscriber
, user_data
);
569 cal_data_model_foreach_subscriber (ECalDataModel
*data_model
,
571 ECalDataModelForeachSubscriberFunc func
,
574 g_return_if_fail (func
!= NULL
);
576 cal_data_model_foreach_subscriber_in_range (data_model
, client
, (time_t) 0, (time_t) 0, func
, user_data
);
580 cal_data_model_freeze_subscriber_cb (ECalDataModel
*data_model
,
582 ECalDataModelSubscriber
*subscriber
,
585 e_cal_data_model_subscriber_freeze (subscriber
);
589 cal_data_model_thaw_subscriber_cb (ECalDataModel
*data_model
,
591 ECalDataModelSubscriber
*subscriber
,
594 e_cal_data_model_subscriber_thaw (subscriber
);
598 cal_data_model_freeze_all_subscribers (ECalDataModel
*data_model
)
600 cal_data_model_foreach_subscriber (data_model
, NULL
, cal_data_model_freeze_subscriber_cb
, NULL
);
604 cal_data_model_thaw_all_subscribers (ECalDataModel
*data_model
)
606 cal_data_model_foreach_subscriber (data_model
, NULL
, cal_data_model_thaw_subscriber_cb
, NULL
);
610 cal_data_model_add_component_cb (ECalDataModel
*data_model
,
612 ECalDataModelSubscriber
*subscriber
,
615 ECalComponent
*comp
= user_data
;
617 g_return_if_fail (comp
!= NULL
);
619 e_cal_data_model_subscriber_component_added (subscriber
, client
, comp
);
623 cal_data_model_modify_component_cb (ECalDataModel
*data_model
,
625 ECalDataModelSubscriber
*subscriber
,
628 ECalComponent
*comp
= user_data
;
630 g_return_if_fail (comp
!= NULL
);
632 e_cal_data_model_subscriber_component_modified (subscriber
, client
, comp
);
636 cal_data_model_remove_one_view_component_cb (ECalDataModel
*data_model
,
638 ECalDataModelSubscriber
*subscriber
,
641 const ECalComponentId
*id
= user_data
;
643 g_return_if_fail (id
!= NULL
);
645 e_cal_data_model_subscriber_component_removed (subscriber
, client
, id
->uid
, id
->rid
);
649 cal_data_model_remove_components (ECalDataModel
*data_model
,
651 GHashTable
*components
,
652 GHashTable
*also_remove_from
)
656 g_return_if_fail (data_model
!= NULL
);
657 g_return_if_fail (components
!= NULL
);
659 cal_data_model_freeze_all_subscribers (data_model
);
661 ids
= g_hash_table_get_keys (components
);
663 for (ilink
= ids
; ilink
; ilink
= g_list_next (ilink
)) {
664 ECalComponentId
*id
= ilink
->data
;
665 ComponentData
*comp_data
;
666 time_t instance_start
= (time_t) 0, instance_end
= (time_t) 0;
671 /* Try to limit which subscribers will be notified about removal */
672 comp_data
= g_hash_table_lookup (components
, id
);
674 instance_start
= comp_data
->instance_start
;
675 instance_end
= comp_data
->instance_end
;
678 cal_data_model_foreach_subscriber_in_range (data_model
, client
,
679 instance_start
, instance_end
,
680 cal_data_model_remove_one_view_component_cb
, id
);
682 if (also_remove_from
)
683 g_hash_table_remove (also_remove_from
, id
);
688 cal_data_model_thaw_all_subscribers (data_model
);
692 cal_data_model_calc_range (ECalDataModel
*data_model
,
698 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
699 g_return_if_fail (range_start
!= NULL
);
700 g_return_if_fail (range_end
!= NULL
);
702 *range_start
= (time_t) 0;
703 *range_end
= (time_t) 0;
707 for (link
= data_model
->priv
->subscribers
; link
; link
= g_slist_next (link
)) {
708 SubscriberData
*subs_data
= link
->data
;
713 if (subs_data
->range_start
== (time_t) 0 && subs_data
->range_end
== (time_t) 0) {
714 *range_start
= (time_t) 0;
715 *range_end
= (time_t) 0;
719 if (link
== data_model
->priv
->subscribers
) {
720 *range_start
= subs_data
->range_start
;
721 *range_end
= subs_data
->range_end
;
723 if (*range_start
> subs_data
->range_start
)
724 *range_start
= subs_data
->range_start
;
725 if (*range_end
< subs_data
->range_end
)
726 *range_end
= subs_data
->range_end
;
734 cal_data_model_update_full_filter (ECalDataModel
*data_model
)
737 time_t range_start
, range_end
;
742 cal_data_model_calc_range (data_model
, &range_start
, &range_end
);
744 if (range_start
!= (time_t) 0 || range_end
!= (time_t) 0) {
745 gchar
*iso_start
, *iso_end
;
746 const gchar
*default_tzloc
= NULL
;
748 iso_start
= isodate_from_time_t (range_start
);
749 iso_end
= isodate_from_time_t (range_end
);
751 if (data_model
->priv
->zone
&& data_model
->priv
->zone
!= icaltimezone_get_utc_timezone ())
752 default_tzloc
= icaltimezone_get_location (data_model
->priv
->zone
);
756 if (data_model
->priv
->filter
) {
757 filter
= g_strdup_printf (
758 "(and (occur-in-time-range? (make-time \"%s\") (make-time \"%s\") \"%s\") %s)",
759 iso_start
, iso_end
, default_tzloc
, data_model
->priv
->filter
);
761 filter
= g_strdup_printf (
762 "(occur-in-time-range? (make-time \"%s\") (make-time \"%s\") \"%s\")",
763 iso_start
, iso_end
, default_tzloc
);
768 } else if (data_model
->priv
->filter
) {
769 filter
= g_strdup (data_model
->priv
->filter
);
771 filter
= g_strdup ("#t");
774 changed
= g_strcmp0 (data_model
->priv
->full_filter
, filter
) != 0;
777 g_free (data_model
->priv
->full_filter
);
778 data_model
->priv
->full_filter
= filter
;
788 /* This consumes the comp_data - not so nice, but simpler
789 than adding reference counter for the structure */
791 cal_data_model_process_added_component (ECalDataModel
*data_model
,
793 ComponentData
*comp_data
,
794 GHashTable
*known_instances
)
797 ComponentData
*old_comp_data
= NULL
;
798 gboolean comp_data_equal
;
800 g_return_if_fail (data_model
!= NULL
);
801 g_return_if_fail (view_data
!= NULL
);
802 g_return_if_fail (comp_data
!= NULL
);
804 id
= e_cal_component_get_id (comp_data
->component
);
805 g_return_if_fail (id
!= NULL
);
807 view_data_lock (view_data
);
809 if (!old_comp_data
&& view_data
->lost_components
)
810 old_comp_data
= g_hash_table_lookup (view_data
->lost_components
, id
);
812 if (!old_comp_data
&& known_instances
)
813 old_comp_data
= g_hash_table_lookup (known_instances
, id
);
816 old_comp_data
= g_hash_table_lookup (view_data
->components
, id
);
819 /* It can be a previously added detached instance received
820 during recurrences expand */
821 if (!comp_data
->is_detached
)
822 comp_data
->is_detached
= old_comp_data
->is_detached
;
825 comp_data_equal
= component_data_equal (comp_data
, old_comp_data
);
827 if (view_data
->lost_components
)
828 g_hash_table_remove (view_data
->lost_components
, id
);
831 g_hash_table_remove (known_instances
, id
);
833 /* Note: old_comp_data is freed or NULL now */
835 /* 'id' is stolen by view_data->components */
836 g_hash_table_insert (view_data
->components
, id
, comp_data
);
838 if (!comp_data_equal
) {
840 cal_data_model_foreach_subscriber_in_range (data_model
, view_data
->client
,
841 comp_data
->instance_start
, comp_data
->instance_end
,
842 cal_data_model_add_component_cb
, comp_data
->component
);
844 cal_data_model_foreach_subscriber_in_range (data_model
, view_data
->client
,
845 comp_data
->instance_start
, comp_data
->instance_end
,
846 cal_data_model_modify_component_cb
, comp_data
->component
);
849 view_data_unlock (view_data
);
852 typedef struct _GatherComponentsData
{
854 GList
**pcomponent_ids
; /* ECalComponentId, can be owned by the hash table */
855 GHashTable
*component_ids_hash
;
857 gboolean all_instances
; /* FALSE to get only nondetached component instances */
858 } GatherComponentsData
;
861 cal_data_model_gather_components (gpointer key
,
865 ECalComponentId
*id
= key
;
866 ComponentData
*comp_data
= value
;
867 GatherComponentsData
*gather_data
= user_data
;
869 g_return_if_fail (id
!= NULL
);
870 g_return_if_fail (comp_data
!= NULL
);
871 g_return_if_fail (gather_data
!= NULL
);
872 g_return_if_fail (gather_data
->pcomponent_ids
!= NULL
|| gather_data
->component_ids_hash
!= NULL
);
873 g_return_if_fail (gather_data
->pcomponent_ids
== NULL
|| gather_data
->component_ids_hash
== NULL
);
875 if ((gather_data
->all_instances
|| !comp_data
->is_detached
) && g_strcmp0 (id
->uid
, gather_data
->uid
) == 0) {
876 if (gather_data
->component_ids_hash
) {
877 ComponentData
*comp_data_copy
;
879 comp_data_copy
= component_data_new (comp_data
->component
,
880 comp_data
->instance_start
, comp_data
->instance_end
,
881 comp_data
->is_detached
);
883 if (gather_data
->copy_ids
) {
884 g_hash_table_insert (gather_data
->component_ids_hash
,
885 e_cal_component_id_copy (id
), comp_data_copy
);
887 g_hash_table_insert (gather_data
->component_ids_hash
, id
, comp_data_copy
);
889 } else if (gather_data
->copy_ids
) {
890 *gather_data
->pcomponent_ids
= g_list_prepend (*gather_data
->pcomponent_ids
,
891 e_cal_component_id_copy (id
));
893 *gather_data
->pcomponent_ids
= g_list_prepend (*gather_data
->pcomponent_ids
, id
);
898 typedef struct _NotifyRecurrencesData
{
899 ECalDataModel
*data_model
;
901 } NotifyRecurrencesData
;
904 cal_data_model_notify_recurrences_cb (gpointer user_data
)
906 NotifyRecurrencesData
*notif_data
= user_data
;
907 ECalDataModel
*data_model
;
910 g_return_val_if_fail (notif_data
!= NULL
, FALSE
);
912 data_model
= notif_data
->data_model
;
916 view_data
= g_hash_table_lookup (data_model
->priv
->views
, notif_data
->client
);
918 view_data_ref (view_data
);
923 GHashTable
*gathered_uids
;
924 GHashTable
*known_instances
;
925 GSList
*expanded_recurrences
, *link
;
927 view_data_lock (view_data
);
928 expanded_recurrences
= view_data
->expanded_recurrences
;
929 view_data
->expanded_recurrences
= NULL
;
931 cal_data_model_freeze_all_subscribers (data_model
);
933 gathered_uids
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
934 known_instances
= g_hash_table_new_full (
935 (GHashFunc
) e_cal_component_id_hash
, (GEqualFunc
) e_cal_component_id_equal
,
936 (GDestroyNotify
) e_cal_component_free_id
, component_data_free
);
938 for (link
= expanded_recurrences
; link
&& view_data
->is_used
; link
= g_slist_next (link
)) {
939 ComponentData
*comp_data
= link
->data
;
940 icalcomponent
*icomp
;
946 icomp
= e_cal_component_get_icalcomponent (comp_data
->component
);
947 if (!icomp
|| !icalcomponent_get_uid (icomp
))
950 uid
= icalcomponent_get_uid (icomp
);
952 if (!g_hash_table_contains (gathered_uids
, uid
)) {
953 GatherComponentsData gather_data
;
955 gather_data
.uid
= uid
;
956 gather_data
.pcomponent_ids
= NULL
;
957 gather_data
.component_ids_hash
= known_instances
;
958 gather_data
.copy_ids
= TRUE
;
959 gather_data
.all_instances
= FALSE
;
961 g_hash_table_foreach (view_data
->components
,
962 cal_data_model_gather_components
, &gather_data
);
964 g_hash_table_insert (gathered_uids
, g_strdup (uid
), GINT_TO_POINTER (1));
967 /* Steal the comp_data */
970 cal_data_model_process_added_component (data_model
, view_data
, comp_data
, known_instances
);
973 if (view_data
->is_used
&& g_hash_table_size (known_instances
) > 0) {
974 cal_data_model_remove_components (data_model
, view_data
->client
, known_instances
, view_data
->components
);
975 g_hash_table_remove_all (known_instances
);
978 if (g_atomic_int_dec_and_test (&view_data
->pending_expand_recurrences
) &&
979 view_data
->is_used
&& view_data
->lost_components
&& view_data
->received_complete
) {
980 cal_data_model_remove_components (data_model
, view_data
->client
, view_data
->lost_components
, NULL
);
981 g_hash_table_destroy (view_data
->lost_components
);
982 view_data
->lost_components
= NULL
;
985 g_hash_table_destroy (gathered_uids
);
986 g_hash_table_destroy (known_instances
);
988 view_data_unlock (view_data
);
990 cal_data_model_thaw_all_subscribers (data_model
);
992 view_data_unref (view_data
);
994 g_slist_free_full (expanded_recurrences
, component_data_free
);
997 g_clear_object (¬if_data
->client
);
998 g_clear_object (¬if_data
->data_model
);
1008 GSList
**pexpanded_recurrences
;
1009 } GenerateInstancesData
;
1012 cal_data_model_instance_generated (ECalComponent
*comp
,
1013 time_t instance_start
,
1014 time_t instance_end
,
1017 GenerateInstancesData
*gid
= data
;
1018 ComponentData
*comp_data
;
1019 ECalComponent
*comp_copy
;
1020 icaltimetype tt
, tt2
;
1022 g_return_val_if_fail (gid
!= NULL
, FALSE
);
1024 comp_copy
= e_cal_component_clone (comp
);
1025 g_return_val_if_fail (comp_copy
!= NULL
, FALSE
);
1027 tt
= icalcomponent_get_dtstart (e_cal_component_get_icalcomponent (comp_copy
));
1028 tt2
= icaltime_from_timet_with_zone (instance_start
, tt
.is_date
, gid
->zone
);
1029 if (tt
.is_date
|| !tt
.zone
|| tt
.zone
== icaltimezone_get_utc_timezone ())
1032 tt2
.zone
= gid
->zone
;
1033 icalcomponent_set_dtstart (e_cal_component_get_icalcomponent (comp_copy
), tt2
);
1035 tt
= icalcomponent_get_dtend (e_cal_component_get_icalcomponent (comp_copy
));
1036 tt2
= icaltime_from_timet_with_zone (instance_end
, tt
.is_date
, gid
->zone
);
1037 if (tt
.is_date
|| !tt
.zone
|| tt
.zone
== icaltimezone_get_utc_timezone ())
1040 tt2
.zone
= gid
->zone
;
1041 icalcomponent_set_dtend (e_cal_component_get_icalcomponent (comp_copy
), tt2
);
1043 e_cal_component_rescan (comp_copy
);
1045 cal_comp_get_instance_times (gid
->client
, e_cal_component_get_icalcomponent (comp_copy
),
1046 gid
->zone
, &instance_start
, NULL
, &instance_end
, NULL
, NULL
);
1048 if (instance_end
> instance_start
)
1051 comp_data
= component_data_new (comp_copy
, instance_start
, instance_end
, FALSE
);
1052 *gid
->pexpanded_recurrences
= g_slist_prepend (*gid
->pexpanded_recurrences
, comp_data
);
1054 g_object_unref (comp_copy
);
1060 cal_data_model_expand_recurrences_thread (ECalDataModel
*data_model
,
1063 ECalClient
*client
= user_data
;
1064 GSList
*to_expand_recurrences
, *link
;
1065 GSList
*expanded_recurrences
= NULL
;
1066 time_t range_start
, range_end
;
1067 ViewData
*view_data
;
1069 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
1073 view_data
= g_hash_table_lookup (data_model
->priv
->views
, client
);
1075 view_data_ref (view_data
);
1077 range_start
= data_model
->priv
->range_start
;
1078 range_end
= data_model
->priv
->range_end
;
1083 g_object_unref (client
);
1087 view_data_lock (view_data
);
1089 if (!view_data
->is_used
) {
1090 view_data_unlock (view_data
);
1091 view_data_unref (view_data
);
1092 g_object_unref (client
);
1096 to_expand_recurrences
= view_data
->to_expand_recurrences
;
1097 view_data
->to_expand_recurrences
= NULL
;
1099 view_data_unlock (view_data
);
1101 for (link
= to_expand_recurrences
; link
&& view_data
->is_used
; link
= g_slist_next (link
)) {
1102 icalcomponent
*icomp
= link
->data
;
1103 GenerateInstancesData gid
;
1108 gid
.client
= client
;
1109 gid
.pexpanded_recurrences
= &expanded_recurrences
;
1110 gid
.zone
= data_model
->priv
->zone
;
1112 e_cal_client_generate_instances_for_object_sync (client
, icomp
, range_start
, range_end
,
1113 cal_data_model_instance_generated
, &gid
);
1116 g_slist_free_full (to_expand_recurrences
, (GDestroyNotify
) icalcomponent_free
);
1118 view_data_lock (view_data
);
1119 if (expanded_recurrences
)
1120 view_data
->expanded_recurrences
= g_slist_concat (view_data
->expanded_recurrences
, expanded_recurrences
);
1121 if (view_data
->is_used
) {
1122 NotifyRecurrencesData
*notif_data
;
1124 notif_data
= g_new0 (NotifyRecurrencesData
, 1);
1125 notif_data
->data_model
= g_object_ref (data_model
);
1126 notif_data
->client
= g_object_ref (client
);
1128 g_timeout_add (1, cal_data_model_notify_recurrences_cb
, notif_data
);
1131 view_data_unlock (view_data
);
1132 view_data_unref (view_data
);
1133 g_object_unref (client
);
1137 cal_data_model_process_modified_or_added_objects (ECalClientView
*view
,
1138 const GSList
*objects
,
1139 ECalDataModel
*data_model
,
1142 ViewData
*view_data
;
1145 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
1149 client
= e_cal_client_view_ref_client (view
);
1155 view_data
= g_hash_table_lookup (data_model
->priv
->views
, client
);
1157 view_data_ref (view_data
);
1158 g_warn_if_fail (view_data
->view
== view
);
1164 g_clear_object (&client
);
1168 view_data_lock (view_data
);
1170 if (view_data
->is_used
) {
1172 GSList
*to_expand_recurrences
= NULL
;
1175 /* Received a modify before the view was claimed as being complete,
1176 aka fully populated, thus drop any previously known components,
1177 because there is no hope for a merge. */
1178 if (view_data
->lost_components
) {
1179 cal_data_model_remove_components (data_model
, client
, view_data
->lost_components
, NULL
);
1180 g_hash_table_destroy (view_data
->lost_components
);
1181 view_data
->lost_components
= NULL
;
1185 cal_data_model_freeze_all_subscribers (data_model
);
1187 for (link
= objects
; link
; link
= g_slist_next (link
)) {
1188 icalcomponent
*icomp
= link
->data
;
1190 if (!icomp
|| !icalcomponent_get_uid (icomp
))
1193 if (data_model
->priv
->expand_recurrences
&&
1194 !e_cal_util_component_is_instance (icomp
) &&
1195 e_cal_util_component_has_recurrences (icomp
)) {
1196 /* This component requires an expand of recurrences, which
1197 will be done in a dedicated thread, thus remember it */
1198 to_expand_recurrences
= g_slist_prepend (to_expand_recurrences
,
1199 icalcomponent_new_clone (icomp
));
1201 /* Single or detached instance, the simple case */
1202 ECalComponent
*comp
;
1203 ComponentData
*comp_data
;
1204 time_t instance_start
, instance_end
;
1206 comp
= e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icomp
));
1210 cal_comp_get_instance_times (client
, icomp
, data_model
->priv
->zone
, &instance_start
, NULL
, &instance_end
, NULL
, NULL
);
1212 if (instance_end
> instance_start
)
1215 comp_data
= component_data_new (comp
, instance_start
, instance_end
,
1216 e_cal_util_component_is_instance (icomp
));
1218 cal_data_model_process_added_component (data_model
, view_data
, comp_data
, NULL
);
1220 g_object_unref (comp
);
1224 cal_data_model_thaw_all_subscribers (data_model
);
1226 if (to_expand_recurrences
) {
1227 view_data_lock (view_data
);
1228 view_data
->to_expand_recurrences
= g_slist_concat (
1229 view_data
->to_expand_recurrences
, to_expand_recurrences
);
1230 g_atomic_int_inc (&view_data
->pending_expand_recurrences
);
1231 view_data_unlock (view_data
);
1233 cal_data_model_submit_internal_thread_job (data_model
,
1234 cal_data_model_expand_recurrences_thread
, g_object_ref (client
));
1238 view_data_unlock (view_data
);
1239 view_data_unref (view_data
);
1241 g_clear_object (&client
);
1245 cal_data_model_view_objects_added (ECalClientView
*view
,
1246 const GSList
*objects
,
1247 ECalDataModel
*data_model
)
1249 cal_data_model_process_modified_or_added_objects (view
, objects
, data_model
, TRUE
);
1253 cal_data_model_view_objects_modified (ECalClientView
*view
,
1254 const GSList
*objects
,
1255 ECalDataModel
*data_model
)
1257 cal_data_model_process_modified_or_added_objects (view
, objects
, data_model
, FALSE
);
1261 cal_data_model_view_objects_removed (ECalClientView
*view
,
1263 ECalDataModel
*data_model
)
1265 ViewData
*view_data
;
1269 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
1273 client
= e_cal_client_view_ref_client (view
);
1279 view_data
= g_hash_table_lookup (data_model
->priv
->views
, client
);
1281 g_clear_object (&client
);
1284 view_data_ref (view_data
);
1285 g_warn_if_fail (view_data
->view
== view
);
1293 view_data_lock (view_data
);
1294 if (view_data
->is_used
) {
1295 GHashTable
*gathered_uids
;
1296 GList
*removed
= NULL
, *rlink
;
1298 gathered_uids
= g_hash_table_new (g_str_hash
, g_str_equal
);
1300 for (link
= uids
; link
; link
= g_slist_next (link
)) {
1301 const ECalComponentId
*id
= link
->data
;
1304 if (!id
->rid
|| !*id
->rid
) {
1305 if (!g_hash_table_contains (gathered_uids
, id
->uid
)) {
1306 GatherComponentsData gather_data
;
1308 gather_data
.uid
= id
->uid
;
1309 gather_data
.pcomponent_ids
= &removed
;
1310 gather_data
.component_ids_hash
= NULL
;
1311 gather_data
.copy_ids
= TRUE
;
1312 gather_data
.all_instances
= TRUE
;
1314 g_hash_table_foreach (view_data
->components
,
1315 cal_data_model_gather_components
, &gather_data
);
1316 if (view_data
->lost_components
)
1317 g_hash_table_foreach (view_data
->lost_components
,
1318 cal_data_model_gather_components
, &gather_data
);
1320 g_hash_table_insert (gathered_uids
, id
->uid
, GINT_TO_POINTER (1));
1323 removed
= g_list_prepend (removed
, e_cal_component_id_copy (id
));
1328 cal_data_model_freeze_all_subscribers (data_model
);
1330 for (rlink
= removed
; rlink
; rlink
= g_list_next (rlink
)) {
1331 ECalComponentId
*id
= rlink
->data
;
1334 ComponentData
*comp_data
;
1335 time_t instance_start
= (time_t) 0, instance_end
= (time_t) 0;
1337 /* Try to limit which subscribers will be notified about removal */
1338 comp_data
= g_hash_table_lookup (view_data
->components
, id
);
1340 instance_start
= comp_data
->instance_start
;
1341 instance_end
= comp_data
->instance_end
;
1342 } else if (view_data
->lost_components
) {
1343 comp_data
= g_hash_table_lookup (view_data
->lost_components
, id
);
1345 instance_start
= comp_data
->instance_start
;
1346 instance_end
= comp_data
->instance_end
;
1350 g_hash_table_remove (view_data
->components
, id
);
1351 if (view_data
->lost_components
)
1352 g_hash_table_remove (view_data
->lost_components
, id
);
1354 cal_data_model_foreach_subscriber_in_range (data_model
, view_data
->client
,
1355 instance_start
, instance_end
,
1356 cal_data_model_remove_one_view_component_cb
, id
);
1360 cal_data_model_thaw_all_subscribers (data_model
);
1362 g_list_free_full (removed
, (GDestroyNotify
) e_cal_component_free_id
);
1363 g_hash_table_destroy (gathered_uids
);
1365 view_data_unlock (view_data
);
1366 view_data_unref (view_data
);
1370 cal_data_model_view_progress (ECalClientView
*view
,
1372 const gchar
*message
,
1373 ECalDataModel
*data_model
)
1375 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
1377 cal_data_model_emit_view_state_changed (data_model
, view
, E_CAL_DATA_MODEL_VIEW_STATE_PROGRESS
, percent
, message
, NULL
);
1381 cal_data_model_view_complete (ECalClientView
*view
,
1382 const GError
*error
,
1383 ECalDataModel
*data_model
)
1385 ViewData
*view_data
;
1388 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
1392 client
= e_cal_client_view_ref_client (view
);
1398 view_data
= g_hash_table_lookup (data_model
->priv
->views
, client
);
1400 g_clear_object (&client
);
1403 view_data_ref (view_data
);
1404 g_warn_if_fail (view_data
->view
== view
);
1412 view_data_lock (view_data
);
1414 view_data
->received_complete
= TRUE
;
1415 if (view_data
->is_used
&&
1416 view_data
->lost_components
&&
1417 !view_data
->pending_expand_recurrences
) {
1418 cal_data_model_remove_components (data_model
, view_data
->client
, view_data
->lost_components
, NULL
);
1419 g_hash_table_destroy (view_data
->lost_components
);
1420 view_data
->lost_components
= NULL
;
1423 cal_data_model_emit_view_state_changed (data_model
, view
, E_CAL_DATA_MODEL_VIEW_STATE_COMPLETE
, 0, NULL
, error
);
1425 view_data_unlock (view_data
);
1426 view_data_unref (view_data
);
1429 typedef struct _CreateViewData
{
1430 ECalDataModel
*data_model
;
1435 create_view_data_free (gpointer ptr
)
1437 CreateViewData
*cv_data
= ptr
;
1440 g_clear_object (&cv_data
->data_model
);
1441 g_clear_object (&cv_data
->client
);
1447 cal_data_model_create_view_thread (EAlertSinkThreadJobData
*job_data
,
1449 GCancellable
*cancellable
,
1452 CreateViewData
*cv_data
= user_data
;
1453 ViewData
*view_data
;
1454 ECalDataModel
*data_model
;
1456 ECalClientView
*view
;
1459 g_return_if_fail (cv_data
!= NULL
);
1461 data_model
= cv_data
->data_model
;
1462 client
= cv_data
->client
;
1463 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
1464 g_return_if_fail (E_IS_CAL_CLIENT (client
));
1468 if (g_cancellable_set_error_if_cancelled (cancellable
, error
)) {
1473 view_data
= g_hash_table_lookup (data_model
->priv
->views
, client
);
1476 g_warn_if_reached ();
1480 filter
= g_strdup (data_model
->priv
->full_filter
);
1482 view_data_ref (view_data
);
1485 view_data_lock (view_data
);
1486 g_warn_if_fail (view_data
->view
== NULL
);
1488 if (!e_cal_client_get_view_sync (client
, filter
, &view_data
->view
, cancellable
, error
)) {
1489 view_data_unlock (view_data
);
1490 view_data_unref (view_data
);
1495 g_warn_if_fail (view_data
->view
!= NULL
);
1497 view_data
->objects_added_id
= g_signal_connect (view_data
->view
, "objects-added",
1498 G_CALLBACK (cal_data_model_view_objects_added
), data_model
);
1499 view_data
->objects_modified_id
= g_signal_connect (view_data
->view
, "objects-modified",
1500 G_CALLBACK (cal_data_model_view_objects_modified
), data_model
);
1501 view_data
->objects_removed_id
= g_signal_connect (view_data
->view
, "objects-removed",
1502 G_CALLBACK (cal_data_model_view_objects_removed
), data_model
);
1503 view_data
->progress_id
= g_signal_connect (view_data
->view
, "progress",
1504 G_CALLBACK (cal_data_model_view_progress
), data_model
);
1505 view_data
->complete_id
= g_signal_connect (view_data
->view
, "complete",
1506 G_CALLBACK (cal_data_model_view_complete
), data_model
);
1508 view
= g_object_ref (view_data
->view
);
1510 view_data_unlock (view_data
);
1511 view_data_unref (view_data
);
1515 if (!g_cancellable_is_cancelled (cancellable
)) {
1516 cal_data_model_emit_view_state_changed (data_model
, view
, E_CAL_DATA_MODEL_VIEW_STATE_START
, 0, NULL
, NULL
);
1517 e_cal_client_view_start (view
, error
);
1520 g_clear_object (&view
);
1523 typedef struct _NotifyRemoveComponentsData
{
1524 ECalDataModel
*data_model
;
1526 } NotifyRemoveComponentsData
;
1529 cal_data_model_notify_remove_components_cb (gpointer key
,
1533 ECalComponentId
*id
= key
;
1534 ComponentData
*comp_data
= value
;
1535 NotifyRemoveComponentsData
*nrc_data
= user_data
;
1537 g_return_if_fail (id
!= NULL
);
1538 g_return_if_fail (comp_data
!= NULL
);
1539 g_return_if_fail (nrc_data
!= NULL
);
1541 cal_data_model_foreach_subscriber_in_range (nrc_data
->data_model
, nrc_data
->client
,
1542 comp_data
->instance_start
, comp_data
->instance_end
,
1543 cal_data_model_remove_one_view_component_cb
, id
);
1547 cal_data_model_update_client_view (ECalDataModel
*data_model
,
1551 ViewData
*view_data
;
1552 CreateViewData
*cv_data
;
1553 const gchar
*alert_ident
= NULL
;
1554 gchar
*description
= NULL
;
1558 view_data
= g_hash_table_lookup (data_model
->priv
->views
, client
);
1560 view_data
= view_data_new (client
);
1561 g_hash_table_insert (data_model
->priv
->views
, client
, view_data
);
1564 view_data_lock (view_data
);
1566 if (view_data
->cancellable
)
1567 g_cancellable_cancel (view_data
->cancellable
);
1568 g_clear_object (&view_data
->cancellable
);
1570 if (view_data
->view
) {
1571 view_data_disconnect_view (view_data
);
1572 cal_data_model_emit_view_state_changed (data_model
, view_data
->view
, E_CAL_DATA_MODEL_VIEW_STATE_STOP
, 0, NULL
, NULL
);
1573 g_clear_object (&view_data
->view
);
1576 if (!view_data
->received_complete
) {
1577 NotifyRemoveComponentsData nrc_data
;
1579 nrc_data
.data_model
= data_model
;
1580 nrc_data
.client
= client
;
1582 cal_data_model_freeze_all_subscribers (data_model
);
1584 g_hash_table_foreach (view_data
->components
,
1585 cal_data_model_notify_remove_components_cb
, &nrc_data
);
1587 g_hash_table_remove_all (view_data
->components
);
1588 if (view_data
->lost_components
) {
1589 g_hash_table_foreach (view_data
->lost_components
,
1590 cal_data_model_notify_remove_components_cb
, &nrc_data
);
1592 g_hash_table_destroy (view_data
->lost_components
);
1593 view_data
->lost_components
= NULL
;
1596 cal_data_model_thaw_all_subscribers (data_model
);
1598 if (view_data
->lost_components
) {
1599 NotifyRemoveComponentsData nrc_data
;
1601 nrc_data
.data_model
= data_model
;
1602 nrc_data
.client
= client
;
1604 cal_data_model_freeze_all_subscribers (data_model
);
1605 g_hash_table_foreach (view_data
->lost_components
,
1606 cal_data_model_notify_remove_components_cb
, &nrc_data
);
1607 cal_data_model_thaw_all_subscribers (data_model
);
1609 g_hash_table_destroy (view_data
->lost_components
);
1610 view_data
->lost_components
= NULL
;
1613 view_data
->lost_components
= view_data
->components
;
1614 view_data
->components
= g_hash_table_new_full (
1615 (GHashFunc
) e_cal_component_id_hash
, (GEqualFunc
) e_cal_component_id_equal
,
1616 (GDestroyNotify
) e_cal_component_free_id
, component_data_free
);
1619 view_data_unlock (view_data
);
1621 if (!data_model
->priv
->full_filter
) {
1626 source
= e_client_get_source (E_CLIENT (client
));
1628 switch (e_cal_client_get_source_type (client
)) {
1629 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS
:
1630 alert_ident
= "calendar:failed-create-view-calendar";
1631 description
= g_strdup_printf (_("Creating view for calendar “%s”"), e_source_get_display_name (source
));
1633 case E_CAL_CLIENT_SOURCE_TYPE_TASKS
:
1634 alert_ident
= "calendar:failed-create-view-tasks";
1635 description
= g_strdup_printf (_("Creating view for task list “%s”"), e_source_get_display_name (source
));
1637 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS
:
1638 alert_ident
= "calendar:failed-create-view-memos";
1639 description
= g_strdup_printf (_("Creating view for memo list “%s”"), e_source_get_display_name (source
));
1641 case E_CAL_CLIENT_SOURCE_TYPE_LAST
:
1642 g_warn_if_reached ();
1647 cv_data
= g_new0 (CreateViewData
, 1);
1648 cv_data
->data_model
= g_object_ref (data_model
);
1649 cv_data
->client
= g_object_ref (client
);
1651 view_data
->received_complete
= FALSE
;
1652 view_data
->cancellable
= e_cal_data_model_submit_thread_job (data_model
,
1653 description
, alert_ident
, e_source_get_display_name (source
),
1654 cal_data_model_create_view_thread
, cv_data
, create_view_data_free
);
1656 g_free (description
);
1662 cal_data_model_remove_client_view (ECalDataModel
*data_model
,
1665 ViewData
*view_data
;
1669 view_data
= g_hash_table_lookup (data_model
->priv
->views
, client
);
1672 NotifyRemoveComponentsData nrc_data
;
1674 view_data_lock (view_data
);
1676 nrc_data
.data_model
= data_model
;
1677 nrc_data
.client
= client
;
1679 cal_data_model_freeze_all_subscribers (data_model
);
1681 g_hash_table_foreach (view_data
->components
,
1682 cal_data_model_notify_remove_components_cb
, &nrc_data
);
1683 g_hash_table_remove_all (view_data
->components
);
1685 if (view_data
->lost_components
) {
1686 g_hash_table_foreach (view_data
->lost_components
,
1687 cal_data_model_notify_remove_components_cb
, &nrc_data
);
1688 g_hash_table_remove_all (view_data
->lost_components
);
1691 cal_data_model_thaw_all_subscribers (data_model
);
1693 if (view_data
->view
)
1694 cal_data_model_emit_view_state_changed (data_model
, view_data
->view
, E_CAL_DATA_MODEL_VIEW_STATE_STOP
, 0, NULL
, NULL
);
1696 view_data
->is_used
= FALSE
;
1697 view_data_unlock (view_data
);
1699 g_hash_table_remove (data_model
->priv
->views
, client
);
1706 cal_data_model_add_to_subscriber (ECalDataModel
*data_model
,
1708 const ECalComponentId
*id
,
1709 ECalComponent
*component
,
1710 time_t instance_start
,
1711 time_t instance_end
,
1714 ECalDataModelSubscriber
*subscriber
= user_data
;
1716 g_return_val_if_fail (subscriber
!= NULL
, FALSE
);
1717 g_return_val_if_fail (id
!= NULL
, FALSE
);
1719 e_cal_data_model_subscriber_component_added (subscriber
, client
, component
);
1725 cal_data_model_add_to_subscriber_except_its_range (ECalDataModel
*data_model
,
1727 const ECalComponentId
*id
,
1728 ECalComponent
*component
,
1729 time_t instance_start
,
1730 time_t instance_end
,
1733 SubscriberData
*subs_data
= user_data
;
1735 g_return_val_if_fail (subs_data
!= NULL
, FALSE
);
1736 g_return_val_if_fail (id
!= NULL
, FALSE
);
1738 /* subs_data should have set the old time range, which
1739 means only components which didn't fit into the old
1740 time range will be added */
1741 if (!(instance_start
<= subs_data
->range_end
&&
1742 instance_end
>= subs_data
->range_start
))
1743 e_cal_data_model_subscriber_component_added (subs_data
->subscriber
, client
, component
);
1749 cal_data_model_remove_from_subscriber_except_its_range (ECalDataModel
*data_model
,
1751 const ECalComponentId
*id
,
1752 ECalComponent
*component
,
1753 time_t instance_start
,
1754 time_t instance_end
,
1757 SubscriberData
*subs_data
= user_data
;
1759 g_return_val_if_fail (subs_data
!= NULL
, FALSE
);
1760 g_return_val_if_fail (id
!= NULL
, FALSE
);
1762 /* subs_data should have set the new time range, which
1763 means only components which don't fit into this new
1764 time range will be removed */
1765 if (!(instance_start
<= subs_data
->range_end
&&
1766 instance_end
>= subs_data
->range_start
))
1767 e_cal_data_model_subscriber_component_removed (subs_data
->subscriber
, client
, id
->uid
, id
->rid
);
1773 cal_data_model_set_client_default_zone_cb (gpointer key
,
1777 ECalClient
*client
= value
;
1778 icaltimezone
*zone
= user_data
;
1780 g_return_if_fail (E_IS_CAL_CLIENT (client
));
1781 g_return_if_fail (zone
!= NULL
);
1783 e_cal_client_set_default_timezone (client
, zone
);
1787 cal_data_model_rebuild_everything (ECalDataModel
*data_model
,
1788 gboolean complete_rebuild
)
1790 GHashTableIter iter
;
1791 gpointer key
, value
;
1793 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
1797 if (data_model
->priv
->views_update_freeze
> 0) {
1798 data_model
->priv
->views_update_required
= TRUE
;
1803 data_model
->priv
->views_update_required
= FALSE
;
1805 g_hash_table_iter_init (&iter
, data_model
->priv
->clients
);
1806 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
1807 ECalClient
*client
= value
;
1809 if (complete_rebuild
)
1810 cal_data_model_remove_client_view (data_model
, client
);
1811 cal_data_model_update_client_view (data_model
, client
);
1818 cal_data_model_update_time_range (ECalDataModel
*data_model
)
1820 time_t range_start
, range_end
;
1822 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
1826 if (data_model
->priv
->disposing
) {
1832 range_start
= data_model
->priv
->range_start
;
1833 range_end
= data_model
->priv
->range_end
;
1835 cal_data_model_calc_range (data_model
, &range_start
, &range_end
);
1837 if (data_model
->priv
->range_start
!= range_start
||
1838 data_model
->priv
->range_end
!= range_end
) {
1839 data_model
->priv
->range_start
= range_start
;
1840 data_model
->priv
->range_end
= range_end
;
1842 if (cal_data_model_update_full_filter (data_model
))
1843 cal_data_model_rebuild_everything (data_model
, FALSE
);
1850 cal_data_model_set_property (GObject
*object
,
1852 const GValue
*value
,
1855 switch (property_id
) {
1856 case PROP_EXPAND_RECURRENCES
:
1857 e_cal_data_model_set_expand_recurrences (
1858 E_CAL_DATA_MODEL (object
),
1859 g_value_get_boolean (value
));
1863 e_cal_data_model_set_timezone (
1864 E_CAL_DATA_MODEL (object
),
1865 g_value_get_pointer (value
));
1869 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1873 cal_data_model_get_property (GObject
*object
,
1878 switch (property_id
) {
1879 case PROP_EXPAND_RECURRENCES
:
1880 g_value_set_boolean (
1882 e_cal_data_model_get_expand_recurrences (
1883 E_CAL_DATA_MODEL (object
)));
1887 g_value_set_pointer (
1889 e_cal_data_model_get_timezone (
1890 E_CAL_DATA_MODEL (object
)));
1894 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1898 cal_data_model_dispose (GObject
*object
)
1900 ECalDataModel
*data_model
= E_CAL_DATA_MODEL (object
);
1902 data_model
->priv
->disposing
= TRUE
;
1904 /* Chain up to parent's method. */
1905 G_OBJECT_CLASS (e_cal_data_model_parent_class
)->dispose (object
);
1909 cal_data_model_finalize (GObject
*object
)
1911 ECalDataModel
*data_model
= E_CAL_DATA_MODEL (object
);
1913 g_thread_pool_free (data_model
->priv
->thread_pool
, TRUE
, FALSE
);
1914 g_hash_table_destroy (data_model
->priv
->clients
);
1915 g_hash_table_destroy (data_model
->priv
->views
);
1916 g_slist_free_full (data_model
->priv
->subscribers
, subscriber_data_free
);
1917 g_free (data_model
->priv
->filter
);
1918 g_free (data_model
->priv
->full_filter
);
1920 e_weak_ref_free (data_model
->priv
->submit_thread_job_responder
);
1921 g_rec_mutex_clear (&data_model
->priv
->props_lock
);
1923 /* Chain up to parent's method. */
1924 G_OBJECT_CLASS (e_cal_data_model_parent_class
)->finalize (object
);
1928 e_cal_data_model_class_init (ECalDataModelClass
*class)
1930 GObjectClass
*object_class
;
1932 g_type_class_add_private (class, sizeof (ECalDataModelPrivate
));
1934 object_class
= G_OBJECT_CLASS (class);
1935 object_class
->set_property
= cal_data_model_set_property
;
1936 object_class
->get_property
= cal_data_model_get_property
;
1937 object_class
->dispose
= cal_data_model_dispose
;
1938 object_class
->finalize
= cal_data_model_finalize
;
1940 g_object_class_install_property (
1942 PROP_EXPAND_RECURRENCES
,
1943 g_param_spec_boolean (
1944 "expand-recurrences",
1945 "Expand Recurrences",
1948 G_PARAM_READWRITE
));
1950 g_object_class_install_property (
1953 g_param_spec_pointer (
1957 G_PARAM_READWRITE
));
1959 signals
[VIEW_STATE_CHANGED
] = g_signal_new (
1960 "view-state-changed",
1961 G_TYPE_FROM_CLASS (class),
1963 G_STRUCT_OFFSET (ECalDataModelClass
, view_state_changed
),
1966 G_TYPE_NONE
, 5, E_TYPE_CAL_CLIENT_VIEW
, G_TYPE_UINT
, G_TYPE_UINT
, G_TYPE_STRING
, G_TYPE_ERROR
);
1970 e_cal_data_model_init (ECalDataModel
*data_model
)
1972 data_model
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (data_model
, E_TYPE_CAL_DATA_MODEL
, ECalDataModelPrivate
);
1974 /* Suppose the data_model is always created in the main/UI thread */
1975 data_model
->priv
->main_thread
= g_thread_self ();
1976 data_model
->priv
->thread_pool
= g_thread_pool_new (
1977 cal_data_model_internal_thread_job_func
, data_model
, 5, FALSE
, NULL
);
1979 data_model
->priv
->clients
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_object_unref
);
1980 data_model
->priv
->views
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, view_data_unref
);
1981 data_model
->priv
->subscribers
= NULL
;
1983 data_model
->priv
->disposing
= FALSE
;
1984 data_model
->priv
->expand_recurrences
= FALSE
;
1985 data_model
->priv
->zone
= icaltimezone_get_utc_timezone ();
1987 data_model
->priv
->views_update_freeze
= 0;
1988 data_model
->priv
->views_update_required
= FALSE
;
1990 g_rec_mutex_init (&data_model
->priv
->props_lock
);
1994 * e_cal_data_model_new:
1995 * @func: a function to be called when the data model needs to create
1996 * a thread job within UI
1997 * @func_responder: (allow none): a responder for @func, which is passed
1998 * as the first paramter; can be #NULL
2000 * Creates a new instance of #ECalDataModel. The @func is mandatory, because
2001 * it is used to create new thread jobs with UI feedback.
2003 * Returns: (transfer full): A new #ECalDataModel instance
2008 e_cal_data_model_new (ECalDataModelSubmitThreadJobFunc func
,
2009 GObject
*func_responder
)
2011 ECalDataModel
*data_model
;
2013 g_return_val_if_fail (func
!= NULL
, NULL
);
2015 data_model
= g_object_new (E_TYPE_CAL_DATA_MODEL
, NULL
);
2016 data_model
->priv
->submit_thread_job_func
= func
;
2017 data_model
->priv
->submit_thread_job_responder
= e_weak_ref_new (func_responder
);
2023 * e_cal_data_model_new_clone:
2024 * @src_data_model: an #ECalDataModel to clone
2026 * Creates a clone of @src_data_model, which means a copy with the same clients, filter and
2027 * other properties set (not the subscribers).
2029 * Returns: (transfer full): A new #ECalDataModel instance deriving settings from @src_data_model
2034 e_cal_data_model_new_clone (ECalDataModel
*src_data_model
)
2036 ECalDataModel
*clone
;
2037 GObject
*func_responder
;
2038 GList
*clients
, *link
;
2040 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (src_data_model
), NULL
);
2042 func_responder
= g_weak_ref_get (src_data_model
->priv
->submit_thread_job_responder
);
2043 g_return_val_if_fail (func_responder
!= NULL
, NULL
);
2045 clone
= e_cal_data_model_new (src_data_model
->priv
->submit_thread_job_func
, func_responder
);
2047 g_clear_object (&func_responder
);
2049 e_cal_data_model_set_expand_recurrences (clone
, e_cal_data_model_get_expand_recurrences (src_data_model
));
2050 e_cal_data_model_set_timezone (clone
, e_cal_data_model_get_timezone (src_data_model
));
2051 e_cal_data_model_set_filter (clone
, src_data_model
->priv
->filter
);
2053 clients
= e_cal_data_model_get_clients (src_data_model
);
2054 for (link
= clients
; link
; link
= g_list_next (link
)) {
2055 ECalClient
*client
= link
->data
;
2057 e_cal_data_model_add_client (clone
, client
);
2060 g_list_free_full (clients
, g_object_unref
);
2066 * e_cal_data_model_get_disposing:
2067 * @data_model: an #EDataModel instance
2069 * Obtains whether the @data_model is disposing and will be freed (soon).
2071 * Returns: Whether the @data_model is disposing.
2076 e_cal_data_model_get_disposing (ECalDataModel
*data_model
)
2080 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), FALSE
);
2084 disposing
= data_model
->priv
->disposing
;
2092 * e_cal_data_model_set_disposing:
2093 * @data_model: an #EDataModel instance
2094 * @disposing: whether the object is disposing
2096 * Sets whether the @data_model is disposing itself (soon).
2097 * If set to %TRUE, then no updates are done on changes
2098 * which would otherwise trigger view and subscriber updates.
2103 e_cal_data_model_set_disposing (ECalDataModel
*data_model
,
2106 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2110 if ((data_model
->priv
->disposing
? 1 : 0) == (disposing
? 1 : 0)) {
2115 data_model
->priv
->disposing
= disposing
;
2121 * e_cal_data_model_get_expand_recurrences:
2122 * @data_model: an #EDataModel instance
2124 * Obtains whether the @data_model expands recurrences of recurring
2125 * components by default. The default value is #FALSE, to not expand
2128 * Returns: Whether the @data_model expands recurrences of recurring
2134 e_cal_data_model_get_expand_recurrences (ECalDataModel
*data_model
)
2136 gboolean expand_recurrences
;
2138 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), FALSE
);
2142 expand_recurrences
= data_model
->priv
->expand_recurrences
;
2146 return expand_recurrences
;
2150 * e_cal_data_model_set_expand_recurrences:
2151 * @data_model: an #EDataModel instance
2152 * @expand_recurrences: whether to expand recurrences
2154 * Sets whether the @data_model should expand recurrences of recurring
2155 * components by default.
2160 e_cal_data_model_set_expand_recurrences (ECalDataModel
*data_model
,
2161 gboolean expand_recurrences
)
2163 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2167 if ((data_model
->priv
->expand_recurrences
? 1 : 0) == (expand_recurrences
? 1 : 0)) {
2172 data_model
->priv
->expand_recurrences
= expand_recurrences
;
2174 cal_data_model_rebuild_everything (data_model
, TRUE
);
2180 * e_cal_data_model_get_timezone:
2181 * @data_model: an #EDataModel instance
2183 * Obtains a timezone being used for calendar views. The returned
2184 * timezone is owned by the @data_model.
2186 * Returns: (transfer none): An #icaltimezone being used for calendar views.
2191 e_cal_data_model_get_timezone (ECalDataModel
*data_model
)
2195 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), NULL
);
2199 zone
= data_model
->priv
->zone
;
2206 * e_cal_data_model_set_timezone:
2207 * @data_model: an #EDataModel instance
2208 * @zone: an #icaltimezone
2210 * Sets a trimezone to be used for calendar views. This change
2211 * regenerates all views.
2216 e_cal_data_model_set_timezone (ECalDataModel
*data_model
,
2219 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2220 g_return_if_fail (zone
!= NULL
);
2224 if (data_model
->priv
->zone
!= zone
) {
2225 data_model
->priv
->zone
= zone
;
2227 g_hash_table_foreach (data_model
->priv
->clients
, cal_data_model_set_client_default_zone_cb
, zone
);
2229 if (cal_data_model_update_full_filter (data_model
))
2230 cal_data_model_rebuild_everything (data_model
, TRUE
);
2237 * e_cal_data_model_set_filter:
2238 * @data_model: an #EDataModel instance
2239 * @sexp: an expression defining a filter
2241 * Sets an additional filter for the views. The filter should not
2242 * contain time constraints, these are meant to be defined by
2248 e_cal_data_model_set_filter (ECalDataModel
*data_model
,
2251 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2252 g_return_if_fail (sexp
!= NULL
);
2259 if (g_strcmp0 (data_model
->priv
->filter
, sexp
) != 0) {
2260 g_free (data_model
->priv
->filter
);
2261 data_model
->priv
->filter
= g_strdup (sexp
);
2263 if (cal_data_model_update_full_filter (data_model
))
2264 cal_data_model_rebuild_everything (data_model
, TRUE
);
2271 * e_cal_data_model_dup_filter:
2272 * @data_model: an #EDataModel instance
2274 * Obtains currently used filter (an expression) for the views.
2276 * Returns: (transfer full): A copy of the currently used
2277 * filter for views. Free it with g_free() when done with it.
2278 * Returns #NULL when there is no extra filter set.
2283 e_cal_data_model_dup_filter (ECalDataModel
*data_model
)
2287 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), NULL
);
2291 filter
= g_strdup (data_model
->priv
->filter
);
2299 * e_cal_data_model_add_client:
2300 * @data_model: an #EDataModel instance
2301 * @client: an #ECalClient
2303 * Adds a new @client into the set of clients which should be used
2304 * to populate data for subscribers. Adding the same client multiple
2305 * times does nothing.
2310 e_cal_data_model_add_client (ECalDataModel
*data_model
,
2315 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2316 g_return_if_fail (E_IS_CAL_CLIENT (client
));
2318 source
= e_client_get_source (E_CLIENT (client
));
2319 g_return_if_fail (E_IS_SOURCE (source
));
2320 g_return_if_fail (e_source_get_uid (source
) != NULL
);
2324 if (g_hash_table_contains (data_model
->priv
->clients
, e_source_get_uid (source
))) {
2329 g_hash_table_insert (data_model
->priv
->clients
, e_source_dup_uid (source
), g_object_ref (client
));
2331 e_cal_client_set_default_timezone (client
, data_model
->priv
->zone
);
2333 cal_data_model_update_client_view (data_model
, client
);
2339 * e_cal_data_model_remove_client:
2340 * @uid: a UID of a client to remove
2342 * Removes a client identified by @uid from a set of clients
2343 * which populate the data for subscribers. Removing the client
2344 * which is not used in the @data_model does nothing.
2349 e_cal_data_model_remove_client (ECalDataModel
*data_model
,
2354 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2355 g_return_if_fail (uid
!= NULL
);
2359 client
= g_hash_table_lookup (data_model
->priv
->clients
, uid
);
2365 cal_data_model_remove_client_view (data_model
, client
);
2366 g_hash_table_remove (data_model
->priv
->clients
, uid
);
2372 * e_cal_data_model_ref_client:
2373 * @data_model: an #EDataModel instance
2374 * @uid: a UID of a client to return
2376 * Obtains an #ECalClient with given @uid from the set of clients
2377 * being used by the @data_modal. Returns #NULL, if no such client
2378 * is used by the @data_model.
2380 * Returns: (tranfer full): An #ECalClient with given @uid being
2381 * used by @data_model, or NULL, when no such is used by
2382 * the @data_model. Unref returned (non-NULL) client with
2383 * g_object_unref() when done with it.
2388 e_cal_data_model_ref_client (ECalDataModel
*data_model
,
2393 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), NULL
);
2397 client
= g_hash_table_lookup (data_model
->priv
->clients
, uid
);
2399 g_object_ref (client
);
2407 * e_cal_data_model_get_clients:
2408 * @data_model: an #EDataModel instance
2410 * Obtains a list of all clients being used by the @data_model.
2411 * Each client in the returned list is referenced and the list
2412 * itself is also newly allocated, thus free it with
2413 * g_list_free_full (list, g_object_unref); when done with it.
2415 * Returns: (transfer full): A list of currently used #ECalClient-s.
2420 e_cal_data_model_get_clients (ECalDataModel
*data_model
)
2424 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), NULL
);
2428 clients
= g_hash_table_get_values (data_model
->priv
->clients
);
2429 g_list_foreach (clients
, (GFunc
) g_object_ref
, NULL
);
2437 cal_data_model_prepend_component (ECalDataModel
*data_model
,
2439 const ECalComponentId
*id
,
2440 ECalComponent
*comp
,
2441 time_t instance_start
,
2442 time_t instance_end
,
2445 GSList
**components
= user_data
;
2447 g_return_val_if_fail (components
!= NULL
, FALSE
);
2448 g_return_val_if_fail (comp
!= NULL
, FALSE
);
2450 *components
= g_slist_prepend (*components
, g_object_ref (comp
));
2456 * e_cal_data_model_get_components:
2457 * @data_model: an #EDataModel instance
2458 * @in_range_start: Start of the time range
2459 * @in_range_end: End of the time range
2461 * Obtains a list of components from the given time range. The time range is
2462 * clamp by the actual time range defined by subscribers (if there is no
2463 * subscriber, or all subscribers define times out of the given time range,
2464 * then no components are returned).
2466 * Returns: (transfer full): A #GSList of #ECalComponent-s known for the given
2467 * time range in the time of the call. The #GSList, togher with the components,
2468 * is owned by the caller, which should free it with
2469 * g_slist_free_full (list, g_object_unref); when done with it.
2471 * Note: A special case when both @in_range_start and @in_range_end are zero
2472 * is treated as a request for all known components.
2477 e_cal_data_model_get_components (ECalDataModel
*data_model
,
2478 time_t in_range_start
,
2479 time_t in_range_end
)
2481 GSList
*components
= NULL
;
2483 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), NULL
);
2485 e_cal_data_model_foreach_component (data_model
, in_range_start
, in_range_end
,
2486 cal_data_model_prepend_component
, &components
);
2488 return g_slist_reverse (components
);
2492 cal_data_model_foreach_component (ECalDataModel
*data_model
,
2493 time_t in_range_start
,
2494 time_t in_range_end
,
2495 ECalDataModelForeachFunc func
,
2497 gboolean include_lost_components
)
2499 GHashTableIter viter
;
2500 gpointer key
, value
;
2501 gboolean checked_all
= TRUE
;
2503 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), FALSE
);
2504 g_return_val_if_fail (func
!= NULL
, FALSE
);
2508 /* Is the given time range in the currently used time range? */
2509 if (!(in_range_start
== in_range_end
&& in_range_start
== (time_t) 0) &&
2510 (in_range_start
>= data_model
->priv
->range_end
||
2511 in_range_end
<= data_model
->priv
->range_start
)) {
2516 g_hash_table_iter_init (&viter
, data_model
->priv
->views
);
2517 while (checked_all
&& g_hash_table_iter_next (&viter
, &key
, &value
)) {
2518 ViewData
*view_data
= value
;
2519 GHashTableIter citer
;
2524 view_data_lock (view_data
);
2526 g_hash_table_iter_init (&citer
, view_data
->components
);
2527 while (checked_all
&& g_hash_table_iter_next (&citer
, &key
, &value
)) {
2528 ECalComponentId
*id
= key
;
2529 ComponentData
*comp_data
= value
;
2534 if ((in_range_start
== in_range_end
&& in_range_start
== (time_t) 0) ||
2535 (comp_data
->instance_start
< in_range_end
&& comp_data
->instance_end
> in_range_start
) ||
2536 (comp_data
->instance_start
== comp_data
->instance_end
&& comp_data
->instance_end
== in_range_start
)) {
2537 if (!func (data_model
, view_data
->client
, id
, comp_data
->component
,
2538 comp_data
->instance_start
, comp_data
->instance_end
, user_data
))
2539 checked_all
= FALSE
;
2543 if (include_lost_components
&& view_data
->lost_components
) {
2544 g_hash_table_iter_init (&citer
, view_data
->lost_components
);
2545 while (checked_all
&& g_hash_table_iter_next (&citer
, &key
, &value
)) {
2546 ECalComponentId
*id
= key
;
2547 ComponentData
*comp_data
= value
;
2552 if ((in_range_start
== in_range_end
&& in_range_start
== (time_t) 0) ||
2553 (comp_data
->instance_start
< in_range_end
&& comp_data
->instance_end
> in_range_start
) ||
2554 (comp_data
->instance_start
== comp_data
->instance_end
&& comp_data
->instance_end
== in_range_start
)) {
2555 if (!func (data_model
, view_data
->client
, id
, comp_data
->component
,
2556 comp_data
->instance_start
, comp_data
->instance_end
, user_data
))
2557 checked_all
= FALSE
;
2562 view_data_unlock (view_data
);
2571 * e_cal_data_model_foreach_component:
2572 * @data_model: an #EDataModel instance
2573 * @in_range_start: Start of the time range
2574 * @in_range_end: End of the time range
2575 * @func: a function to be called for each component in the given time range
2576 * @user_data: user data being passed into the @func
2578 * Calls @func for each component in the given time range. The time range is
2579 * clamp by the actual time range defined by subscribers (if there is no
2580 * subscriber, or all subscribers define times out of the given time range,
2581 * then the function is not called at all and a #FALSE is returned).
2583 * The @func returns #TRUE to continue the traversal. If it wants to stop
2584 * the traversal earlier, then it returns #FALSE.
2586 * Returns: Whether all the components were checked. The returned value is
2587 * usually #TRUE, unless the @func stops traversal earlier.
2589 * Note: A special case when both @in_range_start and @in_range_end are zero
2590 * is treated as a request for all known components.
2595 e_cal_data_model_foreach_component (ECalDataModel
*data_model
,
2596 time_t in_range_start
,
2597 time_t in_range_end
,
2598 ECalDataModelForeachFunc func
,
2601 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), FALSE
);
2602 g_return_val_if_fail (func
!= NULL
, FALSE
);
2604 return cal_data_model_foreach_component (data_model
, in_range_start
, in_range_end
, func
, user_data
, FALSE
);
2608 * e_cal_data_model_subscribe:
2609 * @data_model: an #EDataModel instance
2610 * @subscriber: an #ECalDataModelSubscriber instance
2611 * @range_start: Start of the time range used by the @subscriber
2612 * @range_end: End of the time range used by the @subscriber
2614 * Either adds a new @subscriber to the set of subscribers for this
2615 * @data_model, or changes a time range used by the @subscriber,
2616 * in case it was added to the @data_model earlier.
2618 * Reference count of the @subscriber is increased by one, in case
2619 * it is newly added. The reference count is decreased by one
2620 * when e_cal_data_model_unsubscribe() is called.
2622 * Note: A special case when both @range_start and @range_end are zero
2623 * is treated as a request with no time constraint. This limits
2624 * the result only to those components which satisfy given filter.
2629 e_cal_data_model_subscribe (ECalDataModel
*data_model
,
2630 ECalDataModelSubscriber
*subscriber
,
2634 SubscriberData
*subs_data
= NULL
;
2637 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2638 g_return_if_fail (E_IS_CAL_DATA_MODEL_SUBSCRIBER (subscriber
));
2642 for (link
= data_model
->priv
->subscribers
; link
; link
= g_slist_next (link
)) {
2643 SubscriberData
*subs_data
= link
->data
;
2648 if (subs_data
->subscriber
== subscriber
)
2653 time_t new_range_start
= range_start
, new_range_end
= range_end
;
2654 time_t old_range_start
, old_range_end
;
2656 /* The subscriber updates its time range (it is already known) */
2657 subs_data
= link
->data
;
2659 /* No range change */
2660 if (range_start
== subs_data
->range_start
&&
2661 range_end
== subs_data
->range_end
) {
2666 old_range_start
= subs_data
->range_start
;
2667 old_range_end
= subs_data
->range_end
;
2669 if (new_range_start
== (time_t) 0 && new_range_end
== (time_t) 0) {
2670 new_range_start
= data_model
->priv
->range_start
;
2671 new_range_end
= data_model
->priv
->range_end
;
2674 if (new_range_start
== (time_t) 0 && new_range_end
== (time_t) 0) {
2675 /* The subscriber is looking for everything and the data_model has everything too */
2676 e_cal_data_model_subscriber_freeze (subs_data
->subscriber
);
2677 cal_data_model_foreach_component (data_model
,
2678 new_range_start
, old_range_start
,
2679 cal_data_model_add_to_subscriber_except_its_range
, subs_data
, TRUE
);
2680 e_cal_data_model_subscriber_thaw (subs_data
->subscriber
);
2682 e_cal_data_model_subscriber_freeze (subs_data
->subscriber
);
2684 if (new_range_start
>= old_range_end
||
2685 new_range_end
<= old_range_start
) {
2686 subs_data
->range_start
= range_start
;
2687 subs_data
->range_end
= range_end
;
2689 /* Completely new range, not overlapping with the former range,
2690 everything previously added can be removed... */
2691 cal_data_model_foreach_component (data_model
,
2692 old_range_start
, old_range_end
,
2693 cal_data_model_remove_from_subscriber_except_its_range
, subs_data
, TRUE
);
2695 subs_data
->range_start
= old_range_start
;
2696 subs_data
->range_end
= old_range_end
;
2698 /* ...and components from the new range can be added */
2699 cal_data_model_foreach_component (data_model
,
2700 new_range_start
, new_range_end
,
2701 cal_data_model_add_to_subscriber_except_its_range
, subs_data
, TRUE
);
2703 if (new_range_start
< old_range_start
) {
2704 /* Add those known in the new extended range from the start */
2705 cal_data_model_foreach_component (data_model
,
2706 new_range_start
, old_range_start
,
2707 cal_data_model_add_to_subscriber_except_its_range
, subs_data
, TRUE
);
2708 } else if (new_range_start
> old_range_start
) {
2709 subs_data
->range_start
= range_start
;
2710 subs_data
->range_end
= range_end
;
2712 /* Remove those out of the new range from the start */
2713 cal_data_model_foreach_component (data_model
,
2714 old_range_start
, new_range_start
,
2715 cal_data_model_remove_from_subscriber_except_its_range
, subs_data
, TRUE
);
2717 subs_data
->range_start
= old_range_start
;
2718 subs_data
->range_end
= old_range_end
;
2721 if (new_range_end
> old_range_end
) {
2722 /* Add those known in the new extended range from the end */
2723 cal_data_model_foreach_component (data_model
,
2724 old_range_end
, new_range_end
,
2725 cal_data_model_add_to_subscriber_except_its_range
, subs_data
, TRUE
);
2726 } else if (new_range_end
< old_range_end
) {
2727 subs_data
->range_start
= range_start
;
2728 subs_data
->range_end
= range_end
;
2730 /* Remove those out of the new range from the end */
2731 cal_data_model_foreach_component (data_model
,
2732 new_range_end
, old_range_end
,
2733 cal_data_model_remove_from_subscriber_except_its_range
, subs_data
, TRUE
);
2735 subs_data
->range_start
= old_range_start
;
2736 subs_data
->range_end
= old_range_end
;
2740 e_cal_data_model_subscriber_thaw (subs_data
->subscriber
);
2743 subs_data
->range_start
= range_start
;
2744 subs_data
->range_end
= range_end
;
2746 subs_data
= subscriber_data_new (subscriber
, range_start
, range_end
);
2748 data_model
->priv
->subscribers
= g_slist_prepend (data_model
->priv
->subscribers
, subs_data
);
2750 e_cal_data_model_subscriber_freeze (subscriber
);
2751 cal_data_model_foreach_component (data_model
, range_start
, range_end
,
2752 cal_data_model_add_to_subscriber
, subscriber
, TRUE
);
2753 e_cal_data_model_subscriber_thaw (subscriber
);
2756 cal_data_model_update_time_range (data_model
);
2762 * e_cal_data_model_unsubscribe:
2763 * @data_model: an #EDataModel instance
2764 * @subscriber: an #ECalDataModelSubscriber instance
2766 * Removes the @subscriber from the set of subscribers for the @data_model.
2767 * Remove of the @subscriber, which is not in the set of subscribers for
2768 * the @data_model does nothing.
2770 * Note: The @subscriber is not notified about a removal of the components
2771 * which could be added previously, while it was subscribed for the change
2777 e_cal_data_model_unsubscribe (ECalDataModel
*data_model
,
2778 ECalDataModelSubscriber
*subscriber
)
2782 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2783 g_return_if_fail (E_IS_CAL_DATA_MODEL_SUBSCRIBER (subscriber
));
2787 for (link
= data_model
->priv
->subscribers
; link
; link
= g_slist_next (link
)) {
2788 SubscriberData
*subs_data
= link
->data
;
2793 if (subs_data
->subscriber
== subscriber
) {
2794 data_model
->priv
->subscribers
= g_slist_remove (data_model
->priv
->subscribers
, subs_data
);
2795 subscriber_data_free (subs_data
);
2800 cal_data_model_update_time_range (data_model
);
2806 * e_cal_data_model_get_subscriber_range:
2807 * @data_model: an #EDataModel instance
2808 * @subscriber: an #ECalDataModelSubscriber instance
2809 * @range_start: (out): time range start for the @subscriber
2810 * @range_end: (out): time range end for the @subscriber
2812 * Obtains currently set time range for the @subscriber. In case
2813 * the subscriber is not found returns #FALSE and both @range_start
2814 * and @range_end are left untouched.
2816 * Returns: Whether the @subscriber was found and the @range_start with
2817 * the @range_end were set to its current time range it uses.
2822 e_cal_data_model_get_subscriber_range (ECalDataModel
*data_model
,
2823 ECalDataModelSubscriber
*subscriber
,
2824 time_t *range_start
,
2829 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), FALSE
);
2830 g_return_val_if_fail (E_IS_CAL_DATA_MODEL_SUBSCRIBER (subscriber
), FALSE
);
2831 g_return_val_if_fail (range_start
, FALSE
);
2832 g_return_val_if_fail (range_end
, FALSE
);
2836 for (link
= data_model
->priv
->subscribers
; link
; link
= g_slist_next (link
)) {
2837 SubscriberData
*subs_data
= link
->data
;
2842 if (subs_data
->subscriber
== subscriber
) {
2843 *range_start
= subs_data
->range_start
;
2844 *range_end
= subs_data
->range_end
;
2851 return link
!= NULL
;
2855 * e_cal_data_model_freeze_views_update:
2856 * @data_model: an #EDataModel instance
2858 * Freezes any views updates until e_cal_data_model_thaw_views_update() is
2859 * called. This can be called nested, then the same count of the calls of
2860 * e_cal_data_model_thaw_views_update() is expected to unlock the views update.
2865 e_cal_data_model_freeze_views_update (ECalDataModel
*data_model
)
2867 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2871 data_model
->priv
->views_update_freeze
++;
2877 * e_cal_data_model_thaw_views_update:
2878 * @data_model: an #EDataModel instance
2880 * A pair function for e_cal_data_model_freeze_views_update(), to unlock
2886 e_cal_data_model_thaw_views_update (ECalDataModel
*data_model
)
2888 g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model
));
2892 if (!data_model
->priv
->views_update_freeze
) {
2894 g_warn_if_reached ();
2898 data_model
->priv
->views_update_freeze
--;
2899 if (data_model
->priv
->views_update_freeze
== 0 &&
2900 data_model
->priv
->views_update_required
)
2901 cal_data_model_rebuild_everything (data_model
, TRUE
);
2907 * e_cal_data_model_is_views_update_frozen:
2908 * @data_model: an #EDataModel instance
2910 * Check whether any views updates are currently frozen. This is influenced by
2911 * e_cal_data_model_freeze_views_update() and e_cal_data_model_thaw_views_update().
2913 * Returns: Whether any views updates are currently frozen.
2918 e_cal_data_model_is_views_update_frozen (ECalDataModel
*data_model
)
2922 g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model
), FALSE
);
2926 is_frozen
= data_model
->priv
->views_update_freeze
> 0;