Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
e-calendar-view.c:636:25 | clang-analyzer | The left operand of '-' is a garbage value | ||
e-calendar-view.c:636:25 | clang-analyzer | The left operand of '-' is a garbage value |
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Rodrigo Moya <rodrigo@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <time.h>
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31 #include <glib/gstdio.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <libebackend/libebackend.h>
34 #include <libedataserverui/libedataserverui.h>
35
36 #include <e-util/e-util.h>
37 #include <libevolution-utils/e-alert-dialog.h>
38 #include <e-util/e-selection.h>
39 #include <e-util/e-datetime-format.h>
40 #include <e-util/e-dialog-utils.h>
41 #include <e-util/e-icon-factory.h>
42 #include <misc/e-selectable.h>
43 #include <shell/e-shell.h>
44
45 #include "comp-util.h"
46 #include "ea-calendar.h"
47 #include "e-cal-model-calendar.h"
48 #include "e-calendar-view.h"
49 #include "itip-utils.h"
50 #include "dialogs/comp-editor-util.h"
51 #include "dialogs/delete-comp.h"
52 #include "dialogs/delete-error.h"
53 #include "dialogs/event-editor.h"
54 #include "dialogs/send-comp.h"
55 #include "dialogs/cancel-comp.h"
56 #include "dialogs/recur-comp.h"
57 #include "dialogs/select-source-dialog.h"
58 #include "dialogs/goto-dialog.h"
59 #include "print.h"
60 #include "misc.h"
61
62 #define E_CALENDAR_VIEW_GET_PRIVATE(obj) \
63 (G_TYPE_INSTANCE_GET_PRIVATE \
64 ((obj), E_TYPE_CALENDAR_VIEW, ECalendarViewPrivate))
65
66 struct _ECalendarViewPrivate {
67 /* The GnomeCalendar we are associated to */
68 GnomeCalendar *calendar;
69
70 /* The calendar model we are monitoring */
71 ECalModel *model;
72
73 gchar *default_category;
74 gint time_divisions;
75 GSList *selected_cut_list;
76
77 GtkTargetList *copy_target_list;
78 GtkTargetList *paste_target_list;
79 };
80
81 enum {
82 PROP_0,
83 PROP_COPY_TARGET_LIST,
84 PROP_MODEL,
85 PROP_PASTE_TARGET_LIST,
86 PROP_TIME_DIVISIONS
87 };
88
89 /* FIXME Why are we emitting these event signals here? Can't the model just be listened to? */
90 /* Signal IDs */
91 enum {
92 POPUP_EVENT,
93 SELECTION_CHANGED,
94 SELECTED_TIME_CHANGED,
95 TIMEZONE_CHANGED,
96 EVENT_CHANGED,
97 EVENT_ADDED,
98 USER_CREATED,
99 OPEN_EVENT,
100 LAST_SIGNAL
101 };
102
103 static guint signals[LAST_SIGNAL];
104
105 static void calendar_view_selectable_init (ESelectableInterface *interface);
106
107 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
108 ECalendarView, e_calendar_view, GTK_TYPE_TABLE,
109 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)
110 G_IMPLEMENT_INTERFACE (E_TYPE_SELECTABLE, calendar_view_selectable_init));
111
112 static void
113 calendar_view_add_retract_data (ECalComponent *comp,
114 const gchar *retract_comment,
115 CalObjModType mod)
116 {
117 icalcomponent *icalcomp = NULL;
118 icalproperty *icalprop = NULL;
119
120 icalcomp = e_cal_component_get_icalcomponent (comp);
121 if (retract_comment && *retract_comment)
122 icalprop = icalproperty_new_x (retract_comment);
123 else
124 icalprop = icalproperty_new_x ("0");
125 icalproperty_set_x_name (icalprop, "X-EVOLUTION-RETRACT-COMMENT");
126 icalcomponent_add_property (icalcomp, icalprop);
127
128 if (mod == CALOBJ_MOD_ALL)
129 icalprop = icalproperty_new_x ("All");
130 else
131 icalprop = icalproperty_new_x ("This");
132 icalproperty_set_x_name (icalprop, "X-EVOLUTION-RECUR-MOD");
133 icalcomponent_add_property (icalcomp, icalprop);
134 }
135
136 static gboolean
137 calendar_view_check_for_retract (ECalComponent *comp,
138 ECalClient *client)
139 {
140 ECalComponentOrganizer organizer;
141 const gchar *strip;
142 gchar *email = NULL;
143 gboolean ret_val;
144
145 if (!e_cal_component_has_attendees (comp))
146 return FALSE;
147
148 if (!e_cal_client_check_save_schedules (client))
149 return FALSE;
150
151 e_cal_component_get_organizer (comp, &organizer);
152 strip = itip_strip_mailto (organizer.value);
153
154 ret_val =
155 e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &email, NULL, NULL) &&
156 (g_ascii_strcasecmp (email, strip) == 0);
157
158 g_free (email);
159
160 return ret_val;
161 }
162
163 static void
164 calendar_view_delete_event (ECalendarView *cal_view,
165 ECalendarViewEvent *event)
166 {
167 ECalModel *model;
168 ECalComponent *comp;
169 ECalComponentVType vtype;
170 ESourceRegistry *registry;
171 gboolean delete = TRUE;
172 GError *error = NULL;
173
174 if (!is_comp_data_valid (event))
175 return;
176
177 model = e_calendar_view_get_model (cal_view);
178 registry = e_cal_model_get_registry (model);
179
180 comp = e_cal_component_new ();
181 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
182 vtype = e_cal_component_get_vtype (comp);
183
184 /*FIXME remove it once the we dont set the recurrence id for all the generated instances */
185 if (!e_cal_client_check_recurrences_no_master (event->comp_data->client))
186 e_cal_component_set_recurid (comp, NULL);
187
188 /*FIXME Retract should be moved to Groupwise features plugin */
189 if (calendar_view_check_for_retract (comp, event->comp_data->client)) {
190 gchar *retract_comment = NULL;
191 gboolean retract = FALSE;
192
193 delete = prompt_retract_dialog (comp, &retract_comment, GTK_WIDGET (cal_view), &retract);
194 if (retract) {
195 GSList *users = NULL;
196 icalcomponent *icalcomp = NULL, *mod_comp = NULL;
197
198 calendar_view_add_retract_data (
199 comp, retract_comment, CALOBJ_MOD_ALL);
200 icalcomp = e_cal_component_get_icalcomponent (comp);
201 icalcomponent_set_method (icalcomp, ICAL_METHOD_CANCEL);
202 if (!e_cal_client_send_objects_sync (event->comp_data->client, icalcomp, &users,
203 &mod_comp, NULL, &error)) {
204 delete_error_dialog (error, E_CAL_COMPONENT_EVENT);
205 g_clear_error (&error);
206 error = NULL;
207 } else {
208
209 if (mod_comp)
210 icalcomponent_free (mod_comp);
211
212 if (users) {
213 g_slist_foreach (users, (GFunc) g_free, NULL);
214 g_slist_free (users);
215 }
216 }
217 }
218 } else if (e_cal_model_get_confirm_delete (model))
219 delete = delete_component_dialog (
220 comp, FALSE, 1, vtype, GTK_WIDGET (cal_view));
221
222 if (delete) {
223 const gchar *uid;
224 gchar *rid = NULL;
225
226 if ((itip_organizer_is_user (registry, comp, event->comp_data->client) ||
227 itip_sentby_is_user (registry, comp, event->comp_data->client))
228 && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)),
229 event->comp_data->client,
230 comp, TRUE))
231 itip_send_comp (
232 registry, E_CAL_COMPONENT_METHOD_CANCEL,
233 comp, event->comp_data->client, NULL, NULL,
234 NULL, TRUE, FALSE);
235
236 e_cal_component_get_uid (comp, &uid);
237 if (!uid || !*uid) {
238 g_object_unref (comp);
239 return;
240 }
241 rid = e_cal_component_get_recurid_as_string (comp);
242 if (e_cal_util_component_is_instance (event->comp_data->icalcomp) || e_cal_util_component_has_recurrences (event->comp_data->icalcomp))
243 e_cal_client_remove_object_sync (
244 event->comp_data->client, uid,
245 rid, CALOBJ_MOD_ALL, NULL, &error);
246 else
247 e_cal_client_remove_object_sync (event->comp_data->client, uid, NULL, CALOBJ_MOD_THIS, NULL, &error);
248
249 delete_error_dialog (error, E_CAL_COMPONENT_EVENT);
250 g_clear_error (&error);
251 g_free (rid);
252 }
253
254 g_object_unref (comp);
255 }
256
257 static void
258 calendar_view_set_model (ECalendarView *calendar_view,
259 ECalModel *model)
260 {
261 g_return_if_fail (calendar_view->priv->model == NULL);
262 g_return_if_fail (E_IS_CAL_MODEL (model));
263
264 calendar_view->priv->model = g_object_ref (model);
265 }
266
267 static void
268 calendar_view_set_property (GObject *object,
269 guint property_id,
270 const GValue *value,
271 GParamSpec *pspec)
272 {
273 switch (property_id) {
274 case PROP_MODEL:
275 calendar_view_set_model (
276 E_CALENDAR_VIEW (object),
277 g_value_get_object (value));
278 return;
279
280 case PROP_TIME_DIVISIONS:
281 e_calendar_view_set_time_divisions (
282 E_CALENDAR_VIEW (object),
283 g_value_get_int (value));
284 return;
285 }
286
287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
288 }
289
290 static void
291 calendar_view_get_property (GObject *object,
292 guint property_id,
293 GValue *value,
294 GParamSpec *pspec)
295 {
296 switch (property_id) {
297 case PROP_COPY_TARGET_LIST:
298 g_value_set_boxed (
299 value, e_calendar_view_get_copy_target_list (
300 E_CALENDAR_VIEW (object)));
301 return;
302
303 case PROP_MODEL:
304 g_value_set_object (
305 value, e_calendar_view_get_model (
306 E_CALENDAR_VIEW (object)));
307 return;
308
309 case PROP_PASTE_TARGET_LIST:
310 g_value_set_boxed (
311 value, e_calendar_view_get_paste_target_list (
312 E_CALENDAR_VIEW (object)));
313 return;
314
315 case PROP_TIME_DIVISIONS:
316 g_value_set_int (
317 value, e_calendar_view_get_time_divisions (
318 E_CALENDAR_VIEW (object)));
319 return;
320 }
321
322 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
323 }
324
325 static void
326 calendar_view_dispose (GObject *object)
327 {
328 ECalendarViewPrivate *priv;
329
330 priv = E_CALENDAR_VIEW_GET_PRIVATE (object);
331
332 if (priv->model != NULL) {
333 g_signal_handlers_disconnect_matched (
334 priv->model, G_SIGNAL_MATCH_DATA,
335 0, 0, NULL, NULL, object);
336 g_object_unref (priv->model);
337 priv->model = NULL;
338 }
339
340 if (priv->copy_target_list != NULL) {
341 gtk_target_list_unref (priv->copy_target_list);
342 priv->copy_target_list = NULL;
343 }
344
345 if (priv->paste_target_list != NULL) {
346 gtk_target_list_unref (priv->paste_target_list);
347 priv->paste_target_list = NULL;
348 }
349
350 if (priv->selected_cut_list) {
351 g_slist_foreach (priv->selected_cut_list, (GFunc) g_object_unref, NULL);
352 g_slist_free (priv->selected_cut_list);
353 priv->selected_cut_list = NULL;
354 }
355
356 /* Chain up to parent's dispose() method. */
357 G_OBJECT_CLASS (e_calendar_view_parent_class)->dispose (object);
358 }
359
360 static void
361 calendar_view_finalize (GObject *object)
362 {
363 ECalendarViewPrivate *priv;
364
365 priv = E_CALENDAR_VIEW_GET_PRIVATE (object);
366
367 g_free (priv->default_category);
368
369 /* Chain up to parent's finalize() method. */
370 G_OBJECT_CLASS (e_calendar_view_parent_class)->finalize (object);
371 }
372
373 static void
374 calendar_view_constructed (GObject *object)
375 {
376 /* Do this after calendar_view_init() so extensions can query
377 * the GType accurately. See GInstanceInitFunc documentation
378 * for details of the problem. */
379 e_extensible_load_extensions (E_EXTENSIBLE (object));
380
381 /* Chain up to parent's constructed() method. */
382 G_OBJECT_CLASS (e_calendar_view_parent_class)->constructed (object);
383 }
384
385 static void
386 calendar_view_update_actions (ESelectable *selectable,
387 EFocusTracker *focus_tracker,
388 GdkAtom *clipboard_targets,
389 gint n_clipboard_targets)
390 {
391 ECalendarView *view;
392 GtkAction *action;
393 GtkTargetList *target_list;
394 GList *list, *iter;
395 gboolean can_paste = FALSE;
396 gboolean sources_are_editable = TRUE;
397 gboolean recurring = FALSE;
398 gboolean sensitive;
399 const gchar *tooltip;
400 gint n_selected;
401 gint ii;
402
403 view = E_CALENDAR_VIEW (selectable);
404
405 list = e_calendar_view_get_selected_events (view);
406 n_selected = g_list_length (list);
407
408 for (iter = list; iter != NULL; iter = iter->next) {
409 ECalendarViewEvent *event = iter->data;
410 ECalClient *client;
411 icalcomponent *icalcomp;
412
413 if (event == NULL || event->comp_data == NULL)
414 continue;
415
416 client = event->comp_data->client;
417 icalcomp = event->comp_data->icalcomp;
418
419 sources_are_editable = sources_are_editable && !e_client_is_readonly (E_CLIENT (client));
420
421 recurring |=
422 e_cal_util_component_is_instance (icalcomp) ||
423 e_cal_util_component_has_recurrences (icalcomp);
424 }
425
426 g_list_free (list);
427
428 target_list = e_selectable_get_paste_target_list (selectable);
429 for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
430 can_paste = gtk_target_list_find (
431 target_list, clipboard_targets[ii], NULL);
432
433 action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
434 sensitive = (n_selected > 0) && sources_are_editable;
435 tooltip = _("Cut selected events to the clipboard");
436 gtk_action_set_sensitive (action, sensitive);
437 gtk_action_set_tooltip (action, tooltip);
438
439 action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
440 sensitive = (n_selected > 0);
441 tooltip = _("Copy selected events to the clipboard");
442 gtk_action_set_sensitive (action, sensitive);
443 gtk_action_set_tooltip (action, tooltip);
444
445 action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
446 sensitive = sources_are_editable && can_paste;
447 tooltip = _("Paste events from the clipboard");
448 gtk_action_set_sensitive (action, sensitive);
449 gtk_action_set_tooltip (action, tooltip);
450
451 action = e_focus_tracker_get_delete_selection_action (focus_tracker);
452 sensitive = (n_selected > 0) && sources_are_editable && !recurring;
453 tooltip = _("Delete selected events");
454 gtk_action_set_sensitive (action, sensitive);
455 gtk_action_set_tooltip (action, tooltip);
456 }
457
458 static void
459 calendar_view_cut_clipboard (ESelectable *selectable)
460 {
461 ECalendarView *cal_view;
462 ECalendarViewPrivate *priv;
463 GList *selected, *l;
464
465 cal_view = E_CALENDAR_VIEW (selectable);
466 priv = cal_view->priv;
467
468 selected = e_calendar_view_get_selected_events (cal_view);
469 if (!selected)
470 return;
471
472 #if 0 /* KILL-BONOBO */
473 e_calendar_view_set_status_message (cal_view, _("Deleting selected objects"), -1);
474 #endif
475
476 e_selectable_copy_clipboard (selectable);
477
478 for (l = selected; l != NULL; l = g_list_next (l)) {
479 ECalendarViewEvent *event = (ECalendarViewEvent *) l->data;
480
481 priv->selected_cut_list = g_slist_prepend (priv->selected_cut_list, g_object_ref (event->comp_data));
482 }
483
484 #if 0 /* KILL-BONOBO */
485 e_calendar_view_set_status_message (cal_view, NULL, -1);
486 #endif
487
488 g_list_free (selected);
489 }
490
491 static void
492 add_related_timezones (icalcomponent *des_icalcomp,
493 icalcomponent *src_icalcomp,
494 ECalClient *client)
495 {
496 icalproperty_kind look_in[] = {
497 ICAL_DTSTART_PROPERTY,
498 ICAL_DTEND_PROPERTY,
499 ICAL_NO_PROPERTY
500 };
501 gint i;
502
503 g_return_if_fail (des_icalcomp != NULL);
504 g_return_if_fail (src_icalcomp != NULL);
505 g_return_if_fail (client != NULL);
506
507 for (i = 0; look_in[i] != ICAL_NO_PROPERTY; i++) {
508 icalproperty *prop = icalcomponent_get_first_property (src_icalcomp, look_in[i]);
509
510 if (prop) {
511 icalparameter *par = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
512
513 if (par) {
514 const gchar *tzid = icalparameter_get_tzid (par);
515
516 if (tzid) {
517 GError *error = NULL;
518 icaltimezone *zone = NULL;
519
520 if (!e_cal_client_get_timezone_sync (client, tzid, &zone, NULL, &error)) {
521 g_warning ("%s: Cannot get timezone for '%s'. %s", G_STRFUNC, tzid, error ? error->message : "");
522 if (error)
523 g_error_free (error);
524 } else if (zone &&
525 icalcomponent_get_timezone (des_icalcomp, icaltimezone_get_tzid (zone)) == NULL) {
526 /* do not duplicate timezones in the component */
527 icalcomponent *vtz_comp;
528
529 vtz_comp = icaltimezone_get_component (zone);
530 if (vtz_comp)
531 icalcomponent_add_component (des_icalcomp, icalcomponent_new_clone (vtz_comp));
532 }
533 }
534 }
535 }
536 }
537 }
538
539 static void
540 calendar_view_copy_clipboard (ESelectable *selectable)
541 {
542 ECalendarView *cal_view;
543 ECalendarViewPrivate *priv;
544 GList *selected, *l;
545 gchar *comp_str;
546 icalcomponent *vcal_comp;
547 icalcomponent *new_icalcomp;
548 ECalendarViewEvent *event;
549 GtkClipboard *clipboard;
550
551 cal_view = E_CALENDAR_VIEW (selectable);
552 priv = cal_view->priv;
553
554 selected = e_calendar_view_get_selected_events (cal_view);
555 if (!selected)
556 return;
557
558 if (priv->selected_cut_list) {
559 g_slist_foreach (priv->selected_cut_list, (GFunc) g_object_unref, NULL);
560 g_slist_free (priv->selected_cut_list);
561 priv->selected_cut_list = NULL;
562 }
563
564 /* create top-level VCALENDAR component and add VTIMEZONE's */
565 vcal_comp = e_cal_util_new_top_level ();
566 for (l = selected; l != NULL; l = l->next) {
567 event = (ECalendarViewEvent *) l->data;
568
569 if (event && is_comp_data_valid (event)) {
570 e_cal_util_add_timezones_from_component (vcal_comp, event->comp_data->icalcomp);
571
572 add_related_timezones (vcal_comp, event->comp_data->icalcomp, event->comp_data->client);
573 }
574 }
575
576 for (l = selected; l != NULL; l = l->next) {
577 event = (ECalendarViewEvent *) l->data;
578
579 if (!is_comp_data_valid (event))
580 continue;
581
582 new_icalcomp = icalcomponent_new_clone (event->comp_data->icalcomp);
583
584 /* do not remove RECURRENCE-IDs from copied objects */
585 icalcomponent_add_component (vcal_comp, new_icalcomp);
586 }
587
588 comp_str = icalcomponent_as_ical_string_r (vcal_comp);
589
590 /* copy the VCALENDAR to the clipboard */
591 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
592 e_clipboard_set_calendar (clipboard, comp_str, -1);
593 gtk_clipboard_store (clipboard);
594
595 /* free memory */
596 icalcomponent_free (vcal_comp);
597 g_free (comp_str);
598 g_list_free (selected);
599 }
600
601 static gboolean
602 clipboard_get_calendar_data (ECalendarView *cal_view,
603 const gchar *text,
604 GSList **copied_list)
605 {
606 icalcomponent *icalcomp;
607 icalcomponent_kind kind;
608 time_t selected_time_start, selected_time_end;
609 icaltimezone *default_zone;
610 ECalClient *client;
611 gboolean in_top_canvas, ret = FALSE;
612
613 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), FALSE);
614
615 if (!text || !*text)
616 return FALSE;
617
618 icalcomp = icalparser_parse_string (text);
619 if (!icalcomp)
620 return FALSE;
621
622 default_zone = e_cal_model_get_timezone (cal_view->priv->model);
623 client = e_cal_model_get_default_client (cal_view->priv->model);
624
625 /* check the type of the component */
626 /* FIXME An error dialog if we return? */
627 kind = icalcomponent_isa (icalcomp);
628 if (kind != ICAL_VCALENDAR_COMPONENT && kind != ICAL_VEVENT_COMPONENT)
629 return FALSE;
630
631 #if 0 /* KILL-BONOBO */
632 e_calendar_view_set_status_message (cal_view, _("Updating objects"), -1);
633 #endif
634 e_calendar_view_get_selected_time_range (cal_view, &selected_time_start, &selected_time_end);
635
636 if ((selected_time_end - selected_time_start) == 60 * 60 * 24)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
637 in_top_canvas = TRUE;
638 else
639 in_top_canvas = FALSE;
640
641 if (kind == ICAL_VCALENDAR_COMPONENT) {
642 icalcomponent *subcomp;
643
644 /* add timezones first, to have them ready */
645 for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VTIMEZONE_COMPONENT);
646 subcomp;
647 subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VTIMEZONE_COMPONENT)) {
648 icaltimezone *zone;
649 GError *error = NULL;
650
651 zone = icaltimezone_new ();
652 icaltimezone_set_component (zone, subcomp);
653 if (!e_cal_client_add_timezone_sync (client, zone, NULL, &error)) {
654 icalproperty *tzidprop = icalcomponent_get_first_property (subcomp, ICAL_TZID_PROPERTY);
655
656 g_warning ("%s: Add zone '%s' failed. %s", G_STRFUNC, tzidprop ? icalproperty_get_tzid (tzidprop) : "???", error ? error->message : "");
657 if (error)
658 g_error_free (error);
659 }
660
661 icaltimezone_free (zone, 1);
662 }
663
664 for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VEVENT_COMPONENT);
665 subcomp;
666 subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VEVENT_COMPONENT)) {
667 if (e_cal_util_component_has_recurrences (subcomp)) {
668 icalproperty *icalprop = icalcomponent_get_first_property (subcomp, ICAL_RRULE_PROPERTY);
669 if (icalprop)
670 icalproperty_remove_parameter_by_name (icalprop, "X-EVOLUTION-ENDDATE");
671 }
672
673 ret = e_calendar_view_add_event (cal_view, client, selected_time_start, default_zone, subcomp, in_top_canvas);
674 if (!ret)
675 break;
676
677 if (copied_list)
678 *copied_list = g_slist_prepend (*copied_list, g_strdup (icalcomponent_get_uid (subcomp)));
679 }
680
681 icalcomponent_free (icalcomp);
682 } else {
683 ret = e_calendar_view_add_event (cal_view, client, selected_time_start, default_zone, icalcomp, in_top_canvas);
684 if (ret && copied_list)
685 *copied_list = g_slist_prepend (*copied_list, g_strdup (icalcomponent_get_uid (icalcomp)));
686 }
687
688 return ret;
689
690 #if 0 /* KILL-BONOBO */
691 e_calendar_view_set_status_message (cal_view, NULL, -1);
692 #endif
693 }
694
695 static void
696 calendar_view_paste_clipboard (ESelectable *selectable)
697 {
698 ECalModel *model;
699 ECalendarView *cal_view;
700 ECalendarViewPrivate *priv;
701 ESourceRegistry *registry;
702 GtkClipboard *clipboard;
703
704 cal_view = E_CALENDAR_VIEW (selectable);
705 priv = cal_view->priv;
706
707 model = e_calendar_view_get_model (cal_view);
708 registry = e_cal_model_get_registry (model);
709
710 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
711
712 /* Paste text into an event being edited. */
713 if (gtk_clipboard_wait_is_text_available (clipboard)) {
714 ECalendarViewClass *class;
715
716 class = E_CALENDAR_VIEW_GET_CLASS (cal_view);
717 g_return_if_fail (class->paste_text != NULL);
718
719 class->paste_text (cal_view);
720
721 /* Paste iCalendar data into the view. */
722 } else if (e_clipboard_wait_is_calendar_available (clipboard)) {
723 gchar *calendar_source;
724 GSList *copied_list = NULL, *l;
725
726 calendar_source = e_clipboard_wait_for_calendar (clipboard);
727
728 if (priv->selected_cut_list)
729 clipboard_get_calendar_data (cal_view, calendar_source, &copied_list);
730 else
731 clipboard_get_calendar_data (cal_view, calendar_source, NULL);
732
733 if (copied_list && priv->selected_cut_list) {
734 for (l = priv->selected_cut_list; l != NULL; l = l->next) {
735 ECalComponent *comp;
736 ECalModelComponent *comp_data = (ECalModelComponent *) l->data;
737 const gchar *uid;
738 GError *error = NULL;
739 GSList *found = NULL;
740
741 /* Remove them one by one after ensuring it has been copied to the destination successfully */
742 found = g_slist_find_custom (copied_list, icalcomponent_get_uid (comp_data->icalcomp), (GCompareFunc) strcmp);
743 if (!found)
744 continue;
745
746 g_free (found->data);
747 copied_list = g_slist_delete_link (copied_list, found);
748
749 comp = e_cal_component_new ();
750 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp));
751
752 if ((itip_organizer_is_user (registry, comp, comp_data->client) ||
753 itip_sentby_is_user (registry, comp, comp_data->client))
754 && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)),
755 comp_data->client, comp, TRUE))
756 itip_send_comp (
757 registry,
758 E_CAL_COMPONENT_METHOD_CANCEL,
759 comp, comp_data->client,
760 NULL, NULL, NULL, TRUE, FALSE);
761
762 e_cal_component_get_uid (comp, &uid);
763 if (e_cal_component_is_instance (comp)) {
764 gchar *rid = NULL;
765 icalcomponent *icalcomp;
766
767 /* when cutting detached instances, only cut that instance */
768 rid = e_cal_component_get_recurid_as_string (comp);
769 if (e_cal_client_get_object_sync (comp_data->client, uid, rid, &icalcomp, NULL, NULL)) {
770 e_cal_client_remove_object_sync (comp_data->client, uid, rid, CALOBJ_MOD_THIS, NULL, &error);
771 icalcomponent_free (icalcomp);
772 } else
773 e_cal_client_remove_object_sync (comp_data->client, uid, NULL, CALOBJ_MOD_ALL, NULL, &error);
774 g_free (rid);
775 } else
776 e_cal_client_remove_object_sync (comp_data->client, uid, NULL, CALOBJ_MOD_ALL, NULL, &error);
777 delete_error_dialog (error, E_CAL_COMPONENT_EVENT);
778
779 g_clear_error (&error);
780 g_object_unref (comp);
781 }
782 }
783
784 if (priv->selected_cut_list) {
785 g_slist_foreach (priv->selected_cut_list, (GFunc) g_object_unref, NULL);
786 g_slist_free (priv->selected_cut_list);
787 }
788 priv->selected_cut_list = NULL;
789
790 g_free (calendar_source);
791
792 }
793 }
794
795 static void
796 calendar_view_delete_selection (ESelectable *selectable)
797 {
798 ECalendarView *cal_view;
799 GList *selected, *iter;
800
801 cal_view = E_CALENDAR_VIEW (selectable);
802
803 selected = e_calendar_view_get_selected_events (cal_view);
804
805 for (iter = selected; iter != NULL; iter = iter->next) {
806 ECalendarViewEvent *event = iter->data;
807
808 /* XXX Why would this ever be NULL? */
809 if (event == NULL)
810 continue;
811
812 calendar_view_delete_event (cal_view, event);
813 }
814
815 g_list_free (selected);
816 }
817
818 static void
819 e_calendar_view_class_init (ECalendarViewClass *class)
820 {
821 GObjectClass *object_class;
822 GtkBindingSet *binding_set;
823
824 g_type_class_add_private (class, sizeof (ECalendarViewPrivate));
825
826 object_class = G_OBJECT_CLASS (class);
827 object_class->set_property = calendar_view_set_property;
828 object_class->get_property = calendar_view_get_property;
829 object_class->dispose = calendar_view_dispose;
830 object_class->finalize = calendar_view_finalize;
831 object_class->constructed = calendar_view_constructed;
832
833 class->selection_changed = NULL;
834 class->selected_time_changed = NULL;
835 class->event_changed = NULL;
836 class->event_added = NULL;
837 class->user_created = NULL;
838
839 class->get_selected_events = NULL;
840 class->get_selected_time_range = NULL;
841 class->set_selected_time_range = NULL;
842 class->get_visible_time_range = NULL;
843 class->update_query = NULL;
844 class->open_event = e_calendar_view_open_event;
845 class->paste_text = NULL;
846
847 /* Inherited from ESelectableInterface */
848 g_object_class_override_property (
849 object_class,
850 PROP_COPY_TARGET_LIST,
851 "copy-target-list");
852
853 g_object_class_install_property (
854 object_class,
855 PROP_MODEL,
856 g_param_spec_object (
857 "model",
858 "Model",
859 NULL,
860 E_TYPE_CAL_MODEL,
861 G_PARAM_READWRITE |
862 G_PARAM_CONSTRUCT_ONLY));
863
864 /* Inherited from ESelectableInterface */
865 g_object_class_override_property (
866 object_class,
867 PROP_PASTE_TARGET_LIST,
868 "paste-target-list");
869
870 g_object_class_install_property (
871 object_class,
872 PROP_TIME_DIVISIONS,
873 g_param_spec_int (
874 "time-divisions",
875 "Time Divisions",
876 NULL,
877 G_MININT,
878 G_MAXINT,
879 30,
880 G_PARAM_READWRITE));
881
882 signals[POPUP_EVENT] = g_signal_new (
883 "popup-event",
884 G_TYPE_FROM_CLASS (class),
885 G_SIGNAL_RUN_FIRST,
886 G_STRUCT_OFFSET (ECalendarViewClass, popup_event),
887 NULL, NULL,
888 g_cclosure_marshal_VOID__BOXED,
889 G_TYPE_NONE, 1,
890 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
891
892 signals[SELECTION_CHANGED] = g_signal_new (
893 "selection-changed",
894 G_TYPE_FROM_CLASS (class),
895 G_SIGNAL_RUN_LAST,
896 G_STRUCT_OFFSET (ECalendarViewClass, selection_changed),
897 NULL, NULL,
898 g_cclosure_marshal_VOID__VOID,
899 G_TYPE_NONE, 0);
900
901 signals[SELECTED_TIME_CHANGED] = g_signal_new (
902 "selected-time-changed",
903 G_TYPE_FROM_CLASS (class),
904 G_SIGNAL_RUN_LAST,
905 G_STRUCT_OFFSET (ECalendarViewClass, selected_time_changed),
906 NULL, NULL,
907 g_cclosure_marshal_VOID__VOID,
908 G_TYPE_NONE, 0);
909
910 signals[TIMEZONE_CHANGED] = g_signal_new (
911 "timezone-changed",
912 G_TYPE_FROM_CLASS (class),
913 G_SIGNAL_RUN_LAST,
914 G_STRUCT_OFFSET (ECalendarViewClass, timezone_changed),
915 NULL, NULL,
916 e_marshal_VOID__POINTER_POINTER,
917 G_TYPE_NONE, 2,
918 G_TYPE_POINTER,
919 G_TYPE_POINTER);
920
921 signals[EVENT_CHANGED] = g_signal_new (
922 "event-changed",
923 G_TYPE_FROM_CLASS (object_class),
924 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
925 G_STRUCT_OFFSET (ECalendarViewClass, event_changed),
926 NULL, NULL,
927 g_cclosure_marshal_VOID__POINTER,
928 G_TYPE_NONE, 1,
929 G_TYPE_POINTER);
930
931 signals[EVENT_ADDED] = g_signal_new (
932 "event-added",
933 G_TYPE_FROM_CLASS (object_class),
934 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
935 G_STRUCT_OFFSET (ECalendarViewClass, event_added),
936 NULL, NULL,
937 g_cclosure_marshal_VOID__POINTER,
938 G_TYPE_NONE, 1,
939 G_TYPE_POINTER);
940
941 signals[USER_CREATED] = g_signal_new (
942 "user-created",
943 G_TYPE_FROM_CLASS (class),
944 G_SIGNAL_RUN_LAST,
945 G_STRUCT_OFFSET (ECalendarViewClass, user_created),
946 NULL, NULL,
947 g_cclosure_marshal_VOID__OBJECT,
948 G_TYPE_NONE, 1, G_TYPE_OBJECT);
949
950 signals[OPEN_EVENT] = g_signal_new (
951 "open-event",
952 G_TYPE_FROM_CLASS (class),
953 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
954 G_STRUCT_OFFSET (ECalendarViewClass, open_event),
955 NULL, NULL,
956 g_cclosure_marshal_VOID__VOID,
957 G_TYPE_NONE, 0);
958
959 /* Key bindings */
960
961 binding_set = gtk_binding_set_by_class (class);
962
963 gtk_binding_entry_add_signal (
964 binding_set, GDK_KEY_o, GDK_CONTROL_MASK, "open-event", 0);
965
966 /* init the accessibility support for e_day_view */
967 e_cal_view_a11y_init ();
968 }
969
970 static void
971 e_calendar_view_init (ECalendarView *calendar_view)
972 {
973 GtkTargetList *target_list;
974
975 calendar_view->priv = E_CALENDAR_VIEW_GET_PRIVATE (calendar_view);
976
977 /* Set this early to avoid a divide-by-zero during init. */
978 calendar_view->priv->time_divisions = 30;
979
980 target_list = gtk_target_list_new (NULL, 0);
981 e_target_list_add_calendar_targets (target_list, 0);
982 calendar_view->priv->copy_target_list = target_list;
983
984 target_list = gtk_target_list_new (NULL, 0);
985 e_target_list_add_calendar_targets (target_list, 0);
986 calendar_view->priv->paste_target_list = target_list;
987 }
988
989 static void
990 calendar_view_selectable_init (ESelectableInterface *interface)
991 {
992 interface->update_actions = calendar_view_update_actions;
993 interface->cut_clipboard = calendar_view_cut_clipboard;
994 interface->copy_clipboard = calendar_view_copy_clipboard;
995 interface->paste_clipboard = calendar_view_paste_clipboard;
996 interface->delete_selection = calendar_view_delete_selection;
997 }
998
999 void
1000 e_calendar_view_popup_event (ECalendarView *calendar_view,
1001 GdkEventButton *event)
1002 {
1003 g_return_if_fail (E_IS_CALENDAR_VIEW (calendar_view));
1004 g_return_if_fail (event != NULL);
1005
1006 g_signal_emit (calendar_view, signals[POPUP_EVENT], 0, event);
1007 }
1008
1009 gboolean
1010 e_calendar_view_add_event (ECalendarView *cal_view,
1011 ECalClient *client,
1012 time_t dtstart,
1013 icaltimezone *default_zone,
1014 icalcomponent *icalcomp,
1015 gboolean in_top_canvas)
1016 {
1017 ECalModel *model;
1018 ECalComponent *comp;
1019 ESourceRegistry *registry;
1020 struct icaltimetype itime, old_dtstart, old_dtend;
1021 time_t tt_start, tt_end, new_dtstart = 0;
1022 struct icaldurationtype ic_dur, ic_oneday;
1023 gchar *uid;
1024 gint start_offset, end_offset;
1025 gboolean all_day_event = FALSE;
1026 GnomeCalendarViewType view_type;
1027 gboolean ret = TRUE;
1028 GError *error = NULL;
1029
1030 model = e_calendar_view_get_model (cal_view);
1031 registry = e_cal_model_get_registry (model);
1032
1033 start_offset = 0;
1034 end_offset = 0;
1035
1036 old_dtstart = icalcomponent_get_dtstart (icalcomp);
1037 tt_start = icaltime_as_timet (old_dtstart);
1038 old_dtend = icalcomponent_get_dtend (icalcomp);
1039 tt_end = icaltime_as_timet (old_dtend);
1040 ic_dur = icaldurationtype_from_int (tt_end - tt_start);
1041
1042 if (icaldurationtype_as_int (ic_dur) > 60 *60 *24) {
1043 /* This is a long event */
1044 start_offset = old_dtstart.hour * 60 + old_dtstart.minute;
1045 end_offset = old_dtstart.hour * 60 + old_dtend.minute;
1046 }
1047
1048 ic_oneday = icaldurationtype_null_duration ();
1049 ic_oneday.days = 1;
1050
1051 view_type = gnome_calendar_get_view (cal_view->priv->calendar);
1052
1053 switch (view_type) {
1054 case GNOME_CAL_DAY_VIEW:
1055 case GNOME_CAL_WORK_WEEK_VIEW:
1056 if (start_offset == 0 && end_offset == 0 && in_top_canvas)
1057 all_day_event = TRUE;
1058
1059 if (all_day_event) {
1060 ic_dur = ic_oneday;
1061 } else if (icaldurationtype_as_int (ic_dur) >= 60 *60 *24
1062 && !in_top_canvas) {
1063 /* copy & paste from top canvas to main canvas */
1064 gint time_divisions;
1065
1066 time_divisions = e_calendar_view_get_time_divisions (cal_view);
1067 ic_dur = icaldurationtype_from_int (time_divisions * 60);
1068 }
1069
1070 if (in_top_canvas)
1071 new_dtstart = dtstart + start_offset * 60;
1072 else
1073 new_dtstart = dtstart;
1074 break;
1075 case GNOME_CAL_WEEK_VIEW:
1076 case GNOME_CAL_MONTH_VIEW:
1077 case GNOME_CAL_LIST_VIEW:
1078 if (old_dtstart.is_date && old_dtend.is_date
1079 && memcmp (&ic_dur, &ic_oneday, sizeof (ic_dur)) == 0) {
1080 all_day_event = TRUE;
1081 new_dtstart = dtstart;
1082 } else {
1083 icaltimetype new_time = icaltime_from_timet_with_zone (dtstart, FALSE, default_zone);
1084
1085 new_time.hour = old_dtstart.hour;
1086 new_time.minute = old_dtstart.minute;
1087 new_time.second = old_dtstart.second;
1088
1089 new_dtstart = icaltime_as_timet_with_zone (new_time, old_dtstart.zone ? old_dtstart.zone : default_zone);
1090 }
1091 break;
1092 default:
1093 g_return_val_if_reached (FALSE);
1094 }
1095
1096 itime = icaltime_from_timet_with_zone (new_dtstart, FALSE, old_dtstart.zone ? old_dtstart.zone : default_zone);
1097 /* set the timezone properly */
1098 itime.zone = old_dtstart.zone ? old_dtstart.zone : default_zone;
1099 if (all_day_event)
1100 itime.is_date = TRUE;
1101 icalcomponent_set_dtstart (icalcomp, itime);
1102
1103 itime.is_date = FALSE;
1104 itime = icaltime_add (itime, ic_dur);
1105 if (all_day_event)
1106 itime.is_date = TRUE;
1107 icalcomponent_set_dtend (icalcomp, itime);
1108
1109 /* FIXME The new uid stuff can go away once we actually set it in the backend */
1110 uid = e_cal_component_gen_uid ();
1111 comp = e_cal_component_new ();
1112 e_cal_component_set_icalcomponent (
1113 comp, icalcomponent_new_clone (icalcomp));
1114 e_cal_component_set_uid (comp, uid);
1115 g_free (uid);
1116
1117 e_cal_component_commit_sequence (comp);
1118
1119 uid = NULL;
1120 if (e_cal_client_create_object_sync (client, e_cal_component_get_icalcomponent (comp), &uid, NULL, &error)) {
1121 gboolean strip_alarms = TRUE;
1122
1123 if (uid) {
1124 e_cal_component_set_uid (comp, uid);
1125 g_free (uid);
1126 }
1127
1128 if ((itip_organizer_is_user (registry, comp, client) ||
1129 itip_sentby_is_user (registry, comp, client)) &&
1130 send_component_dialog (
1131 (GtkWindow *) gtk_widget_get_toplevel (
1132 GTK_WIDGET (cal_view)),
1133 client, comp, TRUE, &strip_alarms, NULL)) {
1134 itip_send_comp (
1135 registry, E_CAL_COMPONENT_METHOD_REQUEST,
1136 comp, client, NULL, NULL, NULL, strip_alarms,
1137 FALSE);
1138 }
1139 } else {
1140 g_message (G_STRLOC ": Could not create the object! %s", error ? error->message : "");
1141 if (error)
1142 g_error_free (error);
1143 ret = FALSE;
1144 }
1145
1146 g_object_unref (comp);
1147 return ret;
1148 }
1149
1150 GnomeCalendar *
1151 e_calendar_view_get_calendar (ECalendarView *cal_view)
1152 {
1153 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL);
1154
1155 return cal_view->priv->calendar;
1156 }
1157
1158 void
1159 e_calendar_view_set_calendar (ECalendarView *cal_view,
1160 GnomeCalendar *calendar)
1161 {
1162 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1163
1164 cal_view->priv->calendar = calendar;
1165 }
1166
1167 ECalModel *
1168 e_calendar_view_get_model (ECalendarView *cal_view)
1169 {
1170 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL);
1171
1172 return cal_view->priv->model;
1173 }
1174
1175 icaltimezone *
1176 e_calendar_view_get_timezone (ECalendarView *cal_view)
1177 {
1178 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL);
1179 return e_cal_model_get_timezone (cal_view->priv->model);
1180 }
1181
1182 void
1183 e_calendar_view_set_timezone (ECalendarView *cal_view,
1184 icaltimezone *zone)
1185 {
1186 icaltimezone *old_zone;
1187
1188 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1189
1190 old_zone = e_cal_model_get_timezone (cal_view->priv->model);
1191 if (old_zone == zone)
1192 return;
1193
1194 e_cal_model_set_timezone (cal_view->priv->model, zone);
1195 g_signal_emit (
1196 cal_view, signals[TIMEZONE_CHANGED], 0,
1197 old_zone, zone);
1198 }
1199
1200 const gchar *
1201 e_calendar_view_get_default_category (ECalendarView *cal_view)
1202 {
1203 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL);
1204
1205 return cal_view->priv->default_category;
1206 }
1207
1208 /**
1209 * e_calendar_view_set_default_category
1210 * @cal_view: A calendar view.
1211 * @category: Default category name or NULL for no category.
1212 *
1213 * Sets the default category that will be used when creating new calendar
1214 * components from the given calendar view.
1215 */
1216 void
1217 e_calendar_view_set_default_category (ECalendarView *cal_view,
1218 const gchar *category)
1219 {
1220 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1221
1222 g_free (cal_view->priv->default_category);
1223 cal_view->priv->default_category = g_strdup (category);
1224 }
1225
1226 GtkTargetList *
1227 e_calendar_view_get_copy_target_list (ECalendarView *cal_view)
1228 {
1229 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL);
1230
1231 return cal_view->priv->copy_target_list;
1232 }
1233
1234 GtkTargetList *
1235 e_calendar_view_get_paste_target_list (ECalendarView *cal_view)
1236 {
1237 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL);
1238
1239 return cal_view->priv->paste_target_list;
1240 }
1241
1242 gint
1243 e_calendar_view_get_time_divisions (ECalendarView *cal_view)
1244 {
1245 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), 0);
1246
1247 return cal_view->priv->time_divisions;
1248 }
1249
1250 void
1251 e_calendar_view_set_time_divisions (ECalendarView *cal_view,
1252 gint time_divisions)
1253 {
1254 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1255
1256 if (cal_view->priv->time_divisions == time_divisions)
1257 return;
1258
1259 cal_view->priv->time_divisions = time_divisions;
1260
1261 g_object_notify (G_OBJECT (cal_view), "time-divisions");
1262 }
1263
1264 GList *
1265 e_calendar_view_get_selected_events (ECalendarView *cal_view)
1266 {
1267 ECalendarViewClass *class;
1268
1269 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL);
1270
1271 class = E_CALENDAR_VIEW_GET_CLASS (cal_view);
1272 g_return_val_if_fail (class->get_selected_events != NULL, NULL);
1273
1274 return class->get_selected_events (cal_view);
1275 }
1276
1277 gboolean
1278 e_calendar_view_get_selected_time_range (ECalendarView *cal_view,
1279 time_t *start_time,
1280 time_t *end_time)
1281 {
1282 ECalendarViewClass *class;
1283
1284 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), FALSE);
1285
1286 class = E_CALENDAR_VIEW_GET_CLASS (cal_view);
1287 g_return_val_if_fail (class->get_selected_time_range != NULL, FALSE);
1288
1289 return class->get_selected_time_range (cal_view, start_time, end_time);
1290 }
1291
1292 void
1293 e_calendar_view_set_selected_time_range (ECalendarView *cal_view,
1294 time_t start_time,
1295 time_t end_time)
1296 {
1297 ECalendarViewClass *class;
1298
1299 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1300
1301 /* Not all views implement this, so return silently. */
1302 class = E_CALENDAR_VIEW_GET_CLASS (cal_view);
1303 if (class->set_selected_time_range == NULL)
1304 return;
1305
1306 class->set_selected_time_range (cal_view, start_time, end_time);
1307 }
1308
1309 gboolean
1310 e_calendar_view_get_visible_time_range (ECalendarView *cal_view,
1311 time_t *start_time,
1312 time_t *end_time)
1313 {
1314 ECalendarViewClass *class;
1315
1316 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), FALSE);
1317
1318 class = E_CALENDAR_VIEW_GET_CLASS (cal_view);
1319 g_return_val_if_fail (class->get_visible_time_range != NULL, FALSE);
1320
1321 return class->get_visible_time_range (cal_view, start_time, end_time);
1322 }
1323
1324 void
1325 e_calendar_view_update_query (ECalendarView *cal_view)
1326 {
1327 ECalendarViewClass *class;
1328
1329 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1330
1331 class = E_CALENDAR_VIEW_GET_CLASS (cal_view);
1332 g_return_if_fail (class->update_query != NULL);
1333
1334 class->update_query (cal_view);
1335 }
1336
1337 void
1338 e_calendar_view_delete_selected_occurrence (ECalendarView *cal_view)
1339 {
1340 GList *selected;
1341 ECalModel *model;
1342 ECalComponent *comp;
1343 ECalendarViewEvent *event;
1344 ECalComponentVType vtype;
1345 ESourceRegistry *registry;
1346 gboolean delete = TRUE;
1347 GError *error = NULL;
1348
1349 model = e_calendar_view_get_model (cal_view);
1350 registry = e_cal_model_get_registry (model);
1351
1352 selected = e_calendar_view_get_selected_events (cal_view);
1353 if (!selected)
1354 return;
1355 event = (ECalendarViewEvent *) selected->data;
1356 if (!is_comp_data_valid (event))
1357 return;
1358
1359 comp = e_cal_component_new ();
1360 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
1361 vtype = e_cal_component_get_vtype (comp);
1362
1363 /*FIXME Retract should be moved to Groupwise features plugin */
1364 if (calendar_view_check_for_retract (comp, event->comp_data->client)) {
1365 gchar *retract_comment = NULL;
1366 gboolean retract = FALSE;
1367
1368 delete = prompt_retract_dialog (comp, &retract_comment, GTK_WIDGET (cal_view), &retract);
1369 if (retract) {
1370 GSList *users = NULL;
1371 icalcomponent *icalcomp = NULL, *mod_comp = NULL;
1372
1373 calendar_view_add_retract_data (
1374 comp, retract_comment, CALOBJ_MOD_THIS);
1375 icalcomp = e_cal_component_get_icalcomponent (comp);
1376 icalcomponent_set_method (icalcomp, ICAL_METHOD_CANCEL);
1377 if (!e_cal_client_send_objects_sync (event->comp_data->client, icalcomp, &users,
1378 &mod_comp, NULL, &error)) {
1379 delete_error_dialog (error, E_CAL_COMPONENT_EVENT);
1380 g_clear_error (&error);
1381 error = NULL;
1382 } else {
1383 if (mod_comp)
1384 icalcomponent_free (mod_comp);
1385 if (users) {
1386 g_slist_foreach (users, (GFunc) g_free, NULL);
1387 g_slist_free (users);
1388 }
1389 }
1390 }
1391 } else if (e_cal_model_get_confirm_delete (model))
1392 delete = delete_component_dialog (
1393 comp, FALSE, 1, vtype, GTK_WIDGET (cal_view));
1394
1395 if (delete) {
1396 const gchar *uid;
1397 gchar *rid = NULL;
1398 ECalComponentDateTime dt;
1399 icaltimezone *zone = NULL;
1400 gboolean is_instance = FALSE;
1401
1402 e_cal_component_get_uid (comp, &uid);
1403 e_cal_component_get_dtstart (comp, &dt);
1404 is_instance = e_cal_component_is_instance (comp);
1405
1406 if (dt.tzid) {
1407 GError *error = NULL;
1408
1409 e_cal_client_get_timezone_sync (event->comp_data->client, dt.tzid, &zone, NULL, &error);
1410 if (error) {
1411 zone = e_calendar_view_get_timezone (cal_view);
1412 g_clear_error (&error);
1413 }
1414 } else
1415 zone = e_calendar_view_get_timezone (cal_view);
1416
1417 if (is_instance)
1418 rid = e_cal_component_get_recurid_as_string (comp);
1419
1420 e_cal_component_free_datetime (&dt);
1421
1422 if ((itip_organizer_is_user (registry, comp, event->comp_data->client) ||
1423 itip_sentby_is_user (registry, comp, event->comp_data->client))
1424 && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)),
1425 event->comp_data->client,
1426 comp, TRUE) && !e_cal_client_check_save_schedules (event->comp_data->client)) {
1427 if (!e_cal_component_is_instance (comp)) {
1428 ECalComponentRange range;
1429
1430 /* set the recurrence ID of the object we send */
1431 range.type = E_CAL_COMPONENT_RANGE_SINGLE;
1432 e_cal_component_get_dtstart (comp, &range.datetime);
1433 range.datetime.value->is_date = 1;
1434 e_cal_component_set_recurid (comp, &range);
1435
1436 e_cal_component_free_datetime (&range.datetime);
1437 }
1438
1439 itip_send_comp (
1440 registry, E_CAL_COMPONENT_METHOD_CANCEL,
1441 comp, event->comp_data->client, NULL, NULL,
1442 NULL, TRUE, FALSE);
1443 }
1444
1445 if (is_instance)
1446 e_cal_client_remove_object_sync (event->comp_data->client, uid, rid, CALOBJ_MOD_THIS, NULL, &error);
1447 else {
1448 struct icaltimetype instance_rid;
1449
1450 instance_rid = icaltime_from_timet_with_zone (
1451 event->comp_data->instance_start,
1452 TRUE, zone ? zone : icaltimezone_get_utc_timezone ());
1453 e_cal_util_remove_instances (event->comp_data->icalcomp, instance_rid, CALOBJ_MOD_THIS);
1454 e_cal_client_modify_object_sync (event->comp_data->client, event->comp_data->icalcomp, CALOBJ_MOD_THIS, NULL, &error);
1455 }
1456
1457 delete_error_dialog (error, E_CAL_COMPONENT_EVENT);
1458 g_clear_error (&error);
1459 g_free (rid);
1460 }
1461
1462 /* free memory */
1463 g_list_free (selected);
1464 g_object_unref (comp);
1465 }
1466
1467 void
1468 e_calendar_view_open_event (ECalendarView *cal_view)
1469 {
1470 GList *selected;
1471
1472 selected = e_calendar_view_get_selected_events (cal_view);
1473 if (selected) {
1474 ECalendarViewEvent *event = (ECalendarViewEvent *) selected->data;
1475 if (event && is_comp_data_valid (event))
1476 e_calendar_view_edit_appointment (cal_view, event->comp_data->client, event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT);
1477
1478 g_list_free (selected);
1479 }
1480 }
1481
1482 /**
1483 * e_calendar_view_new_appointment_for
1484 * @cal_view: A calendar view.
1485 * @dtstart: A Unix time_t that marks the beginning of the appointment.
1486 * @dtend: A Unix time_t that marks the end of the appointment.
1487 * @all_day: If TRUE, the dtstart and dtend are expanded to cover
1488 * the entire day, and the event is set to TRANSPARENT.
1489 * @meeting: Whether the appointment is a meeting or not.
1490 *
1491 * Opens an event editor dialog for a new appointment.
1492 */
1493 void
1494 e_calendar_view_new_appointment_for (ECalendarView *cal_view,
1495 time_t dtstart,
1496 time_t dtend,
1497 gboolean all_day,
1498 gboolean meeting)
1499 {
1500 ECalendarViewPrivate *priv;
1501 struct icaltimetype itt;
1502 ECalComponentDateTime dt;
1503 ECalComponent *comp;
1504 icalcomponent *icalcomp;
1505 ECalComponentTransparency transparency;
1506 ECalClient *default_client = NULL;
1507 gpointer parent;
1508 guint32 flags = 0;
1509
1510 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1511
1512 parent = gtk_widget_get_toplevel (GTK_WIDGET (cal_view));
1513 parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
1514
1515 priv = cal_view->priv;
1516
1517 default_client = e_cal_model_get_default_client (priv->model);
1518
1519 if (!default_client || !e_client_is_opened (E_CLIENT (default_client))) {
1520 g_warning ("Default client not loaded \n");
1521 return;
1522 }
1523
1524 if (e_client_is_readonly (E_CLIENT (default_client))) {
1525 GtkWidget *widget;
1526 ESource *source;
1527
1528 source = e_client_get_source (E_CLIENT (default_client));
1529
1530 widget = e_alert_dialog_new_for_args (
1531 parent, "calendar:prompt-read-only-cal",
1532 e_source_get_display_name (source),
1533 NULL);
1534
1535 g_signal_connect (
1536 widget, "response",
1537 G_CALLBACK (gtk_widget_destroy), widget);
1538 gtk_widget_show (widget);
1539 return;
1540 }
1541
1542 dt.value = &itt;
1543 if (all_day)
1544 dt.tzid = NULL;
1545 else
1546 dt.tzid = icaltimezone_get_tzid (e_cal_model_get_timezone (cal_view->priv->model));
1547
1548 icalcomp = e_cal_model_create_component_with_defaults (priv->model, all_day);
1549 comp = e_cal_component_new ();
1550 e_cal_component_set_icalcomponent (comp, icalcomp);
1551
1552 /* DTSTART, DTEND */
1553 itt = icaltime_from_timet_with_zone (dtstart, FALSE, e_cal_model_get_timezone (cal_view->priv->model));
1554 if (all_day) {
1555 itt.hour = itt.minute = itt.second = 0;
1556 itt.is_date = TRUE;
1557 }
1558 e_cal_component_set_dtstart (comp, &dt);
1559
1560 itt = icaltime_from_timet_with_zone (dtend, FALSE, e_cal_model_get_timezone (cal_view->priv->model));
1561 if (all_day) {
1562 /* We round it up to the end of the day, unless it is
1563 * already set to midnight */
1564 if (itt.hour != 0 || itt.minute != 0 || itt.second != 0) {
1565 icaltime_adjust (&itt, 1, 0, 0, 0);
1566 }
1567 itt.hour = itt.minute = itt.second = 0;
1568 itt.is_date = TRUE;
1569 }
1570 e_cal_component_set_dtend (comp, &dt);
1571
1572 /* TRANSPARENCY */
1573 transparency = all_day ? E_CAL_COMPONENT_TRANSP_TRANSPARENT
1574 : E_CAL_COMPONENT_TRANSP_OPAQUE;
1575 e_cal_component_set_transparency (comp, transparency);
1576
1577 /* CATEGORY */
1578 e_cal_component_set_categories (comp, priv->default_category);
1579
1580 /* edit the object */
1581 e_cal_component_commit_sequence (comp);
1582
1583 flags |= COMP_EDITOR_NEW_ITEM;
1584 if (meeting) {
1585 flags |= COMP_EDITOR_MEETING;
1586 flags |= COMP_EDITOR_USER_ORG;
1587 }
1588
1589 e_calendar_view_open_event_with_flags (
1590 cal_view, default_client, icalcomp, flags);
1591
1592 g_object_unref (comp);
1593 }
1594
1595 /**
1596 * e_calendar_view_new_appointment_full
1597 * @cal_view: an #ECalendarView
1598 * @all_day: Whether create all day event or not.
1599 * @meeting: This is a meeting or an appointment.
1600 * @no_past_date: Don't create event in past date, use actual date instead
1601 * (if %TRUE).
1602 *
1603 * Opens an event editor dialog for a new appointment. The appointment's
1604 * start and end times are set to the currently selected time range in
1605 * the calendar view.
1606 *
1607 * When the selection is for all day and we don't need @all_day event,
1608 * then this do a rounding to the actual hour for actual day (today) and
1609 * to the 'day begins' from preferences in other selected day.
1610 */
1611 void
1612 e_calendar_view_new_appointment_full (ECalendarView *cal_view,
1613 gboolean all_day,
1614 gboolean meeting,
1615 gboolean no_past_date)
1616 {
1617 ECalModel *model;
1618 time_t dtstart, dtend, now;
1619 gboolean do_rounding = FALSE;
1620
1621 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1622
1623 model = e_calendar_view_get_model (cal_view);
1624
1625 now = time (NULL);
1626
1627 if (!e_calendar_view_get_selected_time_range (cal_view, &dtstart, &dtend)) {
1628 dtstart = now;
1629 dtend = dtstart + 3600;
1630 }
1631
1632 if (no_past_date && dtstart < now) {
1633 dtend = time_day_begin (now) + (dtend - dtstart);
1634 dtstart = time_day_begin (now);
1635 do_rounding = TRUE;
1636 }
1637
1638 /* We either need rounding or don't want to set all_day for this, we will rather use actual */
1639 /* time in this cases; dtstart should be a midnight in this case */
1640 if (do_rounding || (!all_day && (dtend - dtstart) == (60 * 60 * 24))) {
1641 struct tm local = *localtime (&now);
1642 gint time_div = e_calendar_view_get_time_divisions (cal_view);
1643 gint hours, mins;
1644
1645 if (!time_div) /* Possible if your settings values aren't so nice */
1646 time_div = 30;
1647
1648 if (time_day_begin (now) == time_day_begin (dtstart)) {
1649 /* same day as today */
1650 hours = local.tm_hour;
1651 mins = local.tm_min;
1652
1653 /* round minutes to nearest time division, up or down */
1654 if ((mins % time_div) >= time_div / 2)
1655 mins += time_div;
1656 mins = (mins - (mins % time_div));
1657 } else {
1658 /* other day than today */
1659 hours = e_cal_model_get_work_day_start_hour (model);
1660 mins = e_cal_model_get_work_day_start_minute (model);
1661 }
1662
1663 dtstart = dtstart + (60 * 60 * hours) + (mins * 60);
1664 dtend = dtstart + (time_div * 60);
1665 }
1666
1667 e_calendar_view_new_appointment_for (cal_view, dtstart, dtend, all_day, meeting);
1668 }
1669
1670 void
1671 e_calendar_view_new_appointment (ECalendarView *cal_view)
1672 {
1673 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1674
1675 e_calendar_view_new_appointment_full (cal_view, FALSE, FALSE, FALSE);
1676 }
1677
1678 /* Ensures the calendar is selected */
1679 static void
1680 object_created_cb (CompEditor *ce,
1681 ECalendarView *cal_view)
1682 {
1683 e_calendar_view_emit_user_created (cal_view, comp_editor_get_client (ce));
1684 }
1685
1686 CompEditor *
1687 e_calendar_view_open_event_with_flags (ECalendarView *cal_view,
1688 ECalClient *client,
1689 icalcomponent *icalcomp,
1690 guint32 flags)
1691 {
1692 CompEditor *ce;
1693 const gchar *uid;
1694 ECalComponent *comp;
1695 EShell *shell;
1696
1697 /* FIXME ECalendarView should own an EShell pointer. */
1698 shell = e_shell_get_default ();
1699
1700 uid = icalcomponent_get_uid (icalcomp);
1701
1702 ce = comp_editor_find_instance (uid);
1703 if (!ce) {
1704 ce = event_editor_new (client, shell, flags);
1705
1706 g_signal_connect (
1707 ce, "object_created",
1708 G_CALLBACK (object_created_cb), cal_view);
1709
1710 comp = e_cal_component_new ();
1711 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
1712 comp_editor_edit_comp (ce, comp);
1713 if (flags & COMP_EDITOR_MEETING)
1714 event_editor_show_meeting (EVENT_EDITOR (ce));
1715
1716 g_object_unref (comp);
1717 }
1718
1719 gtk_window_present (GTK_WINDOW (ce));
1720
1721 return ce;
1722 }
1723
1724 /**
1725 * e_calendar_view_edit_appointment
1726 * @cal_view: A calendar view.
1727 * @client: Calendar client.
1728 * @icalcomp: The object to be edited.
1729 * @mode: one of #EEditEventMode
1730 *
1731 * Opens an editor window to allow the user to edit the selected
1732 * object.
1733 */
1734 void
1735 e_calendar_view_edit_appointment (ECalendarView *cal_view,
1736 ECalClient *client,
1737 icalcomponent *icalcomp,
1738 EEditEventMode mode)
1739 {
1740 ECalModel *model;
1741 ESourceRegistry *registry;
1742 guint32 flags = 0;
1743
1744 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1745 g_return_if_fail (E_IS_CAL_CLIENT (client));
1746 g_return_if_fail (icalcomp != NULL);
1747
1748 model = e_calendar_view_get_model (cal_view);
1749 registry = e_cal_model_get_registry (model);
1750
1751 if ((mode == EDIT_EVENT_AUTODETECT && icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY) != NULL)
1752 || mode == EDIT_EVENT_FORCE_MEETING) {
1753 ECalComponent *comp = e_cal_component_new ();
1754 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
1755 flags |= COMP_EDITOR_MEETING;
1756 if (itip_organizer_is_user (registry, comp, client) ||
1757 itip_sentby_is_user (registry, comp, client) ||
1758 !e_cal_component_has_attendees (comp))
1759 flags |= COMP_EDITOR_USER_ORG;
1760 g_object_unref (comp);
1761 }
1762
1763 e_calendar_view_open_event_with_flags (cal_view, client, icalcomp, flags);
1764 }
1765
1766 void
1767 e_calendar_view_modify_and_send (ECalendarView *cal_view,
1768 ECalComponent *comp,
1769 ECalClient *client,
1770 CalObjModType mod,
1771 GtkWindow *toplevel,
1772 gboolean new)
1773 {
1774 ECalModel *model;
1775 ESourceRegistry *registry;
1776 gboolean only_new_attendees = FALSE;
1777 GError *error = NULL;
1778
1779 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1780
1781 model = e_calendar_view_get_model (cal_view);
1782 registry = e_cal_model_get_registry (model);
1783
1784 e_cal_component_commit_sequence (comp);
1785
1786 e_cal_client_modify_object_sync (
1787 client, e_cal_component_get_icalcomponent (comp),
1788 mod, NULL, &error);
1789
1790 if (error == NULL) {
1791 gboolean strip_alarms = TRUE;
1792
1793 if ((itip_organizer_is_user (registry, comp, client) ||
1794 itip_sentby_is_user (registry, comp, client)) &&
1795 send_component_dialog (toplevel, client, comp, new, &strip_alarms, &only_new_attendees)) {
1796 ECalComponent *send_comp = NULL;
1797
1798 if (mod == CALOBJ_MOD_ALL && e_cal_component_is_instance (comp)) {
1799 /* Ensure we send the master object, not the instance only */
1800 icalcomponent *icalcomp = NULL;
1801 const gchar *uid = NULL;
1802
1803 e_cal_component_get_uid (comp, &uid);
1804 if (e_cal_client_get_object_sync (client, uid, NULL, &icalcomp, NULL, NULL) && icalcomp) {
1805 send_comp = e_cal_component_new ();
1806 if (!e_cal_component_set_icalcomponent (send_comp, icalcomp)) {
1807 icalcomponent_free (icalcomp);
1808 g_object_unref (send_comp);
1809 send_comp = NULL;
1810 } else if (only_new_attendees) {
1811 /* copy new-attendees information too if required for later use */
1812 comp_editor_copy_new_attendees (send_comp, comp);
1813 }
1814 }
1815 }
1816
1817 itip_send_comp (
1818 registry, E_CAL_COMPONENT_METHOD_REQUEST,
1819 send_comp ? send_comp : comp, client, NULL,
1820 NULL, NULL, strip_alarms, only_new_attendees);
1821
1822 if (send_comp)
1823 g_object_unref (send_comp);
1824 }
1825 } else {
1826 g_message (
1827 G_STRLOC ": Could not update the object! %s",
1828 error->message);
1829
1830 g_error_free (error);
1831 }
1832 }
1833
1834 static gboolean
1835 tooltip_grab (GtkWidget *tooltip,
1836 GdkEventKey *event,
1837 ECalendarView *view)
1838 {
1839 GtkWidget *widget = (GtkWidget *) g_object_get_data (G_OBJECT (view), "tooltip-window");
1840
1841 if (!widget)
1842 return TRUE;
1843
1844 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
1845 gtk_widget_destroy (widget);
1846 g_object_set_data (G_OBJECT (view), "tooltip-window", NULL);
1847
1848 return FALSE;
1849 }
1850
1851 static gchar *
1852 get_label (struct icaltimetype *tt,
1853 icaltimezone *f_zone,
1854 icaltimezone *t_zone)
1855 {
1856 struct tm tmp_tm;
1857
1858 tmp_tm = icaltimetype_to_tm_with_zone (tt, f_zone, t_zone);
1859
1860 return e_datetime_format_format_tm ("calendar", "table", DTFormatKindDateTime, &tmp_tm);
1861 }
1862
1863 void
1864 e_calendar_view_move_tip (GtkWidget *widget,
1865 gint x,
1866 gint y)
1867 {
1868 GtkAllocation allocation;
1869 GtkRequisition requisition;
1870 gint w, h;
1871 GdkScreen *screen;
1872 GdkScreen *pointer_screen;
1873 gint monitor_num, px, py;
1874 GdkRectangle monitor;
1875
1876 screen = gtk_widget_get_screen (widget);
1877
1878 gtk_widget_get_preferred_size (widget, &requisition, NULL);
1879 w = requisition.width;
1880 h = requisition.height;
1881
1882 gdk_display_get_pointer (
1883 gdk_screen_get_display (screen),
1884 &pointer_screen, &px, &py, NULL);
1885 if (pointer_screen != screen) {
1886 px = x;
1887 py = y;
1888 }
1889 monitor_num = gdk_screen_get_monitor_at_point (screen, px, py);
1890 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1891
1892 if ((x + w) > monitor.x + monitor.width)
1893 x -= (x + w) - (monitor.x + monitor.width);
1894 else if (x < monitor.x)
1895 x = monitor.x;
1896
1897 gtk_widget_get_allocation (widget, &allocation);
1898
1899 if ((y + h + allocation.height + 4) > monitor.y + monitor.height)
1900 y = y - h - 36;
1901
1902 gtk_window_move (GTK_WINDOW (widget), x, y);
1903 gtk_widget_show (widget);
1904 }
1905
1906 /*
1907 * It is expected to show the tooltips in this below format
1908 *
1909 * <B>SUBJECT OF THE MEETING</B>
1910 * Organiser: NameOfTheUser<email@ofuser.com>
1911 * Location: PlaceOfTheMeeting
1912 * Time : DateAndTime (xx Minutes)
1913 * Status: Accepted: X Declined: Y ...
1914 */
1915
1916 gboolean
1917 e_calendar_view_get_tooltips (const ECalendarViewEventData *data)
1918 {
1919 GtkWidget *label, *box, *hbox, *ebox, *frame;
1920 const gchar *str;
1921 gchar *tmp, *tmp1, *tmp2;
1922 ECalComponentOrganizer organiser;
1923 ECalComponentDateTime dtstart, dtend;
1924 icalcomponent *clone_comp;
1925 time_t t_start, t_end;
1926 ECalendarViewEvent *pevent;
1927 GtkStyle *style = gtk_widget_get_default_style ();
1928 GtkWidget *widget;
1929 GdkWindow *window;
1930 ECalComponent *newcomp = e_cal_component_new ();
1931 icaltimezone *zone, *default_zone;
1932 ECalModel *model;
1933 ECalClient *client = NULL;
1934 gboolean free_text = FALSE;
1935
1936 /* This function is a timeout callback. */
1937
1938 g_return_val_if_fail (data != NULL, FALSE);
1939 g_return_val_if_fail (E_IS_CALENDAR_VIEW (data->cal_view), FALSE);
1940
1941 model = e_calendar_view_get_model (data->cal_view);
1942
1943 /* Delete any stray tooltip if left */
1944 widget = g_object_get_data (
1945 G_OBJECT (data->cal_view), "tooltip-window");
1946 if (GTK_IS_WIDGET (widget))
1947 gtk_widget_destroy (widget);
1948
1949 default_zone = e_calendar_view_get_timezone (data->cal_view);
1950 pevent = data->get_view_event (data->cal_view, data->day, data->event_num);
1951
1952 if (!is_comp_data_valid (pevent))
1953 return FALSE;
1954
1955 client = pevent->comp_data->client;
1956
1957 clone_comp = icalcomponent_new_clone (pevent->comp_data->icalcomp);
1958 if (!e_cal_component_set_icalcomponent (newcomp, clone_comp))
1959 g_warning ("couldn't update calendar component with modified data from backend\n");
1960
1961 box = gtk_vbox_new (FALSE, 0);
1962
1963 str = e_calendar_view_get_icalcomponent_summary (pevent->comp_data->client, pevent->comp_data->icalcomp, &free_text);
1964
1965 if (!(str && *str)) {
1966 g_object_unref (newcomp);
1967 gtk_widget_destroy (box);
1968
1969 return FALSE;
1970 }
1971
1972 tmp = g_markup_printf_escaped ("<b>%s</b>", str);
1973 label = gtk_label_new (NULL);
1974 gtk_label_set_line_wrap ((GtkLabel *) label, TRUE);
1975 gtk_label_set_markup ((GtkLabel *) label, tmp);
1976
1977 if (free_text) {
1978 g_free ((gchar *) str);
1979 str = NULL;
1980 }
1981
1982 hbox = gtk_hbox_new (FALSE, 0);
1983 gtk_box_pack_start ((GtkBox *) hbox, label, FALSE, FALSE, 0);
1984 ebox = gtk_event_box_new ();
1985 gtk_container_add ((GtkContainer *) ebox, hbox);
1986 gtk_widget_modify_bg (ebox, GTK_STATE_NORMAL, &(style->bg[GTK_STATE_SELECTED]));
1987 gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &(style->text[GTK_STATE_SELECTED]));
1988
1989 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0);
1990 g_free (tmp);
1991
1992 e_cal_component_get_organizer (newcomp, &organiser);
1993 if (organiser.cn) {
1994 gchar *ptr;
1995 ptr = strchr (organiser.value, ':');
1996
1997 if (ptr) {
1998 ptr++;
1999 /* To Translators: It will display "Organiser: NameOfTheUser <email@ofuser.com>" */
2000 tmp = g_strdup_printf (_("Organizer: %s <%s>"), organiser.cn, ptr);
2001 }
2002 else
2003 /* With SunOne accouts, there may be no ':' in organiser.value*/
2004 tmp = g_strdup_printf (_("Organizer: %s"), organiser.cn);
2005
2006 label = gtk_label_new (tmp);
2007 hbox = gtk_hbox_new (FALSE, 0);
2008 gtk_box_pack_start ((GtkBox *) hbox, label, FALSE, FALSE, 0);
2009 ebox = gtk_event_box_new ();
2010 gtk_container_add ((GtkContainer *) ebox, hbox);
2011 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0);
2012
2013 g_free (tmp);
2014 }
2015
2016 e_cal_component_get_location (newcomp, &str);
2017
2018 if (str) {
2019 /* To Translators: It will display "Location: PlaceOfTheMeeting" */
2020 tmp = g_markup_printf_escaped (_("Location: %s"), str);
2021 label = gtk_label_new (NULL);
2022 gtk_label_set_markup ((GtkLabel *) label, tmp);
2023 hbox = gtk_hbox_new (FALSE, 0);
2024 gtk_box_pack_start ((GtkBox *) hbox, label, FALSE, FALSE, 0);
2025 ebox = gtk_event_box_new ();
2026 gtk_container_add ((GtkContainer *) ebox, hbox);
2027 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0);
2028 g_free (tmp);
2029 }
2030 e_cal_component_get_dtstart (newcomp, &dtstart);
2031 e_cal_component_get_dtend (newcomp, &dtend);
2032
2033 if (dtstart.tzid) {
2034 zone = icalcomponent_get_timezone (e_cal_component_get_icalcomponent (newcomp), dtstart.tzid);
2035 if (!zone)
2036 e_cal_client_get_timezone_sync (client, dtstart.tzid, &zone, NULL, NULL);
2037
2038 if (!zone)
2039 zone = default_zone;
2040
2041 } else {
2042 zone = NULL;
2043 }
2044 t_start = icaltime_as_timet_with_zone (*dtstart.value, zone);
2045 t_end = icaltime_as_timet_with_zone (*dtend.value, zone);
2046
2047 tmp1 = get_label (dtstart.value, zone, default_zone);
2048 tmp = calculate_time (t_start, t_end);
2049
2050 /* To Translators: It will display "Time: ActualStartDateAndTime (DurationOfTheMeeting)"*/
2051 tmp2 = g_strdup_printf (_("Time: %s %s"), tmp1, tmp);
2052 if (zone && !cal_comp_util_compare_event_timezones (newcomp, client, default_zone)) {
2053 g_free (tmp);
2054 g_free (tmp1);
2055
2056 tmp1 = get_label (dtstart.value, zone, zone);
2057 tmp = g_strconcat (tmp2, "\n\t[ ", tmp1, " ", icaltimezone_get_display_name (zone), " ]", NULL);
2058 } else {
2059 g_free (tmp);
2060 tmp = tmp2;
2061 tmp2 = NULL;
2062 }
2063
2064 e_cal_component_free_datetime (&dtstart);
2065 e_cal_component_free_datetime (&dtend);
2066
2067 hbox = gtk_hbox_new (FALSE, 0);
2068 gtk_box_pack_start ((GtkBox *) hbox, gtk_label_new_with_mnemonic (tmp), FALSE, FALSE, 0);
2069 ebox = gtk_event_box_new ();
2070 gtk_container_add ((GtkContainer *) ebox, hbox);
2071 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0);
2072
2073 g_free (tmp);
2074 g_free (tmp2);
2075 g_free (tmp1);
2076
2077 tmp = e_cal_model_get_attendees_status_info (
2078 model, newcomp, pevent->comp_data->client);
2079 if (tmp) {
2080 hbox = gtk_hbox_new (FALSE, 0);
2081 gtk_box_pack_start ((GtkBox *) hbox, gtk_label_new (tmp), FALSE, FALSE, 0);
2082 ebox = gtk_event_box_new ();
2083 gtk_container_add ((GtkContainer *) ebox, hbox);
2084 gtk_box_pack_start ((GtkBox *) box, ebox, FALSE, FALSE, 0);
2085
2086 g_free (tmp);
2087 }
2088
2089 pevent->tooltip = gtk_window_new (GTK_WINDOW_POPUP);
2090 frame = gtk_frame_new (NULL);
2091 gtk_frame_set_shadow_type ((GtkFrame *) frame, GTK_SHADOW_IN);
2092
2093 gtk_window_set_type_hint (GTK_WINDOW (pevent->tooltip), GDK_WINDOW_TYPE_HINT_TOOLTIP);
2094 gtk_window_move ((GtkWindow *) pevent->tooltip, pevent->x +16, pevent->y + 16);
2095 gtk_container_add ((GtkContainer *) frame, box);
2096 gtk_container_add ((GtkContainer *) pevent->tooltip, frame);
2097
2098 gtk_widget_show_all (pevent->tooltip);
2099
2100 e_calendar_view_move_tip (pevent->tooltip, pevent->x +16, pevent->y + 16);
2101
2102 window = gtk_widget_get_window (pevent->tooltip);
2103 gdk_keyboard_grab (window, FALSE, GDK_CURRENT_TIME);
2104 g_signal_connect (
2105 pevent->tooltip, "key-press-event",
2106 G_CALLBACK (tooltip_grab), data->cal_view);
2107 pevent->timeout = -1;
2108
2109 g_object_set_data (G_OBJECT (data->cal_view), "tooltip-window", pevent->tooltip);
2110 g_object_unref (newcomp);
2111
2112 return FALSE;
2113 }
2114
2115 static gboolean
2116 icalcomp_contains_category (icalcomponent *icalcomp,
2117 const gchar *category)
2118 {
2119 icalproperty *property;
2120
2121 g_return_val_if_fail (icalcomp != NULL && category != NULL, FALSE);
2122
2123 for (property = icalcomponent_get_first_property (icalcomp, ICAL_CATEGORIES_PROPERTY);
2124 property != NULL;
2125 property = icalcomponent_get_next_property (icalcomp, ICAL_CATEGORIES_PROPERTY)) {
2126 gchar *value = icalproperty_get_value_as_string_r (property);
2127
2128 if (value && strcmp (category, value) == 0) {
2129 g_free (value);
2130 return TRUE;
2131 }
2132 g_free (value);
2133 }
2134
2135 return FALSE;
2136 }
2137
2138 /* e_calendar_view_get_icalcomponent_summary returns summary of calcomp,
2139 * and for type of birthday or anniversary it append number of years since
2140 * beginning. In this case, the free_text is set to TRUE and caller need
2141 * to g_free returned string, otherwise free_text is set to FALSE and
2142 * returned value is owned by calcomp.
2143 */
2144
2145 const gchar *
2146 e_calendar_view_get_icalcomponent_summary (ECalClient *client,
2147 icalcomponent *icalcomp,
2148 gboolean *free_text)
2149 {
2150 const gchar *summary;
2151
2152 g_return_val_if_fail (icalcomp != NULL && free_text != NULL, NULL);
2153
2154 *free_text = FALSE;
2155 summary = icalcomponent_get_summary (icalcomp);
2156
2157 if (icalcomp_contains_category (icalcomp, _("Birthday")) ||
2158 icalcomp_contains_category (icalcomp, _("Anniversary"))) {
2159 icalproperty *xprop;
2160
2161 for (xprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
2162 xprop;
2163 xprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
2164 const gchar *xname = icalproperty_get_x_name (xprop);
2165
2166 if (xname && g_ascii_strcasecmp (xname, "X-EVOLUTION-SINCE-YEAR") == 0) {
2167 struct icaltimetype dtnow;
2168 gint since_year;
2169 gchar *str;
2170
2171 str = icalproperty_get_value_as_string_r (xprop);
2172 since_year = str ? atoi (str) : 0;
2173 g_free (str);
2174
2175 dtnow = icalcomponent_get_dtstart (icalcomp);
2176
2177 if (since_year > 0 && dtnow.year - since_year > 0) {
2178 summary = g_strdup_printf ("%s (%d)", summary ? summary : "", dtnow.year - since_year);
2179 *free_text = summary != NULL;
2180 }
2181
2182 break;
2183 }
2184 }
2185 }
2186
2187 return summary;
2188 }
2189
2190 void
2191 e_calendar_view_emit_user_created (ECalendarView *cal_view,
2192 ECalClient *where_was_created)
2193 {
2194 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
2195
2196 g_signal_emit (cal_view, signals[USER_CREATED], 0, where_was_created);
2197 }
2198
2199 void
2200 draw_curved_rectangle (cairo_t *cr,
2201 gdouble x0,
2202 gdouble y0,
2203 gdouble rect_width,
2204 gdouble rect_height,
2205 gdouble radius)
2206 {
2207 gdouble x1, y1;
2208
2209 x1 = x0 + rect_width;
2210 y1 = y0 + rect_height;
2211
2212 if (!rect_width || !rect_height)
2213 return;
2214 if (rect_width / 2 < radius) {
2215 if (rect_height / 2 < radius) {
2216 cairo_move_to (cr, x0, (y0 + y1) / 2);
2217 cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0);
2218 cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
2219 cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
2220 cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
2221 } else {
2222 cairo_move_to (cr, x0, y0 + radius);
2223 cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0);
2224 cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
2225 cairo_line_to (cr, x1 , y1 - radius);
2226 cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
2227 cairo_curve_to (cr, x0, y1, x0, y1, x0, y1- radius);
2228 }
2229 } else {
2230 if (rect_height / 2 < radius) {
2231 cairo_move_to (cr, x0, (y0 + y1) / 2);
2232 cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0);
2233 cairo_line_to (cr, x1 - radius, y0);
2234 cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
2235 cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
2236 cairo_line_to (cr, x0 + radius, y1);
2237 cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
2238 } else {
2239 cairo_move_to (cr, x0, y0 + radius);
2240 cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0);
2241 cairo_line_to (cr, x1 - radius, y0);
2242 cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
2243 cairo_line_to (cr, x1 , y1 - radius);
2244 cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
2245 cairo_line_to (cr, x0 + radius, y1);
2246 cairo_curve_to (cr, x0, y1, x0, y1, x0, y1- radius);
2247 }
2248 }
2249 cairo_close_path (cr);
2250 }
2251
2252 /* returns either light or dark yellow, based on the base_background,
2253 * which is the default background color */
2254 GdkColor
2255 get_today_background (const GdkColor base_background)
2256 {
2257 GdkColor res = base_background;
2258
2259 if (res.red > 0x7FFF) {
2260 /* light yellow for a light theme */
2261 res.red = 0xFFFF;
2262 res.green = 0xFFFF;
2263 res.blue = 0xC0C0;
2264 } else {
2265 /* dark yellow for a dark theme */
2266 res.red = 0x3F3F;
2267 res.green = 0x3F3F;
2268 res.blue = 0x0000;
2269 }
2270
2271 return res;
2272 }
2273
2274 gboolean
2275 is_comp_data_valid_func (ECalendarViewEvent *event,
2276 const gchar *location)
2277 {
2278 g_return_val_if_fail (location != NULL, FALSE);
2279
2280 if (!event) {
2281 g_warning ("%s: event is NULL", location);
2282 return FALSE;
2283 }
2284
2285 if (!event->comp_data) {
2286 g_warning ("%s: event's (%p) comp_data is NULL", location, event);
2287 return FALSE;
2288 }
2289
2290 return TRUE;
2291 }
2292
2293 gboolean
2294 is_array_index_in_bounds_func (GArray *array,
2295 gint index,
2296 const gchar *location)
2297 {
2298 g_return_val_if_fail (location != NULL, FALSE);
2299
2300 if (!array) {
2301 g_warning ("%s: array is NULL", location);
2302 return FALSE;
2303 }
2304
2305 if (index < 0 || index >= array->len) {
2306 g_warning ("%s: index %d is out of bounds [0,%d) at array %p", location, index, array->len, array);
2307 return FALSE;
2308 }
2309
2310 return TRUE;
2311 }