No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-cal-model.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-cal-model.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * Evolution calendar - Data model for ETable
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Authors:
19 * Rodrigo Moya <rodrigo@ximian.com>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30 #include <glib/gi18n.h>
31
32 #include <libebackend/libebackend.h>
33
34 #include <e-util/e-util.h>
35 #include <e-util/e-util-enumtypes.h>
36
37 #include "comp-util.h"
38 #include "e-cal-model.h"
39 #include "itip-utils.h"
40 #include "misc.h"
41
42 struct _ECalModelComponentPrivate {
43 GString *categories_str;
44 };
45
46 #define E_CAL_MODEL_GET_PRIVATE(obj) \
47 (G_TYPE_INSTANCE_GET_PRIVATE \
48 ((obj), E_TYPE_CAL_MODEL, ECalModelPrivate))
49
50 #define E_CAL_MODEL_COMPONENT_GET_PRIVATE(obj) \
51 (G_TYPE_INSTANCE_GET_PRIVATE \
52 ((obj), E_TYPE_CAL_MODEL_COMPONENT, ECalModelComponentPrivate))
53
54 typedef struct {
55 ECalClient *client;
56 ECalClientView *view;
57
58 gboolean do_query;
59 GCancellable *cancellable;
60 } ECalModelClient;
61
62 struct _ECalModelPrivate {
63 ESourceRegistry *registry;
64
65 /* The list of clients we are managing. Each element is of type ECalModelClient */
66 GList *clients;
67
68 /* The default client in the list */
69 ECalClient *default_client;
70
71 /* Array for storing the objects. Each element is of type ECalModelComponent */
72 GPtrArray *objects;
73
74 icalcomponent_kind kind;
75 ECalModelFlags flags;
76 icaltimezone *zone;
77
78 /* The time range to display */
79 time_t start;
80 time_t end;
81
82 /* The search regular expression */
83 gchar *search_sexp;
84
85 /* The full regular expression, including time range */
86 gchar *full_sexp;
87
88 /* The default category */
89 gchar *default_category;
90
91 /* Whether we display dates in 24-hour format. */
92 gboolean use_24_hour_format;
93
94 /* Whether to compress weekends into one cell. */
95 gboolean compress_weekend;
96
97 /* First day of the week: 0 (Monday) to 6 (Sunday) */
98 gint week_start_day;
99
100 /* Work day timespan */
101 gint work_day_start_hour;
102 gint work_day_start_minute;
103 gint work_day_end_hour;
104 gint work_day_end_minute;
105
106 /* callback, to retrieve start time for newly added rows by click-to-add */
107 ECalModelDefaultTimeFunc get_default_time;
108 gpointer get_default_time_user_data;
109
110 /* Default reminder for events */
111 gboolean use_default_reminder;
112 gint default_reminder_interval;
113 EDurationType default_reminder_units;
114
115 /* Ask user to confirm before deleting components. */
116 gboolean confirm_delete;
117
118 gboolean in_added;
119 gboolean in_modified;
120 gboolean in_removed;
121
122 GHashTable *notify_added;
123 GHashTable *notify_modified;
124 GHashTable *notify_removed;
125
126 GMutex *notify_lock;
127
128 GCancellable *loading_clients;
129 };
130
131 static gint ecm_column_count (ETableModel *etm);
132 static gint ecm_row_count (ETableModel *etm);
133 static gpointer ecm_value_at (ETableModel *etm, gint col, gint row);
134 static void ecm_set_value_at (ETableModel *etm, gint col, gint row, gconstpointer value);
135 static gboolean ecm_is_cell_editable (ETableModel *etm, gint col, gint row);
136 static void ecm_append_row (ETableModel *etm, ETableModel *source, gint row);
137 static gpointer ecm_duplicate_value (ETableModel *etm, gint col, gconstpointer value);
138 static void ecm_free_value (ETableModel *etm, gint col, gpointer value);
139 static gpointer ecm_initialize_value (ETableModel *etm, gint col);
140 static gboolean ecm_value_is_empty (ETableModel *etm, gint col, gconstpointer value);
141 static gchar *ecm_value_to_string (ETableModel *etm, gint col, gconstpointer value);
142
143 static const gchar *ecm_get_color_for_component (ECalModel *model, ECalModelComponent *comp_data);
144
145 static ECalModelClient *add_new_client (ECalModel *model, ECalClient *client, gboolean do_query);
146 static ECalModelClient *find_client_data (ECalModel *model, ECalClient *client);
147 static void remove_client_objects (ECalModel *model, ECalModelClient *client_data);
148 static void remove_client (ECalModel *model, ECalModelClient *client_data);
149 static void redo_queries (ECalModel *model);
150
151 enum {
152 PROP_0,
153 PROP_COMPRESS_WEEKEND,
154 PROP_CONFIRM_DELETE,
155 PROP_DEFAULT_CLIENT,
156 PROP_DEFAULT_REMINDER_INTERVAL,
157 PROP_DEFAULT_REMINDER_UNITS,
158 PROP_REGISTRY,
159 PROP_TIMEZONE,
160 PROP_USE_24_HOUR_FORMAT,
161 PROP_USE_DEFAULT_REMINDER,
162 PROP_WEEK_START_DAY,
163 PROP_WORK_DAY_END_HOUR,
164 PROP_WORK_DAY_END_MINUTE,
165 PROP_WORK_DAY_START_HOUR,
166 PROP_WORK_DAY_START_MINUTE
167 };
168
169 enum {
170 TIME_RANGE_CHANGED,
171 ROW_APPENDED,
172 COMPS_DELETED,
173 CAL_VIEW_PROGRESS,
174 CAL_VIEW_COMPLETE,
175 STATUS_MESSAGE,
176 TIMEZONE_CHANGED,
177 LAST_SIGNAL
178 };
179
180 static guint signals[LAST_SIGNAL];
181
182 G_DEFINE_TYPE_WITH_CODE (
183 ECalModel, e_cal_model, E_TYPE_TABLE_MODEL,
184 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
185
186 G_DEFINE_TYPE (
187 ECalModelComponent,
188 e_cal_model_component,
189 G_TYPE_OBJECT)
190
191 static void
192 cal_model_set_registry (ECalModel *model,
193 ESourceRegistry *registry)
194 {
195 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
196 g_return_if_fail (model->priv->registry == NULL);
197
198 model->priv->registry = g_object_ref (registry);
199 }
200
201 static void
202 cal_model_set_property (GObject *object,
203 guint property_id,
204 const GValue *value,
205 GParamSpec *pspec)
206 {
207 switch (property_id) {
208 case PROP_COMPRESS_WEEKEND:
209 e_cal_model_set_compress_weekend (
210 E_CAL_MODEL (object),
211 g_value_get_boolean (value));
212 return;
213
214 case PROP_CONFIRM_DELETE:
215 e_cal_model_set_confirm_delete (
216 E_CAL_MODEL (object),
217 g_value_get_boolean (value));
218 return;
219
220 case PROP_DEFAULT_CLIENT:
221 e_cal_model_set_default_client (
222 E_CAL_MODEL (object),
223 g_value_get_object (value));
224 return;
225
226 case PROP_DEFAULT_REMINDER_INTERVAL:
227 e_cal_model_set_default_reminder_interval (
228 E_CAL_MODEL (object),
229 g_value_get_int (value));
230 return;
231
232 case PROP_DEFAULT_REMINDER_UNITS:
233 e_cal_model_set_default_reminder_units (
234 E_CAL_MODEL (object),
235 g_value_get_enum (value));
236 return;
237
238 case PROP_REGISTRY:
239 cal_model_set_registry (
240 E_CAL_MODEL (object),
241 g_value_get_object (value));
242 return;
243
244 case PROP_TIMEZONE:
245 e_cal_model_set_timezone (
246 E_CAL_MODEL (object),
247 g_value_get_pointer (value));
248 return;
249
250 case PROP_USE_24_HOUR_FORMAT:
251 e_cal_model_set_use_24_hour_format (
252 E_CAL_MODEL (object),
253 g_value_get_boolean (value));
254 return;
255
256 case PROP_USE_DEFAULT_REMINDER:
257 e_cal_model_set_use_default_reminder (
258 E_CAL_MODEL (object),
259 g_value_get_boolean (value));
260 return;
261
262 case PROP_WEEK_START_DAY:
263 e_cal_model_set_week_start_day (
264 E_CAL_MODEL (object),
265 g_value_get_int (value));
266 return;
267
268 case PROP_WORK_DAY_END_HOUR:
269 e_cal_model_set_work_day_end_hour (
270 E_CAL_MODEL (object),
271 g_value_get_int (value));
272 return;
273
274 case PROP_WORK_DAY_END_MINUTE:
275 e_cal_model_set_work_day_end_minute (
276 E_CAL_MODEL (object),
277 g_value_get_int (value));
278 return;
279
280 case PROP_WORK_DAY_START_HOUR:
281 e_cal_model_set_work_day_start_hour (
282 E_CAL_MODEL (object),
283 g_value_get_int (value));
284 return;
285
286 case PROP_WORK_DAY_START_MINUTE:
287 e_cal_model_set_work_day_start_minute (
288 E_CAL_MODEL (object),
289 g_value_get_int (value));
290 return;
291 }
292
293 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
294 }
295
296 static void
297 cal_model_get_property (GObject *object,
298 guint property_id,
299 GValue *value,
300 GParamSpec *pspec)
301 {
302 switch (property_id) {
303 case PROP_COMPRESS_WEEKEND:
304 g_value_set_boolean (
305 value,
306 e_cal_model_get_compress_weekend (
307 E_CAL_MODEL (object)));
308 return;
309
310 case PROP_CONFIRM_DELETE:
311 g_value_set_boolean (
312 value,
313 e_cal_model_get_confirm_delete (
314 E_CAL_MODEL (object)));
315 return;
316
317 case PROP_DEFAULT_CLIENT:
318 g_value_set_object (
319 value,
320 e_cal_model_get_default_client (
321 E_CAL_MODEL (object)));
322 return;
323
324 case PROP_DEFAULT_REMINDER_INTERVAL:
325 g_value_set_int (
326 value,
327 e_cal_model_get_default_reminder_interval (
328 E_CAL_MODEL (object)));
329 return;
330
331 case PROP_DEFAULT_REMINDER_UNITS:
332 g_value_set_enum (
333 value,
334 e_cal_model_get_default_reminder_units (
335 E_CAL_MODEL (object)));
336 return;
337
338 case PROP_REGISTRY:
339 g_value_set_object (
340 value,
341 e_cal_model_get_registry (
342 E_CAL_MODEL (object)));
343 return;
344
345 case PROP_TIMEZONE:
346 g_value_set_pointer (
347 value,
348 e_cal_model_get_timezone (
349 E_CAL_MODEL (object)));
350 return;
351
352 case PROP_USE_24_HOUR_FORMAT:
353 g_value_set_boolean (
354 value,
355 e_cal_model_get_use_24_hour_format (
356 E_CAL_MODEL (object)));
357 return;
358
359 case PROP_USE_DEFAULT_REMINDER:
360 g_value_set_boolean (
361 value,
362 e_cal_model_get_use_default_reminder (
363 E_CAL_MODEL (object)));
364 return;
365
366 case PROP_WEEK_START_DAY:
367 g_value_set_int (
368 value,
369 e_cal_model_get_week_start_day (
370 E_CAL_MODEL (object)));
371 return;
372
373 case PROP_WORK_DAY_END_HOUR:
374 g_value_set_int (
375 value,
376 e_cal_model_get_work_day_end_hour (
377 E_CAL_MODEL (object)));
378 return;
379
380 case PROP_WORK_DAY_END_MINUTE:
381 g_value_set_int (
382 value,
383 e_cal_model_get_work_day_end_minute (
384 E_CAL_MODEL (object)));
385 return;
386
387 case PROP_WORK_DAY_START_HOUR:
388 g_value_set_int (
389 value,
390 e_cal_model_get_work_day_start_hour (
391 E_CAL_MODEL (object)));
392 return;
393
394 case PROP_WORK_DAY_START_MINUTE:
395 g_value_set_int (
396 value,
397 e_cal_model_get_work_day_start_minute (
398 E_CAL_MODEL (object)));
399 return;
400 }
401
402 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
403 }
404
405 static void
406 cal_model_constructed (GObject *object)
407 {
408 e_extensible_load_extensions (E_EXTENSIBLE (object));
409
410 /* Chain up to parent's constructed() method. */
411 G_OBJECT_CLASS (e_cal_model_parent_class)->constructed (object);
412 }
413
414 static void
415 cal_model_dispose (GObject *object)
416 {
417 ECalModelPrivate *priv;
418
419 priv = E_CAL_MODEL_GET_PRIVATE (object);
420
421 if (priv->registry != NULL) {
422 g_object_unref (priv->registry);
423 priv->registry = NULL;
424 }
425
426 if (priv->loading_clients) {
427 g_cancellable_cancel (priv->loading_clients);
428 g_object_unref (priv->loading_clients);
429 priv->loading_clients = NULL;
430 }
431
432 if (priv->clients) {
433 while (priv->clients != NULL) {
434 ECalModelClient *client_data = (ECalModelClient *) priv->clients->data;
435
436 g_signal_handlers_disconnect_matched (
437 client_data->client, G_SIGNAL_MATCH_DATA,
438 0, 0, NULL, NULL, object);
439 if (client_data->view)
440 g_signal_handlers_disconnect_matched (
441 client_data->view, G_SIGNAL_MATCH_DATA,
442 0, 0, NULL, NULL, object);
443
444 priv->clients = g_list_remove (priv->clients, client_data);
445
446 g_object_unref (client_data->client);
447 if (client_data->cancellable) {
448 g_cancellable_cancel (client_data->cancellable);
449 g_object_unref (client_data->cancellable);
450 }
451 if (client_data->view)
452 g_object_unref (client_data->view);
453 g_free (client_data);
454 }
455
456 priv->clients = NULL;
457 priv->default_client = NULL;
458 }
459
460 /* Chain up to parent's dispose() method. */
461 G_OBJECT_CLASS (e_cal_model_parent_class)->dispose (object);
462 }
463
464 static void
465 cal_model_finalize (GObject *object)
466 {
467 ECalModelPrivate *priv;
468 gint ii;
469
470 priv = E_CAL_MODEL_GET_PRIVATE (object);
471
472 g_free (priv->search_sexp);
473 g_free (priv->full_sexp);
474
475 g_free (priv->default_category);
476
477 for (ii = 0; ii < priv->objects->len; ii++) {
478 ECalModelComponent *comp_data;
479
480 comp_data = g_ptr_array_index (priv->objects, ii);
481 if (comp_data == NULL) {
482 g_warning ("comp_data is null\n");
483 continue;
484 }
485 g_object_unref (comp_data);
486 }
487 g_ptr_array_free (priv->objects, FALSE);
488
489 g_mutex_free (priv->notify_lock);
490
491 g_hash_table_destroy (priv->notify_added);
492 g_hash_table_destroy (priv->notify_modified);
493 g_hash_table_destroy (priv->notify_removed);
494
495 /* Chain up to parent's finalize() method. */
496 G_OBJECT_CLASS (e_cal_model_parent_class)->finalize (object);
497 }
498
499 static void
500 e_cal_model_class_init (ECalModelClass *class)
501 {
502 GObjectClass *object_class;
503 ETableModelClass *etm_class;
504
505 g_type_class_add_private (class, sizeof (ECalModelPrivate));
506
507 object_class = G_OBJECT_CLASS (class);
508 object_class->set_property = cal_model_set_property;
509 object_class->get_property = cal_model_get_property;
510 object_class->constructed = cal_model_constructed;
511 object_class->dispose = cal_model_dispose;
512 object_class->finalize = cal_model_finalize;
513
514 etm_class = E_TABLE_MODEL_CLASS (class);
515 etm_class->column_count = ecm_column_count;
516 etm_class->row_count = ecm_row_count;
517 etm_class->value_at = ecm_value_at;
518 etm_class->set_value_at = ecm_set_value_at;
519 etm_class->is_cell_editable = ecm_is_cell_editable;
520 etm_class->append_row = ecm_append_row;
521 etm_class->duplicate_value = ecm_duplicate_value;
522 etm_class->free_value = ecm_free_value;
523 etm_class->initialize_value = ecm_initialize_value;
524 etm_class->value_is_empty = ecm_value_is_empty;
525 etm_class->value_to_string = ecm_value_to_string;
526
527 class->get_color_for_component = ecm_get_color_for_component;
528 class->fill_component_from_model = NULL;
529
530 g_object_class_install_property (
531 object_class,
532 PROP_COMPRESS_WEEKEND,
533 g_param_spec_boolean (
534 "compress-weekend",
535 "Compress Weekend",
536 NULL,
537 FALSE,
538 G_PARAM_READWRITE));
539
540 g_object_class_install_property (
541 object_class,
542 PROP_CONFIRM_DELETE,
543 g_param_spec_boolean (
544 "confirm-delete",
545 "Confirm Delete",
546 NULL,
547 TRUE,
548 G_PARAM_READWRITE));
549
550 g_object_class_install_property (
551 object_class,
552 PROP_DEFAULT_CLIENT,
553 g_param_spec_object (
554 "default-client",
555 "Default ECalClient",
556 NULL,
557 E_TYPE_CAL_CLIENT,
558 G_PARAM_READWRITE));
559
560 g_object_class_install_property (
561 object_class,
562 PROP_DEFAULT_REMINDER_INTERVAL,
563 g_param_spec_int (
564 "default-reminder-interval",
565 "Default Reminder Interval",
566 NULL,
567 G_MININT,
568 G_MAXINT,
569 0,
570 G_PARAM_READWRITE));
571
572 g_object_class_install_property (
573 object_class,
574 PROP_DEFAULT_REMINDER_UNITS,
575 g_param_spec_enum (
576 "default-reminder-units",
577 "Default Reminder Units",
578 NULL,
579 E_TYPE_DURATION_TYPE,
580 E_DURATION_MINUTES,
581 G_PARAM_READWRITE));
582
583 g_object_class_install_property (
584 object_class,
585 PROP_REGISTRY,
586 g_param_spec_object (
587 "registry",
588 "Registry",
589 "Data source registry",
590 E_TYPE_SOURCE_REGISTRY,
591 G_PARAM_READWRITE |
592 G_PARAM_CONSTRUCT_ONLY));
593
594 g_object_class_install_property (
595 object_class,
596 PROP_TIMEZONE,
597 g_param_spec_pointer (
598 "timezone",
599 "Time Zone",
600 NULL,
601 G_PARAM_READWRITE));
602
603 g_object_class_install_property (
604 object_class,
605 PROP_USE_24_HOUR_FORMAT,
606 g_param_spec_boolean (
607 "use-24-hour-format",
608 "Use 24-Hour Format",
609 NULL,
610 TRUE,
611 G_PARAM_READWRITE));
612
613 g_object_class_install_property (
614 object_class,
615 PROP_USE_DEFAULT_REMINDER,
616 g_param_spec_boolean (
617 "use-default-reminder",
618 "Use Default Reminder",
619 NULL,
620 FALSE,
621 G_PARAM_READWRITE));
622
623 g_object_class_install_property (
624 object_class,
625 PROP_WEEK_START_DAY,
626 g_param_spec_int (
627 "week-start-day",
628 "Week Start Day",
629 NULL,
630 0, /* Monday */
631 6, /* Sunday */
632 0,
633 G_PARAM_READWRITE));
634
635 g_object_class_install_property (
636 object_class,
637 PROP_WORK_DAY_END_HOUR,
638 g_param_spec_int (
639 "work-day-end-hour",
640 "Work Day End Hour",
641 NULL,
642 0,
643 23,
644 0,
645 G_PARAM_READWRITE));
646
647 g_object_class_install_property (
648 object_class,
649 PROP_WORK_DAY_END_MINUTE,
650 g_param_spec_int (
651 "work-day-end-minute",
652 "Work Day End Minute",
653 NULL,
654 0,
655 59,
656 0,
657 G_PARAM_READWRITE));
658
659 g_object_class_install_property (
660 object_class,
661 PROP_WORK_DAY_START_HOUR,
662 g_param_spec_int (
663 "work-day-start-hour",
664 "Work Day Start Hour",
665 NULL,
666 0,
667 23,
668 0,
669 G_PARAM_READWRITE));
670
671 g_object_class_install_property (
672 object_class,
673 PROP_WORK_DAY_START_MINUTE,
674 g_param_spec_int (
675 "work-day-start-minute",
676 "Work Day Start Minute",
677 NULL,
678 0,
679 59,
680 0,
681 G_PARAM_READWRITE));
682
683 signals[TIME_RANGE_CHANGED] = g_signal_new (
684 "time_range_changed",
685 G_TYPE_FROM_CLASS (class),
686 G_SIGNAL_RUN_LAST,
687 G_STRUCT_OFFSET (ECalModelClass, time_range_changed),
688 NULL, NULL,
689 e_marshal_VOID__LONG_LONG,
690 G_TYPE_NONE, 2,
691 G_TYPE_LONG,
692 G_TYPE_LONG);
693
694 signals[ROW_APPENDED] = g_signal_new (
695 "row_appended",
696 G_TYPE_FROM_CLASS (class),
697 G_SIGNAL_RUN_LAST,
698 G_STRUCT_OFFSET (ECalModelClass, row_appended),
699 NULL, NULL,
700 g_cclosure_marshal_VOID__VOID,
701 G_TYPE_NONE, 0);
702
703 signals[COMPS_DELETED] = g_signal_new (
704 "comps_deleted",
705 G_TYPE_FROM_CLASS (class),
706 G_SIGNAL_RUN_LAST,
707 G_STRUCT_OFFSET (ECalModelClass, comps_deleted),
708 NULL, NULL,
709 g_cclosure_marshal_VOID__POINTER,
710 G_TYPE_NONE, 1,
711 G_TYPE_POINTER);
712
713 signals[CAL_VIEW_PROGRESS] = g_signal_new (
714 "cal_view_progress",
715 G_TYPE_FROM_CLASS (class),
716 G_SIGNAL_RUN_LAST,
717 G_STRUCT_OFFSET (ECalModelClass, cal_view_progress),
718 NULL, NULL,
719 e_marshal_VOID__STRING_INT_INT,
720 G_TYPE_NONE, 3,
721 G_TYPE_STRING,
722 G_TYPE_INT,
723 G_TYPE_INT);
724
725 signals[CAL_VIEW_COMPLETE] = g_signal_new (
726 "cal_view_complete",
727 G_TYPE_FROM_CLASS (class),
728 G_SIGNAL_RUN_LAST,
729 G_STRUCT_OFFSET (ECalModelClass, cal_view_complete),
730 NULL, NULL,
731 e_marshal_VOID__BOXED_INT,
732 G_TYPE_NONE, 2,
733 G_TYPE_ERROR,
734 G_TYPE_INT);
735
736 signals[STATUS_MESSAGE] = g_signal_new (
737 "status-message",
738 G_TYPE_FROM_CLASS (class),
739 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
740 G_STRUCT_OFFSET (ECalModelClass, status_message),
741 NULL, NULL,
742 e_marshal_VOID__STRING_DOUBLE,
743 G_TYPE_NONE, 2,
744 G_TYPE_STRING,
745 G_TYPE_DOUBLE);
746
747 signals[TIMEZONE_CHANGED] = g_signal_new (
748 "timezone-changed",
749 G_TYPE_FROM_CLASS (class),
750 G_SIGNAL_RUN_LAST,
751 G_STRUCT_OFFSET (ECalModelClass, timezone_changed),
752 NULL, NULL,
753 e_marshal_VOID__POINTER_POINTER,
754 G_TYPE_NONE, 2,
755 G_TYPE_POINTER,
756 G_TYPE_POINTER);
757 }
758
759 static void
760 e_cal_model_init (ECalModel *model)
761 {
762 model->priv = E_CAL_MODEL_GET_PRIVATE (model);
763
764 /* match none by default */
765 model->priv->start = -1;
766 model->priv->end = -1;
767 model->priv->search_sexp = NULL;
768 model->priv->full_sexp = g_strdup ("#f");
769
770 model->priv->objects = g_ptr_array_new ();
771 model->priv->kind = ICAL_NO_COMPONENT;
772 model->priv->flags = 0;
773
774 model->priv->use_24_hour_format = TRUE;
775
776 model->priv->in_added = FALSE;
777 model->priv->in_modified = FALSE;
778 model->priv->in_removed = FALSE;
779 model->priv->notify_added = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
780 model->priv->notify_modified = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
781 model->priv->notify_removed = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
782 model->priv->notify_lock = g_mutex_new ();
783
784 model->priv->loading_clients = g_cancellable_new ();
785 }
786
787 /* ETableModel methods */
788
789 static gint
790 ecm_column_count (ETableModel *etm)
791 {
792 return E_CAL_MODEL_FIELD_LAST;
793 }
794
795 static gint
796 ecm_row_count (ETableModel *etm)
797 {
798 ECalModelPrivate *priv;
799 ECalModel *model = (ECalModel *) etm;
800
801 g_return_val_if_fail (E_IS_CAL_MODEL (model), -1);
802
803 priv = model->priv;
804
805 return priv->objects->len;
806 }
807
808 static gpointer
809 get_categories (ECalModelComponent *comp_data)
810 {
811 if (!comp_data->priv->categories_str) {
812 icalproperty *prop;
813
814 comp_data->priv->categories_str = g_string_new ("");
815
816 for (prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_CATEGORIES_PROPERTY);
817 prop;
818 prop = icalcomponent_get_next_property (comp_data->icalcomp, ICAL_CATEGORIES_PROPERTY)) {
819 const gchar *categories = icalproperty_get_categories (prop);
820 if (!categories)
821 continue;
822
823 if (comp_data->priv->categories_str->len)
824 g_string_append_c (comp_data->priv->categories_str, ',');
825 g_string_append (comp_data->priv->categories_str, categories);
826 }
827 }
828
829 return comp_data->priv->categories_str->str;
830 }
831
832 static gchar *
833 get_classification (ECalModelComponent *comp_data)
834 {
835 icalproperty *prop;
836 icalproperty_class class;
837
838 prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_CLASS_PROPERTY);
839
840 if (!prop)
841 return _("Public");
842
843 class = icalproperty_get_class (prop);
844
845 switch (class)
846 {
847 case ICAL_CLASS_PUBLIC:
848 return _("Public");
849 case ICAL_CLASS_PRIVATE:
850 return _("Private");
851 case ICAL_CLASS_CONFIDENTIAL:
852 return _("Confidential");
853 default:
854 return _("Unknown");
855 }
856 }
857
858 static const gchar *
859 get_color (ECalModel *model,
860 ECalModelComponent *comp_data)
861 {
862 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
863
864 return e_cal_model_get_color_for_component (model, comp_data);
865 }
866
867 static gpointer
868 get_description (ECalModelComponent *comp_data)
869 {
870 icalproperty *prop;
871 static GString *str = NULL;
872
873 if (str) {
874 g_string_free (str, TRUE);
875 str = NULL;
876 }
877
878 prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DESCRIPTION_PROPERTY);
879 if (prop) {
880 str = g_string_new (NULL);
881 do {
882 str = g_string_append (str, icalproperty_get_description (prop));
883 } while ((prop = icalcomponent_get_next_property (comp_data->icalcomp, ICAL_DESCRIPTION_PROPERTY)));
884
885 return str->str;
886 }
887
888 return (gpointer) "";
889 }
890
891 static ECellDateEditValue *
892 get_dtstart (ECalModel *model,
893 ECalModelComponent *comp_data)
894 {
895 ECalModelPrivate *priv;
896 struct icaltimetype tt_start;
897
898 priv = model->priv;
899
900 if (!comp_data->dtstart) {
901 icalproperty *prop;
902 icaltimezone *zone;
903 gboolean got_zone = FALSE;
904
905 prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DTSTART_PROPERTY);
906 if (!prop)
907 return NULL;
908
909 tt_start = icalproperty_get_dtstart (prop);
910
911 if (icaltime_get_tzid (tt_start)
912 && e_cal_client_get_timezone_sync (comp_data->client, icaltime_get_tzid (tt_start), &zone, NULL, NULL))
913 got_zone = TRUE;
914
915 if (e_cal_model_get_flags (model) & E_CAL_MODEL_FLAGS_EXPAND_RECURRENCES) {
916 if (got_zone) {
917 tt_start = icaltime_from_timet_with_zone (comp_data->instance_start, tt_start.is_date, zone);
918 if (priv->zone)
919 icaltimezone_convert_time (&tt_start, zone, priv->zone);
920 } else
921 if (priv->zone)
922 tt_start = icaltime_from_timet_with_zone (comp_data->instance_start, tt_start.is_date, priv->zone);
923 }
924
925 if (!icaltime_is_valid_time (tt_start) || icaltime_is_null_time (tt_start))
926 return NULL;
927
928 comp_data->dtstart = g_new0 (ECellDateEditValue, 1);
929 comp_data->dtstart->tt = tt_start;
930
931 if (got_zone)
932 comp_data->dtstart->zone = zone;
933 else
934 comp_data->dtstart->zone = NULL;
935 }
936
937 return comp_data->dtstart;
938 }
939
940 static ECellDateEditValue *
941 get_datetime_from_utc (ECalModel *model,
942 ECalModelComponent *comp_data,
943 icalproperty_kind propkind,
944 struct icaltimetype (*get_value) (const icalproperty *prop),
945 ECellDateEditValue **buffer)
946 {
947 ECalModelPrivate *priv;
948 struct icaltimetype tt_value;
949 icalproperty *prop;
950 ECellDateEditValue *res;
951
952 g_return_val_if_fail (buffer!= NULL, NULL);
953
954 if (*buffer)
955 return *buffer;
956
957 priv = model->priv;
958
959 prop = icalcomponent_get_first_property (comp_data->icalcomp, propkind);
960 if (!prop)
961 return NULL;
962
963 tt_value = get_value (prop);
964
965 /* these are always in UTC, thus convert to default zone, if any and done */
966 if (priv->zone)
967 icaltimezone_convert_time (&tt_value, icaltimezone_get_utc_timezone (), priv->zone);
968
969 if (!icaltime_is_valid_time (tt_value) || icaltime_is_null_time (tt_value))
970 return NULL;
971
972 res = g_new0 (ECellDateEditValue, 1);
973 res->tt = tt_value;
974 res->zone = NULL;
975
976 *buffer = res;
977
978 return res;
979 }
980
981 static gpointer
982 get_summary (ECalModelComponent *comp_data)
983 {
984 icalproperty *prop;
985
986 prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_SUMMARY_PROPERTY);
987 if (prop)
988 return (gpointer) icalproperty_get_summary (prop);
989
990 return (gpointer) "";
991 }
992
993 static gchar *
994 get_uid (ECalModelComponent *comp_data)
995 {
996 return (gchar *) icalcomponent_get_uid (comp_data->icalcomp);
997 }
998
999 static gpointer
1000 ecm_value_at (ETableModel *etm,
1001 gint col,
1002 gint row)
1003 {
1004 ECalModelPrivate *priv;
1005 ECalModelComponent *comp_data;
1006 ECalModel *model = (ECalModel *) etm;
1007 ESourceRegistry *registry;
1008
1009 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1010
1011 priv = model->priv;
1012
1013 registry = e_cal_model_get_registry (model);
1014
1015 g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_FIELD_LAST, NULL);
1016 g_return_val_if_fail (row >= 0 && row < priv->objects->len, NULL);
1017
1018 comp_data = g_ptr_array_index (priv->objects, row);
1019 g_return_val_if_fail (comp_data != NULL, NULL);
1020 g_return_val_if_fail (comp_data->icalcomp != NULL, NULL);
1021
1022 switch (col) {
1023 case E_CAL_MODEL_FIELD_CATEGORIES :
1024 return get_categories (comp_data);
1025 case E_CAL_MODEL_FIELD_CLASSIFICATION :
1026 return get_classification (comp_data);
1027 case E_CAL_MODEL_FIELD_COLOR :
1028 return (gpointer) get_color (model, comp_data);
1029 case E_CAL_MODEL_FIELD_COMPONENT :
1030 return comp_data->icalcomp;
1031 case E_CAL_MODEL_FIELD_DESCRIPTION :
1032 return get_description (comp_data);
1033 case E_CAL_MODEL_FIELD_DTSTART :
1034 return (gpointer) get_dtstart (model, comp_data);
1035 case E_CAL_MODEL_FIELD_CREATED :
1036 return (gpointer) get_datetime_from_utc (
1037 model, comp_data, ICAL_CREATED_PROPERTY,
1038 icalproperty_get_created, &comp_data->created);
1039 case E_CAL_MODEL_FIELD_LASTMODIFIED :
1040 return (gpointer) get_datetime_from_utc (
1041 model, comp_data, ICAL_LASTMODIFIED_PROPERTY,
1042 icalproperty_get_lastmodified, &comp_data->lastmodified);
1043 case E_CAL_MODEL_FIELD_HAS_ALARMS :
1044 return GINT_TO_POINTER (
1045 icalcomponent_get_first_component (
1046 comp_data->icalcomp,
1047 ICAL_VALARM_COMPONENT) != NULL);
1048 case E_CAL_MODEL_FIELD_ICON :
1049 {
1050 ECalComponent *comp;
1051 icalcomponent *icalcomp;
1052 gint retval = 0;
1053
1054 comp = e_cal_component_new ();
1055 icalcomp = icalcomponent_new_clone (comp_data->icalcomp);
1056 if (e_cal_component_set_icalcomponent (comp, icalcomp)) {
1057 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_JOURNAL) {
1058 g_object_unref (comp);
1059 return GINT_TO_POINTER (retval);
1060 }
1061
1062 if (e_cal_component_has_recurrences (comp))
1063 retval = 1;
1064 else if (itip_organizer_is_user (registry, comp, comp_data->client))
1065 retval = 3;
1066 else {
1067 GSList *attendees = NULL, *sl;
1068
1069 e_cal_component_get_attendee_list (comp, &attendees);
1070 for (sl = attendees; sl != NULL; sl = sl->next) {
1071 ECalComponentAttendee *ca = sl->data;
1072 const gchar *text;
1073
1074 text = itip_strip_mailto (ca->value);
1075 if (itip_address_is_user (registry, text)) {
1076 if (ca->delto != NULL)
1077 retval = 3;
1078 else
1079 retval = 2;
1080 break;
1081 }
1082 }
1083
1084 e_cal_component_free_attendee_list (attendees);
1085 }
1086 } else
1087 icalcomponent_free (icalcomp);
1088
1089 g_object_unref (comp);
1090
1091 return GINT_TO_POINTER (retval);
1092 }
1093 case E_CAL_MODEL_FIELD_SUMMARY :
1094 return get_summary (comp_data);
1095 case E_CAL_MODEL_FIELD_UID :
1096 return get_uid (comp_data);
1097 }
1098
1099 return (gpointer) "";
1100 }
1101
1102 static void
1103 set_categories (ECalModelComponent *comp_data,
1104 const gchar *value)
1105 {
1106 icalproperty *prop;
1107
1108 /* remove all categories first */
1109 prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_CATEGORIES_PROPERTY);
1110 while (prop) {
1111 icalproperty *to_remove = prop;
1112 prop = icalcomponent_get_next_property (comp_data->icalcomp, ICAL_CATEGORIES_PROPERTY);
1113
1114 icalcomponent_remove_property (comp_data->icalcomp, to_remove);
1115 icalproperty_free (to_remove);
1116 }
1117
1118 if (comp_data->priv->categories_str)
1119 g_string_free (comp_data->priv->categories_str, TRUE);
1120 comp_data->priv->categories_str = NULL;
1121
1122 /* then set a new value; no need to populate categories_str,
1123 * it'll be populated on demand (in the get_categories() function)
1124 */
1125 if (value && *value) {
1126 prop = icalproperty_new_categories (value);
1127 icalcomponent_add_property (comp_data->icalcomp, prop);
1128 }
1129 }
1130
1131 static void
1132 set_classification (ECalModelComponent *comp_data,
1133 const gchar *value)
1134 {
1135 icalproperty *prop;
1136
1137 prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_CLASS_PROPERTY);
1138 if (!value || !(*value)) {
1139 if (prop) {
1140 icalcomponent_remove_property (comp_data->icalcomp, prop);
1141 icalproperty_free (prop);
1142 }
1143 } else {
1144 icalproperty_class ical_class;
1145
1146 if (!g_ascii_strcasecmp (value, "PUBLIC"))
1147 ical_class = ICAL_CLASS_PUBLIC;
1148 else if (!g_ascii_strcasecmp (value, "PRIVATE"))
1149 ical_class = ICAL_CLASS_PRIVATE;
1150 else if (!g_ascii_strcasecmp (value, "CONFIDENTIAL"))
1151 ical_class = ICAL_CLASS_CONFIDENTIAL;
1152 else
1153 ical_class = ICAL_CLASS_NONE;
1154
1155 if (!prop) {
1156 prop = icalproperty_new_class (ical_class);
1157 icalcomponent_add_property (comp_data->icalcomp, prop);
1158 } else
1159 icalproperty_set_class (prop, ical_class);
1160 }
1161 }
1162
1163 static void
1164 set_description (ECalModelComponent *comp_data,
1165 const gchar *value)
1166 {
1167 icalproperty *prop;
1168
1169 /* remove old description(s) */
1170 prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DESCRIPTION_PROPERTY);
1171 while (prop) {
1172 icalproperty *next;
1173
1174 next = icalcomponent_get_next_property (comp_data->icalcomp, ICAL_DESCRIPTION_PROPERTY);
1175
1176 icalcomponent_remove_property (comp_data->icalcomp, prop);
1177 icalproperty_free (prop);
1178
1179 prop = next;
1180 }
1181
1182 /* now add the new description */
1183 if (!value || !(*value))
1184 return;
1185
1186 prop = icalproperty_new_description (value);
1187 icalcomponent_add_property (comp_data->icalcomp, prop);
1188 }
1189
1190 static void
1191 datetime_to_zone (ECalClient *client,
1192 struct icaltimetype *tt,
1193 icaltimezone *tt_zone,
1194 const gchar *tzid)
1195 {
1196 icaltimezone *from, *to;
1197 const gchar *tt_tzid = NULL;
1198
1199 g_return_if_fail (tt != NULL);
1200
1201 if (tt_zone)
1202 tt_tzid = icaltimezone_get_tzid (tt_zone);
1203
1204 if (tt_tzid == NULL || tzid == NULL ||
1205 tt_tzid == tzid || g_str_equal (tt_tzid, tzid))
1206 return;
1207
1208 from = tt_zone;
1209 to = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1210 if (!to) {
1211 /* do not check failure here, maybe the zone is not available there */
1212 e_cal_client_get_timezone_sync (client, tzid, &to, NULL, NULL);
1213 }
1214
1215 icaltimezone_convert_time (tt, from, to);
1216 }
1217
1218 /* updates time in a component, and keeps the timezone used in it, if exists */
1219 void
1220 e_cal_model_update_comp_time (ECalModel *model,
1221 ECalModelComponent *comp_data,
1222 gconstpointer time_value,
1223 icalproperty_kind kind,
1224 void (*set_func) (icalproperty *prop,
1225 struct icaltimetype v),
1226 icalproperty * (*new_func) (struct icaltimetype v))
1227 {
1228 ECellDateEditValue *dv = (ECellDateEditValue *) time_value;
1229 icalproperty *prop;
1230 icalparameter *param;
1231 struct icaltimetype tt;
1232
1233 g_return_if_fail (model != NULL);
1234 g_return_if_fail (comp_data != NULL);
1235 g_return_if_fail (set_func != NULL);
1236 g_return_if_fail (new_func != NULL);
1237
1238 prop = icalcomponent_get_first_property (comp_data->icalcomp, kind);
1239 if (prop)
1240 param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
1241 else
1242 param = NULL;
1243
1244 /* If we are setting the property to NULL (i.e. removing it), then
1245 * we remove it if it exists. */
1246 if (!dv) {
1247 if (prop) {
1248 icalcomponent_remove_property (comp_data->icalcomp, prop);
1249 icalproperty_free (prop);
1250 }
1251
1252 return;
1253 }
1254
1255 tt = dv->tt;
1256 datetime_to_zone (comp_data->client, &tt, e_cal_model_get_timezone (model), param ? icalparameter_get_tzid (param) : NULL);
1257
1258 if (prop) {
1259 set_func (prop, tt);
1260 } else {
1261 prop = new_func (tt);
1262 icalcomponent_add_property (comp_data->icalcomp, prop);
1263 }
1264
1265 if (param) {
1266 const gchar *tzid = icalparameter_get_tzid (param);
1267
1268 /* If the TZID is set to "UTC", we don't want to save the TZID. */
1269 if (tzid && strcmp (tzid, "UTC")) {
1270 if (param) {
1271 icalparameter_set_tzid (param, (gchar *) tzid);
1272 } else {
1273 param = icalparameter_new_tzid ((gchar *) tzid);
1274 icalproperty_add_parameter (prop, param);
1275 }
1276 } else {
1277 icalproperty_remove_parameter (prop, ICAL_TZID_PARAMETER);
1278 }
1279 }
1280 }
1281
1282 static void
1283 set_dtstart (ECalModel *model,
1284 ECalModelComponent *comp_data,
1285 gconstpointer value)
1286 {
1287 e_cal_model_update_comp_time (
1288 model, comp_data, value,
1289 ICAL_DTSTART_PROPERTY,
1290 icalproperty_set_dtstart,
1291 icalproperty_new_dtstart);
1292 }
1293
1294 static void
1295 set_summary (ECalModelComponent *comp_data,
1296 const gchar *value)
1297 {
1298 icalproperty *prop;
1299
1300 prop = icalcomponent_get_first_property (
1301 comp_data->icalcomp, ICAL_SUMMARY_PROPERTY);
1302
1303 if (string_is_empty (value)) {
1304 if (prop) {
1305 icalcomponent_remove_property (comp_data->icalcomp, prop);
1306 icalproperty_free (prop);
1307 }
1308 } else {
1309 if (prop)
1310 icalproperty_set_summary (prop, value);
1311 else {
1312 prop = icalproperty_new_summary (value);
1313 icalcomponent_add_property (comp_data->icalcomp, prop);
1314 }
1315 }
1316 }
1317
1318 static void
1319 ecm_set_value_at (ETableModel *etm,
1320 gint col,
1321 gint row,
1322 gconstpointer value)
1323 {
1324 ECalModelPrivate *priv;
1325 ECalModelComponent *comp_data;
1326 ECalModel *model = (ECalModel *) etm;
1327 GError *error = NULL;
1328
1329 g_return_if_fail (E_IS_CAL_MODEL (model));
1330
1331 priv = model->priv;
1332
1333 g_return_if_fail (col >= 0 && col < E_CAL_MODEL_FIELD_LAST);
1334 g_return_if_fail (row >= 0 && row < priv->objects->len);
1335
1336 comp_data = g_ptr_array_index (priv->objects, row);
1337 g_return_if_fail (comp_data != NULL);
1338
1339 switch (col) {
1340 case E_CAL_MODEL_FIELD_CATEGORIES :
1341 set_categories (comp_data, value);
1342 break;
1343 case E_CAL_MODEL_FIELD_CLASSIFICATION :
1344 set_classification (comp_data, value);
1345 break;
1346 case E_CAL_MODEL_FIELD_DESCRIPTION :
1347 set_description (comp_data, value);
1348 break;
1349 case E_CAL_MODEL_FIELD_DTSTART :
1350 set_dtstart (model, comp_data, value);
1351 break;
1352 case E_CAL_MODEL_FIELD_SUMMARY :
1353 set_summary (comp_data, value);
1354 break;
1355 }
1356
1357 /* FIXME ask about mod type */
1358 e_cal_client_modify_object_sync (
1359 comp_data->client, comp_data->icalcomp,
1360 CALOBJ_MOD_ALL, NULL, &error);
1361
1362 if (error != NULL) {
1363 g_warning (
1364 G_STRLOC ": Could not modify the object! %s",
1365 error->message);
1366
1367 /* FIXME Show error dialog */
1368 g_error_free (error);
1369 }
1370 }
1371
1372 /**
1373 * e_cal_model_test_row_editable
1374 * @model: an #ECalModel
1375 * @row: Row of our interest. -1 is editable only when default client is
1376 * editable.
1377 *
1378 * Checks if component at @row is editable or not. It doesn't check bounds
1379 * for @row.
1380 *
1381 * Returns: Whether @row is editable or not.
1382 **/
1383 gboolean
1384 e_cal_model_test_row_editable (ECalModel *model,
1385 gint row)
1386 {
1387 gboolean readonly;
1388 ECalClient *client = NULL;
1389
1390 if (row != -1) {
1391 ECalModelComponent *comp_data;
1392
1393 comp_data = e_cal_model_get_component_at (model, row);
1394
1395 if (comp_data)
1396 client = comp_data->client;
1397
1398 } else {
1399 client = e_cal_model_get_default_client (model);
1400 }
1401
1402 readonly = client == NULL;
1403
1404 if (!readonly)
1405 readonly = e_client_is_readonly (E_CLIENT (client));
1406
1407 return !readonly;
1408 }
1409
1410 static gboolean
1411 ecm_is_cell_editable (ETableModel *etm,
1412 gint col,
1413 gint row)
1414 {
1415 ECalModelPrivate *priv;
1416 ECalModel *model = (ECalModel *) etm;
1417
1418 g_return_val_if_fail (E_IS_CAL_MODEL (model), FALSE);
1419
1420 priv = model->priv;
1421
1422 g_return_val_if_fail (col >= 0 && col <= E_CAL_MODEL_FIELD_LAST, FALSE);
1423 g_return_val_if_fail (row >= -1 || (row >= 0 && row < priv->objects->len), FALSE);
1424
1425 if (!e_cal_model_test_row_editable (E_CAL_MODEL (etm), row))
1426 return FALSE;
1427
1428 switch (col) {
1429 case E_CAL_MODEL_FIELD_CATEGORIES :
1430 case E_CAL_MODEL_FIELD_CLASSIFICATION :
1431 case E_CAL_MODEL_FIELD_DESCRIPTION :
1432 case E_CAL_MODEL_FIELD_DTSTART :
1433 case E_CAL_MODEL_FIELD_SUMMARY :
1434 return TRUE;
1435 }
1436
1437 return FALSE;
1438 }
1439
1440 static void
1441 ecm_append_row (ETableModel *etm,
1442 ETableModel *source,
1443 gint row)
1444 {
1445 ECalModelClass *model_class;
1446 ECalModelComponent *comp_data;
1447 ECalModel *model = (ECalModel *) etm;
1448 gchar *uid = NULL;
1449 GError *error = NULL;
1450
1451 g_return_if_fail (E_IS_CAL_MODEL (model));
1452 g_return_if_fail (E_IS_TABLE_MODEL (source));
1453
1454 comp_data = g_object_new (E_TYPE_CAL_MODEL_COMPONENT, NULL);
1455
1456 comp_data->client = e_cal_model_get_default_client (model);
1457 if (comp_data->client)
1458 g_object_ref (comp_data->client);
1459
1460 /* guard against saving before the calendar is open */
1461 if (!comp_data->client || !e_client_is_opened (E_CLIENT (comp_data->client))) {
1462 g_object_unref (comp_data);
1463 return;
1464 }
1465
1466 comp_data->icalcomp = e_cal_model_create_component_with_defaults (model, FALSE);
1467
1468 /* set values for our fields */
1469 set_categories (comp_data, e_table_model_value_at (source, E_CAL_MODEL_FIELD_CATEGORIES, row));
1470 set_classification (comp_data, e_table_model_value_at (source, E_CAL_MODEL_FIELD_CLASSIFICATION, row));
1471 set_description (comp_data, e_table_model_value_at (source, E_CAL_MODEL_FIELD_DESCRIPTION, row));
1472 set_summary (comp_data, e_table_model_value_at (source, E_CAL_MODEL_FIELD_SUMMARY, row));
1473
1474 if (e_table_model_value_at (source, E_CAL_MODEL_FIELD_DTSTART, row)) {
1475 set_dtstart (model, comp_data, e_table_model_value_at (source, E_CAL_MODEL_FIELD_DTSTART, row));
1476 } else if (model->priv->get_default_time) {
1477 time_t tt = model->priv->get_default_time (model, model->priv->get_default_time_user_data);
1478
1479 if (tt > 0) {
1480 struct icaltimetype itt = icaltime_from_timet_with_zone (tt, FALSE, e_cal_model_get_timezone (model));
1481 icalproperty *prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DTSTART_PROPERTY);
1482
1483 if (prop) {
1484 icalproperty_set_dtstart (prop, itt);
1485 } else {
1486 prop = icalproperty_new_dtstart (itt);
1487 icalcomponent_add_property (comp_data->icalcomp, prop);
1488 }
1489 }
1490 }
1491
1492 /* call the class' method for filling the component */
1493 model_class = (ECalModelClass *) G_OBJECT_GET_CLASS (model);
1494 if (model_class->fill_component_from_model != NULL) {
1495 model_class->fill_component_from_model (model, comp_data, source, row);
1496 }
1497
1498 e_cal_client_create_object_sync (
1499 comp_data->client, comp_data->icalcomp, &uid, NULL, &error);
1500
1501 if (error != NULL) {
1502 g_warning (
1503 G_STRLOC ": Could not create the object! %s",
1504 error->message);
1505
1506 /* FIXME: show error dialog */
1507 g_error_free (error);
1508 } else {
1509 if (uid)
1510 icalcomponent_set_uid (comp_data->icalcomp, uid);
1511
1512 g_signal_emit (model, signals[ROW_APPENDED], 0);
1513 }
1514
1515 g_free (uid);
1516 g_object_unref (comp_data);
1517 }
1518
1519 static gpointer
1520 ecm_duplicate_value (ETableModel *etm,
1521 gint col,
1522 gconstpointer value)
1523 {
1524 g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_FIELD_LAST, NULL);
1525
1526 switch (col) {
1527 case E_CAL_MODEL_FIELD_CATEGORIES :
1528 case E_CAL_MODEL_FIELD_CLASSIFICATION :
1529 case E_CAL_MODEL_FIELD_DESCRIPTION :
1530 case E_CAL_MODEL_FIELD_SUMMARY :
1531 return g_strdup (value);
1532 case E_CAL_MODEL_FIELD_HAS_ALARMS :
1533 case E_CAL_MODEL_FIELD_ICON :
1534 case E_CAL_MODEL_FIELD_COLOR :
1535 return (gpointer) value;
1536 case E_CAL_MODEL_FIELD_COMPONENT :
1537 return icalcomponent_new_clone ((icalcomponent *) value);
1538 case E_CAL_MODEL_FIELD_DTSTART :
1539 case E_CAL_MODEL_FIELD_CREATED :
1540 case E_CAL_MODEL_FIELD_LASTMODIFIED :
1541 if (value) {
1542 ECellDateEditValue *dv, *orig_dv;
1543
1544 orig_dv = (ECellDateEditValue *) value;
1545 dv = g_new0 (ECellDateEditValue, 1);
1546 *dv = *orig_dv;
1547
1548 return dv;
1549 }
1550 break;
1551 }
1552
1553 return NULL;
1554 }
1555
1556 static void
1557 ecm_free_value (ETableModel *etm,
1558 gint col,
1559 gpointer value)
1560 {
1561 g_return_if_fail (col >= 0 && col < E_CAL_MODEL_FIELD_LAST);
1562
1563 switch (col) {
1564 case E_CAL_MODEL_FIELD_CATEGORIES :
1565 case E_CAL_MODEL_FIELD_DESCRIPTION :
1566 case E_CAL_MODEL_FIELD_SUMMARY :
1567 if (value)
1568 g_free (value);
1569 break;
1570 case E_CAL_MODEL_FIELD_CLASSIFICATION :
1571 case E_CAL_MODEL_FIELD_HAS_ALARMS :
1572 case E_CAL_MODEL_FIELD_ICON :
1573 case E_CAL_MODEL_FIELD_COLOR :
1574 break;
1575 case E_CAL_MODEL_FIELD_DTSTART :
1576 case E_CAL_MODEL_FIELD_CREATED :
1577 case E_CAL_MODEL_FIELD_LASTMODIFIED :
1578 if (value)
1579 g_free (value);
1580 break;
1581 case E_CAL_MODEL_FIELD_COMPONENT :
1582 if (value)
1583 icalcomponent_free ((icalcomponent *) value);
1584 break;
1585 }
1586 }
1587
1588 static gpointer
1589 ecm_initialize_value (ETableModel *etm,
1590 gint col)
1591 {
1592 ECalModelPrivate *priv;
1593 ECalModel *model = (ECalModel *) etm;
1594
1595 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1596 g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_FIELD_LAST, NULL);
1597
1598 priv = model->priv;
1599
1600 switch (col) {
1601 case E_CAL_MODEL_FIELD_CATEGORIES :
1602 return g_strdup (priv->default_category ? priv->default_category:"");
1603 case E_CAL_MODEL_FIELD_CLASSIFICATION :
1604 case E_CAL_MODEL_FIELD_DESCRIPTION :
1605 case E_CAL_MODEL_FIELD_SUMMARY :
1606 return g_strdup ("");
1607 case E_CAL_MODEL_FIELD_DTSTART :
1608 case E_CAL_MODEL_FIELD_CREATED :
1609 case E_CAL_MODEL_FIELD_LASTMODIFIED :
1610 case E_CAL_MODEL_FIELD_HAS_ALARMS :
1611 case E_CAL_MODEL_FIELD_ICON :
1612 case E_CAL_MODEL_FIELD_COLOR :
1613 case E_CAL_MODEL_FIELD_COMPONENT :
1614 return NULL;
1615 }
1616
1617 return NULL;
1618 }
1619
1620 static gboolean
1621 ecm_value_is_empty (ETableModel *etm,
1622 gint col,
1623 gconstpointer value)
1624 {
1625 ECalModelPrivate *priv;
1626 ECalModel *model = (ECalModel *) etm;
1627
1628 g_return_val_if_fail (E_IS_CAL_MODEL (model), TRUE);
1629 g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_FIELD_LAST, TRUE);
1630
1631 priv = model->priv;
1632
1633 switch (col) {
1634 case E_CAL_MODEL_FIELD_CATEGORIES :
1635 /* This could be a hack or not. If the categories field only
1636 * contains the default category, then it possibly means that
1637 * the user has not entered anything at all in the click-to-add;
1638 * the category is in the value because we put it there in
1639 * ecm_initialize_value().
1640 */
1641 if (priv->default_category && value && strcmp (priv->default_category, value) == 0)
1642 return TRUE;
1643 else
1644 return string_is_empty (value);
1645 case E_CAL_MODEL_FIELD_CLASSIFICATION :
1646 case E_CAL_MODEL_FIELD_DESCRIPTION :
1647 case E_CAL_MODEL_FIELD_SUMMARY :
1648 return string_is_empty (value);
1649 case E_CAL_MODEL_FIELD_DTSTART :
1650 case E_CAL_MODEL_FIELD_CREATED :
1651 case E_CAL_MODEL_FIELD_LASTMODIFIED :
1652 return value ? FALSE : TRUE;
1653 case E_CAL_MODEL_FIELD_HAS_ALARMS :
1654 case E_CAL_MODEL_FIELD_ICON :
1655 case E_CAL_MODEL_FIELD_COLOR :
1656 case E_CAL_MODEL_FIELD_COMPONENT :
1657 return TRUE;
1658 }
1659
1660 return TRUE;
1661 }
1662
1663 static gchar *
1664 ecm_value_to_string (ETableModel *etm,
1665 gint col,
1666 gconstpointer value)
1667 {
1668 g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_FIELD_LAST, g_strdup (""));
1669
1670 switch (col) {
1671 case E_CAL_MODEL_FIELD_CATEGORIES :
1672 case E_CAL_MODEL_FIELD_CLASSIFICATION :
1673 case E_CAL_MODEL_FIELD_DESCRIPTION :
1674 case E_CAL_MODEL_FIELD_SUMMARY :
1675 return g_strdup (value);
1676 case E_CAL_MODEL_FIELD_DTSTART :
1677 case E_CAL_MODEL_FIELD_CREATED :
1678 case E_CAL_MODEL_FIELD_LASTMODIFIED :
1679 return e_cal_model_date_value_to_string (E_CAL_MODEL (etm), value);
1680 case E_CAL_MODEL_FIELD_ICON :
1681 if (GPOINTER_TO_INT (value) == 0)
1682 return g_strdup (_("Normal"));
1683 else if (GPOINTER_TO_INT (value) == 1)
1684 return g_strdup (_("Recurring"));
1685 else
1686 return g_strdup (_("Assigned"));
1687 case E_CAL_MODEL_FIELD_HAS_ALARMS :
1688 return g_strdup (value ? _("Yes") : _("No"));
1689 case E_CAL_MODEL_FIELD_COLOR :
1690 case E_CAL_MODEL_FIELD_COMPONENT :
1691 return g_strdup ("");
1692 }
1693
1694 return g_strdup ("");
1695 }
1696
1697 /* ECalModel class methods */
1698
1699 typedef struct {
1700 const gchar *color;
1701 GList *uids;
1702 } AssignedColorData;
1703
1704 static const gchar *
1705 ecm_get_color_for_component (ECalModel *model,
1706 ECalModelComponent *comp_data)
1707 {
1708 ESource *source;
1709 ESourceSelectable *extension;
1710 const gchar *color_spec;
1711 const gchar *extension_name;
1712 const gchar *uid;
1713 gint i, first_empty = 0;
1714
1715 static AssignedColorData assigned_colors[] = {
1716 { "#BECEDD", NULL }, /* 190 206 221 Blue */
1717 { "#E2F0EF", NULL }, /* 226 240 239 Light Blue */
1718 { "#C6E2B7", NULL }, /* 198 226 183 Green */
1719 { "#E2F0D3", NULL }, /* 226 240 211 Light Green */
1720 { "#E2D4B7", NULL }, /* 226 212 183 Khaki */
1721 { "#EAEAC1", NULL }, /* 234 234 193 Light Khaki */
1722 { "#F0B8B7", NULL }, /* 240 184 183 Pink */
1723 { "#FED4D3", NULL }, /* 254 212 211 Light Pink */
1724 { "#E2C6E1", NULL }, /* 226 198 225 Purple */
1725 { "#F0E2EF", NULL } /* 240 226 239 Light Purple */
1726 };
1727
1728 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1729
1730 switch (e_cal_client_get_source_type (comp_data->client)) {
1731 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1732 extension_name = E_SOURCE_EXTENSION_CALENDAR;
1733 break;
1734 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1735 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
1736 break;
1737 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1738 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
1739 break;
1740 default:
1741 g_return_val_if_reached (NULL);
1742 }
1743
1744 source = e_client_get_source (E_CLIENT (comp_data->client));
1745 extension = e_source_get_extension (source, extension_name);
1746 color_spec = e_source_selectable_get_color (extension);
1747
1748 if (color_spec != NULL) {
1749 g_free (comp_data->color);
1750 comp_data->color = g_strdup (color_spec);
1751 return comp_data->color;
1752 }
1753
1754 uid = e_source_get_uid (source);
1755
1756 for (i = 0; i < G_N_ELEMENTS (assigned_colors); i++) {
1757 GList *l;
1758
1759 if (assigned_colors[i].uids == NULL) {
1760 first_empty = i;
1761 continue;
1762 }
1763
1764 for (l = assigned_colors[i].uids; l != NULL; l = l->next)
1765 if (g_strcmp0 (l->data, uid) == 0)
1766 return assigned_colors[i].color;
1767 }
1768
1769 /* return the first unused color */
1770 assigned_colors[first_empty].uids = g_list_append (
1771 assigned_colors[first_empty].uids, g_strdup (uid));
1772
1773 return assigned_colors[first_empty].color;
1774 }
1775
1776 gboolean
1777 e_cal_model_get_confirm_delete (ECalModel *model)
1778 {
1779 g_return_val_if_fail (E_IS_CAL_MODEL (model), FALSE);
1780
1781 return model->priv->confirm_delete;
1782 }
1783
1784 void
1785 e_cal_model_set_confirm_delete (ECalModel *model,
1786 gboolean confirm_delete)
1787 {
1788 g_return_if_fail (E_IS_CAL_MODEL (model));
1789
1790 if (model->priv->confirm_delete == confirm_delete)
1791 return;
1792
1793 model->priv->confirm_delete = confirm_delete;
1794
1795 g_object_notify (G_OBJECT (model), "confirm-delete");
1796 }
1797
1798 icalcomponent_kind
1799 e_cal_model_get_component_kind (ECalModel *model)
1800 {
1801 g_return_val_if_fail (E_IS_CAL_MODEL (model), ICAL_NO_COMPONENT);
1802
1803 return model->priv->kind;
1804 }
1805
1806 void
1807 e_cal_model_set_component_kind (ECalModel *model,
1808 icalcomponent_kind kind)
1809 {
1810 g_return_if_fail (E_IS_CAL_MODEL (model));
1811
1812 model->priv->kind = kind;
1813 }
1814
1815 ECalModelFlags
1816 e_cal_model_get_flags (ECalModel *model)
1817 {
1818 g_return_val_if_fail (E_IS_CAL_MODEL (model), 0);
1819
1820 return model->priv->flags;
1821 }
1822
1823 void
1824 e_cal_model_set_flags (ECalModel *model,
1825 ECalModelFlags flags)
1826 {
1827 g_return_if_fail (E_IS_CAL_MODEL (model));
1828
1829 model->priv->flags = flags;
1830 }
1831
1832 ESourceRegistry *
1833 e_cal_model_get_registry (ECalModel *model)
1834 {
1835 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1836
1837 return model->priv->registry;
1838 }
1839
1840 icaltimezone *
1841 e_cal_model_get_timezone (ECalModel *model)
1842 {
1843 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1844
1845 return model->priv->zone;
1846 }
1847
1848 void
1849 e_cal_model_set_timezone (ECalModel *model,
1850 icaltimezone *zone)
1851 {
1852 icaltimezone *old_zone;
1853 g_return_if_fail (E_IS_CAL_MODEL (model));
1854
1855 if (model->priv->zone == zone)
1856 return;
1857
1858 e_table_model_pre_change (E_TABLE_MODEL (model));
1859 old_zone = model->priv->zone;
1860 model->priv->zone = zone;
1861
1862 /* the timezone affects the times shown for date fields,
1863 * so we need to redisplay everything */
1864 e_table_model_changed (E_TABLE_MODEL (model));
1865 redo_queries (model);
1866
1867 g_object_notify (G_OBJECT (model), "timezone");
1868 g_signal_emit (
1869 model, signals[TIMEZONE_CHANGED], 0,
1870 old_zone, zone);
1871 }
1872
1873 gboolean
1874 e_cal_model_get_compress_weekend (ECalModel *model)
1875 {
1876 g_return_val_if_fail (E_IS_CAL_MODEL (model), FALSE);
1877
1878 return model->priv->compress_weekend;
1879 }
1880
1881 void
1882 e_cal_model_set_compress_weekend (ECalModel *model,
1883 gboolean compress_weekend)
1884 {
1885 g_return_if_fail (E_IS_CAL_MODEL (model));
1886
1887 if (model->priv->compress_weekend == compress_weekend)
1888 return;
1889
1890 model->priv->compress_weekend = compress_weekend;
1891
1892 g_object_notify (G_OBJECT (model), "compress-weekend");
1893 }
1894
1895 void
1896 e_cal_model_set_default_category (ECalModel *model,
1897 const gchar *default_category)
1898 {
1899 g_return_if_fail (E_IS_CAL_MODEL (model));
1900
1901 g_free (model->priv->default_category);
1902 model->priv->default_category = g_strdup (default_category);
1903 }
1904
1905 gint
1906 e_cal_model_get_default_reminder_interval (ECalModel *model)
1907 {
1908 g_return_val_if_fail (E_IS_CAL_MODEL (model), 0);
1909
1910 return model->priv->default_reminder_interval;
1911 }
1912
1913 void
1914 e_cal_model_set_default_reminder_interval (ECalModel *model,
1915 gint default_reminder_interval)
1916 {
1917 g_return_if_fail (E_IS_CAL_MODEL (model));
1918
1919 if (model->priv->default_reminder_interval == default_reminder_interval)
1920 return;
1921
1922 model->priv->default_reminder_interval = default_reminder_interval;
1923
1924 g_object_notify (G_OBJECT (model), "default-reminder-interval");
1925 }
1926
1927 EDurationType
1928 e_cal_model_get_default_reminder_units (ECalModel *model)
1929 {
1930 g_return_val_if_fail (E_IS_CAL_MODEL (model), 0);
1931
1932 return model->priv->default_reminder_units;
1933 }
1934
1935 void
1936 e_cal_model_set_default_reminder_units (ECalModel *model,
1937 EDurationType default_reminder_units)
1938 {
1939 g_return_if_fail (E_IS_CAL_MODEL (model));
1940
1941 if (model->priv->default_reminder_units == default_reminder_units)
1942 return;
1943
1944 model->priv->default_reminder_units = default_reminder_units;
1945
1946 g_object_notify (G_OBJECT (model), "default-reminder-units");
1947 }
1948
1949 gboolean
1950 e_cal_model_get_use_24_hour_format (ECalModel *model)
1951 {
1952 g_return_val_if_fail (E_IS_CAL_MODEL (model), FALSE);
1953
1954 return model->priv->use_24_hour_format;
1955 }
1956
1957 void
1958 e_cal_model_set_use_24_hour_format (ECalModel *model,
1959 gboolean use_24_hour_format)
1960 {
1961 g_return_if_fail (E_IS_CAL_MODEL (model));
1962
1963 if (model->priv->use_24_hour_format == use_24_hour_format)
1964 return;
1965
1966 e_table_model_pre_change (E_TABLE_MODEL (model));
1967 model->priv->use_24_hour_format = use_24_hour_format;
1968
1969 /* Get the views to redraw themselves. */
1970 e_table_model_changed (E_TABLE_MODEL (model));
1971
1972 g_object_notify (G_OBJECT (model), "use-24-hour-format");
1973 }
1974
1975 gboolean
1976 e_cal_model_get_use_default_reminder (ECalModel *model)
1977 {
1978 g_return_val_if_fail (E_IS_CAL_MODEL (model), FALSE);
1979
1980 return model->priv->use_default_reminder;
1981 }
1982
1983 void
1984 e_cal_model_set_use_default_reminder (ECalModel *model,
1985 gboolean use_default_reminder)
1986 {
1987 g_return_if_fail (E_IS_CAL_MODEL (model));
1988
1989 if (model->priv->use_default_reminder == use_default_reminder)
1990 return;
1991
1992 model->priv->use_default_reminder = use_default_reminder;
1993
1994 g_object_notify (G_OBJECT (model), "use-default-reminder");
1995 }
1996
1997 gint
1998 e_cal_model_get_week_start_day (ECalModel *model)
1999 {
2000 g_return_val_if_fail (E_IS_CAL_MODEL (model), 0);
2001
2002 return model->priv->week_start_day;
2003 }
2004
2005 void
2006 e_cal_model_set_week_start_day (ECalModel *model,
2007 gint week_start_day)
2008 {
2009 g_return_if_fail (E_IS_CAL_MODEL (model));
2010 g_return_if_fail (week_start_day >= 0);
2011 g_return_if_fail (week_start_day < 7);
2012
2013 if (model->priv->week_start_day == week_start_day)
2014 return;
2015
2016 model->priv->week_start_day = week_start_day;
2017
2018 g_object_notify (G_OBJECT (model), "week-start-day");
2019 }
2020
2021 gint
2022 e_cal_model_get_work_day_end_hour (ECalModel *model)
2023 {
2024 g_return_val_if_fail (E_IS_CAL_MODEL (model), 0);
2025
2026 return model->priv->work_day_end_hour;
2027 }
2028
2029 void
2030 e_cal_model_set_work_day_end_hour (ECalModel *model,
2031 gint work_day_end_hour)
2032 {
2033 g_return_if_fail (E_IS_CAL_MODEL (model));
2034
2035 if (model->priv->work_day_end_hour == work_day_end_hour)
2036 return;
2037
2038 model->priv->work_day_end_hour = work_day_end_hour;
2039
2040 g_object_notify (G_OBJECT (model), "work-day-end-hour");
2041 }
2042
2043 gint
2044 e_cal_model_get_work_day_end_minute (ECalModel *model)
2045 {
2046 g_return_val_if_fail (E_IS_CAL_MODEL (model), 0);
2047
2048 return model->priv->work_day_end_minute;
2049 }
2050
2051 void
2052 e_cal_model_set_work_day_end_minute (ECalModel *model,
2053 gint work_day_end_minute)
2054 {
2055 g_return_if_fail (E_IS_CAL_MODEL (model));
2056
2057 if (model->priv->work_day_end_minute == work_day_end_minute)
2058 return;
2059
2060 model->priv->work_day_end_minute = work_day_end_minute;
2061
2062 g_object_notify (G_OBJECT (model), "work-day-end-minute");
2063 }
2064
2065 gint
2066 e_cal_model_get_work_day_start_hour (ECalModel *model)
2067 {
2068 g_return_val_if_fail (E_IS_CAL_MODEL (model), 0);
2069
2070 return model->priv->work_day_start_hour;
2071 }
2072
2073 void
2074 e_cal_model_set_work_day_start_hour (ECalModel *model,
2075 gint work_day_start_hour)
2076 {
2077 g_return_if_fail (E_IS_CAL_MODEL (model));
2078
2079 if (model->priv->work_day_start_hour == work_day_start_hour)
2080 return;
2081
2082 model->priv->work_day_start_hour = work_day_start_hour;
2083
2084 g_object_notify (G_OBJECT (model), "work-day-start-hour");
2085 }
2086
2087 gint
2088 e_cal_model_get_work_day_start_minute (ECalModel *model)
2089 {
2090 g_return_val_if_fail (E_IS_CAL_MODEL (model), 0);
2091
2092 return model->priv->work_day_start_minute;
2093 }
2094
2095 void
2096 e_cal_model_set_work_day_start_minute (ECalModel *model,
2097 gint work_day_start_minute)
2098 {
2099 g_return_if_fail (E_IS_CAL_MODEL (model));
2100
2101 if (model->priv->work_day_start_minute == work_day_start_minute)
2102 return;
2103
2104 model->priv->work_day_start_minute = work_day_start_minute;
2105
2106 g_object_notify (G_OBJECT (model), "work-day-start-minute");
2107 }
2108
2109 ECalClient *
2110 e_cal_model_get_default_client (ECalModel *model)
2111 {
2112 ECalModelPrivate *priv;
2113 ECalModelClient *client_data;
2114
2115 g_return_val_if_fail (model != NULL, NULL);
2116 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
2117
2118 priv = model->priv;
2119
2120 /* FIXME Should we force the client to be open? */
2121
2122 /* we always return a valid ECal, since we rely on it in many places */
2123 if (priv->default_client)
2124 return priv->default_client;
2125
2126 if (!priv->clients)
2127 return NULL;
2128
2129 client_data = (ECalModelClient *) priv->clients->data;
2130
2131 return client_data ? client_data->client : NULL;
2132 }
2133
2134 void
2135 e_cal_model_set_default_client (ECalModel *model,
2136 ECalClient *client)
2137 {
2138 ECalModelPrivate *priv;
2139 ECalModelClient *client_data;
2140
2141 g_return_if_fail (E_IS_CAL_MODEL (model));
2142
2143 if (client != NULL)
2144 g_return_if_fail (E_IS_CAL_CLIENT (client));
2145
2146 priv = model->priv;
2147
2148 if (priv->default_client == client)
2149 return;
2150
2151 if (priv->default_client) {
2152 client_data = find_client_data (model, priv->default_client);
2153 if (!client_data) {
2154 g_warning ("client_data is NULL\n");
2155 } else {
2156 if (!client_data->do_query)
2157 remove_client (model, client_data);
2158 }
2159 }
2160
2161 if (client != NULL) {
2162 /* Make sure its in the model */
2163 client_data = add_new_client (model, client, FALSE);
2164
2165 /* Store the default client */
2166 priv->default_client = client_data->client;
2167 } else
2168 priv->default_client = NULL;
2169
2170 g_object_notify (G_OBJECT (model), "default-client");
2171 }
2172
2173 GList *
2174 e_cal_model_get_client_list (ECalModel *model)
2175 {
2176 GList *list = NULL, *l;
2177 ECalClient *default_client;
2178
2179 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
2180
2181 default_client = model->priv->default_client;
2182
2183 for (l = model->priv->clients; l != NULL; l = l->next) {
2184 ECalModelClient *client_data = (ECalModelClient *) l->data;
2185
2186 /* Exclude the default client if we're not querying it. */
2187 if (client_data->client == default_client && !client_data->do_query)
2188 continue;
2189
2190 list = g_list_append (list, client_data->client);
2191 }
2192
2193 return list;
2194 }
2195
2196 /**
2197 * e_cal_model_get_client_for_source:
2198 * @model: an #ECalModel
2199 * @source: an #ESource
2200 */
2201 ECalClient *
2202 e_cal_model_get_client_for_source (ECalModel *model,
2203 ESource *source)
2204 {
2205 GList *link;
2206
2207 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
2208 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
2209
2210 for (link = model->priv->clients; link != NULL; link = link->next) {
2211 ECalModelClient *client_data;
2212 ESource *client_source;
2213 EClient *client;
2214
2215 client_data = (ECalModelClient *) link->data;
2216 client = E_CLIENT (client_data->client);
2217 client_source = e_client_get_source (client);
2218
2219 if (e_source_equal (source, client_source))
2220 return client_data->client;
2221 }
2222
2223 return NULL;
2224 }
2225
2226 static ECalModelClient *
2227 find_client_data (ECalModel *model,
2228 ECalClient *client)
2229 {
2230 ECalModelPrivate *priv;
2231 GList *l;
2232
2233 priv = model->priv;
2234
2235 for (l = priv->clients; l != NULL; l = l->next) {
2236 ECalModelClient *client_data = (ECalModelClient *) l->data;
2237
2238 if (client_data->client == client)
2239 return client_data;
2240 }
2241
2242 return NULL;
2243 }
2244
2245 static ECalModelComponent *
2246 search_by_id_and_client (ECalModelPrivate *priv,
2247 ECalClient *client,
2248 const ECalComponentId *id)
2249 {
2250 gint i;
2251
2252 for (i = 0; i < priv->objects->len; i++) {
2253 ECalModelComponent *comp_data = g_ptr_array_index (priv->objects, i);
2254
2255 if (comp_data) {
2256 const gchar *uid;
2257 gchar *rid = NULL;
2258 struct icaltimetype icalrid;
2259 gboolean has_rid = (id->rid && *id->rid);
2260
2261 uid = icalcomponent_get_uid (comp_data->icalcomp);
2262 icalrid = icalcomponent_get_recurrenceid (comp_data->icalcomp);
2263 if (!icaltime_is_null_time (icalrid))
2264 rid = icaltime_as_ical_string_r (icalrid);
2265
2266 if (uid && *uid) {
2267 if ((!client || comp_data->client == client) && !strcmp (id->uid, uid)) {
2268 if (has_rid) {
2269 if (!(rid && *rid && !strcmp (rid, id->rid))) {
2270 g_free (rid);
2271 continue;
2272 }
2273 }
2274 g_free (rid);
2275 return comp_data;
2276 }
2277 }
2278
2279 g_free (rid);
2280 }
2281 }
2282
2283 return NULL;
2284 }
2285
2286 static void
2287 remove_all_for_id_and_client (ECalModel *model,
2288 ECalClient *client,
2289 const ECalComponentId *id)
2290 {
2291 ECalModelComponent *comp_data;
2292
2293 while ((comp_data = search_by_id_and_client (model->priv, client, id))) {
2294 gint pos;
2295 GSList *list = NULL;
2296
2297 pos = get_position_in_array (model->priv->objects, comp_data);
2298
2299 if (!g_ptr_array_remove (model->priv->objects, comp_data))
2300 continue;
2301
2302 list = g_slist_append (list, comp_data);
2303 g_signal_emit (model, signals[COMPS_DELETED], 0, list);
2304
2305 g_slist_free (list);
2306 g_object_unref (comp_data);
2307
2308 e_table_model_pre_change (E_TABLE_MODEL (model));
2309 e_table_model_row_deleted (E_TABLE_MODEL (model), pos);
2310 }
2311 }
2312
2313 typedef struct {
2314 ECalClient *client;
2315 ECalClientView *view;
2316 ECalModel *model;
2317 icalcomponent *icalcomp;
2318 } RecurrenceExpansionData;
2319
2320 static void
2321 free_rdata (gpointer data)
2322 {
2323 RecurrenceExpansionData *rdata = data;
2324
2325 if (!rdata)
2326 return;
2327
2328 g_object_unref (rdata->client);
2329 g_object_unref (rdata->view);
2330 g_object_unref (rdata->model);
2331 g_free (rdata);
2332 }
2333
2334 static gboolean
2335 add_instance_cb (ECalComponent *comp,
2336 time_t instance_start,
2337 time_t instance_end,
2338 gpointer user_data)
2339 {
2340 ECalModelComponent *comp_data;
2341 ECalModelPrivate *priv;
2342 RecurrenceExpansionData *rdata = user_data;
2343 icaltimetype time;
2344 ECalComponentDateTime datetime, to_set;
2345 icaltimezone *zone = NULL;
2346 ECalComponentId *id;
2347
2348 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), TRUE);
2349
2350 priv = rdata->model->priv;
2351
2352 id = e_cal_component_get_id (comp);
2353 remove_all_for_id_and_client (rdata->model, rdata->client, id);
2354 e_cal_component_free_id (id);
2355
2356 e_table_model_pre_change (E_TABLE_MODEL (rdata->model));
2357
2358 /* set the right instance start date to component */
2359 e_cal_component_get_dtstart (comp, &datetime);
2360 if (datetime.tzid)
2361 e_cal_client_get_timezone_sync (rdata->client, datetime.tzid, &zone, NULL, NULL);
2362 time = icaltime_from_timet_with_zone (instance_start, FALSE, zone ? zone : priv->zone);
2363 to_set.value = &time;
2364 to_set.tzid = datetime.tzid;
2365 e_cal_component_set_dtstart (comp, &to_set);
2366 e_cal_component_free_datetime (&datetime);
2367
2368 /* set the right instance end date to component*/
2369 e_cal_component_get_dtend (comp, &datetime);
2370 zone = NULL;
2371 if (datetime.tzid)
2372 e_cal_client_get_timezone_sync (rdata->client, datetime.tzid, &zone, NULL, NULL);
2373 time = icaltime_from_timet_with_zone (instance_end, FALSE, zone ? zone : priv->zone);
2374 to_set.value = &time;
2375 to_set.tzid = datetime.tzid;
2376 e_cal_component_set_dtend (comp, &to_set);
2377 e_cal_component_free_datetime (&datetime);
2378
2379 comp_data = g_object_new (E_TYPE_CAL_MODEL_COMPONENT, NULL);
2380 comp_data->client = g_object_ref (rdata->client);
2381 comp_data->icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
2382 comp_data->instance_start = instance_start;
2383 comp_data->instance_end = instance_end;
2384
2385 g_ptr_array_add (priv->objects, comp_data);
2386 e_table_model_row_inserted (E_TABLE_MODEL (rdata->model), priv->objects->len - 1);
2387
2388 return TRUE;
2389 }
2390
2391 /* We do this check since the calendar items are downloaded from the server
2392 * in the open_method, since the default timezone might not be set there. */
2393 static void
2394 ensure_dates_are_in_default_zone (ECalModel *model,
2395 icalcomponent *icalcomp)
2396 {
2397 icaltimetype dt;
2398 icaltimezone *zone;
2399
2400 zone = e_cal_model_get_timezone (model);
2401 if (!zone)
2402 return;
2403
2404 dt = icalcomponent_get_dtstart (icalcomp);
2405 if (dt.is_utc) {
2406 dt = icaltime_convert_to_zone (dt, zone);
2407 icalcomponent_set_dtstart (icalcomp, dt);
2408 }
2409
2410 dt = icalcomponent_get_dtend (icalcomp);
2411 if (dt.is_utc) {
2412 dt = icaltime_convert_to_zone (dt, zone);
2413 icalcomponent_set_dtend (icalcomp, dt);
2414 }
2415 }
2416
2417 static gint
2418 place_master_object_first_cb (gconstpointer p1,
2419 gconstpointer p2)
2420 {
2421 icalcomponent *c1 = (icalcomponent *) p1, *c2 = (icalcomponent *) p2;
2422 const gchar *uid1, *uid2;
2423 gint res;
2424
2425 g_return_val_if_fail (c1 != NULL, 0);
2426 g_return_val_if_fail (c2 != NULL, 0);
2427
2428 uid1 = icalcomponent_get_uid (c1);
2429 uid2 = icalcomponent_get_uid (c2);
2430
2431 res = g_strcmp0 (uid1, uid2);
2432 if (res == 0) {
2433 struct icaltimetype rid1, rid2;
2434
2435 rid1 = icalcomponent_get_recurrenceid (c1);
2436 rid2 = icalcomponent_get_recurrenceid (c2);
2437
2438 if (icaltime_is_null_time (rid1)) {
2439 if (!icaltime_is_null_time (rid2))
2440 res = -1;
2441 } else if (icaltime_is_null_time (rid2)) {
2442 res = 1;
2443 } else {
2444 res = icaltime_compare (rid1, rid2);
2445 }
2446 }
2447
2448 return res;
2449 }
2450
2451 static void client_view_objects_added_cb (ECalClientView *view, const GSList *objects, ECalModel *model);
2452
2453 static void
2454 process_added (ECalClientView *view,
2455 const GSList *objects,
2456 ECalModel *model)
2457 {
2458 ECalModelPrivate *priv;
2459 const GSList *l;
2460 GSList *copy;
2461
2462 priv = model->priv;
2463
2464 /* order matters, process master object first, then detached instances */
2465 copy = g_slist_sort (g_slist_copy ((GSList *) objects), place_master_object_first_cb);
2466
2467 for (l = copy; l; l = l->next) {
2468 ECalModelComponent *comp_data;
2469 ECalComponentId *id;
2470 ECalComponent *comp = e_cal_component_new ();
2471 ECalClient *client = e_cal_client_view_get_client (view);
2472
2473 /* This will fail for alarm or VCalendar component */
2474 if (!e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (l->data))) {
2475 g_object_unref (comp);
2476 continue;
2477 }
2478
2479 id = e_cal_component_get_id (comp);
2480
2481 /* remove the components if they are already present and re-add them */
2482 remove_all_for_id_and_client (model, client, id);
2483
2484 e_cal_component_free_id (id);
2485 g_object_unref (comp);
2486 ensure_dates_are_in_default_zone (model, l->data);
2487
2488 if (e_cal_util_component_has_recurrences (l->data) && (priv->flags & E_CAL_MODEL_FLAGS_EXPAND_RECURRENCES)) {
2489 ECalModelClient *client_data = find_client_data (model, client);
2490
2491 if (client_data) {
2492 RecurrenceExpansionData *rdata = g_new0 (RecurrenceExpansionData, 1);
2493 rdata->client = g_object_ref (client);
2494 rdata->view = g_object_ref (view);
2495 rdata->model = g_object_ref (model);
2496
2497 e_cal_client_generate_instances_for_object (rdata->client, l->data, priv->start, priv->end, client_data->cancellable,
2498 (ECalRecurInstanceFn) add_instance_cb, rdata, free_rdata);
2499 }
2500 } else {
2501 e_table_model_pre_change (E_TABLE_MODEL (model));
2502
2503 comp_data = g_object_new (E_TYPE_CAL_MODEL_COMPONENT, NULL);
2504 comp_data->client = g_object_ref (client);
2505 comp_data->icalcomp = icalcomponent_new_clone (l->data);
2506 e_cal_model_set_instance_times (comp_data, priv->zone);
2507
2508 g_ptr_array_add (priv->objects, comp_data);
2509 e_table_model_row_inserted (E_TABLE_MODEL (model), priv->objects->len - 1);
2510 }
2511 }
2512
2513 g_slist_free (copy);
2514 }
2515
2516 static void
2517 process_modified (ECalClientView *view,
2518 const GSList *objects,
2519 ECalModel *model)
2520 {
2521 ECalModelPrivate *priv;
2522 const GSList *l;
2523 GSList *list = NULL;
2524
2525 priv = model->priv;
2526
2527 /* re-add only the recurrence objects */
2528 for (l = objects; l != NULL; l = g_slist_next (l)) {
2529 if (!e_cal_util_component_is_instance (l->data) && e_cal_util_component_has_recurrences (l->data) && (priv->flags & E_CAL_MODEL_FLAGS_EXPAND_RECURRENCES))
2530 list = g_slist_prepend (list, l->data);
2531 else {
2532 gint pos;
2533 ECalModelComponent *comp_data;
2534 ECalComponentId *id;
2535 ECalComponent *comp = e_cal_component_new ();
2536 ECalClient *client = e_cal_client_view_get_client (view);
2537
2538 if (!e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (l->data))) {
2539 g_object_unref (comp);
2540 continue;
2541 }
2542
2543 e_table_model_pre_change (E_TABLE_MODEL (model));
2544
2545 id = e_cal_component_get_id (comp);
2546
2547 comp_data = search_by_id_and_client (priv, client, id);
2548
2549 e_cal_component_free_id (id);
2550 g_object_unref (comp);
2551
2552 if (!comp_data) {
2553 /* the modified component is not in the model yet, just skip it */
2554 continue;
2555 }
2556
2557 if (comp_data->icalcomp)
2558 icalcomponent_free (comp_data->icalcomp);
2559 if (comp_data->dtstart) {
2560 g_free (comp_data->dtstart);
2561 comp_data->dtstart = NULL;
2562 }
2563 if (comp_data->dtend) {
2564 g_free (comp_data->dtend);
2565 comp_data->dtend = NULL;
2566 }
2567 if (comp_data->due) {
2568 g_free (comp_data->due);
2569 comp_data->due = NULL;
2570 }
2571 if (comp_data->completed) {
2572 g_free (comp_data->completed);
2573 comp_data->completed = NULL;
2574 }
2575 if (comp_data->created) {
2576 g_free (comp_data->created);
2577 comp_data->created = NULL;
2578 }
2579 if (comp_data->lastmodified) {
2580 g_free (comp_data->lastmodified);
2581 comp_data->lastmodified = NULL;
2582 }
2583 if (comp_data->color) {
2584 g_free (comp_data->color);
2585 comp_data->color = NULL;
2586 }
2587
2588 comp_data->icalcomp = icalcomponent_new_clone (l->data);
2589 e_cal_model_set_instance_times (comp_data, priv->zone);
2590
2591 pos = get_position_in_array (priv->objects, comp_data);
2592
2593 e_table_model_row_changed (E_TABLE_MODEL (model), pos);
2594 }
2595 }
2596
2597 client_view_objects_added_cb (view, list, model);
2598 g_slist_free (list);
2599 }
2600
2601 static void
2602 process_removed (ECalClientView *view,
2603 const GSList *ids,
2604 ECalModel *model)
2605 {
2606 ECalModelPrivate *priv;
2607 const GSList *l;
2608
2609 priv = model->priv;
2610
2611 for (l = ids; l; l = l->next) {
2612 ECalModelComponent *comp_data = NULL;
2613 ECalComponentId *id = l->data;
2614 gint pos;
2615
2616 /* make sure we remove all objects with this UID */
2617 while ((comp_data = search_by_id_and_client (priv, e_cal_client_view_get_client (view), id))) {
2618 GSList *l = NULL;
2619
2620 pos = get_position_in_array (priv->objects, comp_data);
2621
2622 if (!g_ptr_array_remove (priv->objects, comp_data))
2623 continue;
2624
2625 l = g_slist_append (l, comp_data);
2626 g_signal_emit (model, signals[COMPS_DELETED], 0, l);
2627
2628 g_slist_free (l);
2629 g_object_unref (comp_data);
2630
2631 e_table_model_pre_change (E_TABLE_MODEL (model));
2632 e_table_model_row_deleted (E_TABLE_MODEL (model), pos);
2633 }
2634 }
2635
2636 /* to notify about changes, because in call of row_deleted there are still all events */
2637 e_table_model_changed (E_TABLE_MODEL (model));
2638 }
2639
2640 static gpointer
2641 copy_comp_id (gpointer id)
2642 {
2643 ECalComponentId *comp_id = (ECalComponentId *) id, *copy;
2644
2645 g_return_val_if_fail (comp_id != NULL, NULL);
2646
2647 copy = g_new0 (ECalComponentId, 1);
2648 copy->uid = g_strdup (comp_id->uid);
2649 copy->rid = g_strdup (comp_id->rid);
2650
2651 return copy;
2652 }
2653
2654 static void
2655 free_comp_id (gpointer id)
2656 {
2657 ECalComponentId *comp_id = (ECalComponentId *) id;
2658
2659 g_return_if_fail (comp_id != NULL);
2660
2661 g_free (comp_id->uid);
2662 g_free (comp_id->rid);
2663 g_free (comp_id);
2664 }
2665
2666 static void
2667 process_event (ECalClientView *view,
2668 const GSList *objects,
2669 ECalModel *model,
2670 void (*process_fn) (ECalClientView *view,
2671 const GSList *objects,
2672 ECalModel *model),
2673 gboolean *in,
2674 GHashTable *save_hash,
2675 gpointer (*copy_fn) (gpointer data),
2676 void (*free_fn) (gpointer data))
2677 {
2678 gboolean skip = FALSE;
2679 const GSList *l;
2680
2681 g_return_if_fail (save_hash != NULL);
2682
2683 g_mutex_lock (model->priv->notify_lock);
2684 if (*in) {
2685 GSList *save_list = g_hash_table_lookup (save_hash, view);
2686
2687 skip = TRUE;
2688 for (l = objects; l; l = l->next) {
2689 if (l->data)
2690 save_list = g_slist_append (save_list, copy_fn (l->data));
2691 }
2692
2693 g_hash_table_insert (save_hash, g_object_ref (view), save_list);
2694 } else {
2695 *in = TRUE;
2696 }
2697
2698 g_mutex_unlock (model->priv->notify_lock);
2699
2700 if (skip)
2701 return;
2702
2703 /* do it */
2704 process_fn (view, objects, model);
2705
2706 g_mutex_lock (model->priv->notify_lock);
2707 while (g_hash_table_size (save_hash)) {
2708 gpointer key = NULL, value = NULL;
2709 GHashTableIter iter;
2710 GSList *save_list;
2711
2712 g_hash_table_iter_init (&iter, save_hash);
2713 if (!g_hash_table_iter_next (&iter, &key, &value)) {
2714 g_debug ("%s: Failed to get first item of the save_hash", G_STRFUNC);
2715 break;
2716 }
2717
2718 save_list = value;
2719 view = g_object_ref (key);
2720
2721 g_hash_table_remove (save_hash, view);
2722
2723 g_mutex_unlock (model->priv->notify_lock);
2724
2725 /* do it */
2726 process_fn (view, save_list, model);
2727
2728 for (l = save_list; l; l = l->next) {
2729 if (l->data) {
2730 free_fn (l->data);
2731 }
2732 }
2733 g_slist_free (save_list);
2734 g_object_unref (view);
2735
2736 g_mutex_lock (model->priv->notify_lock);
2737 }
2738
2739 *in = FALSE;
2740 g_mutex_unlock (model->priv->notify_lock);
2741 }
2742
2743 static void
2744 client_view_objects_added_cb (ECalClientView *view,
2745 const GSList *objects,
2746 ECalModel *model)
2747 {
2748 process_event (
2749 view, objects, model, process_added,
2750 &model->priv->in_added,
2751 model->priv->notify_added,
2752 (gpointer (*)(gpointer)) icalcomponent_new_clone,
2753 (void (*)(gpointer)) icalcomponent_free);
2754 }
2755
2756 static void
2757 client_view_objects_modified_cb (ECalClientView *view,
2758 const GSList *objects,
2759 ECalModel *model)
2760 {
2761 process_event (
2762 view, objects, model, process_modified,
2763 &model->priv->in_modified,
2764 model->priv->notify_modified,
2765 (gpointer (*)(gpointer)) icalcomponent_new_clone,
2766 (void (*)(gpointer)) icalcomponent_free);
2767 }
2768
2769 static void
2770 client_view_objects_removed_cb (ECalClientView *view,
2771 const GSList *ids,
2772 ECalModel *model)
2773 {
2774 process_event (
2775 view, ids, model, process_removed,
2776 &model->priv->in_removed,
2777 model->priv->notify_removed,
2778 copy_comp_id, free_comp_id);
2779 }
2780
2781 static void
2782 client_view_progress_cb (ECalClientView *view,
2783 gint percent,
2784 const gchar *message,
2785 gpointer user_data)
2786 {
2787 ECalModel *model = (ECalModel *) user_data;
2788 ECalClient *client = e_cal_client_view_get_client (view);
2789
2790 g_return_if_fail (E_IS_CAL_MODEL (model));
2791
2792 g_signal_emit (
2793 model, signals[CAL_VIEW_PROGRESS], 0, message,
2794 percent, e_cal_client_get_source_type (client));
2795 }
2796
2797 static void
2798 client_view_complete_cb (ECalClientView *view,
2799 const GError *error,
2800 gpointer user_data)
2801 {
2802 ECalModel *model = (ECalModel *) user_data;
2803 ECalClient *client = e_cal_client_view_get_client (view);
2804
2805 g_return_if_fail (E_IS_CAL_MODEL (model));
2806
2807 g_signal_emit (
2808 model, signals[CAL_VIEW_COMPLETE], 0, error,
2809 e_cal_client_get_source_type (client));
2810 }
2811
2812 struct get_view_data
2813 {
2814 ECalModel *model; /* do not touch this, if cancelled */
2815 ECalModelClient *client_data; /* do not touch this, if cancelled */
2816 GCancellable *cancellable;
2817 guint tries;
2818 };
2819
2820 static void
2821 free_get_view_data (struct get_view_data *gvd)
2822 {
2823 if (!gvd)
2824 return;
2825
2826 g_object_unref (gvd->cancellable);
2827 g_free (gvd);
2828 }
2829
2830 static gboolean retry_get_view_timeout_cb (gpointer user_data);
2831
2832 static void
2833 get_view_cb (GObject *source_object,
2834 GAsyncResult *result,
2835 gpointer user_data)
2836 {
2837 struct get_view_data *gvd = user_data;
2838 GError *error = NULL;
2839 ECalClientView *view = NULL;
2840
2841 g_return_if_fail (source_object != NULL);
2842 g_return_if_fail (result != NULL);
2843 g_return_if_fail (gvd != NULL);
2844 g_return_if_fail (gvd->model != NULL);
2845 g_return_if_fail (gvd->client_data != NULL);
2846
2847 e_cal_client_get_view_finish (
2848 E_CAL_CLIENT (source_object), result, &view, &error);
2849
2850 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
2851 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
2852 free_get_view_data (gvd);
2853 g_error_free (error);
2854 return;
2855 }
2856
2857 if (error != NULL) {
2858 if (gvd->tries < 10) {
2859 gvd->tries++;
2860 g_timeout_add (500, retry_get_view_timeout_cb, gvd);
2861 g_error_free (error);
2862 return;
2863 }
2864
2865 g_warning (
2866 "%s: Failed to get view: %s",
2867 G_STRFUNC, error->message);
2868 g_error_free (error);
2869
2870 } else {
2871 gvd->client_data->view = view;
2872
2873 g_signal_connect (
2874 gvd->client_data->view, "objects-added",
2875 G_CALLBACK (client_view_objects_added_cb), gvd->model);
2876 g_signal_connect (
2877 gvd->client_data->view, "objects-modified",
2878 G_CALLBACK (client_view_objects_modified_cb), gvd->model);
2879 g_signal_connect (
2880 gvd->client_data->view, "objects-removed",
2881 G_CALLBACK (client_view_objects_removed_cb), gvd->model);
2882 g_signal_connect (
2883 gvd->client_data->view, "progress",
2884 G_CALLBACK (client_view_progress_cb), gvd->model);
2885 g_signal_connect (
2886 gvd->client_data->view, "complete",
2887 G_CALLBACK (client_view_complete_cb), gvd->model);
2888
2889 e_cal_client_view_start (gvd->client_data->view, &error);
2890
2891 if (error != NULL) {
2892 g_warning (
2893 "%s: Failed to start view: %s",
2894 G_STRFUNC, error->message);
2895 g_error_free (error);
2896 }
2897 }
2898
2899 free_get_view_data (gvd);
2900 }
2901
2902 static gboolean
2903 retry_get_view_timeout_cb (gpointer user_data)
2904 {
2905 struct get_view_data *gvd = user_data;
2906
2907 if (g_cancellable_is_cancelled (gvd->cancellable)) {
2908 free_get_view_data (gvd);
2909 return FALSE;
2910 }
2911
2912 e_cal_client_get_view (gvd->client_data->client, gvd->model->priv->full_sexp, gvd->cancellable, get_view_cb, gvd);
2913
2914 return FALSE;
2915 }
2916
2917 static void
2918 update_e_cal_view_for_client (ECalModel *model,
2919 ECalModelClient *client_data)
2920 {
2921 ECalModelPrivate *priv;
2922 struct get_view_data *gvd;
2923
2924 priv = model->priv;
2925
2926 /* Skip if this client has not finished loading yet */
2927 if (!e_client_is_opened (E_CLIENT (client_data->client)))
2928 return;
2929
2930 /* free the previous view, if any */
2931 if (client_data->view) {
2932 g_signal_handlers_disconnect_matched (
2933 client_data->view, G_SIGNAL_MATCH_DATA,
2934 0, 0, NULL, NULL, model);
2935 g_object_unref (client_data->view);
2936 client_data->view = NULL;
2937 }
2938
2939 /* prepare the view */
2940 g_return_if_fail (priv->full_sexp != NULL);
2941
2942 /* Don't create the new query if we won't use it */
2943 if (!client_data->do_query)
2944 return;
2945
2946 if (client_data->cancellable) {
2947 g_cancellable_cancel (client_data->cancellable);
2948 g_object_unref (client_data->cancellable);
2949 }
2950
2951 client_data->cancellable = g_cancellable_new ();
2952
2953 gvd = g_new0 (struct get_view_data, 1);
2954 gvd->client_data = client_data;
2955 gvd->model = model;
2956 gvd->tries = 0;
2957 gvd->cancellable = g_object_ref (client_data->cancellable);
2958
2959 e_cal_client_get_view (client_data->client, priv->full_sexp, gvd->cancellable, get_view_cb, gvd);
2960 }
2961
2962 void
2963 e_cal_model_update_status_message (ECalModel *model,
2964 const gchar *message,
2965 gdouble percent)
2966 {
2967 g_return_if_fail (model != NULL);
2968
2969 g_signal_emit (model, signals[STATUS_MESSAGE], 0, message, percent);
2970 }
2971
2972 static void
2973 backend_died_cb (ECalClient *client,
2974 gpointer user_data)
2975 {
2976 ECalModel *model;
2977
2978 model = E_CAL_MODEL (user_data);
2979
2980 e_cal_model_remove_client (model, client);
2981 }
2982
2983 static void
2984 cal_model_retrieve_capabilies_cb (GObject *source_object,
2985 GAsyncResult *result,
2986 gpointer user_data)
2987 {
2988 ECalClient *client = E_CAL_CLIENT (source_object);
2989 ECalModel *model = user_data;
2990 ECalModelClient *client_data;
2991 gchar *capabilities = NULL;
2992
2993 g_return_if_fail (client != NULL);
2994 g_return_if_fail (model != NULL);
2995
2996 e_client_retrieve_capabilities_finish (
2997 E_CLIENT (client), result, &capabilities, NULL);
2998 g_free (capabilities);
2999
3000 e_cal_model_update_status_message (model, NULL, -1.0);
3001
3002 client_data = find_client_data (model, client);
3003 g_return_if_fail (client_data);
3004
3005 update_e_cal_view_for_client (model, client_data);
3006 }
3007
3008 struct RetryOpenData
3009 {
3010 EClient *client;
3011 ECalModel *model;
3012 GCancellable *cancellable;
3013 };
3014
3015 static void
3016 free_retry_open_data (gpointer data)
3017 {
3018 struct RetryOpenData *rod = data;
3019
3020 if (!rod)
3021 return;
3022
3023 g_object_unref (rod->client);
3024 g_object_unref (rod->cancellable);
3025 g_free (rod);
3026 }
3027
3028 static gboolean cal_model_retry_open_timeout_cb (gpointer user_data);
3029
3030 static void
3031 client_opened_cb (GObject *source_object,
3032 GAsyncResult *result,
3033 gpointer user_data)
3034 {
3035 ECalClient *client = E_CAL_CLIENT (source_object);
3036 ECalModel *model = (ECalModel *) user_data;
3037 GError *error = NULL;
3038
3039 e_client_open_finish (E_CLIENT (client), result, &error);
3040
3041 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3042 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
3043 g_error_free (error);
3044 return;
3045 }
3046
3047 if (error && g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY)) {
3048 struct RetryOpenData *rod;
3049
3050 rod = g_new0 (struct RetryOpenData, 1);
3051 rod->client = g_object_ref (client);
3052 rod->model = model;
3053 rod->cancellable = g_object_ref (model->priv->loading_clients);
3054
3055 /* postpone for 1/2 of a second, backend is busy now */
3056 g_timeout_add_full (
3057 G_PRIORITY_DEFAULT, 500,
3058 cal_model_retry_open_timeout_cb,
3059 rod, free_retry_open_data);
3060
3061 g_error_free (error);
3062
3063 return;
3064 }
3065
3066 if (error != NULL) {
3067 ESource *source;
3068
3069 source = e_client_get_source (E_CLIENT (client));
3070 e_cal_model_remove_client (model, client);
3071 g_warning (
3072 "%s: Failed to open '%s': %s",
3073 G_STRFUNC,
3074 e_source_get_display_name (source),
3075 error->message);
3076 g_error_free (error);
3077 e_cal_model_update_status_message (model, NULL, -1.0);
3078 return;
3079 }
3080
3081 /* to have them ready for later use */
3082 e_client_retrieve_capabilities (
3083 E_CLIENT (client), model->priv->loading_clients,
3084 cal_model_retrieve_capabilies_cb, model);
3085 }
3086
3087 static gboolean
3088 cal_model_retry_open_timeout_cb (gpointer user_data)
3089 {
3090 struct RetryOpenData *rod = user_data;
3091
3092 g_return_val_if_fail (rod != NULL, FALSE);
3093 g_return_val_if_fail (rod->client != NULL, FALSE);
3094 g_return_val_if_fail (rod->model != NULL, FALSE);
3095
3096 e_client_open (
3097 rod->client, TRUE, rod->cancellable,
3098 client_opened_cb, rod->model);
3099
3100 return FALSE;
3101 }
3102
3103 static ECalModelClient *
3104 add_new_client (ECalModel *model,
3105 ECalClient *client,
3106 gboolean do_query)
3107 {
3108 ECalModelPrivate *priv;
3109 ECalModelClient *client_data;
3110
3111 priv = model->priv;
3112
3113 /* DEBUG the load state should always be loaded here
3114 if (e_cal_get_load_state (client) != E_CAL_LOAD_LOADED) {
3115 g_assert_not_reached ();
3116 } */
3117
3118 /* Look to see if we already have this client */
3119 client_data = find_client_data (model, client);
3120 if (client_data) {
3121 if (client_data->do_query)
3122 return client_data;
3123 else
3124 client_data->do_query = do_query;
3125
3126 goto load;
3127 }
3128
3129 client_data = g_new0 (ECalModelClient, 1);
3130 client_data->client = g_object_ref (client);
3131 client_data->view = NULL;
3132 client_data->do_query = do_query;
3133
3134 priv->clients = g_list_append (priv->clients, client_data);
3135
3136 g_signal_connect (
3137 client_data->client, "backend_died",
3138 G_CALLBACK (backend_died_cb), model);
3139
3140 load:
3141 if (e_client_is_opened (E_CLIENT (client))) {
3142 update_e_cal_view_for_client (model, client_data);
3143 } else {
3144 ESource *source;
3145 const gchar *display_name;
3146 gchar *msg;
3147
3148 source = e_client_get_source (E_CLIENT (client));
3149 display_name = e_source_get_display_name (source);
3150 msg = g_strdup_printf (_("Opening %s"), display_name);
3151 e_cal_model_update_status_message (model, msg, -1.0);
3152 g_free (msg);
3153
3154 e_cal_client_set_default_timezone (client, e_cal_model_get_timezone (model));
3155
3156 e_client_open (E_CLIENT (client), TRUE, model->priv->loading_clients, client_opened_cb, model);
3157 }
3158
3159 return client_data;
3160 }
3161
3162 /**
3163 * e_cal_model_add_client
3164 */
3165 void
3166 e_cal_model_add_client (ECalModel *model,
3167 ECalClient *client)
3168 {
3169 g_return_if_fail (E_IS_CAL_MODEL (model));
3170 g_return_if_fail (E_IS_CAL_CLIENT (client));
3171
3172 add_new_client (model, client, TRUE);
3173 }
3174
3175 static void
3176 remove_client_objects (ECalModel *model,
3177 ECalModelClient *client_data)
3178 {
3179 gint i;
3180
3181 /* remove all objects belonging to this client */
3182 for (i = model->priv->objects->len; i > 0; i--) {
3183 ECalModelComponent *comp_data = (ECalModelComponent *) g_ptr_array_index (model->priv->objects, i - 1);
3184
3185 g_return_if_fail (comp_data != NULL);
3186
3187 if (comp_data->client == client_data->client) {
3188 GSList *l = NULL;
3189
3190 g_ptr_array_remove (model->priv->objects, comp_data);
3191
3192 l = g_slist_append (l, comp_data);
3193 g_signal_emit (model, signals[COMPS_DELETED], 0, l);
3194
3195 g_slist_free (l);
3196 g_object_unref (comp_data);
3197
3198 e_table_model_pre_change (E_TABLE_MODEL (model));
3199 e_table_model_row_deleted (E_TABLE_MODEL (model), i - 1);
3200 }
3201 }
3202
3203 /* to notify about changes, because in call of row_deleted there are still all events */
3204 e_table_model_changed (E_TABLE_MODEL (model));
3205 }
3206
3207 static void
3208 remove_client (ECalModel *model,
3209 ECalModelClient *client_data)
3210 {
3211 /* FIXME We might not want to disconnect the open signal for the default client */
3212 g_signal_handlers_disconnect_matched (client_data->client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, model);
3213 if (client_data->view)
3214 g_signal_handlers_disconnect_matched (client_data->view, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, model);
3215
3216 remove_client_objects (model, client_data);
3217
3218 /* If this is the default client and we were querying (so it
3219 * was also a source), keep it around but don't query it */
3220 if (model->priv->default_client == client_data->client && client_data->do_query) {
3221 client_data->do_query = FALSE;
3222
3223 return;
3224 }
3225
3226 if (model->priv->default_client == client_data->client)
3227 model->priv->default_client = NULL;
3228
3229 /* Remove the client from the list */
3230 model->priv->clients = g_list_remove (model->priv->clients, client_data);
3231
3232 /* free all remaining memory */
3233 g_object_unref (client_data->client);
3234 if (client_data->view)
3235 g_object_unref (client_data->view);
3236 g_free (client_data);
3237 }
3238
3239 /**
3240 * e_cal_model_remove_client
3241 */
3242 void
3243 e_cal_model_remove_client (ECalModel *model,
3244 ECalClient *client)
3245 {
3246 ECalModelClient *client_data;
3247
3248 g_return_if_fail (E_IS_CAL_MODEL (model));
3249 g_return_if_fail (E_IS_CAL_CLIENT (client));
3250
3251 client_data = find_client_data (model, client);
3252 if (client_data)
3253 remove_client (model, client_data);
3254 }
3255
3256 /**
3257 * e_cal_model_remove_all_clients
3258 */
3259 void
3260 e_cal_model_remove_all_clients (ECalModel *model)
3261 {
3262 ECalModelPrivate *priv;
3263
3264 g_return_if_fail (E_IS_CAL_MODEL (model));
3265
3266 priv = model->priv;
3267 while (priv->clients != NULL) {
3268 ECalModelClient *client_data = (ECalModelClient *) priv->clients->data;
3269 remove_client (model, client_data);
3270 }
3271 }
3272
3273 static GSList *
3274 get_objects_as_list (ECalModel *model)
3275 {
3276 gint i;
3277 GSList *l = NULL;
3278 ECalModelPrivate *priv = model->priv;
3279
3280 for (i = 0; i < priv->objects->len; i++) {
3281 ECalModelComponent *comp_data;
3282
3283 comp_data = g_ptr_array_index (priv->objects, i);
3284 if (comp_data == NULL) {
3285 g_warning ("comp_data is null\n");
3286 continue;
3287 }
3288
3289 l = g_slist_prepend (l, comp_data);
3290 }
3291
3292 return l;
3293 }
3294
3295 struct cc_data
3296 {
3297 ECalModel *model;
3298 EFlag *eflag;
3299 };
3300
3301 static gboolean
3302 cleanup_content_cb (gpointer user_data)
3303 {
3304 ECalModel *model;
3305 ECalModelPrivate *priv;
3306 GSList *slist;
3307 gint len;
3308 struct cc_data *data = user_data;
3309
3310 g_return_val_if_fail (data != NULL, FALSE);
3311 g_return_val_if_fail (data->model != NULL, FALSE);
3312 g_return_val_if_fail (data->eflag != NULL, FALSE);
3313
3314 model = data->model;
3315 priv = model->priv;
3316
3317 g_return_val_if_fail (priv != NULL, FALSE);
3318
3319 e_table_model_pre_change (E_TABLE_MODEL (model));
3320 len = priv->objects->len;
3321
3322 slist = get_objects_as_list (model);
3323 g_ptr_array_set_size (priv->objects, 0);
3324 g_signal_emit (model, signals[COMPS_DELETED], 0, slist);
3325
3326 e_table_model_rows_deleted (E_TABLE_MODEL (model), 0, len);
3327
3328 g_slist_foreach (slist, (GFunc) g_object_unref, NULL);
3329 g_slist_free (slist);
3330
3331 e_flag_set (data->eflag);
3332
3333 return FALSE;
3334 }
3335
3336 static void
3337 redo_queries (ECalModel *model)
3338 {
3339 ECalModelPrivate *priv;
3340 GList *l;
3341 struct cc_data data;
3342
3343 priv = model->priv;
3344
3345 if (priv->full_sexp)
3346 g_free (priv->full_sexp);
3347
3348 if (priv->start != -1 && priv->end != -1) {
3349 gchar *iso_start, *iso_end;
3350 const gchar *default_tzloc = NULL;
3351
3352 iso_start = isodate_from_time_t (priv->start);
3353 iso_end = isodate_from_time_t (priv->end);
3354
3355 if (priv->zone && priv->zone != icaltimezone_get_utc_timezone ())
3356 default_tzloc = icaltimezone_get_location (priv->zone);
3357 if (!default_tzloc)
3358 default_tzloc = "";
3359
3360 if (priv->search_sexp) {
3361 priv->full_sexp = g_strdup_printf (
3362 "(and (occur-in-time-range? (make-time \"%s\") (make-time \"%s\") \"%s\") %s)",
3363 iso_start, iso_end, default_tzloc,
3364 priv->search_sexp ? priv->search_sexp : "");
3365 } else {
3366 priv->full_sexp = g_strdup_printf (
3367 "(occur-in-time-range? (make-time \"%s\") (make-time \"%s\") \"%s\")",
3368 iso_start, iso_end, default_tzloc);
3369 }
3370
3371 g_free (iso_start);
3372 g_free (iso_end);
3373 } else if (priv->search_sexp) {
3374 priv->full_sexp = g_strdup (priv->search_sexp);
3375 } else {
3376 priv->full_sexp = g_strdup ("#f");
3377 }
3378
3379 /* clean up the current contents, which should be done
3380 * always from the main thread, because of gtk calls during removal */
3381 data.model = model;
3382 data.eflag = e_flag_new ();
3383
3384 if (!g_main_context_is_owner (g_main_context_default ())) {
3385 /* function called from other than main thread */
3386 g_timeout_add (10, cleanup_content_cb, &data);
3387 e_flag_wait (data.eflag);
3388 } else {
3389 cleanup_content_cb (&data);
3390 }
3391
3392 e_flag_free (data.eflag);
3393
3394 /* update the view for all clients */
3395 for (l = priv->clients; l != NULL; l = l->next) {
3396 ECalModelClient *client_data;
3397
3398 client_data = (ECalModelClient *) l->data;
3399 update_e_cal_view_for_client (model, client_data);
3400 }
3401 }
3402
3403 void
3404 e_cal_model_get_time_range (ECalModel *model,
3405 time_t *start,
3406 time_t *end)
3407 {
3408 ECalModelPrivate *priv;
3409
3410 g_return_if_fail (model != NULL);
3411 g_return_if_fail (E_IS_CAL_MODEL (model));
3412
3413 priv = model->priv;
3414
3415 if (start)
3416 *start = priv->start;
3417
3418 if (end)
3419 *end = priv->end;
3420 }
3421
3422 void
3423 e_cal_model_set_time_range (ECalModel *model,
3424 time_t start,
3425 time_t end)
3426 {
3427 ECalModelPrivate *priv;
3428
3429 g_return_if_fail (model != NULL);
3430 g_return_if_fail (E_IS_CAL_MODEL (model));
3431 g_return_if_fail (start >= 0 && end >= 0);
3432 g_return_if_fail (start <= end);
3433
3434 priv = model->priv;
3435
3436 if (priv->start == start && priv->end == end)
3437 return;
3438
3439 priv->start = start;
3440 priv->end = end;
3441
3442 g_signal_emit (model, signals[TIME_RANGE_CHANGED], 0, start, end);
3443 redo_queries (model);
3444 }
3445
3446 const gchar *
3447 e_cal_model_get_search_query (ECalModel *model)
3448 {
3449 ECalModelPrivate *priv;
3450
3451 g_return_val_if_fail (model != NULL, NULL);
3452 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
3453
3454 priv = model->priv;
3455
3456 return priv->search_sexp;
3457 }
3458
3459 /**
3460 * e_cal_model_set_query
3461 */
3462 void
3463 e_cal_model_set_search_query (ECalModel *model,
3464 const gchar *sexp)
3465 {
3466 ECalModelPrivate *priv;
3467
3468 g_return_if_fail (E_IS_CAL_MODEL (model));
3469
3470 priv = model->priv;
3471
3472 if (!strcmp (sexp ? sexp : "", priv->search_sexp ? priv->search_sexp : ""))
3473 return;
3474
3475 if (priv->search_sexp)
3476 g_free (priv->search_sexp);
3477
3478 if (!sexp || !*sexp)
3479 priv->search_sexp = NULL;
3480 else
3481 priv->search_sexp = g_strdup (sexp);
3482
3483 redo_queries (model);
3484 }
3485
3486 /**
3487 * e_cal_model_set_query
3488 */
3489 void
3490 e_cal_model_set_search_query_with_time_range (ECalModel *model,
3491 const gchar *sexp,
3492 time_t start,
3493 time_t end)
3494 {
3495 ECalModelPrivate *priv;
3496 gboolean do_query = FALSE;
3497
3498 g_return_if_fail (E_IS_CAL_MODEL (model));
3499
3500 priv = model->priv;
3501
3502 if (strcmp (sexp ? sexp : "", priv->search_sexp ? priv->search_sexp : "")) {
3503 if (priv->search_sexp)
3504 g_free (priv->search_sexp);
3505
3506 if (!sexp || !*sexp)
3507 priv->search_sexp = NULL;
3508 else
3509 priv->search_sexp = g_strdup (sexp);
3510 do_query = TRUE;
3511 }
3512
3513 if (!(priv->start == start && priv->end == end)) {
3514 priv->start = start;
3515 priv->end = end;
3516 do_query = TRUE;
3517
3518 g_signal_emit (model, signals[TIME_RANGE_CHANGED], 0, start, end);
3519 }
3520
3521 if (do_query)
3522 redo_queries (model);
3523 }
3524
3525 /**
3526 * e_cal_model_create_component_with_defaults
3527 */
3528 icalcomponent *
3529 e_cal_model_create_component_with_defaults (ECalModel *model,
3530 gboolean all_day)
3531 {
3532 ECalModelPrivate *priv;
3533 ECalComponent *comp;
3534 icalcomponent *icalcomp;
3535 ECalClient *client;
3536
3537 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
3538
3539 priv = model->priv;
3540
3541 g_return_val_if_fail (priv->clients != NULL, NULL);
3542
3543 client = e_cal_model_get_default_client (model);
3544 if (!client)
3545 return icalcomponent_new (priv->kind);
3546
3547 switch (priv->kind) {
3548 case ICAL_VEVENT_COMPONENT :
3549 comp = cal_comp_event_new_with_defaults (
3550 client, all_day,
3551 e_cal_model_get_use_default_reminder (model),
3552 e_cal_model_get_default_reminder_interval (model),
3553 e_cal_model_get_default_reminder_units (model));
3554 break;
3555 case ICAL_VTODO_COMPONENT :
3556 comp = cal_comp_task_new_with_defaults (client);
3557 break;
3558 case ICAL_VJOURNAL_COMPONENT :
3559 comp = cal_comp_memo_new_with_defaults (client);
3560 break;
3561 default:
3562 return NULL;
3563 }
3564
3565 if (!comp)
3566 return icalcomponent_new (priv->kind);
3567
3568 icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
3569 g_object_unref (comp);
3570
3571 /* make sure the component has an UID */
3572 if (!icalcomponent_get_uid (icalcomp)) {
3573 gchar *uid;
3574
3575 uid = e_cal_component_gen_uid ();
3576 icalcomponent_set_uid (icalcomp, uid);
3577
3578 g_free (uid);
3579 }
3580
3581 return icalcomp;
3582 }
3583
3584 /**
3585 * Returns information about attendees in the component.
3586 * If there are no attendees, the function returns NULL.
3587 *
3588 * The information is like "Status: Accepted: X Declined: Y ...".
3589 *
3590 * Free returned pointer with g_free.
3591 **/
3592 gchar *
3593 e_cal_model_get_attendees_status_info (ECalModel *model,
3594 ECalComponent *comp,
3595 ECalClient *cal_client)
3596 {
3597 struct _values {
3598 icalparameter_partstat status;
3599 const gchar *caption;
3600 gint count;
3601 } values[] = {
3602 { ICAL_PARTSTAT_ACCEPTED, N_("Accepted"), 0 },
3603 { ICAL_PARTSTAT_DECLINED, N_("Declined"), 0 },
3604 { ICAL_PARTSTAT_TENTATIVE, N_("Tentative"), 0 },
3605 { ICAL_PARTSTAT_DELEGATED, N_("Delegated"), 0 },
3606 { ICAL_PARTSTAT_NEEDSACTION, N_("Needs action"), 0 },
3607 { ICAL_PARTSTAT_NONE, N_("Other"), 0 },
3608 { ICAL_PARTSTAT_X, NULL, -1 }
3609 };
3610
3611 ESourceRegistry *registry;
3612 GSList *attendees = NULL, *a;
3613 gboolean have = FALSE;
3614 gchar *res = NULL;
3615 gint i;
3616
3617 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
3618
3619 registry = e_cal_model_get_registry (model);
3620
3621 if (!comp || !e_cal_component_has_attendees (comp) ||
3622 !itip_organizer_is_user_ex (registry, comp, cal_client, TRUE))
3623 return NULL;
3624
3625 e_cal_component_get_attendee_list (comp, &attendees);
3626
3627 for (a = attendees; a; a = a->next) {
3628 ECalComponentAttendee *att = a->data;
3629
3630 if (att && att->cutype == ICAL_CUTYPE_INDIVIDUAL &&
3631 (att->role == ICAL_ROLE_CHAIR ||
3632 att->role == ICAL_ROLE_REQPARTICIPANT ||
3633 att->role == ICAL_ROLE_OPTPARTICIPANT)) {
3634 have = TRUE;
3635
3636 for (i = 0; values[i].count != -1; i++) {
3637 if (att->status == values[i].status || values[i].status == ICAL_PARTSTAT_NONE) {
3638 values[i].count++;
3639 break;
3640 }
3641 }
3642 }
3643 }
3644
3645 if (have) {
3646 GString *str = g_string_new ("");
3647
3648 for (i = 0; values[i].count != -1; i++) {
3649 if (values[i].count > 0) {
3650 if (str->str && *str->str)
3651 g_string_append (str, " ");
3652
3653 g_string_append_printf (str, "%s: %d", _(values[i].caption), values[i].count);
3654 }
3655 }
3656
3657 g_string_prepend (str, ": ");
3658
3659 /* To Translators: 'Status' here means the state of the attendees, the resulting string will be in a form:
3660 * Status: Accepted: X Declined: Y ... */
3661 g_string_prepend (str, _("Status"));
3662
3663 res = g_string_free (str, FALSE);
3664 }
3665
3666 if (attendees)
3667 e_cal_component_free_attendee_list (attendees);
3668
3669 return res;
3670 }
3671
3672 /**
3673 * e_cal_model_get_color_for_component
3674 */
3675 const gchar *
3676 e_cal_model_get_color_for_component (ECalModel *model,
3677 ECalModelComponent *comp_data)
3678 {
3679 ECalModelClass *model_class;
3680 const gchar *color = NULL;
3681
3682 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
3683 g_return_val_if_fail (comp_data != NULL, NULL);
3684
3685 model_class = (ECalModelClass *) G_OBJECT_GET_CLASS (model);
3686 if (model_class->get_color_for_component != NULL)
3687 color = model_class->get_color_for_component (model, comp_data);
3688
3689 if (!color)
3690 color = ecm_get_color_for_component (model, comp_data);
3691
3692 return color;
3693 }
3694
3695 /**
3696 * e_cal_model_get_rgb_color_for_component
3697 */
3698 gboolean
3699 e_cal_model_get_rgb_color_for_component (ECalModel *model,
3700 ECalModelComponent *comp_data,
3701 gdouble *red,
3702 gdouble *green,
3703 gdouble *blue)
3704 {
3705 GdkColor gdk_color;
3706 const gchar *color;
3707
3708 color = e_cal_model_get_color_for_component (model, comp_data);
3709 if (color && gdk_color_parse (color, &gdk_color)) {
3710
3711 if (red)
3712 *red = ((gdouble) gdk_color.red)/0xffff;
3713 if (green)
3714 *green = ((gdouble) gdk_color.green)/0xffff;
3715 if (blue)
3716 *blue = ((gdouble) gdk_color.blue)/0xffff;
3717
3718 return TRUE;
3719 }
3720
3721 return FALSE;
3722 }
3723
3724 /**
3725 * e_cal_model_get_component_at
3726 */
3727 ECalModelComponent *
3728 e_cal_model_get_component_at (ECalModel *model,
3729 gint row)
3730 {
3731 ECalModelPrivate *priv;
3732
3733 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
3734
3735 priv = model->priv;
3736
3737 g_return_val_if_fail (row >= 0 && row < priv->objects->len, NULL);
3738
3739 return g_ptr_array_index (priv->objects, row);
3740 }
3741
3742 ECalModelComponent *
3743 e_cal_model_get_component_for_uid (ECalModel *model,
3744 const ECalComponentId *id)
3745 {
3746 ECalModelPrivate *priv;
3747
3748 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
3749
3750 priv = model->priv;
3751
3752 return search_by_id_and_client (priv, NULL, id);
3753 }
3754
3755 /**
3756 * e_cal_model_date_value_to_string
3757 */
3758 gchar *
3759 e_cal_model_date_value_to_string (ECalModel *model,
3760 gconstpointer value)
3761 {
3762 ECalModelPrivate *priv;
3763 ECellDateEditValue *dv = (ECellDateEditValue *) value;
3764 struct icaltimetype tt;
3765 struct tm tmp_tm;
3766 gchar buffer[64];
3767
3768 g_return_val_if_fail (E_IS_CAL_MODEL (model), g_strdup (""));
3769
3770 priv = model->priv;
3771
3772 if (!dv)
3773 return g_strdup ("");
3774
3775 /* We currently convert all the dates to the current timezone. */
3776 tt = dv->tt;
3777 icaltimezone_convert_time (&tt, dv->zone, priv->zone);
3778
3779 tmp_tm.tm_year = tt.year - 1900;
3780 tmp_tm.tm_mon = tt.month - 1;
3781 tmp_tm.tm_mday = tt.day;
3782 tmp_tm.tm_hour = tt.hour;
3783 tmp_tm.tm_min = tt.minute;
3784 tmp_tm.tm_sec = tt.second;
3785 tmp_tm.tm_isdst = -1;
3786
3787 tmp_tm.tm_wday = time_day_of_week (tt.day, tt.month - 1, tt.year);
3788
3789 memset (buffer, 0, sizeof (buffer));
3790 e_time_format_date_and_time (&tmp_tm, priv->use_24_hour_format,
3791 TRUE, FALSE,
3792 buffer, sizeof (buffer));
3793 return g_strdup (buffer);
3794 }
3795
3796 /* FIXME is it still needed ?
3797 static ECellDateEditValue *
3798 copy_ecdv (ECellDateEditValue *ecdv)
3799 {
3800 ECellDateEditValue *new_ecdv;
3801 *
3802 new_ecdv = g_new0 (ECellDateEditValue, 1);
3803 new_ecdv->tt = ecdv ? ecdv->tt : icaltime_null_time ();
3804 new_ecdv->zone = ecdv ? ecdv->zone : NULL;
3805 *
3806 return new_ecdv;
3807 } */
3808
3809 static void e_cal_model_component_finalize (GObject *object);
3810
3811 /* Class initialization function for the calendar component object */
3812 static void
3813 e_cal_model_component_class_init (ECalModelComponentClass *class)
3814 {
3815 GObjectClass *object_class;
3816
3817 object_class = (GObjectClass *) class;
3818 g_type_class_add_private (class, sizeof (ECalModelComponentPrivate));
3819
3820 object_class->finalize = e_cal_model_component_finalize;
3821 }
3822
3823 static void
3824 e_cal_model_component_finalize (GObject *object)
3825 {
3826 ECalModelComponent *comp_data = E_CAL_MODEL_COMPONENT (object);
3827
3828 if (comp_data->client) {
3829 g_object_unref (comp_data->client);
3830 comp_data->client = NULL;
3831 }
3832 if (comp_data->icalcomp) {
3833 icalcomponent_free (comp_data->icalcomp);
3834 comp_data->icalcomp = NULL;
3835 }
3836 if (comp_data->dtstart) {
3837 g_free (comp_data->dtstart);
3838 comp_data->dtstart = NULL;
3839 }
3840 if (comp_data->dtend) {
3841 g_free (comp_data->dtend);
3842 comp_data->dtend = NULL;
3843 }
3844 if (comp_data->due) {
3845 g_free (comp_data->due);
3846 comp_data->due = NULL;
3847 }
3848 if (comp_data->completed) {
3849 g_free (comp_data->completed);
3850 comp_data->completed = NULL;
3851 }
3852 if (comp_data->created) {
3853 g_free (comp_data->created);
3854 comp_data->created = NULL;
3855 }
3856 if (comp_data->lastmodified) {
3857 g_free (comp_data->lastmodified);
3858 comp_data->lastmodified = NULL;
3859 }
3860 if (comp_data->color) {
3861 g_free (comp_data->color);
3862 comp_data->color = NULL;
3863 }
3864
3865 if (comp_data->priv->categories_str)
3866 g_string_free (comp_data->priv->categories_str, TRUE);
3867 comp_data->priv->categories_str = NULL;
3868
3869 /* Chain up to parent's finalize() method. */
3870 G_OBJECT_CLASS (e_cal_model_component_parent_class)->finalize (object);
3871 }
3872
3873 static void
3874 e_cal_model_component_init (ECalModelComponent *comp)
3875 {
3876 comp->priv = E_CAL_MODEL_COMPONENT_GET_PRIVATE (comp);
3877 }
3878
3879 /**
3880 * e_cal_model_generate_instances_sync
3881 *
3882 * cb function is not called with cb_data, but with ECalModelGenerateInstancesData which contains cb_data
3883 */
3884 void
3885 e_cal_model_generate_instances_sync (ECalModel *model,
3886 time_t start,
3887 time_t end,
3888 ECalRecurInstanceFn cb,
3889 gpointer cb_data)
3890 {
3891 ECalModelGenerateInstancesData mdata;
3892 gint i, n;
3893
3894 n = e_table_model_row_count (E_TABLE_MODEL (model));
3895 for (i = 0; i < n; i++) {
3896 ECalModelComponent *comp_data = e_cal_model_get_component_at (model, i);
3897
3898 mdata.comp_data = comp_data;
3899 mdata.cb_data = cb_data;
3900
3901 if (comp_data->instance_start < end && comp_data->instance_end > start)
3902 e_cal_client_generate_instances_for_object_sync (comp_data->client, comp_data->icalcomp, start, end, cb, &mdata);
3903 }
3904 }
3905
3906 /**
3907 * e_cal_model_get_object_array
3908 */
3909 GPtrArray *
3910 e_cal_model_get_object_array (ECalModel *model)
3911 {
3912 g_return_val_if_fail (model != NULL, NULL);
3913 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
3914 g_return_val_if_fail (model->priv != NULL, NULL);
3915
3916 return model->priv->objects;
3917 }
3918
3919 void
3920 e_cal_model_set_instance_times (ECalModelComponent *comp_data,
3921 const icaltimezone *zone)
3922 {
3923 struct icaltimetype start_time, end_time;
3924 icalcomponent_kind kind;
3925
3926 kind = icalcomponent_isa (comp_data->icalcomp);
3927 start_time = icalcomponent_get_dtstart (comp_data->icalcomp);
3928 end_time = icalcomponent_get_dtend (comp_data->icalcomp);
3929
3930 if (kind == ICAL_VEVENT_COMPONENT) {
3931 if (start_time.is_date && icaltime_is_null_time (end_time)) {
3932 /* If end_time is null and it's an all day event,
3933 * just make start_time = end_time so that end_time
3934 * will be a valid date
3935 */
3936 end_time = start_time;
3937 icaltime_adjust (&end_time, 1, 0, 0, 0);
3938 icalcomponent_set_dtend (comp_data->icalcomp, end_time);
3939 } else if (start_time.is_date && end_time.is_date &&
3940 (icaltime_compare_date_only (start_time, end_time) == 0)) {
3941 /* If both DTSTART and DTEND are DATE values, and they are the
3942 * same day, we add 1 day to DTEND. This means that most
3943 * events created with the old Evolution behavior will still
3944 * work OK. */
3945 icaltime_adjust (&end_time, 1, 0, 0, 0);
3946 icalcomponent_set_dtend (comp_data->icalcomp, end_time);
3947 }
3948 }
3949
3950 if (start_time.zone)
3951 zone = start_time.zone;
3952 else {
3953 icalparameter *param = NULL;
3954 icalproperty *prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DTSTART_PROPERTY);
3955
3956 if (prop) {
3957 param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
3958
3959 if (param) {
3960 const gchar *tzid = NULL;
3961 icaltimezone *st_zone = NULL;
3962
3963 tzid = icalparameter_get_tzid (param);
3964 if (tzid)
3965 e_cal_client_get_timezone_sync (comp_data->client, tzid, &st_zone, NULL, NULL);
3966
3967 if (st_zone)
3968 zone = st_zone;
3969 }
3970 }
3971 }
3972
3973 comp_data->instance_start = icaltime_as_timet_with_zone (start_time, zone);
3974
3975 if (end_time.zone)
3976 zone = end_time.zone;
3977 else {
3978 icalparameter *param = NULL;
3979 icalproperty *prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DTSTART_PROPERTY);
3980
3981 if (prop) {
3982 param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
3983
3984 if (param) {
3985 const gchar *tzid = NULL;
3986 icaltimezone *end_zone = NULL;
3987
3988 tzid = icalparameter_get_tzid (param);
3989 if (tzid)
3990 e_cal_client_get_timezone_sync (comp_data->client, tzid, &end_zone, NULL, NULL);
3991
3992 if (end_zone)
3993 zone = end_zone;
3994 }
3995 }
3996
3997 }
3998 comp_data->instance_end = icaltime_as_timet_with_zone (end_time, zone);
3999 }
4000
4001 /**
4002 * e_cal_model_set_default_time_func:
4003 * This function will be used when creating new item from the "click-to-add",
4004 * when user didn't fill a start date there.
4005 **/
4006 void
4007 e_cal_model_set_default_time_func (ECalModel *model,
4008 ECalModelDefaultTimeFunc func,
4009 gpointer user_data)
4010 {
4011 g_return_if_fail (E_IS_CAL_MODEL (model));
4012
4013 model->priv->get_default_time = func;
4014 model->priv->get_default_time_user_data = user_data;
4015 }