2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 * Rodrigo Moya <rodrigo@ximian.com>
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 #include "evolution-config.h"
27 #include <glib/gi18n.h>
28 #include <glib/gstdio.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <libebackend/libebackend.h>
32 #include <shell/e-shell.h>
34 #include "comp-util.h"
35 #include "ea-cal-view.h"
36 #include "ea-calendar.h"
37 #include "e-cal-dialogs.h"
38 #include "e-cal-list-view.h"
39 #include "e-cal-model-calendar.h"
40 #include "e-cal-ops.h"
41 #include "e-calendar-view.h"
42 #include "e-day-view.h"
43 #include "e-month-view.h"
44 #include "itip-utils.h"
48 #define E_CALENDAR_VIEW_GET_PRIVATE(obj) \
49 (G_TYPE_INSTANCE_GET_PRIVATE \
50 ((obj), E_TYPE_CALENDAR_VIEW, ECalendarViewPrivate))
52 struct _ECalendarViewPrivate
{
53 /* The calendar model we are monitoring */
57 GSList
*selected_cut_list
;
59 GtkTargetList
*copy_target_list
;
60 GtkTargetList
*paste_target_list
;
62 /* All keyboard devices are grabbed
63 * while a tooltip window is shown. */
64 GQueue grabbed_keyboards
;
69 PROP_COPY_TARGET_LIST
,
71 PROP_PASTE_TARGET_LIST
,
76 /* FIXME Why are we emitting these event signals here? Can't the model just be listened to? */
81 SELECTED_TIME_CHANGED
,
90 static guint signals
[LAST_SIGNAL
];
92 static void calendar_view_selectable_init (ESelectableInterface
*iface
);
94 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
95 ECalendarView
, e_calendar_view
, GTK_TYPE_GRID
,
96 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE
, NULL
)
97 G_IMPLEMENT_INTERFACE (E_TYPE_SELECTABLE
, calendar_view_selectable_init
));
100 calendar_view_add_retract_data (ECalComponent
*comp
,
101 const gchar
*retract_comment
,
104 icalcomponent
*icalcomp
= NULL
;
105 icalproperty
*icalprop
= NULL
;
107 icalcomp
= e_cal_component_get_icalcomponent (comp
);
108 if (retract_comment
&& *retract_comment
)
109 icalprop
= icalproperty_new_x (retract_comment
);
111 icalprop
= icalproperty_new_x ("0");
112 icalproperty_set_x_name (icalprop
, "X-EVOLUTION-RETRACT-COMMENT");
113 icalcomponent_add_property (icalcomp
, icalprop
);
115 if (mod
== E_CAL_OBJ_MOD_ALL
)
116 icalprop
= icalproperty_new_x ("All");
118 icalprop
= icalproperty_new_x ("This");
119 icalproperty_set_x_name (icalprop
, "X-EVOLUTION-RECUR-MOD");
120 icalcomponent_add_property (icalcomp
, icalprop
);
124 calendar_view_check_for_retract (ECalComponent
*comp
,
127 ECalComponentOrganizer organizer
;
132 if (!e_cal_component_has_attendees (comp
))
135 if (!e_cal_client_check_save_schedules (client
))
138 e_cal_component_get_organizer (comp
, &organizer
);
139 strip
= itip_strip_mailto (organizer
.value
);
142 e_client_get_backend_property_sync (E_CLIENT (client
), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS
, &email
, NULL
, NULL
) &&
143 (g_ascii_strcasecmp (email
, strip
) == 0);
151 calendar_view_delete_event (ECalendarView
*cal_view
,
152 ECalendarViewEvent
*event
,
153 gboolean only_occurrence
)
157 ECalComponentVType vtype
;
158 ESourceRegistry
*registry
;
159 gboolean
delete = TRUE
;
161 if (!is_comp_data_valid (event
))
164 model
= e_calendar_view_get_model (cal_view
);
165 registry
= e_cal_model_get_registry (model
);
167 comp
= e_cal_component_new ();
168 e_cal_component_set_icalcomponent (comp
, icalcomponent_new_clone (event
->comp_data
->icalcomp
));
169 vtype
= e_cal_component_get_vtype (comp
);
171 /*FIXME remove it once the we dont set the recurrence id for all the generated instances */
172 if (!only_occurrence
&& !e_cal_client_check_recurrences_no_master (event
->comp_data
->client
))
173 e_cal_component_set_recurid (comp
, NULL
);
175 /*FIXME Retract should be moved to Groupwise features plugin */
176 if (calendar_view_check_for_retract (comp
, event
->comp_data
->client
)) {
177 gchar
*retract_comment
= NULL
;
178 gboolean retract
= FALSE
;
180 delete = e_cal_dialogs_prompt_retract (GTK_WIDGET (cal_view
), comp
, &retract_comment
, &retract
);
182 icalcomponent
*icalcomp
;
184 calendar_view_add_retract_data (comp
, retract_comment
, E_CAL_OBJ_MOD_ALL
);
185 icalcomp
= e_cal_component_get_icalcomponent (comp
);
186 icalcomponent_set_method (icalcomp
, ICAL_METHOD_CANCEL
);
188 e_cal_ops_send_component (model
, event
->comp_data
->client
, icalcomp
);
190 } else if (e_cal_model_get_confirm_delete (model
))
191 delete = e_cal_dialogs_delete_component (
192 comp
, FALSE
, 1, vtype
, GTK_WIDGET (cal_view
));
198 rid
= e_cal_component_get_recurid_as_string (comp
);
200 if (itip_has_any_attendees (comp
) &&
201 (itip_organizer_is_user (registry
, comp
, event
->comp_data
->client
) ||
202 itip_sentby_is_user (registry
, comp
, event
->comp_data
->client
))
203 && e_cal_dialogs_cancel_component ((GtkWindow
*) gtk_widget_get_toplevel (GTK_WIDGET (cal_view
)),
204 event
->comp_data
->client
,
206 if (only_occurrence
&& !e_cal_component_is_instance (comp
)) {
207 ECalComponentRange range
;
209 /* set the recurrence ID of the object we send */
210 range
.type
= E_CAL_COMPONENT_RANGE_SINGLE
;
211 e_cal_component_get_dtstart (comp
, &range
.datetime
);
212 range
.datetime
.value
->is_date
= 1;
213 e_cal_component_set_recurid (comp
, &range
);
215 e_cal_component_free_datetime (&range
.datetime
);
218 itip_send_component_with_model (model
, E_CAL_COMPONENT_METHOD_CANCEL
,
219 comp
, event
->comp_data
->client
, NULL
, NULL
,
220 NULL
, TRUE
, FALSE
, FALSE
);
223 e_cal_component_get_uid (comp
, &uid
);
225 g_object_unref (comp
);
230 if (only_occurrence
) {
231 if (e_cal_component_is_instance (comp
)) {
232 e_cal_ops_remove_component (model
, event
->comp_data
->client
, uid
, rid
, E_CAL_OBJ_MOD_THIS
, FALSE
);
234 struct icaltimetype instance_rid
;
235 ECalComponentDateTime dt
;
236 icaltimezone
*zone
= NULL
;
238 e_cal_component_get_dtstart (comp
, &dt
);
241 GError
*local_error
= NULL
;
243 e_cal_client_get_timezone_sync (event
->comp_data
->client
, dt
.tzid
, &zone
, NULL
, &local_error
);
244 if (local_error
!= NULL
) {
245 zone
= e_calendar_view_get_timezone (cal_view
);
246 g_clear_error (&local_error
);
249 zone
= e_calendar_view_get_timezone (cal_view
);
252 e_cal_component_free_datetime (&dt
);
254 instance_rid
= icaltime_from_timet_with_zone (
255 event
->comp_data
->instance_start
,
256 TRUE
, zone
? zone
: icaltimezone_get_utc_timezone ());
257 e_cal_util_remove_instances (event
->comp_data
->icalcomp
, instance_rid
, E_CAL_OBJ_MOD_THIS
);
258 e_cal_ops_modify_component (model
, event
->comp_data
->client
, event
->comp_data
->icalcomp
,
259 E_CAL_OBJ_MOD_THIS
, E_CAL_OPS_SEND_FLAG_DONT_SEND
);
261 } else if (e_cal_util_component_is_instance (event
->comp_data
->icalcomp
) ||
262 e_cal_util_component_has_recurrences (event
->comp_data
->icalcomp
))
263 e_cal_ops_remove_component (model
, event
->comp_data
->client
, uid
, rid
, E_CAL_OBJ_MOD_ALL
, FALSE
);
265 e_cal_ops_remove_component (model
, event
->comp_data
->client
, uid
, NULL
, E_CAL_OBJ_MOD_THIS
, FALSE
);
270 g_object_unref (comp
);
274 calendar_view_set_model (ECalendarView
*calendar_view
,
277 g_return_if_fail (calendar_view
->priv
->model
== NULL
);
278 g_return_if_fail (E_IS_CAL_MODEL (model
));
280 calendar_view
->priv
->model
= g_object_ref (model
);
284 calendar_view_set_property (GObject
*object
,
289 switch (property_id
) {
291 calendar_view_set_model (
292 E_CALENDAR_VIEW (object
),
293 g_value_get_object (value
));
296 case PROP_TIME_DIVISIONS
:
297 e_calendar_view_set_time_divisions (
298 E_CALENDAR_VIEW (object
),
299 g_value_get_int (value
));
303 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
307 calendar_view_get_property (GObject
*object
,
312 switch (property_id
) {
313 case PROP_COPY_TARGET_LIST
:
315 value
, e_calendar_view_get_copy_target_list (
316 E_CALENDAR_VIEW (object
)));
321 value
, e_calendar_view_get_model (
322 E_CALENDAR_VIEW (object
)));
325 case PROP_PASTE_TARGET_LIST
:
327 value
, e_calendar_view_get_paste_target_list (
328 E_CALENDAR_VIEW (object
)));
331 case PROP_TIME_DIVISIONS
:
333 value
, e_calendar_view_get_time_divisions (
334 E_CALENDAR_VIEW (object
)));
337 case PROP_IS_EDITING
:
338 g_value_set_boolean (value
, e_calendar_view_is_editing (E_CALENDAR_VIEW (object
)));
342 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
346 calendar_view_dispose (GObject
*object
)
348 ECalendarViewPrivate
*priv
;
350 priv
= E_CALENDAR_VIEW_GET_PRIVATE (object
);
352 if (priv
->model
!= NULL
) {
353 g_signal_handlers_disconnect_matched (
354 priv
->model
, G_SIGNAL_MATCH_DATA
,
355 0, 0, NULL
, NULL
, object
);
356 g_object_unref (priv
->model
);
360 if (priv
->copy_target_list
!= NULL
) {
361 gtk_target_list_unref (priv
->copy_target_list
);
362 priv
->copy_target_list
= NULL
;
365 if (priv
->paste_target_list
!= NULL
) {
366 gtk_target_list_unref (priv
->paste_target_list
);
367 priv
->paste_target_list
= NULL
;
370 if (priv
->selected_cut_list
) {
371 g_slist_foreach (priv
->selected_cut_list
, (GFunc
) g_object_unref
, NULL
);
372 g_slist_free (priv
->selected_cut_list
);
373 priv
->selected_cut_list
= NULL
;
376 while (!g_queue_is_empty (&priv
->grabbed_keyboards
)) {
378 keyboard
= g_queue_pop_head (&priv
->grabbed_keyboards
);
379 gdk_device_ungrab (keyboard
, GDK_CURRENT_TIME
);
380 g_object_unref (keyboard
);
383 /* Chain up to parent's dispose() method. */
384 G_OBJECT_CLASS (e_calendar_view_parent_class
)->dispose (object
);
388 calendar_view_constructed (GObject
*object
)
390 /* Do this after calendar_view_init() so extensions can query
391 * the GType accurately. See GInstanceInitFunc documentation
392 * for details of the problem. */
393 e_extensible_load_extensions (E_EXTENSIBLE (object
));
395 /* Chain up to parent's constructed() method. */
396 G_OBJECT_CLASS (e_calendar_view_parent_class
)->constructed (object
);
400 calendar_view_update_actions (ESelectable
*selectable
,
401 EFocusTracker
*focus_tracker
,
402 GdkAtom
*clipboard_targets
,
403 gint n_clipboard_targets
)
407 GtkTargetList
*target_list
;
409 gboolean can_paste
= FALSE
;
410 gboolean sources_are_editable
= TRUE
;
411 gboolean recurring
= FALSE
;
414 const gchar
*tooltip
;
418 view
= E_CALENDAR_VIEW (selectable
);
419 is_editing
= e_calendar_view_is_editing (view
);
421 list
= e_calendar_view_get_selected_events (view
);
422 n_selected
= g_list_length (list
);
424 for (iter
= list
; iter
!= NULL
; iter
= iter
->next
) {
425 ECalendarViewEvent
*event
= iter
->data
;
427 icalcomponent
*icalcomp
;
429 if (event
== NULL
|| event
->comp_data
== NULL
)
432 client
= event
->comp_data
->client
;
433 icalcomp
= event
->comp_data
->icalcomp
;
435 sources_are_editable
= sources_are_editable
&& !e_client_is_readonly (E_CLIENT (client
));
438 e_cal_util_component_is_instance (icalcomp
) ||
439 e_cal_util_component_has_recurrences (icalcomp
);
444 target_list
= e_selectable_get_paste_target_list (selectable
);
445 for (ii
= 0; ii
< n_clipboard_targets
&& !can_paste
; ii
++)
446 can_paste
= gtk_target_list_find (
447 target_list
, clipboard_targets
[ii
], NULL
);
449 action
= e_focus_tracker_get_cut_clipboard_action (focus_tracker
);
450 sensitive
= (n_selected
> 0) && sources_are_editable
&& !is_editing
;
451 tooltip
= _("Cut selected events to the clipboard");
452 gtk_action_set_sensitive (action
, sensitive
);
453 gtk_action_set_tooltip (action
, tooltip
);
455 action
= e_focus_tracker_get_copy_clipboard_action (focus_tracker
);
456 sensitive
= (n_selected
> 0) && !is_editing
;
457 tooltip
= _("Copy selected events to the clipboard");
458 gtk_action_set_sensitive (action
, sensitive
);
459 gtk_action_set_tooltip (action
, tooltip
);
461 action
= e_focus_tracker_get_paste_clipboard_action (focus_tracker
);
462 sensitive
= sources_are_editable
&& can_paste
&& !is_editing
;
463 tooltip
= _("Paste events from the clipboard");
464 gtk_action_set_sensitive (action
, sensitive
);
465 gtk_action_set_tooltip (action
, tooltip
);
467 action
= e_focus_tracker_get_delete_selection_action (focus_tracker
);
468 sensitive
= (n_selected
> 0) && sources_are_editable
&& !recurring
&& !is_editing
;
469 tooltip
= _("Delete selected events");
470 gtk_action_set_sensitive (action
, sensitive
);
471 gtk_action_set_tooltip (action
, tooltip
);
475 calendar_view_cut_clipboard (ESelectable
*selectable
)
477 ECalendarView
*cal_view
;
478 ECalendarViewPrivate
*priv
;
481 cal_view
= E_CALENDAR_VIEW (selectable
);
482 priv
= cal_view
->priv
;
484 selected
= e_calendar_view_get_selected_events (cal_view
);
488 e_selectable_copy_clipboard (selectable
);
490 for (l
= selected
; l
!= NULL
; l
= g_list_next (l
)) {
491 ECalendarViewEvent
*event
= (ECalendarViewEvent
*) l
->data
;
493 priv
->selected_cut_list
= g_slist_prepend (priv
->selected_cut_list
, g_object_ref (event
->comp_data
));
496 g_list_free (selected
);
500 add_related_timezones (icalcomponent
*des_icalcomp
,
501 icalcomponent
*src_icalcomp
,
504 icalproperty_kind look_in
[] = {
505 ICAL_DTSTART_PROPERTY
,
511 g_return_if_fail (des_icalcomp
!= NULL
);
512 g_return_if_fail (src_icalcomp
!= NULL
);
513 g_return_if_fail (client
!= NULL
);
515 for (i
= 0; look_in
[i
] != ICAL_NO_PROPERTY
; i
++) {
516 icalproperty
*prop
= icalcomponent_get_first_property (src_icalcomp
, look_in
[i
]);
519 icalparameter
*par
= icalproperty_get_first_parameter (prop
, ICAL_TZID_PARAMETER
);
522 const gchar
*tzid
= icalparameter_get_tzid (par
);
525 GError
*error
= NULL
;
526 icaltimezone
*zone
= NULL
;
528 e_cal_client_get_timezone_sync (
529 client
, tzid
, &zone
, NULL
, &error
);
532 "%s: Cannot get timezone for '%s'. %s",
533 G_STRFUNC
, tzid
, error
->message
);
534 g_error_free (error
);
536 icalcomponent_get_timezone (des_icalcomp
, icaltimezone_get_tzid (zone
)) == NULL
) {
537 /* do not duplicate timezones in the component */
538 icalcomponent
*vtz_comp
;
540 vtz_comp
= icaltimezone_get_component (zone
);
542 icalcomponent_add_component (des_icalcomp
, icalcomponent_new_clone (vtz_comp
));
551 calendar_view_copy_clipboard (ESelectable
*selectable
)
553 ECalendarView
*cal_view
;
554 ECalendarViewPrivate
*priv
;
557 icalcomponent
*vcal_comp
;
558 icalcomponent
*new_icalcomp
;
559 ECalendarViewEvent
*event
;
560 GtkClipboard
*clipboard
;
562 cal_view
= E_CALENDAR_VIEW (selectable
);
563 priv
= cal_view
->priv
;
565 selected
= e_calendar_view_get_selected_events (cal_view
);
569 if (priv
->selected_cut_list
) {
570 g_slist_foreach (priv
->selected_cut_list
, (GFunc
) g_object_unref
, NULL
);
571 g_slist_free (priv
->selected_cut_list
);
572 priv
->selected_cut_list
= NULL
;
575 /* create top-level VCALENDAR component and add VTIMEZONE's */
576 vcal_comp
= e_cal_util_new_top_level ();
577 for (l
= selected
; l
!= NULL
; l
= l
->next
) {
578 event
= (ECalendarViewEvent
*) l
->data
;
580 if (event
&& is_comp_data_valid (event
)) {
581 e_cal_util_add_timezones_from_component (vcal_comp
, event
->comp_data
->icalcomp
);
583 add_related_timezones (vcal_comp
, event
->comp_data
->icalcomp
, event
->comp_data
->client
);
587 for (l
= selected
; l
!= NULL
; l
= l
->next
) {
588 event
= (ECalendarViewEvent
*) l
->data
;
590 if (!is_comp_data_valid (event
))
593 new_icalcomp
= icalcomponent_new_clone (event
->comp_data
->icalcomp
);
595 /* do not remove RECURRENCE-IDs from copied objects */
596 icalcomponent_add_component (vcal_comp
, new_icalcomp
);
599 comp_str
= icalcomponent_as_ical_string_r (vcal_comp
);
601 /* copy the VCALENDAR to the clipboard */
602 clipboard
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
);
603 e_clipboard_set_calendar (clipboard
, comp_str
, -1);
604 gtk_clipboard_store (clipboard
);
607 icalcomponent_free (vcal_comp
);
609 g_list_free (selected
);
613 calendar_view_component_created_cb (ECalModel
*model
,
615 icalcomponent
*original_icalcomp
,
616 const gchar
*new_uid
,
619 gboolean strip_alarms
= TRUE
;
621 ESourceRegistry
*registry
;
622 GtkWidget
*toplevel
= user_data
;
624 comp
= e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (original_icalcomp
));
625 g_return_if_fail (comp
!= NULL
);
627 registry
= e_cal_model_get_registry (model
);
630 e_cal_component_set_uid (comp
, new_uid
);
632 if (itip_has_any_attendees (comp
) &&
633 (itip_organizer_is_user (registry
, comp
, client
) ||
634 itip_sentby_is_user (registry
, comp
, client
)) &&
635 e_cal_dialogs_send_component ((GtkWindow
*) toplevel
, client
, comp
, TRUE
, &strip_alarms
, NULL
)) {
636 itip_send_component_with_model (model
, E_CAL_COMPONENT_METHOD_REQUEST
,
637 comp
, client
, NULL
, NULL
, NULL
, strip_alarms
, FALSE
, FALSE
);
640 g_object_unref (comp
);
644 e_calendar_view_add_event_sync (ECalModel
*model
,
647 icaltimezone
*default_zone
,
648 icalcomponent
*icalcomp
,
650 gboolean is_day_view
,
652 GtkWidget
*top_level
)
655 struct icaltimetype itime
, old_dtstart
, old_dtend
;
656 time_t tt_start
, tt_end
, new_dtstart
= 0;
657 struct icaldurationtype ic_dur
, ic_oneday
;
659 gint start_offset
, end_offset
;
660 gboolean all_day_event
= FALSE
;
665 old_dtstart
= icalcomponent_get_dtstart (icalcomp
);
666 tt_start
= icaltime_as_timet (old_dtstart
);
667 old_dtend
= icalcomponent_get_dtend (icalcomp
);
668 tt_end
= icaltime_as_timet (old_dtend
);
669 ic_dur
= icaldurationtype_from_int (tt_end
- tt_start
);
671 if (icaldurationtype_as_int (ic_dur
) > 60 *60 *24) {
672 /* This is a long event */
673 start_offset
= old_dtstart
.hour
* 60 + old_dtstart
.minute
;
674 end_offset
= old_dtstart
.hour
* 60 + old_dtend
.minute
;
677 ic_oneday
= icaldurationtype_null_duration ();
681 if (start_offset
== 0 && end_offset
== 0 && all_day
)
682 all_day_event
= TRUE
;
686 } else if (icaldurationtype_as_int (ic_dur
) >= 60 *60 *24 && !all_day
) {
687 /* copy & paste from top canvas to main canvas */
688 ic_dur
= icaldurationtype_from_int (time_division
* 60);
692 new_dtstart
= dtstart
+ start_offset
* 60;
694 new_dtstart
= dtstart
;
696 if (old_dtstart
.is_date
&& old_dtend
.is_date
697 && memcmp (&ic_dur
, &ic_oneday
, sizeof (ic_dur
)) == 0) {
698 all_day_event
= TRUE
;
699 new_dtstart
= dtstart
;
701 icaltimetype new_time
= icaltime_from_timet_with_zone (dtstart
, FALSE
, default_zone
);
703 new_time
.hour
= old_dtstart
.hour
;
704 new_time
.minute
= old_dtstart
.minute
;
705 new_time
.second
= old_dtstart
.second
;
707 new_dtstart
= icaltime_as_timet_with_zone (new_time
, old_dtstart
.zone
? old_dtstart
.zone
: default_zone
);
711 itime
= icaltime_from_timet_with_zone (new_dtstart
, FALSE
, old_dtstart
.zone
? old_dtstart
.zone
: default_zone
);
712 /* set the timezone properly */
713 itime
.zone
= old_dtstart
.zone
? old_dtstart
.zone
: default_zone
;
715 itime
.is_date
= TRUE
;
716 icalcomponent_set_dtstart (icalcomp
, itime
);
718 itime
.is_date
= FALSE
;
719 itime
= icaltime_add (itime
, ic_dur
);
721 itime
.is_date
= TRUE
;
722 icalcomponent_set_dtend (icalcomp
, itime
);
724 /* The new uid stuff can go away once we actually set it in the backend */
725 uid
= e_util_generate_uid ();
726 comp
= e_cal_component_new ();
727 e_cal_component_set_icalcomponent (
728 comp
, icalcomponent_new_clone (icalcomp
));
729 e_cal_component_set_uid (comp
, uid
);
732 e_cal_component_commit_sequence (comp
);
734 e_cal_ops_create_component (model
, client
, e_cal_component_get_icalcomponent (comp
),
735 calendar_view_component_created_cb
, g_object_ref (top_level
), g_object_unref
);
737 g_object_unref (comp
);
741 ECalendarView
*cal_view
;
742 GSList
*selected_cut_list
; /* ECalModelComponent * */
743 GSList
*copied_uids
; /* gchar * */
745 time_t selection_start
;
746 time_t selection_end
;
747 gboolean is_day_view
;
749 GtkWidget
*top_level
;
752 } PasteClipboardData
;
755 paste_clipboard_data_free (gpointer ptr
)
757 PasteClipboardData
*pcd
= ptr
;
760 if (pcd
->success
&& pcd
->copied_uids
&& pcd
->selected_cut_list
) {
762 ESourceRegistry
*registry
;
765 model
= e_calendar_view_get_model (pcd
->cal_view
);
766 registry
= e_cal_model_get_registry (model
);
768 for (link
= pcd
->selected_cut_list
; link
!= NULL
; link
= g_slist_next (link
)) {
769 ECalModelComponent
*comp_data
= (ECalModelComponent
*) link
->data
;
772 GSList
*found
= NULL
;
774 /* Remove them one by one after ensuring it has been copied to the destination successfully */
775 found
= g_slist_find_custom (pcd
->copied_uids
, icalcomponent_get_uid (comp_data
->icalcomp
), (GCompareFunc
) strcmp
);
779 g_free (found
->data
);
780 pcd
->copied_uids
= g_slist_delete_link (pcd
->copied_uids
, found
);
782 comp
= e_cal_component_new ();
783 e_cal_component_set_icalcomponent (comp
, icalcomponent_new_clone (comp_data
->icalcomp
));
785 if (itip_has_any_attendees (comp
) &&
786 (itip_organizer_is_user (registry
, comp
, comp_data
->client
) ||
787 itip_sentby_is_user (registry
, comp
, comp_data
->client
))
788 && e_cal_dialogs_cancel_component ((GtkWindow
*) pcd
->top_level
, comp_data
->client
, comp
, TRUE
))
789 itip_send_component_with_model (model
, E_CAL_COMPONENT_METHOD_CANCEL
,
790 comp
, comp_data
->client
, NULL
, NULL
, NULL
, TRUE
, FALSE
, TRUE
);
792 e_cal_component_get_uid (comp
, &uid
);
793 if (e_cal_component_is_instance (comp
)) {
796 /* when cutting detached instances, only cut that instance */
797 rid
= e_cal_component_get_recurid_as_string (comp
);
798 e_cal_ops_remove_component (model
, comp_data
->client
, uid
, rid
, E_CAL_OBJ_MOD_THIS
, TRUE
);
801 e_cal_ops_remove_component (model
, comp_data
->client
, uid
, NULL
, E_CAL_OBJ_MOD_ALL
, FALSE
);
804 g_object_unref (comp
);
808 if (pcd
->success
&& pcd
->client
) {
811 model
= e_calendar_view_get_model (pcd
->cal_view
);
812 e_cal_model_emit_object_created (model
, pcd
->client
);
815 g_clear_object (&pcd
->cal_view
);
816 g_clear_object (&pcd
->top_level
);
817 g_clear_object (&pcd
->client
);
818 g_slist_free_full (pcd
->selected_cut_list
, g_object_unref
);
819 g_slist_free_full (pcd
->copied_uids
, g_free
);
820 g_free (pcd
->ical_str
);
826 cal_view_paste_clipboard_thread (EAlertSinkThreadJobData
*job_data
,
828 GCancellable
*cancellable
,
831 PasteClipboardData
*pcd
= user_data
;
832 icalcomponent
*icalcomp
;
833 icalcomponent_kind kind
;
834 icaltimezone
*default_zone
;
836 ESourceRegistry
*registry
;
837 ESource
*source
= NULL
, *default_source
= NULL
;
838 EClientCache
*client_cache
;
840 ECalClient
*client
= NULL
;
841 const gchar
*message
;
842 const gchar
*extension_name
;
844 guint copied_components
= 1;
846 GError
*local_error
= NULL
;
848 g_return_if_fail (pcd
!= NULL
);
850 icalcomp
= icalparser_parse_string (pcd
->ical_str
);
852 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_INVALID_DATA
,
853 _("Pasted text doesn’t contain valid iCalendar data"));
857 model
= e_calendar_view_get_model (pcd
->cal_view
);
858 registry
= e_cal_model_get_registry (model
);
860 switch (e_cal_model_get_component_kind (model
)) {
861 case ICAL_VEVENT_COMPONENT
:
862 default_source
= e_source_registry_ref_default_calendar (registry
);
863 extension_name
= E_SOURCE_EXTENSION_CALENDAR
;
864 message
= _("Default calendar not found");
866 case ICAL_VJOURNAL_COMPONENT
:
867 default_source
= e_source_registry_ref_default_memo_list (registry
);
868 extension_name
= E_SOURCE_EXTENSION_MEMO_LIST
;
869 message
= _("Default memo list not found");
871 case ICAL_VTODO_COMPONENT
:
872 default_source
= e_source_registry_ref_default_task_list (registry
);
873 extension_name
= E_SOURCE_EXTENSION_TASK_LIST
;
874 message
= _("Default task list not found");
877 g_warn_if_reached ();
881 source
= e_source_registry_ref_source (registry
, e_cal_model_get_default_source_uid (model
));
883 source
= default_source
;
884 default_source
= NULL
;
888 const gchar
*default_source_uid
= e_cal_model_get_default_source_uid (model
);
890 e_alert_sink_thread_job_set_alert_arg_0 (job_data
, default_source_uid
? default_source_uid
: "");
891 g_set_error_literal (&local_error
, G_IO_ERROR
, G_IO_ERROR_NOT_FOUND
, message
);
896 display_name
= e_util_get_source_full_name (registry
, source
);
897 e_alert_sink_thread_job_set_alert_arg_0 (job_data
, display_name
);
898 g_free (display_name
);
899 client_cache
= e_cal_model_get_client_cache (model
);
901 e_client
= e_client_cache_get_client_sync (client_cache
, source
, extension_name
, 30, cancellable
, &local_error
);
903 e_util_propagate_open_source_job_error (job_data
, extension_name
, local_error
, error
);
907 client
= E_CAL_CLIENT (e_client
);
908 kind
= icalcomponent_isa (icalcomp
);
909 default_zone
= e_cal_model_get_timezone (model
);
910 all_day
= pcd
->selection_end
- pcd
->selection_start
== 60 * 60 * 24;
911 copied_components
= 0;
913 if (kind
== ICAL_VCALENDAR_COMPONENT
) {
914 icalcomponent
*subcomp
;
916 /* add timezones first, to have them ready */
917 for (subcomp
= icalcomponent_get_first_component (icalcomp
, ICAL_VTIMEZONE_COMPONENT
);
919 subcomp
= icalcomponent_get_next_component (icalcomp
, ICAL_VTIMEZONE_COMPONENT
)) {
922 zone
= icaltimezone_new ();
923 icaltimezone_set_component (zone
, subcomp
);
925 if (!e_cal_client_add_timezone_sync (client
, zone
, cancellable
, error
)) {
926 icaltimezone_free (zone
, 1);
930 icaltimezone_free (zone
, 1);
933 for (subcomp
= icalcomponent_get_first_component (icalcomp
, ICAL_VEVENT_COMPONENT
);
935 subcomp
= icalcomponent_get_next_component (icalcomp
, ICAL_VEVENT_COMPONENT
)) {
936 if (e_cal_util_component_has_recurrences (subcomp
)) {
937 icalproperty
*icalprop
= icalcomponent_get_first_property (subcomp
, ICAL_RRULE_PROPERTY
);
939 icalproperty_remove_parameter_by_name (icalprop
, "X-EVOLUTION-ENDDATE");
942 e_calendar_view_add_event_sync (model
, client
, pcd
->selection_start
, default_zone
, subcomp
, all_day
,
943 pcd
->is_day_view
, pcd
->time_division
, pcd
->top_level
);
946 if (pcd
->selected_cut_list
)
947 pcd
->copied_uids
= g_slist_prepend (pcd
->copied_uids
, g_strdup (icalcomponent_get_uid (subcomp
)));
949 } else if (kind
== e_cal_model_get_component_kind (model
)) {
950 e_calendar_view_add_event_sync (model
, client
, pcd
->selection_start
, default_zone
, icalcomp
, all_day
,
951 pcd
->is_day_view
, pcd
->time_division
, pcd
->top_level
);
954 if (pcd
->selected_cut_list
)
955 pcd
->copied_uids
= g_slist_prepend (pcd
->copied_uids
, g_strdup (icalcomponent_get_uid (icalcomp
)));
958 pcd
->success
= !g_cancellable_is_cancelled (cancellable
);
959 pcd
->client
= g_object_ref (client
);
962 if (!copied_components
&& !g_cancellable_is_cancelled (cancellable
) && error
&& !*error
)
963 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_INVALID_ARGUMENT
, _("No suitable component found"));
965 icalcomponent_free (icalcomp
);
966 g_clear_object (&source
);
967 g_clear_object (&default_source
);
968 g_clear_object (&client
);
972 calendar_view_paste_clipboard (ESelectable
*selectable
)
975 ECalendarView
*cal_view
;
976 GtkClipboard
*clipboard
;
978 cal_view
= E_CALENDAR_VIEW (selectable
);
980 model
= e_calendar_view_get_model (cal_view
);
982 clipboard
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
);
984 /* Paste text into an event being edited. */
985 if (gtk_clipboard_wait_is_text_available (clipboard
)) {
986 ECalendarViewClass
*class;
988 class = E_CALENDAR_VIEW_GET_CLASS (cal_view
);
989 g_return_if_fail (class->paste_text
!= NULL
);
991 class->paste_text (cal_view
);
993 /* Paste iCalendar data into the view. */
994 } else if (e_clipboard_wait_is_calendar_available (clipboard
)) {
995 PasteClipboardData
*pcd
;
996 ECalDataModel
*data_model
;
997 GCancellable
*cancellable
;
998 const gchar
*alert_ident
= NULL
;
1000 switch (e_cal_model_get_component_kind (model
)) {
1001 case ICAL_VEVENT_COMPONENT
:
1002 alert_ident
= "calendar:failed-create-event";
1004 case ICAL_VJOURNAL_COMPONENT
:
1005 alert_ident
= "calendar:failed-create-memo";
1007 case ICAL_VTODO_COMPONENT
:
1008 alert_ident
= "calendar:failed-create-task";
1011 g_warn_if_reached ();
1015 pcd
= g_new0 (PasteClipboardData
, 1);
1016 pcd
->cal_view
= g_object_ref (cal_view
);
1017 pcd
->selected_cut_list
= cal_view
->priv
->selected_cut_list
;
1018 cal_view
->priv
->selected_cut_list
= NULL
;
1019 pcd
->copied_uids
= NULL
; /* gchar * */
1020 pcd
->ical_str
= e_clipboard_wait_for_calendar (clipboard
);
1021 g_warn_if_fail (e_calendar_view_get_selected_time_range (cal_view
, &pcd
->selection_start
, &pcd
->selection_end
));
1022 pcd
->is_day_view
= E_IS_DAY_VIEW (cal_view
);
1023 if (pcd
->is_day_view
)
1024 pcd
->time_division
= e_calendar_view_get_time_divisions (cal_view
);
1025 pcd
->top_level
= gtk_widget_get_toplevel (GTK_WIDGET (cal_view
));
1027 g_object_ref (pcd
->top_level
);
1028 pcd
->success
= FALSE
;
1031 data_model
= e_cal_model_get_data_model (model
);
1033 cancellable
= e_cal_data_model_submit_thread_job (data_model
, _("Pasting iCalendar data"), alert_ident
,
1034 NULL
, cal_view_paste_clipboard_thread
, pcd
, paste_clipboard_data_free
);
1036 g_clear_object (&cancellable
);
1041 calendar_view_delete_selection (ESelectable
*selectable
)
1043 ECalendarView
*cal_view
;
1044 GList
*selected
, *iter
;
1046 cal_view
= E_CALENDAR_VIEW (selectable
);
1048 selected
= e_calendar_view_get_selected_events (cal_view
);
1050 for (iter
= selected
; iter
!= NULL
; iter
= iter
->next
) {
1051 ECalendarViewEvent
*event
= iter
->data
;
1053 /* XXX Why would this ever be NULL? */
1057 calendar_view_delete_event (cal_view
, event
, FALSE
);
1060 g_list_free (selected
);
1064 e_calendar_view_class_init (ECalendarViewClass
*class)
1066 GObjectClass
*object_class
;
1067 GtkWidgetClass
*widget_class
;
1068 GtkBindingSet
*binding_set
;
1070 g_type_class_add_private (class, sizeof (ECalendarViewPrivate
));
1072 object_class
= G_OBJECT_CLASS (class);
1073 object_class
->set_property
= calendar_view_set_property
;
1074 object_class
->get_property
= calendar_view_get_property
;
1075 object_class
->dispose
= calendar_view_dispose
;
1076 object_class
->constructed
= calendar_view_constructed
;
1078 class->selection_changed
= NULL
;
1079 class->selected_time_changed
= NULL
;
1080 class->event_changed
= NULL
;
1081 class->event_added
= NULL
;
1083 class->get_selected_events
= NULL
;
1084 class->get_selected_time_range
= NULL
;
1085 class->set_selected_time_range
= NULL
;
1086 class->get_visible_time_range
= NULL
;
1087 class->precalc_visible_time_range
= NULL
;
1088 class->update_query
= NULL
;
1089 class->open_event
= e_calendar_view_open_event
;
1090 class->paste_text
= NULL
;
1092 /* Inherited from ESelectableInterface */
1093 g_object_class_override_property (
1095 PROP_COPY_TARGET_LIST
,
1096 "copy-target-list");
1098 g_object_class_install_property (
1101 g_param_spec_object (
1107 G_PARAM_CONSTRUCT_ONLY
));
1109 /* Inherited from ESelectableInterface */
1110 g_object_class_override_property (
1112 PROP_PASTE_TARGET_LIST
,
1113 "paste-target-list");
1115 g_object_class_install_property (
1117 PROP_TIME_DIVISIONS
,
1125 G_PARAM_READWRITE
));
1127 g_object_class_install_property (
1130 g_param_spec_boolean (
1132 "Whether is in an editing mode",
1133 "Whether is in an editing mode",
1137 signals
[POPUP_EVENT
] = g_signal_new (
1139 G_TYPE_FROM_CLASS (class),
1141 G_STRUCT_OFFSET (ECalendarViewClass
, popup_event
),
1143 g_cclosure_marshal_VOID__BOXED
,
1145 GDK_TYPE_EVENT
| G_SIGNAL_TYPE_STATIC_SCOPE
);
1147 signals
[SELECTION_CHANGED
] = g_signal_new (
1148 "selection-changed",
1149 G_TYPE_FROM_CLASS (class),
1151 G_STRUCT_OFFSET (ECalendarViewClass
, selection_changed
),
1153 g_cclosure_marshal_VOID__VOID
,
1156 signals
[SELECTED_TIME_CHANGED
] = g_signal_new (
1157 "selected-time-changed",
1158 G_TYPE_FROM_CLASS (class),
1160 G_STRUCT_OFFSET (ECalendarViewClass
, selected_time_changed
),
1162 g_cclosure_marshal_VOID__VOID
,
1165 signals
[TIMEZONE_CHANGED
] = g_signal_new (
1167 G_TYPE_FROM_CLASS (class),
1169 G_STRUCT_OFFSET (ECalendarViewClass
, timezone_changed
),
1171 e_marshal_VOID__POINTER_POINTER
,
1176 signals
[EVENT_CHANGED
] = g_signal_new (
1178 G_TYPE_FROM_CLASS (object_class
),
1179 G_SIGNAL_RUN_FIRST
| G_SIGNAL_ACTION
,
1180 G_STRUCT_OFFSET (ECalendarViewClass
, event_changed
),
1182 g_cclosure_marshal_VOID__POINTER
,
1186 signals
[EVENT_ADDED
] = g_signal_new (
1188 G_TYPE_FROM_CLASS (object_class
),
1189 G_SIGNAL_RUN_FIRST
| G_SIGNAL_ACTION
,
1190 G_STRUCT_OFFSET (ECalendarViewClass
, event_added
),
1192 g_cclosure_marshal_VOID__POINTER
,
1196 signals
[OPEN_EVENT
] = g_signal_new (
1198 G_TYPE_FROM_CLASS (class),
1199 G_SIGNAL_RUN_FIRST
| G_SIGNAL_ACTION
,
1200 G_STRUCT_OFFSET (ECalendarViewClass
, open_event
),
1202 g_cclosure_marshal_VOID__VOID
,
1205 signals
[MOVE_VIEW_RANGE
] = g_signal_new (
1207 G_TYPE_FROM_CLASS (object_class
),
1209 G_STRUCT_OFFSET (ECalendarViewClass
, move_view_range
),
1211 NULL
, /* default marshal */
1212 G_TYPE_NONE
, 2, G_TYPE_INT
, G_TYPE_INT64
);
1216 binding_set
= gtk_binding_set_by_class (class);
1218 gtk_binding_entry_add_signal (
1219 binding_set
, GDK_KEY_o
, GDK_CONTROL_MASK
, "open-event", 0);
1221 /* init the accessibility support for e_day_view */
1222 widget_class
= GTK_WIDGET_CLASS (class);
1223 gtk_widget_class_set_accessible_type (widget_class
, EA_TYPE_CAL_VIEW
);
1227 e_calendar_view_init (ECalendarView
*calendar_view
)
1229 GtkTargetList
*target_list
;
1231 calendar_view
->priv
= E_CALENDAR_VIEW_GET_PRIVATE (calendar_view
);
1233 /* Set this early to avoid a divide-by-zero during init. */
1234 calendar_view
->priv
->time_divisions
= 30;
1236 target_list
= gtk_target_list_new (NULL
, 0);
1237 e_target_list_add_calendar_targets (target_list
, 0);
1238 calendar_view
->priv
->copy_target_list
= target_list
;
1240 target_list
= gtk_target_list_new (NULL
, 0);
1241 e_target_list_add_calendar_targets (target_list
, 0);
1242 calendar_view
->priv
->paste_target_list
= target_list
;
1246 calendar_view_selectable_init (ESelectableInterface
*iface
)
1248 iface
->update_actions
= calendar_view_update_actions
;
1249 iface
->cut_clipboard
= calendar_view_cut_clipboard
;
1250 iface
->copy_clipboard
= calendar_view_copy_clipboard
;
1251 iface
->paste_clipboard
= calendar_view_paste_clipboard
;
1252 iface
->delete_selection
= calendar_view_delete_selection
;
1256 e_calendar_view_popup_event (ECalendarView
*calendar_view
,
1257 GdkEvent
*button_event
)
1259 g_return_if_fail (E_IS_CALENDAR_VIEW (calendar_view
));
1260 g_return_if_fail (button_event
!= NULL
);
1262 g_signal_emit (calendar_view
, signals
[POPUP_EVENT
], 0, button_event
);
1266 e_calendar_view_get_model (ECalendarView
*cal_view
)
1268 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), NULL
);
1270 return cal_view
->priv
->model
;
1274 e_calendar_view_get_timezone (ECalendarView
*cal_view
)
1276 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), NULL
);
1277 return e_cal_model_get_timezone (cal_view
->priv
->model
);
1281 e_calendar_view_set_timezone (ECalendarView
*cal_view
,
1284 icaltimezone
*old_zone
;
1286 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
1288 old_zone
= e_cal_model_get_timezone (cal_view
->priv
->model
);
1289 if (old_zone
== zone
)
1292 e_cal_model_set_timezone (cal_view
->priv
->model
, zone
);
1294 cal_view
, signals
[TIMEZONE_CHANGED
], 0,
1299 e_calendar_view_get_copy_target_list (ECalendarView
*cal_view
)
1301 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), NULL
);
1303 return cal_view
->priv
->copy_target_list
;
1307 e_calendar_view_get_paste_target_list (ECalendarView
*cal_view
)
1309 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), NULL
);
1311 return cal_view
->priv
->paste_target_list
;
1315 e_calendar_view_get_time_divisions (ECalendarView
*cal_view
)
1317 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), 0);
1319 return cal_view
->priv
->time_divisions
;
1323 e_calendar_view_set_time_divisions (ECalendarView
*cal_view
,
1324 gint time_divisions
)
1326 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
1328 if (cal_view
->priv
->time_divisions
== time_divisions
)
1331 cal_view
->priv
->time_divisions
= time_divisions
;
1333 g_object_notify (G_OBJECT (cal_view
), "time-divisions");
1337 e_calendar_view_get_selected_events (ECalendarView
*cal_view
)
1339 ECalendarViewClass
*class;
1341 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), NULL
);
1343 class = E_CALENDAR_VIEW_GET_CLASS (cal_view
);
1344 g_return_val_if_fail (class->get_selected_events
!= NULL
, NULL
);
1346 return class->get_selected_events (cal_view
);
1350 e_calendar_view_get_selected_time_range (ECalendarView
*cal_view
,
1354 ECalendarViewClass
*class;
1356 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), FALSE
);
1358 class = E_CALENDAR_VIEW_GET_CLASS (cal_view
);
1359 g_return_val_if_fail (class->get_selected_time_range
!= NULL
, FALSE
);
1361 return class->get_selected_time_range (cal_view
, start_time
, end_time
);
1365 e_calendar_view_set_selected_time_range (ECalendarView
*cal_view
,
1369 ECalendarViewClass
*class;
1371 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
1373 /* Not all views implement this, so return silently. */
1374 class = E_CALENDAR_VIEW_GET_CLASS (cal_view
);
1375 if (class->set_selected_time_range
== NULL
)
1378 class->set_selected_time_range (cal_view
, start_time
, end_time
);
1382 e_calendar_view_get_visible_time_range (ECalendarView
*cal_view
,
1386 ECalendarViewClass
*class;
1388 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), FALSE
);
1390 class = E_CALENDAR_VIEW_GET_CLASS (cal_view
);
1391 g_return_val_if_fail (class->get_visible_time_range
!= NULL
, FALSE
);
1393 return class->get_visible_time_range (cal_view
, start_time
, end_time
);
1397 e_calendar_view_precalc_visible_time_range (ECalendarView
*cal_view
,
1398 time_t in_start_time
,
1400 time_t *out_start_time
,
1401 time_t *out_end_time
)
1403 ECalendarViewClass
*class;
1405 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
1407 /* Not all views implement this, so return silently. */
1408 class = E_CALENDAR_VIEW_GET_CLASS (cal_view
);
1409 if (class->precalc_visible_time_range
== NULL
)
1412 class->precalc_visible_time_range (cal_view
, in_start_time
, in_end_time
, out_start_time
, out_end_time
);
1416 e_calendar_view_update_query (ECalendarView
*cal_view
)
1418 ECalendarViewClass
*class;
1420 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
1422 class = E_CALENDAR_VIEW_GET_CLASS (cal_view
);
1423 g_return_if_fail (class->update_query
!= NULL
);
1425 class->update_query (cal_view
);
1429 e_calendar_view_delete_selected_occurrence (ECalendarView
*cal_view
)
1431 ECalendarViewEvent
*event
;
1434 selected
= e_calendar_view_get_selected_events (cal_view
);
1438 event
= (ECalendarViewEvent
*) selected
->data
;
1439 if (is_comp_data_valid (event
)) {
1440 calendar_view_delete_event (cal_view
, event
, TRUE
);
1443 g_list_free (selected
);
1447 e_calendar_view_open_event (ECalendarView
*cal_view
)
1451 selected
= e_calendar_view_get_selected_events (cal_view
);
1453 ECalendarViewEvent
*event
= (ECalendarViewEvent
*) selected
->data
;
1454 if (event
&& is_comp_data_valid (event
))
1455 e_calendar_view_edit_appointment (cal_view
, event
->comp_data
->client
, event
->comp_data
->icalcomp
, EDIT_EVENT_AUTODETECT
);
1457 g_list_free (selected
);
1462 * e_calendar_view_new_appointment_full
1463 * @cal_view: an #ECalendarView
1464 * @all_day: Whether create all day event or not.
1465 * @meeting: This is a meeting or an appointment.
1466 * @no_past_date: Don't create event in past date, use actual date instead
1469 * Opens an event editor dialog for a new appointment. The appointment's
1470 * start and end times are set to the currently selected time range in
1471 * the calendar view.
1473 * When the selection is for all day and we don't need @all_day event,
1474 * then this do a rounding to the actual hour for actual day (today) and
1475 * to the 'day begins' from preferences in other selected day.
1478 e_calendar_view_new_appointment_full (ECalendarView
*cal_view
,
1481 gboolean no_past_date
)
1484 time_t dtstart
, dtend
, now
;
1485 gboolean do_rounding
= FALSE
;
1487 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
1489 model
= e_calendar_view_get_model (cal_view
);
1493 if (!e_calendar_view_get_selected_time_range (cal_view
, &dtstart
, &dtend
)) {
1495 dtend
= dtstart
+ 3600;
1498 if (no_past_date
&& dtstart
< now
) {
1499 dtend
= time_day_begin (now
) + (dtend
- dtstart
);
1500 dtstart
= time_day_begin (now
);
1504 /* We either need rounding or don't want to set all_day for this, we will rather use actual */
1505 /* time in this cases; dtstart should be a midnight in this case */
1506 if (do_rounding
|| (!all_day
&& (dtend
- dtstart
) == (60 * 60 * 24))) {
1507 struct tm local
= *localtime (&now
);
1508 gint time_div
= e_calendar_view_get_time_divisions (cal_view
);
1511 if (!time_div
) /* Possible if your settings values aren't so nice */
1514 if (time_day_begin (now
) == time_day_begin (dtstart
)) {
1515 /* same day as today */
1516 hours
= local
.tm_hour
;
1517 mins
= local
.tm_min
;
1519 /* round minutes to nearest time division, up or down */
1520 if ((mins
% time_div
) >= time_div
/ 2)
1522 mins
= (mins
- (mins
% time_div
));
1524 /* other day than today */
1525 hours
= e_cal_model_get_work_day_start_hour (model
);
1526 mins
= e_cal_model_get_work_day_start_minute (model
);
1529 dtstart
= dtstart
+ (60 * 60 * hours
) + (mins
* 60);
1530 dtend
= dtstart
+ (time_div
* 60);
1533 e_cal_ops_new_component_editor_from_model (
1534 e_calendar_view_get_model (cal_view
), NULL
,
1535 dtstart
, dtend
, meeting
, all_day
);
1539 e_calendar_view_new_appointment (ECalendarView
*cal_view
)
1541 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
1543 e_calendar_view_new_appointment_full (cal_view
, FALSE
, FALSE
, FALSE
);
1546 /* Ensures the calendar is selected */
1548 object_created_cb (ECompEditor
*comp_editor
,
1549 ECalendarView
*cal_view
)
1551 e_cal_model_emit_object_created (e_calendar_view_get_model (cal_view
), e_comp_editor_get_target_client (comp_editor
));
1555 e_calendar_view_open_event_with_flags (ECalendarView
*cal_view
,
1557 icalcomponent
*icalcomp
,
1560 ECompEditor
*comp_editor
;
1563 /* FIXME ECalendarView should own an EShell pointer. */
1564 shell
= e_shell_get_default ();
1566 comp_editor
= e_comp_editor_find_existing_for (e_client_get_source (E_CLIENT (client
)), icalcomp
);
1568 GtkWidget
*toplevel
;
1570 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (cal_view
));
1571 if (!GTK_IS_WINDOW (toplevel
))
1574 comp_editor
= e_comp_editor_open_for_component (GTK_WINDOW (toplevel
),
1575 shell
, e_client_get_source (E_CLIENT (client
)), icalcomp
, flags
);
1578 comp_editor
, "object-created",
1579 G_CALLBACK (object_created_cb
), cal_view
);
1582 gtk_window_present (GTK_WINDOW (comp_editor
));
1588 * e_calendar_view_edit_appointment
1589 * @cal_view: A calendar view.
1590 * @client: Calendar client.
1591 * @icalcomp: The object to be edited.
1592 * @mode: one of #EEditEventMode
1594 * Opens an editor window to allow the user to edit the selected
1598 e_calendar_view_edit_appointment (ECalendarView
*cal_view
,
1600 icalcomponent
*icalcomp
,
1601 EEditEventMode mode
)
1604 ESourceRegistry
*registry
;
1607 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
1608 g_return_if_fail (E_IS_CAL_CLIENT (client
));
1609 g_return_if_fail (icalcomp
!= NULL
);
1611 model
= e_calendar_view_get_model (cal_view
);
1612 registry
= e_cal_model_get_registry (model
);
1614 if ((mode
== EDIT_EVENT_AUTODETECT
&& icalcomponent_get_first_property (icalcomp
, ICAL_ATTENDEE_PROPERTY
) != NULL
)
1615 || mode
== EDIT_EVENT_FORCE_MEETING
) {
1616 ECalComponent
*comp
= e_cal_component_new ();
1617 e_cal_component_set_icalcomponent (comp
, icalcomponent_new_clone (icalcomp
));
1618 flags
|= E_COMP_EDITOR_FLAG_WITH_ATTENDEES
;
1619 if (itip_organizer_is_user (registry
, comp
, client
) ||
1620 itip_sentby_is_user (registry
, comp
, client
) ||
1621 !e_cal_component_has_attendees (comp
))
1622 flags
|= E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER
;
1623 g_object_unref (comp
);
1626 e_calendar_view_open_event_with_flags (cal_view
, client
, icalcomp
, flags
);
1630 tooltip_ungrab (ECalendarView
*view
,
1633 GdkDevice
*keyboard
;
1635 while (!g_queue_is_empty (&view
->priv
->grabbed_keyboards
)) {
1636 keyboard
= g_queue_pop_head (&view
->priv
->grabbed_keyboards
);
1637 gdk_device_ungrab (keyboard
, event_time
);
1638 g_object_unref (keyboard
);
1643 tooltip_key_event (GtkWidget
*tooltip
,
1644 GdkEvent
*key_event
,
1645 ECalendarView
*view
)
1649 widget
= g_object_get_data (G_OBJECT (view
), "tooltip-window");
1653 tooltip_ungrab (view
, gdk_event_get_time (key_event
));
1655 gtk_widget_destroy (widget
);
1656 g_object_set_data (G_OBJECT (view
), "tooltip-window", NULL
);
1662 get_label (struct icaltimetype
*tt
,
1663 icaltimezone
*f_zone
,
1664 icaltimezone
*t_zone
)
1668 tmp_tm
= icaltimetype_to_tm_with_zone (tt
, f_zone
, t_zone
);
1670 return e_datetime_format_format_tm ("calendar", "table", tt
->is_date
? DTFormatKindDate
: DTFormatKindDateTime
, &tmp_tm
);
1674 e_calendar_view_move_tip (GtkWidget
*widget
,
1678 GtkAllocation allocation
;
1679 GtkRequisition requisition
;
1680 GdkDisplay
*display
;
1682 GdkScreen
*pointer_screen
;
1683 GdkRectangle monitor
;
1684 GdkDeviceManager
*device_manager
;
1686 gint monitor_num
, px
, py
;
1689 gtk_widget_get_preferred_size (widget
, &requisition
, NULL
);
1690 w
= requisition
.width
;
1691 h
= requisition
.height
;
1693 screen
= gtk_widget_get_screen (widget
);
1694 display
= gdk_screen_get_display (screen
);
1695 device_manager
= gdk_display_get_device_manager (display
);
1696 pointer
= gdk_device_manager_get_client_pointer (device_manager
);
1698 gdk_device_get_position (pointer
, &pointer_screen
, &px
, &py
);
1699 if (pointer_screen
!= screen
) {
1703 monitor_num
= gdk_screen_get_monitor_at_point (screen
, px
, py
);
1704 gdk_screen_get_monitor_geometry (screen
, monitor_num
, &monitor
);
1706 if ((x
+ w
) > monitor
.x
+ monitor
.width
)
1707 x
-= (x
+ w
) - (monitor
.x
+ monitor
.width
);
1708 else if (x
< monitor
.x
)
1711 gtk_widget_get_allocation (widget
, &allocation
);
1713 if ((y
+ h
+ allocation
.height
+ 4) > monitor
.y
+ monitor
.height
)
1716 gtk_window_move (GTK_WINDOW (widget
), x
, y
);
1717 gtk_widget_show (widget
);
1721 tooltip_window_destroyed_cb (gpointer user_data
,
1724 ECalendarView
*view
= user_data
;
1726 tooltip_ungrab (view
, GDK_CURRENT_TIME
);
1727 g_object_unref (view
);
1731 * It is expected to show the tooltips in this below format
1733 * <B>SUBJECT OF THE MEETING</B>
1734 * Organiser: NameOfTheUser<email@ofuser.com>
1735 * Location: PlaceOfTheMeeting
1736 * Time : DateAndTime (xx Minutes)
1737 * Status: Accepted: X Declined: Y ...
1741 e_calendar_view_get_tooltips (const ECalendarViewEventData
*data
)
1743 GtkWidget
*label
, *box
, *hbox
, *ebox
, *frame
, *toplevel
;
1745 gchar
*tmp
, *tmp1
= NULL
, *tmp2
= NULL
;
1746 ECalComponentOrganizer organiser
;
1747 ECalComponentDateTime dtstart
, dtend
;
1748 icalcomponent
*clone_comp
;
1749 time_t t_start
, t_end
;
1750 ECalendarViewEvent
*pevent
;
1753 GdkDisplay
*display
;
1754 GdkDeviceManager
*device_manager
;
1755 GdkRGBA bg_rgba
, fg_rgba
;
1756 GQueue
*grabbed_keyboards
;
1757 ECalComponent
*newcomp
= e_cal_component_new ();
1758 icaltimezone
*zone
, *default_zone
;
1760 ECalClient
*client
= NULL
;
1762 gboolean free_text
= FALSE
;
1764 /* This function is a timeout callback. */
1766 g_return_val_if_fail (data
!= NULL
, FALSE
);
1767 g_return_val_if_fail (E_IS_CALENDAR_VIEW (data
->cal_view
), FALSE
);
1769 e_utils_get_theme_color (GTK_WIDGET (data
->cal_view
), "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR
, &bg_rgba
);
1770 e_utils_get_theme_color (GTK_WIDGET (data
->cal_view
), "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR
, &fg_rgba
);
1772 model
= e_calendar_view_get_model (data
->cal_view
);
1774 /* Delete any stray tooltip if left */
1775 widget
= g_object_get_data (
1776 G_OBJECT (data
->cal_view
), "tooltip-window");
1777 if (GTK_IS_WIDGET (widget
))
1778 gtk_widget_destroy (widget
);
1780 default_zone
= e_calendar_view_get_timezone (data
->cal_view
);
1781 pevent
= data
->get_view_event (data
->cal_view
, data
->day
, data
->event_num
);
1783 if (!is_comp_data_valid (pevent
))
1786 client
= pevent
->comp_data
->client
;
1788 clone_comp
= icalcomponent_new_clone (pevent
->comp_data
->icalcomp
);
1789 if (!e_cal_component_set_icalcomponent (newcomp
, clone_comp
))
1790 g_warning ("couldn't update calendar component with modified data from backend\n");
1792 box
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 0);
1794 str
= e_calendar_view_get_icalcomponent_summary (pevent
->comp_data
->client
, pevent
->comp_data
->icalcomp
, &free_text
);
1796 if (!(str
&& *str
)) {
1797 g_object_unref (newcomp
);
1798 gtk_widget_destroy (box
);
1803 tmp
= g_markup_printf_escaped ("<b>%s</b>", str
);
1804 label
= gtk_label_new (NULL
);
1805 gtk_label_set_line_wrap ((GtkLabel
*) label
, TRUE
);
1806 gtk_label_set_markup ((GtkLabel
*) label
, tmp
);
1809 g_free ((gchar
*) str
);
1813 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1814 gtk_box_pack_start ((GtkBox
*) hbox
, label
, FALSE
, FALSE
, 0);
1815 ebox
= gtk_event_box_new ();
1816 gtk_container_add ((GtkContainer
*) ebox
, hbox
);
1817 gtk_widget_override_background_color (ebox
, GTK_STATE_FLAG_NORMAL
, &bg_rgba
);
1818 gtk_widget_override_color (label
, GTK_STATE_FLAG_NORMAL
, &fg_rgba
);
1820 gtk_box_pack_start ((GtkBox
*) box
, ebox
, FALSE
, FALSE
, 0);
1823 e_cal_component_get_organizer (newcomp
, &organiser
);
1826 ptr
= strchr (organiser
.value
, ':');
1830 /* To Translators: It will display "Organiser: NameOfTheUser <email@ofuser.com>" */
1831 tmp
= g_strdup_printf (_("Organizer: %s <%s>"), organiser
.cn
, ptr
);
1834 /* With SunOne accouts, there may be no ':' in organiser.value*/
1835 tmp
= g_strdup_printf (_("Organizer: %s"), organiser
.cn
);
1837 label
= gtk_label_new (tmp
);
1838 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1839 gtk_box_pack_start ((GtkBox
*) hbox
, label
, FALSE
, FALSE
, 0);
1840 ebox
= gtk_event_box_new ();
1841 gtk_container_add ((GtkContainer
*) ebox
, hbox
);
1842 gtk_box_pack_start ((GtkBox
*) box
, ebox
, FALSE
, FALSE
, 0);
1847 e_cal_component_get_location (newcomp
, &str
);
1850 /* To Translators: It will display "Location: PlaceOfTheMeeting" */
1851 tmp
= g_markup_printf_escaped (_("Location: %s"), str
);
1852 label
= gtk_label_new (NULL
);
1853 gtk_widget_set_halign (label
, GTK_ALIGN_START
);
1854 gtk_misc_set_alignment ((GtkMisc
*) label
, 0.0, 0.0);
1855 gtk_label_set_markup ((GtkLabel
*) label
, tmp
);
1856 gtk_label_set_line_wrap ((GtkLabel
*) label
, TRUE
);
1857 gtk_label_set_max_width_chars ((GtkLabel
*) label
, 80);
1858 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1859 gtk_box_pack_start ((GtkBox
*) hbox
, label
, FALSE
, FALSE
, 0);
1860 ebox
= gtk_event_box_new ();
1861 gtk_container_add ((GtkContainer
*) ebox
, hbox
);
1862 gtk_box_pack_start ((GtkBox
*) box
, ebox
, FALSE
, FALSE
, 0);
1865 e_cal_component_get_dtstart (newcomp
, &dtstart
);
1866 e_cal_component_get_dtend (newcomp
, &dtend
);
1869 zone
= icalcomponent_get_timezone (e_cal_component_get_icalcomponent (newcomp
), dtstart
.tzid
);
1871 e_cal_client_get_timezone_sync (client
, dtstart
.tzid
, &zone
, NULL
, NULL
);
1874 zone
= default_zone
;
1880 if (dtstart
.value
) {
1881 t_start
= icaltime_as_timet_with_zone (*dtstart
.value
, zone
);
1883 t_end
= icaltime_as_timet_with_zone (*dtend
.value
, zone
);
1887 tmp1
= get_label (dtstart
.value
, zone
, default_zone
);
1888 tmp
= calculate_time (t_start
, t_end
);
1890 /* To Translators: It will display "Time: ActualStartDateAndTime (DurationOfTheMeeting)"*/
1891 tmp2
= g_strdup_printf (_("Time: %s %s"), tmp1
, tmp
);
1892 if (zone
&& !cal_comp_util_compare_event_timezones (newcomp
, client
, default_zone
)) {
1896 tmp1
= get_label (dtstart
.value
, zone
, zone
);
1897 tmp
= g_strconcat (tmp2
, "\n\t[ ", tmp1
, " ", icaltimezone_get_display_name (zone
), " ]", NULL
);
1907 e_cal_component_free_datetime (&dtstart
);
1908 e_cal_component_free_datetime (&dtend
);
1910 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1911 gtk_box_pack_start ((GtkBox
*) hbox
, gtk_label_new_with_mnemonic (tmp
), FALSE
, FALSE
, 0);
1912 ebox
= gtk_event_box_new ();
1913 gtk_container_add ((GtkContainer
*) ebox
, hbox
);
1914 gtk_box_pack_start ((GtkBox
*) box
, ebox
, FALSE
, FALSE
, 0);
1920 tmp
= e_cal_model_get_attendees_status_info (
1921 model
, newcomp
, pevent
->comp_data
->client
);
1923 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1924 gtk_box_pack_start ((GtkBox
*) hbox
, gtk_label_new (tmp
), FALSE
, FALSE
, 0);
1925 ebox
= gtk_event_box_new ();
1926 gtk_container_add ((GtkContainer
*) ebox
, hbox
);
1927 gtk_box_pack_start ((GtkBox
*) box
, ebox
, FALSE
, FALSE
, 0);
1932 tmp
= cal_comp_util_get_attendee_comments (e_cal_component_get_icalcomponent (newcomp
));
1934 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1935 gtk_box_pack_start ((GtkBox
*) hbox
, gtk_label_new (tmp
), FALSE
, FALSE
, 0);
1936 ebox
= gtk_event_box_new ();
1937 gtk_container_add ((GtkContainer
*) ebox
, hbox
);
1938 gtk_box_pack_start ((GtkBox
*) box
, ebox
, FALSE
, FALSE
, 0);
1943 pevent
->tooltip
= gtk_window_new (GTK_WINDOW_POPUP
);
1945 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (data
->cal_view
));
1946 if (GTK_IS_WINDOW (toplevel
)) {
1947 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel
)), GTK_WINDOW (pevent
->tooltip
));
1948 gtk_window_set_transient_for (GTK_WINDOW (pevent
->tooltip
), GTK_WINDOW (toplevel
));
1951 frame
= gtk_frame_new (NULL
);
1952 gtk_frame_set_shadow_type ((GtkFrame
*) frame
, GTK_SHADOW_IN
);
1954 gtk_window_set_type_hint (GTK_WINDOW (pevent
->tooltip
), GDK_WINDOW_TYPE_HINT_TOOLTIP
);
1955 gtk_window_move ((GtkWindow
*) pevent
->tooltip
, pevent
->x
+16, pevent
->y
+ 16);
1956 gtk_container_add ((GtkContainer
*) frame
, box
);
1957 gtk_container_add ((GtkContainer
*) pevent
->tooltip
, frame
);
1959 gtk_widget_show_all (pevent
->tooltip
);
1961 e_calendar_view_move_tip (pevent
->tooltip
, pevent
->x
+16, pevent
->y
+ 16);
1963 /* Grab all keyboard devices. A key press from
1964 * any of them will dismiss the tooltip window. */
1966 window
= gtk_widget_get_window (pevent
->tooltip
);
1967 display
= gdk_window_get_display (window
);
1968 device_manager
= gdk_display_get_device_manager (display
);
1970 grabbed_keyboards
= &data
->cal_view
->priv
->grabbed_keyboards
;
1971 g_warn_if_fail (g_queue_is_empty (grabbed_keyboards
));
1973 list
= gdk_device_manager_list_devices (
1974 device_manager
, GDK_DEVICE_TYPE_MASTER
);
1976 for (link
= list
; link
!= NULL
; link
= g_list_next (link
)) {
1977 GdkDevice
*device
= GDK_DEVICE (link
->data
);
1978 GdkGrabStatus grab_status
;
1980 if (gdk_device_get_source (device
) != GDK_SOURCE_KEYBOARD
)
1983 grab_status
= gdk_device_grab (
1988 GDK_KEY_PRESS_MASK
|
1989 GDK_KEY_RELEASE_MASK
,
1993 if (grab_status
== GDK_GRAB_SUCCESS
)
1996 g_object_ref (device
));
2002 pevent
->tooltip
, "key-press-event",
2003 G_CALLBACK (tooltip_key_event
), data
->cal_view
);
2004 pevent
->timeout
= -1;
2006 g_object_weak_ref (G_OBJECT (pevent
->tooltip
), tooltip_window_destroyed_cb
, g_object_ref (data
->cal_view
));
2007 g_object_set_data (G_OBJECT (data
->cal_view
), "tooltip-window", pevent
->tooltip
);
2008 g_object_unref (newcomp
);
2014 icalcomp_contains_category (icalcomponent
*icalcomp
,
2015 const gchar
*category
)
2017 icalproperty
*property
;
2019 g_return_val_if_fail (icalcomp
!= NULL
&& category
!= NULL
, FALSE
);
2021 for (property
= icalcomponent_get_first_property (icalcomp
, ICAL_CATEGORIES_PROPERTY
);
2023 property
= icalcomponent_get_next_property (icalcomp
, ICAL_CATEGORIES_PROPERTY
)) {
2024 gchar
*value
= icalproperty_get_value_as_string_r (property
);
2026 if (value
&& strcmp (category
, value
) == 0) {
2036 /* e_calendar_view_get_icalcomponent_summary returns summary of calcomp,
2037 * and for type of birthday or anniversary it append number of years since
2038 * beginning. In this case, the free_text is set to TRUE and caller need
2039 * to g_free returned string, otherwise free_text is set to FALSE and
2040 * returned value is owned by calcomp.
2044 e_calendar_view_get_icalcomponent_summary (ECalClient
*client
,
2045 icalcomponent
*icalcomp
,
2046 gboolean
*free_text
)
2048 const gchar
*summary
;
2050 g_return_val_if_fail (icalcomp
!= NULL
&& free_text
!= NULL
, NULL
);
2053 summary
= icalcomponent_get_summary (icalcomp
);
2055 if (icalcomp_contains_category (icalcomp
, _("Birthday")) ||
2056 icalcomp_contains_category (icalcomp
, _("Anniversary"))) {
2057 icalproperty
*xprop
;
2059 for (xprop
= icalcomponent_get_first_property (icalcomp
, ICAL_X_PROPERTY
);
2061 xprop
= icalcomponent_get_next_property (icalcomp
, ICAL_X_PROPERTY
)) {
2062 const gchar
*xname
= icalproperty_get_x_name (xprop
);
2064 if (xname
&& g_ascii_strcasecmp (xname
, "X-EVOLUTION-SINCE-YEAR") == 0) {
2065 struct icaltimetype dtnow
;
2069 str
= icalproperty_get_value_as_string_r (xprop
);
2070 since_year
= str
? atoi (str
) : 0;
2073 dtnow
= icalcomponent_get_dtstart (icalcomp
);
2075 if (since_year
> 0 && dtnow
.year
- since_year
> 0) {
2076 summary
= g_strdup_printf ("%s (%d)", summary
? summary
: "", dtnow
.year
- since_year
);
2077 *free_text
= summary
!= NULL
;
2088 /* A callback for e_cal_ops_create_component(), whose @user_data is an ECalendarView instance */
2090 e_calendar_view_component_created_cb (ECalModel
*model
,
2092 icalcomponent
*original_icalcomp
,
2093 const gchar
*new_uid
,
2096 ECalendarView
*cal_view
= user_data
;
2098 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
2100 e_cal_model_emit_object_created (model
, client
);
2105 draw_curved_rectangle (cairo_t
*cr
,
2109 gdouble rect_height
,
2114 x1
= x0
+ rect_width
;
2115 y1
= y0
+ rect_height
;
2117 if (!rect_width
|| !rect_height
)
2119 if (rect_width
/ 2 < radius
) {
2120 if (rect_height
/ 2 < radius
) {
2121 cairo_move_to (cr
, x0
, (y0
+ y1
) / 2);
2122 cairo_curve_to (cr
, x0
,y0
, x0
, y0
, (x0
+ x1
) / 2, y0
);
2123 cairo_curve_to (cr
, x1
, y0
, x1
, y0
, x1
, (y0
+ y1
) / 2);
2124 cairo_curve_to (cr
, x1
, y1
, x1
, y1
, (x1
+ x0
) / 2, y1
);
2125 cairo_curve_to (cr
, x0
, y1
, x0
, y1
, x0
, (y0
+ y1
) / 2);
2127 cairo_move_to (cr
, x0
, y0
+ radius
);
2128 cairo_curve_to (cr
, x0
,y0
, x0
, y0
, (x0
+ x1
) / 2, y0
);
2129 cairo_curve_to (cr
, x1
, y0
, x1
, y0
, x1
, y0
+ radius
);
2130 cairo_line_to (cr
, x1
, y1
- radius
);
2131 cairo_curve_to (cr
, x1
, y1
, x1
, y1
, (x1
+ x0
) / 2, y1
);
2132 cairo_curve_to (cr
, x0
, y1
, x0
, y1
, x0
, y1
- radius
);
2135 if (rect_height
/ 2 < radius
) {
2136 cairo_move_to (cr
, x0
, (y0
+ y1
) / 2);
2137 cairo_curve_to (cr
, x0
, y0
, x0
, y0
, x0
+ radius
, y0
);
2138 cairo_line_to (cr
, x1
- radius
, y0
);
2139 cairo_curve_to (cr
, x1
, y0
, x1
, y0
, x1
, (y0
+ y1
) / 2);
2140 cairo_curve_to (cr
, x1
, y1
, x1
, y1
, x1
- radius
, y1
);
2141 cairo_line_to (cr
, x0
+ radius
, y1
);
2142 cairo_curve_to (cr
, x0
, y1
, x0
, y1
, x0
, (y0
+ y1
) / 2);
2144 cairo_move_to (cr
, x0
, y0
+ radius
);
2145 cairo_curve_to (cr
, x0
, y0
, x0
, y0
, x0
+ radius
, y0
);
2146 cairo_line_to (cr
, x1
- radius
, y0
);
2147 cairo_curve_to (cr
, x1
, y0
, x1
, y0
, x1
, y0
+ radius
);
2148 cairo_line_to (cr
, x1
, y1
- radius
);
2149 cairo_curve_to (cr
, x1
, y1
, x1
, y1
, x1
- radius
, y1
);
2150 cairo_line_to (cr
, x0
+ radius
, y1
);
2151 cairo_curve_to (cr
, x0
, y1
, x0
, y1
, x0
, y1
- radius
);
2154 cairo_close_path (cr
);
2157 /* returns either light or dark yellow, based on the base_background,
2158 * which is the default background color */
2160 get_today_background (const GdkColor base_background
)
2162 GdkColor res
= base_background
;
2164 if (res
.red
> 0x7FFF) {
2165 /* light yellow for a light theme */
2170 /* dark yellow for a dark theme */
2180 is_comp_data_valid_func (ECalendarViewEvent
*event
,
2181 const gchar
*location
)
2183 g_return_val_if_fail (location
!= NULL
, FALSE
);
2186 g_warning ("%s: event is NULL", location
);
2190 if (!event
->comp_data
) {
2191 g_warning ("%s: event's (%p) comp_data is NULL", location
, event
);
2199 is_array_index_in_bounds_func (GArray
*array
,
2201 const gchar
*location
)
2203 g_return_val_if_fail (location
!= NULL
, FALSE
);
2206 g_warning ("%s: array is NULL", location
);
2210 if (index
< 0 || index
>= array
->len
) {
2211 g_warning ("%s: index %d is out of bounds [0,%d) at array %p", location
, index
, array
->len
, array
);
2219 e_calendar_view_is_editing (ECalendarView
*cal_view
)
2221 static gboolean in
= FALSE
;
2222 gboolean is_editing
= FALSE
;
2224 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), FALSE
);
2226 /* this should be called from the main thread only,
2227 * and each descendant overrides the property,
2228 * thus might cause no call recursion */
2230 g_warn_if_reached ();
2236 g_object_get (G_OBJECT (cal_view
), "is-editing", &is_editing
, NULL
);
2243 /* Returns text description of the current view. */
2245 e_calendar_view_get_description_text (ECalendarView
*cal_view
)
2247 time_t start_time
, end_time
;
2248 struct tm start_tm
, end_tm
;
2249 struct icaltimetype start_tt
, end_tt
;
2251 gchar buffer
[1024] = { 0 };
2252 gchar end_buffer
[512] = { 0 };
2254 g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view
), NULL
);
2256 if (!e_calendar_view_get_visible_time_range (cal_view
, &start_time
, &end_time
))
2259 zone
= e_cal_model_get_timezone (cal_view
->priv
->model
);
2261 start_tt
= icaltime_from_timet_with_zone (start_time
, FALSE
, zone
);
2262 start_tm
.tm_year
= start_tt
.year
- 1900;
2263 start_tm
.tm_mon
= start_tt
.month
- 1;
2264 start_tm
.tm_mday
= start_tt
.day
;
2265 start_tm
.tm_hour
= start_tt
.hour
;
2266 start_tm
.tm_min
= start_tt
.minute
;
2267 start_tm
.tm_sec
= start_tt
.second
;
2268 start_tm
.tm_isdst
= -1;
2269 start_tm
.tm_wday
= time_day_of_week (start_tt
.day
, start_tt
.month
- 1, start_tt
.year
);
2271 /* Subtract one from end_time so we don't get an extra day. */
2272 end_tt
= icaltime_from_timet_with_zone (end_time
- 1, FALSE
, zone
);
2273 end_tm
.tm_year
= end_tt
.year
- 1900;
2274 end_tm
.tm_mon
= end_tt
.month
- 1;
2275 end_tm
.tm_mday
= end_tt
.day
;
2276 end_tm
.tm_hour
= end_tt
.hour
;
2277 end_tm
.tm_min
= end_tt
.minute
;
2278 end_tm
.tm_sec
= end_tt
.second
;
2279 end_tm
.tm_isdst
= -1;
2280 end_tm
.tm_wday
= time_day_of_week (end_tt
.day
, end_tt
.month
- 1, end_tt
.year
);
2282 if (E_IS_MONTH_VIEW (cal_view
) || E_IS_CAL_LIST_VIEW (cal_view
)) {
2283 if (start_tm
.tm_year
== end_tm
.tm_year
) {
2284 if (start_tm
.tm_mon
== end_tm
.tm_mon
) {
2285 e_utf8_strftime (buffer
, sizeof (buffer
),
2287 e_utf8_strftime (end_buffer
, sizeof (end_buffer
),
2288 _("%d %b %Y"), &end_tm
);
2289 strcat (buffer
, " - ");
2290 strcat (buffer
, end_buffer
);
2292 e_utf8_strftime (buffer
, sizeof (buffer
),
2293 _("%d %b"), &start_tm
);
2294 e_utf8_strftime (end_buffer
, sizeof (end_buffer
),
2295 _("%d %b %Y"), &end_tm
);
2296 strcat (buffer
, " - ");
2297 strcat (buffer
, end_buffer
);
2301 buffer
, sizeof (buffer
),
2302 _("%d %b %Y"), &start_tm
);
2304 end_buffer
, sizeof (end_buffer
),
2305 _("%d %b %Y"), &end_tm
);
2306 strcat (buffer
, " - ");
2307 strcat (buffer
, end_buffer
);
2310 if (start_tm
.tm_year
== end_tm
.tm_year
&&
2311 start_tm
.tm_mon
== end_tm
.tm_mon
&&
2312 start_tm
.tm_mday
== end_tm
.tm_mday
) {
2314 buffer
, sizeof (buffer
),
2315 _("%A %d %b %Y"), &start_tm
);
2316 } else if (start_tm
.tm_year
== end_tm
.tm_year
) {
2318 buffer
, sizeof (buffer
),
2319 _("%a %d %b"), &start_tm
);
2321 end_buffer
, sizeof (end_buffer
),
2322 _("%a %d %b %Y"), &end_tm
);
2323 strcat (buffer
, " - ");
2324 strcat (buffer
, end_buffer
);
2327 buffer
, sizeof (buffer
),
2328 _("%a %d %b %Y"), &start_tm
);
2330 end_buffer
, sizeof (end_buffer
),
2331 _("%a %d %b %Y"), &end_tm
);
2332 strcat (buffer
, " - ");
2333 strcat (buffer
, end_buffer
);
2337 return g_strdup (buffer
);
2341 e_calendar_view_move_view_range (ECalendarView
*cal_view
,
2342 ECalendarViewMoveType mode_type
,
2345 g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view
));
2347 g_signal_emit (cal_view
, signals
[MOVE_VIEW_RANGE
], 0, mode_type
, (gint64
) exact_date
);