/* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * Copyright (C) 2014 Red Hat, Inc. (www.redhat.com) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #include "evolution-config.h" #include #include #include #include "calendar/gui/calendar-config.h" #include "calendar/gui/calendar-view.h" #include "calendar/gui/comp-util.h" #include "calendar/gui/e-cal-list-view.h" #include "calendar/gui/e-cal-model-calendar.h" #include "calendar/gui/e-cal-model-memos.h" #include "calendar/gui/e-cal-model-tasks.h" #include "calendar/gui/e-calendar-view.h" #include "calendar/gui/e-day-view.h" #include "calendar/gui/e-month-view.h" #include "calendar/gui/e-week-view.h" #include "calendar/gui/itip-utils.h" #include "calendar/gui/tag-calendar.h" #include "e-cal-base-shell-sidebar.h" #include "e-cal-shell-view-private.h" #include "e-cal-shell-content.h" #define E_CAL_SHELL_CONTENT_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentPrivate)) struct _ECalShellContentPrivate { GtkWidget *hpaned; GtkWidget *vpaned; GtkWidget *calendar_notebook; GtkWidget *task_table; ECalModel *task_model; ECalDataModel *task_data_model; GtkWidget *memo_table; ECalModel *memo_model; ECalDataModel *memo_data_model; ETagCalendar *tag_calendar; gulong datepicker_selection_changed_id; gulong datepicker_range_moved_id; ECalViewKind current_view; ECalendarView *views[E_CAL_VIEW_KIND_LAST]; GDate view_start, view_end; guint32 view_start_range_day_offset; GDate last_range_start; /* because "date-range-changed" can be emit with no real change */ time_t previous_selected_start_time; time_t previous_selected_end_time; gulong current_view_id_changed_id; }; enum { PROP_0, PROP_CALENDAR_NOTEBOOK, PROP_MEMO_TABLE, PROP_TASK_TABLE, PROP_CURRENT_VIEW_ID, PROP_CURRENT_VIEW }; /* Used to indicate who has the focus within the calendar view. */ typedef enum { FOCUS_CALENDAR_NOTEBOOK, FOCUS_MEMO_TABLE, FOCUS_TASK_TABLE, FOCUS_OTHER } FocusLocation; G_DEFINE_DYNAMIC_TYPE (ECalShellContent, e_cal_shell_content, E_TYPE_CAL_BASE_SHELL_CONTENT) static time_t convert_to_local_zone (time_t tm, icaltimezone *from_zone) { struct icaltimetype tt; tt = icaltime_from_timet_with_zone (tm, FALSE, from_zone); return icaltime_as_timet (tt); } static void cal_shell_content_update_model_and_current_view_times (ECalShellContent *cal_shell_content, ECalModel *model, ECalendarItem *calitem, time_t view_start_tt, time_t view_end_tt, const GDate *view_start, const GDate *view_end) { ECalendarView *current_view; EDayView *day_view = NULL; gint day_view_selection_start_day = -1, day_view_selection_end_day = -1; gint day_view_selection_start_row = -1, day_view_selection_end_row = -1; gdouble day_view_scrollbar_position = 0.0; gint syy, smm, sdd, eyy, emm, edd; time_t visible_range_start, visible_range_end; gboolean filters_updated = FALSE; icaltimezone *zone; gchar *cal_filter; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); g_return_if_fail (E_IS_CAL_MODEL (model)); g_return_if_fail (E_IS_CALENDAR_ITEM (calitem)); current_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content); g_return_if_fail (current_view != NULL); zone = e_cal_model_get_timezone (model); cal_filter = e_cal_data_model_dup_filter (e_cal_model_get_data_model (model)); if (E_IS_DAY_VIEW (current_view)) { GtkAdjustment *adjustment; day_view = E_DAY_VIEW (current_view); day_view_selection_start_day = day_view->selection_start_day; day_view_selection_end_day = day_view->selection_end_day; day_view_selection_start_row = day_view->selection_start_row; day_view_selection_end_row = day_view->selection_end_row; adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->main_canvas)); day_view_scrollbar_position = gtk_adjustment_get_value (adjustment); } g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_range_moved_id); g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_selection_changed_id); visible_range_start = view_start_tt; visible_range_end = view_end_tt; e_calendar_view_precalc_visible_time_range (current_view, view_start_tt, view_end_tt, &visible_range_start, &visible_range_end); if (view_start_tt != visible_range_start || view_end_tt != visible_range_end) { time_t cmp_range_start = convert_to_local_zone (visible_range_start, zone); time_t cmp_range_end = convert_to_local_zone (visible_range_end, zone); if (view_start_tt != cmp_range_start || view_end_tt != cmp_range_end - 1) { /* Calendar views update their inner time range during e_cal_model_set_time_range() call, while they can change it if needed (like a clamp of a week view with a week start day not being Monday */ GDate new_view_start, new_view_end; /* Midnight means the next day, which is not desired here */ cmp_range_end--; visible_range_end--; /* These times are in the correct zone already */ time_to_gdate_with_zone (&new_view_start, cmp_range_start, NULL); time_to_gdate_with_zone (&new_view_end, cmp_range_end, NULL); e_calendar_item_set_selection (calitem, &new_view_start, &new_view_end); e_cal_shell_content_update_filters (cal_shell_content, cal_filter, visible_range_start, visible_range_end); e_calendar_view_set_selected_time_range (current_view, cmp_range_start, cmp_range_start); filters_updated = TRUE; view_start_tt = cmp_range_start; view_end_tt = cmp_range_end; } } if (!filters_updated) { e_calendar_item_set_selection (calitem, view_start, view_end); e_cal_shell_content_update_filters (cal_shell_content, cal_filter, view_start_tt, view_end_tt); e_calendar_view_set_selected_time_range (current_view, view_start_tt, view_start_tt); } if (day_view && day_view_selection_start_day != -1 && day_view_selection_end_day != -1 && day_view_selection_start_row != -1 && day_view_selection_end_row != -1) { GtkAdjustment *adjustment; day_view->selection_start_day = day_view_selection_start_day; day_view->selection_end_day = day_view_selection_end_day; day_view->selection_start_row = day_view_selection_start_row; day_view->selection_end_row = day_view_selection_end_row; /* This is better than e_day_view_ensure_rows_visible(), because it keeps both selection and the exact scrollbar position in the main canvas, which may not always correspond to each other. */ adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->main_canvas)); gtk_adjustment_set_value (adjustment, day_view_scrollbar_position); } gtk_widget_queue_draw (GTK_WIDGET (current_view)); g_free (cal_filter); g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_range_moved_id); g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_selection_changed_id); if (e_calendar_item_get_date_range (calitem, &syy, &smm, &sdd, &eyy, &emm, &edd)) { GDate range_start; g_date_set_dmy (&range_start, sdd, smm + 1, syy); cal_shell_content->priv->view_start_range_day_offset = g_date_get_julian (&cal_shell_content->priv->view_start) - g_date_get_julian (&range_start); } } static void e_cal_shell_content_change_view (ECalShellContent *cal_shell_content, ECalViewKind to_view, const GDate *view_start, const GDate *view_end, gboolean force_change) { EShellSidebar *shell_sidebar; EShellView *shell_view; ECalendar *calendar; ECalModel *model; icaltimezone *zone; time_t view_start_tt, view_end_tt; gboolean view_changed = FALSE; gint selected_days; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); g_return_if_fail (to_view >= E_CAL_VIEW_KIND_DAY && to_view < E_CAL_VIEW_KIND_LAST); g_return_if_fail (view_start != NULL); g_return_if_fail (g_date_valid (view_start)); g_return_if_fail (view_end != NULL); g_return_if_fail (g_date_valid (view_end)); shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content)); shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (shell_sidebar)); calendar = e_cal_base_shell_sidebar_get_date_navigator (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar)); g_return_if_fail (E_IS_CALENDAR (calendar)); model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); zone = e_cal_model_get_timezone (model); view_start_tt = cal_comp_gdate_to_timet (view_start, zone); view_end_tt = cal_comp_gdate_to_timet (view_end, zone); if (to_view != cal_shell_content->priv->current_view) { g_signal_handler_block (cal_shell_content, cal_shell_content->priv->current_view_id_changed_id); e_cal_shell_content_set_current_view_id (cal_shell_content, to_view); g_signal_handler_unblock (cal_shell_content, cal_shell_content->priv->current_view_id_changed_id); view_changed = TRUE; } selected_days = g_date_get_julian (view_end) - g_date_get_julian (view_start) + 1; if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY) { EDayView *day_view; day_view = E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY]); e_day_view_set_days_shown (day_view, selected_days); } else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH) { EWeekView *month_view; month_view = E_WEEK_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_MONTH]); e_week_view_set_weeks_shown (month_view, selected_days / 7); } if (!force_change && g_date_valid (&cal_shell_content->priv->view_start) && g_date_valid (&cal_shell_content->priv->view_end) && g_date_compare (&cal_shell_content->priv->view_start, view_start) == 0 && g_date_compare (&cal_shell_content->priv->view_end, view_end) == 0) { ECalendarItem *calitem = e_calendar_get_item (calendar); if (view_changed) cal_shell_content_update_model_and_current_view_times ( cal_shell_content, model, calitem, view_start_tt, view_end_tt, view_start, view_end); g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_range_moved_id); g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_selection_changed_id); e_calendar_item_set_selection (calitem, view_start, view_end); g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_range_moved_id); g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_selection_changed_id); return; } cal_shell_content->priv->view_start = *view_start; cal_shell_content->priv->view_end = *view_end; cal_shell_content_update_model_and_current_view_times ( cal_shell_content, model, e_calendar_get_item (calendar), view_start_tt, view_end_tt, view_start, view_end); } static void cal_shell_content_clamp_for_whole_weeks (GDateWeekday week_start_day, GDate *sel_start, GDate *sel_end, gboolean saturday_as_sunday) { GDateWeekday wday; guint32 julian_start, julian_end; g_return_if_fail (sel_start != NULL); g_return_if_fail (sel_end != NULL); wday = g_date_get_weekday (sel_start); /* This is because the month/week view doesn't split weekends */ if (saturday_as_sunday && wday == G_DATE_SATURDAY && week_start_day == G_DATE_SUNDAY) wday = G_DATE_SUNDAY; if (week_start_day > wday) { g_date_subtract_days (sel_start, wday); wday = g_date_get_weekday (sel_start); } if (week_start_day < wday) g_date_subtract_days (sel_start, wday - week_start_day); julian_start = g_date_get_julian (sel_start); julian_end = g_date_get_julian (sel_end); if (((julian_end - julian_start + 1) % 7) != 0) g_date_add_days (sel_end, 7 - ((julian_end - julian_start + 1) % 7)); julian_end = g_date_get_julian (sel_end); /* Can show only up to 6 weeks */ if ((julian_end - julian_start + 1) / 7 > 6) { *sel_end = *sel_start; g_date_add_days (sel_end, (7 * 6) - 1); } if (g_date_compare (sel_start, sel_end) == 0) g_date_add_days (sel_end, 6); } static gboolean cal_shell_content_weekday_within (GDateWeekday start_wday, GDateWeekday end_wday, GDateWeekday test_wday) { gint ii; if (start_wday <= end_wday) return start_wday <= test_wday && test_wday <= end_wday; for (ii = 0; ii < 7; ii++) { if (start_wday == test_wday) return TRUE; if (start_wday == end_wday) break; start_wday = e_weekday_get_next (start_wday); } return FALSE; } static void cal_shell_content_change_selection_in_current_view (ECalShellContent *cal_shell_content, time_t sel_start_tt, time_t sel_end_tt, icaltimezone *zone) { g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); if (cal_shell_content->priv->current_view >= E_CAL_VIEW_KIND_DAY && cal_shell_content->priv->current_view < E_CAL_VIEW_KIND_LAST) { ECalendarView *view; view = cal_shell_content->priv->views[cal_shell_content->priv->current_view]; /* Preserve selected time (change only date) for these views */ if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY || cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK) { time_t current_sel_start = (time_t) -1, current_sel_end = (time_t) -1; if (e_calendar_view_get_selected_time_range (view, ¤t_sel_start, ¤t_sel_end)) { current_sel_start = icaltime_as_timet_with_zone (icaltime_from_timet_with_zone (current_sel_start, 0, zone), NULL); current_sel_end = icaltime_as_timet_with_zone (icaltime_from_timet_with_zone (current_sel_end, 0, zone), NULL); sel_start_tt += current_sel_start % (24 * 60 * 60); sel_end_tt += current_sel_end % (24 * 60 * 60); } } e_calendar_view_set_selected_time_range (view, sel_start_tt, sel_end_tt); } } static void cal_shell_content_datepicker_selection_changed_cb (ECalendarItem *calitem, ECalShellContent *cal_shell_content) { GDate sel_start, sel_end; guint32 selected_days, start_julian, end_julian; icaltimezone *zone; time_t sel_start_tt, sel_end_tt; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); g_return_if_fail (E_IS_CALENDAR_ITEM (calitem)); g_date_clear (&sel_start, 1); g_date_clear (&sel_end, 1); e_calendar_item_get_selection (calitem, &sel_start, &sel_end); start_julian = g_date_get_julian (&sel_start); end_julian = g_date_get_julian (&sel_end); g_return_if_fail (start_julian <= end_julian); if (g_date_compare (&cal_shell_content->priv->view_start, &sel_start) == 0 && g_date_compare (&cal_shell_content->priv->view_end, &sel_end) == 0) { /* No change in the selection range */ return; } zone = e_cal_model_get_timezone (e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content))); sel_start_tt = cal_comp_gdate_to_timet (&sel_start, zone); sel_end_tt = cal_comp_gdate_to_timet (&sel_end, zone); selected_days = end_julian - start_julian + 1; if (selected_days == 1) { GDateWeekday sel_start_wday, sel_end_wday, cur_start_wday, cur_end_wday; /* Clicked inside currently selected view range; do not do anything, just make sure the days are selected again */ if (g_date_compare (&cal_shell_content->priv->view_start, &sel_start) <= 0 && g_date_compare (&sel_start, &cal_shell_content->priv->view_end) <= 0) { sel_start = cal_shell_content->priv->view_start; sel_end = cal_shell_content->priv->view_end; e_calendar_item_set_selection (calitem, &sel_start, &sel_end); cal_shell_content_change_selection_in_current_view (cal_shell_content, sel_start_tt, sel_end_tt, zone); return; } sel_start_wday = g_date_get_weekday (&sel_start); sel_end_wday = g_date_get_weekday (&sel_end); cur_start_wday = g_date_get_weekday (&cal_shell_content->priv->view_start); cur_end_wday = g_date_get_weekday (&cal_shell_content->priv->view_end); if ((cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK || (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY && e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY])) != 1)) && cal_shell_content_weekday_within (cur_start_wday, cur_end_wday, sel_start_wday)) { if (cur_start_wday < sel_start_wday) { g_date_subtract_days (&sel_start, sel_start_wday - cur_start_wday); } else if (cur_start_wday > sel_start_wday) { g_date_subtract_days (&sel_start, 7 - (cur_start_wday - sel_start_wday)); } sel_end = sel_start; if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY) g_date_add_days (&sel_end, e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY])) - 1); else g_date_add_days (&sel_end, e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK])) - 1); e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &sel_start, &sel_end, FALSE); } else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WEEK && cal_shell_content_weekday_within (cur_start_wday, cur_end_wday, sel_start_wday) && cal_shell_content_weekday_within (cur_start_wday, cur_end_wday, sel_end_wday)) { if (cur_start_wday < sel_start_wday) g_date_subtract_days (&sel_start, sel_start_wday - cur_start_wday); sel_end = sel_start; cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, TRUE); e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WEEK, &sel_start, &sel_end, FALSE); } else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH || cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_LIST) { /* whole month */ g_date_set_day (&sel_start, 1); sel_end = sel_start; g_date_set_day (&sel_end, g_date_get_days_in_month (g_date_get_month (&sel_start), g_date_get_year (&sel_start)) - 1); cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH); e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &sel_start, &sel_end, FALSE); } else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK) { cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, TRUE); e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WEEK, &sel_start, &sel_end, FALSE); } else { e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_end, FALSE); } cal_shell_content_change_selection_in_current_view (cal_shell_content, sel_start_tt, sel_end_tt, zone); } else if (selected_days < 7) { GDateWeekday first_work_wday; first_work_wday = e_cal_model_get_work_day_first (e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content))); if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK && first_work_wday == g_date_get_weekday (&sel_start) && selected_days == e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK]))) e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WORKWEEK, &sel_start, &sel_end, FALSE); else e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_end, FALSE); } else if (selected_days == 7) { GDateWeekday sel_start_wday; sel_start_wday = g_date_get_weekday (&sel_start); if (sel_start_wday == calitem->week_start_day && cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY && e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY])) == 7) { e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_end, FALSE); } else { e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WEEK, &sel_start, &sel_end, FALSE); } } else { if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_LIST) { /* whole month */ g_date_set_day (&sel_start, 1); sel_end = sel_start; g_date_set_day (&sel_end, g_date_get_days_in_month (g_date_get_month (&sel_start), g_date_get_year (&sel_start))); cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, FALSE); e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_LIST, &sel_start, &sel_end, FALSE); } else { cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH || cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WEEK); e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_MONTH, &sel_start, &sel_end, FALSE); } } } static void cal_shell_content_datepicker_range_moved_cb (ECalendarItem *calitem, ECalShellContent *cal_shell_content) { gint start_year, start_month, start_day, end_year, end_month, end_day; GDate sel_start_date, sel_end_date, range_start_date; g_return_if_fail (E_IS_CALENDAR_ITEM (calitem)); g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); if (!e_calendar_item_get_date_range (calitem, &start_year, &start_month, &start_day, &end_year, &end_month, &end_day)) return; g_date_set_dmy (&range_start_date, start_day, start_month + 1, start_year); if (g_date_valid (&cal_shell_content->priv->last_range_start) && g_date_compare (&cal_shell_content->priv->last_range_start, &range_start_date) == 0) { return; } cal_shell_content->priv->last_range_start = range_start_date; g_date_clear (&sel_start_date, 1); g_date_clear (&sel_end_date, 1); if (cal_shell_content->priv->view_start_range_day_offset == (guint32) -1) { sel_start_date = cal_shell_content->priv->view_start; sel_end_date = cal_shell_content->priv->view_end; cal_shell_content->priv->view_start_range_day_offset = g_date_get_julian (&cal_shell_content->priv->view_start) - g_date_get_julian (&range_start_date); } else { gint view_days; view_days = g_date_get_julian (&cal_shell_content->priv->view_end) - g_date_get_julian (&cal_shell_content->priv->view_start); sel_start_date = range_start_date; g_date_add_days (&sel_start_date, cal_shell_content->priv->view_start_range_day_offset); sel_end_date = sel_start_date; g_date_add_days (&sel_end_date, view_days); } g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_range_moved_id); e_calendar_item_set_selection (calitem, &sel_start_date, &sel_end_date); g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_range_moved_id); } static gboolean cal_shell_content_datepicker_button_press_cb (ECalendar *calendar, GdkEvent *event, ECalShellContent *cal_shell_content) { g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), FALSE); if (!event) return FALSE; if (event->type == GDK_2BUTTON_PRESS) { ECalendarItem *calitem = e_calendar_get_item (calendar); GDate sel_start, sel_end; g_date_clear (&sel_start, 1); g_date_clear (&sel_end, 1); e_calendar_item_get_selection (calitem, &sel_start, &sel_end); /* Switch to a day view on a double-click */ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_start, FALSE); } return FALSE; } static void cal_shell_content_current_view_id_changed_cb (ECalShellContent *cal_shell_content) { GDate sel_start, sel_end; GDateWeekday work_day_first, week_start_day; ECalModel *model; gint ii; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); work_day_first = e_cal_model_get_work_day_first (model); week_start_day = e_cal_model_get_week_start_day (model); if (cal_shell_content->priv->previous_selected_start_time != -1 && cal_shell_content->priv->previous_selected_end_time != -1) { icaltimezone *zone; zone = e_cal_model_get_timezone (model); time_to_gdate_with_zone (&sel_start, cal_shell_content->priv->previous_selected_start_time, zone); time_to_gdate_with_zone (&sel_end, cal_shell_content->priv->previous_selected_end_time, zone); } else { sel_start = cal_shell_content->priv->view_start; sel_end = cal_shell_content->priv->view_end; } switch (cal_shell_content->priv->current_view) { case E_CAL_VIEW_KIND_DAY: /* Left the start & end being the current view start */ sel_end = sel_start; break; case E_CAL_VIEW_KIND_WORKWEEK: cal_shell_content_clamp_for_whole_weeks (week_start_day, &sel_start, &sel_end, FALSE); ii = 0; while (g_date_get_weekday (&sel_start) != work_day_first && ii < 7) { g_date_add_days (&sel_start, 1); ii++; } sel_end = sel_start; g_date_add_days (&sel_end, e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK])) - 1); break; case E_CAL_VIEW_KIND_WEEK: sel_end = sel_start; cal_shell_content_clamp_for_whole_weeks (week_start_day, &sel_start, &sel_end, TRUE); break; case E_CAL_VIEW_KIND_MONTH: case E_CAL_VIEW_KIND_LIST: if (g_date_get_day (&sel_start) != 1 && (g_date_get_julian (&sel_end) - g_date_get_julian (&sel_start) + 1) / 7 >= 3 && g_date_get_month (&sel_start) != g_date_get_month (&sel_end)) { g_date_set_day (&sel_start, 1); g_date_add_months (&sel_start, 1); } else { g_date_set_day (&sel_start, 1); } sel_end = sel_start; g_date_add_months (&sel_end, 1); g_date_subtract_days (&sel_end, 1); cal_shell_content_clamp_for_whole_weeks (week_start_day, &sel_start, &sel_end, cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH); break; default: g_warn_if_reached (); return; } /* Ensure a change */ e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &sel_start, &sel_end, TRUE); /* Try to preserve selection between the views */ if (cal_shell_content->priv->previous_selected_start_time != -1 && cal_shell_content->priv->previous_selected_end_time != -1) { if (cal_shell_content->priv->current_view >= E_CAL_VIEW_KIND_DAY && cal_shell_content->priv->current_view < E_CAL_VIEW_KIND_LAST) { ECalendarView *cal_view = cal_shell_content->priv->views[cal_shell_content->priv->current_view]; e_calendar_view_set_selected_time_range (cal_view, cal_shell_content->priv->previous_selected_start_time, cal_shell_content->priv->previous_selected_end_time); } } cal_shell_content->priv->previous_selected_start_time = -1; cal_shell_content->priv->previous_selected_end_time = -1; } static void cal_shell_content_display_view_cb (ECalShellContent *cal_shell_content, GalView *gal_view) { ECalViewKind view_kind; GType gal_view_type; gal_view_type = G_OBJECT_TYPE (gal_view); if (gal_view_type == GAL_TYPE_VIEW_ETABLE) { ECalendarView *calendar_view; view_kind = E_CAL_VIEW_KIND_LIST; calendar_view = cal_shell_content->priv->views[view_kind]; gal_view_etable_attach_table ( GAL_VIEW_ETABLE (gal_view), E_CAL_LIST_VIEW (calendar_view)->table); } else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_DAY) { view_kind = E_CAL_VIEW_KIND_DAY; } else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_WORK_WEEK) { view_kind = E_CAL_VIEW_KIND_WORKWEEK; } else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_WEEK) { view_kind = E_CAL_VIEW_KIND_WEEK; } else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_MONTH) { view_kind = E_CAL_VIEW_KIND_MONTH; } else { g_return_if_reached (); } e_cal_shell_content_set_current_view_id (cal_shell_content, view_kind); } static void cal_shell_content_notify_view_id_cb (ECalShellContent *cal_shell_content) { EShellContent *shell_content; EShellView *shell_view; GSettings *settings; GtkWidget *paned; const gchar *key; const gchar *view_id; settings = e_util_ref_settings ("org.gnome.evolution.calendar"); paned = cal_shell_content->priv->hpaned; shell_content = E_SHELL_CONTENT (cal_shell_content); shell_view = e_shell_content_get_shell_view (shell_content); view_id = e_shell_view_get_view_id (shell_view); if (view_id != NULL && strcmp (view_id, "Month_View") == 0) key = "month-hpane-position"; else key = "hpane-position"; g_settings_unbind (paned, "hposition"); g_settings_bind ( settings, key, paned, "hposition", G_SETTINGS_BIND_DEFAULT); g_object_unref (settings); } static void cal_shell_content_is_editing_changed_cb (gpointer cal_view_tasks_memos_table, GParamSpec *param, EShellView *shell_view) { g_return_if_fail (E_IS_SHELL_VIEW (shell_view)); e_shell_view_update_actions (shell_view); } static gchar * cal_shell_content_get_pad_state_filename (EShellContent *shell_content, ETable *table) { EShellBackend *shell_backend; EShellView *shell_view; const gchar *config_dir, *nick = NULL; g_return_val_if_fail (shell_content != NULL, NULL); g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); g_return_val_if_fail (table != NULL, NULL); g_return_val_if_fail (E_IS_TABLE (table), NULL); if (E_IS_TASK_TABLE (table)) nick = "TaskPad"; else if (E_IS_MEMO_TABLE (table)) nick = "MemoPad"; g_return_val_if_fail (nick != NULL, NULL); shell_view = e_shell_content_get_shell_view (shell_content); shell_backend = e_shell_view_get_shell_backend (shell_view); config_dir = e_shell_backend_get_config_dir (shell_backend); return g_build_filename (config_dir, nick, NULL); } static void cal_shell_content_save_table_state (EShellContent *shell_content, ETable *table) { gchar *filename; filename = cal_shell_content_get_pad_state_filename ( shell_content, table); g_return_if_fail (filename != NULL); e_table_save_state (table, filename); g_free (filename); } static void cal_shell_content_load_table_state (EShellContent *shell_content, ETable *table) { gchar *filename; filename = cal_shell_content_get_pad_state_filename (shell_content, table); g_return_if_fail (filename != NULL); e_table_load_state (table, filename); g_free (filename); } static icalproperty * cal_shell_content_get_attendee_prop (icalcomponent *icalcomp, const gchar *address) { icalproperty *prop; if (address == NULL || *address == '\0') return NULL; prop = icalcomponent_get_first_property ( icalcomp, ICAL_ATTENDEE_PROPERTY); while (prop != NULL) { const gchar *attendee; attendee = icalproperty_get_attendee (prop); if (g_str_equal (itip_strip_mailto (attendee), address)) return prop; prop = icalcomponent_get_next_property ( icalcomp, ICAL_ATTENDEE_PROPERTY); } return NULL; } static gboolean cal_shell_content_icalcomp_is_delegated (icalcomponent *icalcomp, const gchar *user_email) { icalproperty *prop; icalparameter *param; const gchar *delto = NULL; gboolean is_delegated = FALSE; prop = cal_shell_content_get_attendee_prop (icalcomp, user_email); if (prop != NULL) { param = icalproperty_get_first_parameter ( prop, ICAL_DELEGATEDTO_PARAMETER); if (param != NULL) { delto = icalparameter_get_delegatedto (param); delto = itip_strip_mailto (delto); } } else return FALSE; prop = cal_shell_content_get_attendee_prop (icalcomp, delto); if (prop != NULL) { const gchar *delfrom = NULL; icalparameter_partstat status = ICAL_PARTSTAT_NONE; param = icalproperty_get_first_parameter ( prop, ICAL_DELEGATEDFROM_PARAMETER); if (param != NULL) { delfrom = icalparameter_get_delegatedfrom (param); delfrom = itip_strip_mailto (delfrom); } param = icalproperty_get_first_parameter ( prop, ICAL_PARTSTAT_PARAMETER); if (param != NULL) status = icalparameter_get_partstat (param); is_delegated = (status != ICAL_PARTSTAT_DECLINED) && (g_strcmp0 (delfrom, user_email) == 0); } return is_delegated; } static guint32 cal_shell_content_check_state (EShellContent *shell_content) { EShell *shell; EShellView *shell_view; EShellBackend *shell_backend; ESourceRegistry *registry; ECalShellContent *cal_shell_content; ECalendarView *calendar_view; gboolean selection_is_editable = FALSE; gboolean selection_is_instance = FALSE; gboolean selection_is_meeting = FALSE; gboolean selection_is_organizer = FALSE; gboolean selection_is_recurring = FALSE; gboolean selection_can_delegate = FALSE; guint32 state = 0; GList *selected; GList *link; guint n_selected; cal_shell_content = E_CAL_SHELL_CONTENT (shell_content); shell_view = e_shell_content_get_shell_view (shell_content); shell_backend = e_shell_view_get_shell_backend (shell_view); shell = e_shell_backend_get_shell (shell_backend); registry = e_shell_get_registry (shell); calendar_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content); selected = e_calendar_view_get_selected_events (calendar_view); n_selected = g_list_length (selected); /* If we have a selection, assume it's * editable until we learn otherwise. */ if (n_selected > 0) selection_is_editable = TRUE; for (link = selected; link != NULL; link = g_list_next (link)) { ECalendarViewEvent *event = link->data; ECalClient *client; ECalComponent *comp; gchar *user_email; icalcomponent *icalcomp; const gchar *capability; gboolean cap_delegate_supported; gboolean cap_delegate_to_many; gboolean icalcomp_is_delegated; gboolean read_only; if (!is_comp_data_valid (event)) continue; client = event->comp_data->client; icalcomp = event->comp_data->icalcomp; read_only = e_client_is_readonly (E_CLIENT (client)); selection_is_editable &= !read_only; selection_is_instance |= e_cal_util_component_is_instance (icalcomp); selection_is_meeting = (n_selected == 1) && e_cal_util_component_has_attendee (icalcomp); selection_is_recurring |= e_cal_util_component_is_instance (icalcomp) || e_cal_util_component_has_recurrences (icalcomp); /* XXX The rest of this is rather expensive and * only applies if a single event is selected, * so continue with the loop iteration if the * rest of this is not applicable. */ if (n_selected > 1) continue; /* XXX This probably belongs in comp-util.c. */ comp = e_cal_component_new (); e_cal_component_set_icalcomponent ( comp, icalcomponent_new_clone (icalcomp)); user_email = itip_get_comp_attendee ( registry, comp, client); selection_is_organizer = e_cal_util_component_has_organizer (icalcomp) && itip_organizer_is_user (registry, comp, client); capability = CAL_STATIC_CAPABILITY_DELEGATE_SUPPORTED; cap_delegate_supported = e_client_check_capability ( E_CLIENT (client), capability); capability = CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY; cap_delegate_to_many = e_client_check_capability ( E_CLIENT (client), capability); icalcomp_is_delegated = (user_email != NULL) && cal_shell_content_icalcomp_is_delegated ( icalcomp, user_email); selection_can_delegate = cap_delegate_supported && (cap_delegate_to_many || (!selection_is_organizer && !icalcomp_is_delegated)); g_free (user_email); g_object_unref (comp); } g_list_free (selected); if (n_selected == 1) state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_SINGLE; if (n_selected > 1) state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_MULTIPLE; if (selection_is_editable) state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_EDITABLE; if (selection_is_instance) state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_INSTANCE; if (selection_is_meeting) state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_MEETING; if (selection_is_organizer) state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_ORGANIZER; if (selection_is_recurring) state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_RECURRING; if (selection_can_delegate) state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_CAN_DELEGATE; return state; } static void cal_shell_content_focus_search_results (EShellContent *shell_content) { ECalendarView *calendar_view; calendar_view = e_cal_shell_content_get_current_calendar_view (E_CAL_SHELL_CONTENT (shell_content)); gtk_widget_grab_focus (GTK_WIDGET (calendar_view)); } static time_t cal_shell_content_get_default_time (ECalModel *model, gpointer user_data) { ECalShellContent *cal_shell_content = user_data; icaltimezone *zone; g_return_val_if_fail (model != NULL, 0); g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), 0); if (e_cal_shell_content_get_current_view_id (cal_shell_content) != E_CAL_VIEW_KIND_LIST) { ECalendarView *cal_view; time_t selected_start = (time_t) 0, selected_end = (time_t) 0; cal_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content); if (cal_view && e_calendar_view_get_selected_time_range (cal_view, &selected_start, &selected_end)) return selected_start; } zone = e_cal_model_get_timezone (model); return icaltime_as_timet_with_zone (icaltime_current_time_with_zone (zone), zone); } static void update_adjustment (ECalShellContent *cal_shell_content, GtkAdjustment *adjustment, EWeekView *week_view, gboolean move_by_week) { GDate start_date, end_date; GDate first_day_shown; ECalModel *model; gint week_offset; struct icaltimetype start_tt = icaltime_null_time (); time_t lower; guint32 old_first_day_julian, new_first_day_julian; icaltimezone *timezone; gdouble value; e_week_view_get_first_day_shown (week_view, &first_day_shown); /* If we don't have a valid date set yet, just return. */ if (!g_date_valid (&first_day_shown)) return; value = gtk_adjustment_get_value (adjustment); /* Determine the first date shown. */ start_date = week_view->base_date; week_offset = floor (value + 0.5); g_date_add_days (&start_date, week_offset * 7); /* Convert the old & new first days shown to julian values. */ old_first_day_julian = g_date_get_julian (&first_day_shown); new_first_day_julian = g_date_get_julian (&start_date); /* If we are already showing the date, just return. */ if (old_first_day_julian == new_first_day_julian) return; /* Convert it to a time_t. */ start_tt.year = g_date_get_year (&start_date); start_tt.month = g_date_get_month (&start_date); start_tt.day = g_date_get_day (&start_date); model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); timezone = e_cal_model_get_timezone (model); lower = icaltime_as_timet_with_zone (start_tt, timezone); end_date = start_date; if (move_by_week) { g_date_add_days (&end_date, 7 - 1); } else { g_date_add_days (&end_date, 7 * e_week_view_get_weeks_shown (week_view) - 1); } e_week_view_set_update_base_date (week_view, FALSE); e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &start_date, &end_date, FALSE); e_calendar_view_set_selected_time_range (E_CALENDAR_VIEW (week_view), lower, lower); e_week_view_set_update_base_date (week_view, TRUE); } static void week_view_adjustment_changed_cb (GtkAdjustment *adjustment, ECalShellContent *cal_shell_content) { ECalendarView *view; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); view = cal_shell_content->priv->views[E_CAL_VIEW_KIND_WEEK]; update_adjustment (cal_shell_content, adjustment, E_WEEK_VIEW (view), TRUE); } static void month_view_adjustment_changed_cb (GtkAdjustment *adjustment, ECalShellContent *cal_shell_content) { ECalendarView *view; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); view = cal_shell_content->priv->views[E_CAL_VIEW_KIND_MONTH]; update_adjustment (cal_shell_content, adjustment, E_WEEK_VIEW (view), FALSE); } static void cal_shell_content_notify_work_day_cb (ECalModel *model, GParamSpec *param, ECalShellContent *cal_shell_content) { GDateWeekday work_day_first, work_day_last; g_return_if_fail (E_IS_CAL_MODEL (model)); g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); if (cal_shell_content->priv->current_view != E_CAL_VIEW_KIND_WORKWEEK) return; work_day_first = e_cal_model_get_work_day_first (model); work_day_last = e_cal_model_get_work_day_last (model); if (work_day_first == g_date_get_weekday (&cal_shell_content->priv->view_start) && work_day_last == g_date_get_weekday (&cal_shell_content->priv->view_end)) return; cal_shell_content->priv->previous_selected_start_time = -1; cal_shell_content->priv->previous_selected_end_time = -1; /* This makes sure that the selection in the datepicker corresponds to the time range used in the Work Week view */ cal_shell_content_current_view_id_changed_cb (cal_shell_content); } static void cal_shell_content_notify_week_start_day_cb (ECalModel *model, GParamSpec *param, ECalShellContent *cal_shell_content) { g_return_if_fail (E_IS_CAL_MODEL (model)); g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); cal_shell_content->priv->previous_selected_start_time = -1; cal_shell_content->priv->previous_selected_end_time = -1; /* This makes sure that the selection in the datepicker corresponds to the time range used in the current view */ cal_shell_content_current_view_id_changed_cb (cal_shell_content); } static void cal_shell_content_move_view_range_cb (ECalendarView *cal_view, ECalendarViewMoveType move_type, gint64 exact_date, ECalShellContent *cal_shell_content) { g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); if (!cal_view->in_focus) return; e_cal_shell_content_move_view_range (cal_shell_content, move_type, (time_t) exact_date); } static void cal_shell_content_foreign_client_opened_cb (ECalBaseShellSidebar *cal_base_shell_sidebar, ECalClient *client, ECalModel *model) { g_return_if_fail (E_IS_CAL_CLIENT (client)); g_return_if_fail (E_IS_CAL_MODEL (model)); e_cal_data_model_add_client (e_cal_model_get_data_model (model), client); } static void cal_shell_content_foreign_client_closed_cb (ECalBaseShellSidebar *cal_base_shell_sidebar, ESource *source, ECalModel *model) { g_return_if_fail (E_IS_SOURCE (source)); g_return_if_fail (E_IS_CAL_MODEL (model)); e_cal_data_model_remove_client (e_cal_model_get_data_model (model), e_source_get_uid (source)); } static void cal_shell_content_setup_foreign_sources (EShellWindow *shell_window, const gchar *view_name, const gchar *extension_name, ECalModel *model) { EShellSidebar *foreign_sidebar; EShellContent *foreign_content; EShellView *foreign_view; ECalModel *foreign_model; gboolean is_new_view; g_return_if_fail (E_IS_SHELL_WINDOW (shell_window)); g_return_if_fail (E_IS_CAL_MODEL (model)); is_new_view = e_shell_window_peek_shell_view (shell_window, view_name) == NULL; foreign_view = e_shell_window_get_shell_view (shell_window, view_name); g_return_if_fail (E_IS_SHELL_VIEW (foreign_view)); foreign_sidebar = e_shell_view_get_shell_sidebar (foreign_view); g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (foreign_sidebar)); if (is_new_view) { /* Preselect default source, when the view was not created yet */ ESourceSelector *source_selector; ESourceRegistry *registry; ESource *source; source_selector = e_cal_base_shell_sidebar_get_selector (E_CAL_BASE_SHELL_SIDEBAR (foreign_sidebar)); registry = e_source_selector_get_registry (source_selector); source = e_source_registry_ref_default_for_extension_name (registry, extension_name); if (source) e_source_selector_set_primary_selection (source_selector, source); g_clear_object (&source); } g_signal_connect_object (foreign_sidebar, "client-opened", G_CALLBACK (cal_shell_content_foreign_client_opened_cb), model, 0); g_signal_connect_object (foreign_sidebar, "client-closed", G_CALLBACK (cal_shell_content_foreign_client_closed_cb), model, 0); foreign_content = e_shell_view_get_shell_content (foreign_view); foreign_model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (foreign_content)); e_binding_bind_property ( foreign_model, "default-source-uid", model, "default-source-uid", G_BINDING_SYNC_CREATE); g_signal_connect_object (model, "row-appended", G_CALLBACK (e_cal_base_shell_view_model_row_appended), foreign_view, G_CONNECT_SWAPPED); /* This makes sure that the local models for memos and tasks in the calendar view get populated with the same sources as those in the respective views. */ e_cal_base_shell_sidebar_ensure_sources_open (E_CAL_BASE_SHELL_SIDEBAR (foreign_sidebar)); } static void cal_shell_content_view_created (ECalBaseShellContent *cal_base_shell_content) { ECalShellContent *cal_shell_content; EShellView *shell_view; EShellWindow *shell_window; EShellSidebar *shell_sidebar; GalViewInstance *view_instance; ECalendar *calendar; ECalModel *model; ECalDataModel *data_model; GDate date; time_t today; cal_shell_content = E_CAL_SHELL_CONTENT (cal_base_shell_content); cal_shell_content->priv->current_view = E_CAL_VIEW_KIND_DAY; today = time (NULL); g_date_clear (&date, 1); g_date_set_time_t (&date, today); shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content)); shell_window = e_shell_view_get_shell_window (shell_view); shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (shell_sidebar)); calendar = e_cal_base_shell_sidebar_get_date_navigator (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar)); g_return_if_fail (E_IS_CALENDAR (calendar)); model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); e_calendar_item_set_selection (e_calendar_get_item (calendar), &date, &date); e_cal_model_set_time_range (model, today, today); /* Show everything known by default in the task and memo pads */ e_cal_model_set_time_range (cal_shell_content->priv->memo_model, 0, 0); e_cal_model_set_time_range (cal_shell_content->priv->task_model, 0, 0); cal_shell_content->priv->datepicker_selection_changed_id = g_signal_connect (e_calendar_get_item (calendar), "selection-changed", G_CALLBACK (cal_shell_content_datepicker_selection_changed_cb), cal_shell_content); cal_shell_content->priv->datepicker_range_moved_id = g_signal_connect (e_calendar_get_item (calendar), "date-range-moved", G_CALLBACK (cal_shell_content_datepicker_range_moved_cb), cal_shell_content); g_signal_connect_after (calendar, "button-press-event", G_CALLBACK (cal_shell_content_datepicker_button_press_cb), cal_shell_content); data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); cal_shell_content->priv->tag_calendar = e_tag_calendar_new (calendar); e_tag_calendar_subscribe (cal_shell_content->priv->tag_calendar, data_model); /* Intentionally not using e_signal_connect_notify() here, no need to filter out "false" notifications, it's dealt with them in another way */ cal_shell_content->priv->current_view_id_changed_id = g_signal_connect ( cal_shell_content, "notify::current-view-id", G_CALLBACK (cal_shell_content_current_view_id_changed_cb), NULL); /* List of selected Task/Memo sources is taken from respective views, which are loaded if necessary. */ cal_shell_content_setup_foreign_sources (shell_window, "memos", E_SOURCE_EXTENSION_MEMO_LIST, cal_shell_content->priv->memo_model); cal_shell_content_setup_foreign_sources (shell_window, "tasks", E_SOURCE_EXTENSION_TASK_LIST, cal_shell_content->priv->task_model); /* Finally load the view instance */ view_instance = e_shell_view_get_view_instance (shell_view); gal_view_instance_load (view_instance); /* Keep the toolbar view buttons in sync with the calendar. */ e_binding_bind_property ( cal_shell_content, "current-view-id", ACTION (CALENDAR_VIEW_DAY), "current-value", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); e_signal_connect_notify ( model, "notify::work-day-monday", G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content); e_signal_connect_notify ( model, "notify::work-day-tuesday", G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content); e_signal_connect_notify ( model, "notify::work-day-wednesday", G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content); e_signal_connect_notify ( model, "notify::work-day-thursday", G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content); e_signal_connect_notify ( model, "notify::work-day-friday", G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content); e_signal_connect_notify ( model, "notify::work-day-saturday", G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content); e_signal_connect_notify ( model, "notify::work-day-sunday", G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content); e_signal_connect_notify ( model, "notify::week-start-day", G_CALLBACK (cal_shell_content_notify_week_start_day_cb), cal_shell_content); } static void e_cal_shell_content_create_calendar_views (ECalShellContent *cal_shell_content) { EShellView *shell_view; ECalModel *model; ECalendarView *calendar_view; GtkAdjustment *adjustment; time_t today; gint ii; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); g_return_if_fail (cal_shell_content->priv->calendar_notebook != NULL); g_return_if_fail (cal_shell_content->priv->views[0] == NULL); model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); /* Day View */ calendar_view = e_day_view_new (model); cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY] = calendar_view; g_object_ref_sink (calendar_view); /* Work Week View */ calendar_view = e_day_view_new (model); e_day_view_set_work_week_view (E_DAY_VIEW (calendar_view), TRUE); e_day_view_set_days_shown (E_DAY_VIEW (calendar_view), 5); cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK] = calendar_view; g_object_ref_sink (calendar_view); /* Week View */ calendar_view = e_week_view_new (model); cal_shell_content->priv->views[E_CAL_VIEW_KIND_WEEK] = calendar_view; g_object_ref_sink (calendar_view); adjustment = gtk_range_get_adjustment ( GTK_RANGE (E_WEEK_VIEW (calendar_view)->vscrollbar)); g_signal_connect ( adjustment, "value-changed", G_CALLBACK (week_view_adjustment_changed_cb), cal_shell_content); /* Month View */ calendar_view = e_month_view_new (model); e_week_view_set_multi_week_view (E_WEEK_VIEW (calendar_view), TRUE); e_week_view_set_weeks_shown (E_WEEK_VIEW (calendar_view), 6); cal_shell_content->priv->views[E_CAL_VIEW_KIND_MONTH] = calendar_view; g_object_ref_sink (calendar_view); adjustment = gtk_range_get_adjustment ( GTK_RANGE (E_WEEK_VIEW (calendar_view)->vscrollbar)); g_signal_connect ( adjustment, "value-changed", G_CALLBACK (month_view_adjustment_changed_cb), cal_shell_content); /* List View */ calendar_view = e_cal_list_view_new (model); cal_shell_content->priv->views[E_CAL_VIEW_KIND_LIST] = calendar_view; g_object_ref_sink (calendar_view); shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content)); today = time (NULL); for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) { calendar_view = cal_shell_content->priv->views[ii]; calendar_view->in_focus = ii == cal_shell_content->priv->current_view; e_calendar_view_set_selected_time_range (calendar_view, today, today); e_signal_connect_notify ( calendar_view, "notify::is-editing", G_CALLBACK (cal_shell_content_is_editing_changed_cb), shell_view); g_signal_connect ( calendar_view, "move-view-range", G_CALLBACK (cal_shell_content_move_view_range_cb), cal_shell_content); gtk_notebook_append_page ( GTK_NOTEBOOK (cal_shell_content->priv->calendar_notebook), GTK_WIDGET (calendar_view), NULL); gtk_widget_show (GTK_WIDGET (calendar_view)); } } static void cal_shell_content_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_CURRENT_VIEW_ID: e_cal_shell_content_set_current_view_id (E_CAL_SHELL_CONTENT (object), g_value_get_int (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void cal_shell_content_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_CALENDAR_NOTEBOOK: g_value_set_object ( value, e_cal_shell_content_get_calendar_notebook ( E_CAL_SHELL_CONTENT (object))); return; case PROP_MEMO_TABLE: g_value_set_object ( value, e_cal_shell_content_get_memo_table ( E_CAL_SHELL_CONTENT (object))); return; case PROP_TASK_TABLE: g_value_set_object ( value, e_cal_shell_content_get_task_table ( E_CAL_SHELL_CONTENT (object))); return; case PROP_CURRENT_VIEW_ID: g_value_set_int (value, e_cal_shell_content_get_current_view_id (E_CAL_SHELL_CONTENT (object))); return; case PROP_CURRENT_VIEW: g_value_set_object (value, e_cal_shell_content_get_current_calendar_view (E_CAL_SHELL_CONTENT (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void cal_shell_content_dispose (GObject *object) { ECalShellContent *cal_shell_content = E_CAL_SHELL_CONTENT (object); gint ii; if (cal_shell_content->priv->task_data_model) { e_cal_data_model_set_disposing (cal_shell_content->priv->task_data_model, TRUE); e_cal_data_model_unsubscribe (cal_shell_content->priv->task_data_model, E_CAL_DATA_MODEL_SUBSCRIBER (cal_shell_content->priv->task_model)); } if (cal_shell_content->priv->memo_data_model) { e_cal_data_model_set_disposing (cal_shell_content->priv->memo_data_model, TRUE); e_cal_data_model_unsubscribe (cal_shell_content->priv->memo_data_model, E_CAL_DATA_MODEL_SUBSCRIBER (cal_shell_content->priv->memo_model)); } if (cal_shell_content->priv->tag_calendar) { ECalDataModel *data_model; data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); e_cal_data_model_set_disposing (data_model, TRUE); e_tag_calendar_unsubscribe (cal_shell_content->priv->tag_calendar, data_model); g_clear_object (&cal_shell_content->priv->tag_calendar); } for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) { g_clear_object (&(cal_shell_content->priv->views[ii])); } g_clear_object (&cal_shell_content->priv->hpaned); g_clear_object (&cal_shell_content->priv->vpaned); g_clear_object (&cal_shell_content->priv->calendar_notebook); g_clear_object (&cal_shell_content->priv->task_table); g_clear_object (&cal_shell_content->priv->task_model); g_clear_object (&cal_shell_content->priv->task_data_model); g_clear_object (&cal_shell_content->priv->memo_table); g_clear_object (&cal_shell_content->priv->memo_model); g_clear_object (&cal_shell_content->priv->memo_data_model); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_cal_shell_content_parent_class)->dispose (object); } static void cal_shell_content_constructed (GObject *object) { ECalShellContent *cal_shell_content; EShellContent *shell_content; EShellView *shell_view; EShellWindow *shell_window; EShell *shell; GalViewInstance *view_instance; GSettings *settings; GtkWidget *container; GtkWidget *widget; gchar *markup; /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_cal_shell_content_parent_class)->constructed (object); cal_shell_content = E_CAL_SHELL_CONTENT (object); shell_content = E_SHELL_CONTENT (cal_shell_content); shell_view = e_shell_content_get_shell_view (shell_content); shell_window = e_shell_view_get_shell_window (shell_view); shell = e_shell_window_get_shell (shell_window); cal_shell_content->priv->memo_data_model = e_cal_base_shell_content_create_new_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); cal_shell_content->priv->memo_model = e_cal_model_memos_new (cal_shell_content->priv->memo_data_model, e_shell_get_registry (shell), shell); cal_shell_content->priv->task_data_model = e_cal_base_shell_content_create_new_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); cal_shell_content->priv->task_model = e_cal_model_tasks_new (cal_shell_content->priv->task_data_model, e_shell_get_registry (shell), shell); e_binding_bind_property ( cal_shell_content->priv->memo_model, "timezone", cal_shell_content->priv->memo_data_model, "timezone", G_BINDING_SYNC_CREATE); e_binding_bind_property ( cal_shell_content->priv->task_model, "timezone", cal_shell_content->priv->task_data_model, "timezone", G_BINDING_SYNC_CREATE); /* Build content widgets. */ container = GTK_WIDGET (object); widget = e_paned_new (GTK_ORIENTATION_HORIZONTAL); gtk_container_add (GTK_CONTAINER (container), widget); cal_shell_content->priv->hpaned = g_object_ref (widget); gtk_widget_show (widget); container = cal_shell_content->priv->hpaned; widget = gtk_notebook_new (); gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE); gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE); gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE); cal_shell_content->priv->calendar_notebook = g_object_ref (widget); gtk_widget_show (widget); widget = e_paned_new (GTK_ORIENTATION_VERTICAL); e_paned_set_fixed_resize (E_PANED (widget), FALSE); gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, TRUE); cal_shell_content->priv->vpaned = g_object_ref (widget); gtk_widget_show (widget); container = cal_shell_content->priv->calendar_notebook; e_cal_shell_content_create_calendar_views (cal_shell_content); e_binding_bind_property ( cal_shell_content, "current-view-id", cal_shell_content->priv->calendar_notebook, "page", G_BINDING_SYNC_CREATE); container = cal_shell_content->priv->vpaned; widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, TRUE); gtk_widget_show (widget); container = widget; widget = gtk_label_new (NULL); markup = g_strdup_printf ("%s", _("Tasks")); gtk_label_set_markup (GTK_LABEL (widget), markup); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0); gtk_widget_show (widget); g_free (markup); widget = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); gtk_widget_show (widget); container = widget; widget = e_task_table_new (shell_view, cal_shell_content->priv->task_model); gtk_container_add (GTK_CONTAINER (container), widget); cal_shell_content->priv->task_table = g_object_ref (widget); gtk_widget_show (widget); cal_shell_content_load_table_state (shell_content, E_TABLE (widget)); g_signal_connect_swapped ( widget, "open-component", G_CALLBACK (e_cal_shell_view_taskpad_open_task), shell_view); e_signal_connect_notify ( widget, "notify::is-editing", G_CALLBACK (cal_shell_content_is_editing_changed_cb), shell_view); container = cal_shell_content->priv->vpaned; widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_paned_pack2 (GTK_PANED (container), widget, TRUE, TRUE); gtk_widget_show (widget); container = widget; widget = gtk_label_new (NULL); markup = g_strdup_printf ("%s", _("Memos")); gtk_label_set_markup (GTK_LABEL (widget), markup); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0); gtk_widget_show (widget); g_free (markup); widget = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); gtk_widget_show (widget); container = widget; widget = e_memo_table_new (shell_view, cal_shell_content->priv->memo_model); gtk_container_add (GTK_CONTAINER (container), widget); cal_shell_content->priv->memo_table = g_object_ref (widget); gtk_widget_show (widget); cal_shell_content_load_table_state (shell_content, E_TABLE (widget)); e_cal_model_set_default_time_func (cal_shell_content->priv->memo_model, cal_shell_content_get_default_time, cal_shell_content); g_signal_connect_swapped ( widget, "open-component", G_CALLBACK (e_cal_shell_view_memopad_open_memo), shell_view); e_signal_connect_notify ( widget, "notify::is-editing", G_CALLBACK (cal_shell_content_is_editing_changed_cb), shell_view); /* Prepare the view instance. */ view_instance = e_shell_view_new_view_instance (shell_view, NULL); g_signal_connect_swapped ( view_instance, "display-view", G_CALLBACK (cal_shell_content_display_view_cb), object); /* Actual load happens at cal_shell_content_view_created() */ e_shell_view_set_view_instance (shell_view, view_instance); g_object_unref (view_instance); e_signal_connect_notify_swapped ( shell_view, "notify::view-id", G_CALLBACK (cal_shell_content_notify_view_id_cb), cal_shell_content); settings = e_util_ref_settings ("org.gnome.evolution.calendar"); g_settings_bind ( settings, "tag-vpane-position", cal_shell_content->priv->vpaned, "proportion", G_SETTINGS_BIND_DEFAULT); g_object_unref (settings); /* Cannot access shell sidebar here, thus rely on cal_shell_content_view_created() with exact widget settings which require it */ } static void e_cal_shell_content_class_init (ECalShellContentClass *class) { GObjectClass *object_class; EShellContentClass *shell_content_class; ECalBaseShellContentClass *cal_base_shell_content_class; g_type_class_add_private (class, sizeof (ECalShellContentPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = cal_shell_content_set_property; object_class->get_property = cal_shell_content_get_property; object_class->dispose = cal_shell_content_dispose; object_class->constructed = cal_shell_content_constructed; shell_content_class = E_SHELL_CONTENT_CLASS (class); shell_content_class->check_state = cal_shell_content_check_state; shell_content_class->focus_search_results = cal_shell_content_focus_search_results; cal_base_shell_content_class = E_CAL_BASE_SHELL_CONTENT_CLASS (class); cal_base_shell_content_class->new_cal_model = e_cal_model_calendar_new; cal_base_shell_content_class->view_created = cal_shell_content_view_created; g_object_class_install_property ( object_class, PROP_CALENDAR_NOTEBOOK, g_param_spec_object ( "calendar-notebook", NULL, NULL, GTK_TYPE_NOTEBOOK, G_PARAM_READABLE)); g_object_class_install_property ( object_class, PROP_MEMO_TABLE, g_param_spec_object ( "memo-table", NULL, NULL, E_TYPE_MEMO_TABLE, G_PARAM_READABLE)); g_object_class_install_property ( object_class, PROP_TASK_TABLE, g_param_spec_object ( "task-table", NULL, NULL, E_TYPE_TASK_TABLE, G_PARAM_READABLE)); g_object_class_install_property ( object_class, PROP_CURRENT_VIEW_ID, g_param_spec_int ( "current-view-id", "Current Calendar View ID", NULL, E_CAL_VIEW_KIND_DAY, E_CAL_VIEW_KIND_LAST - 1, E_CAL_VIEW_KIND_DAY, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_CURRENT_VIEW, g_param_spec_object ( "current-view", "Current Calendar View", NULL, E_TYPE_CALENDAR_VIEW, G_PARAM_READABLE)); } static void e_cal_shell_content_class_finalize (ECalShellContentClass *class) { } static void e_cal_shell_content_init (ECalShellContent *cal_shell_content) { time_t now; cal_shell_content->priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (cal_shell_content); g_date_clear (&cal_shell_content->priv->view_start, 1); g_date_clear (&cal_shell_content->priv->view_end, 1); g_date_clear (&cal_shell_content->priv->last_range_start, 1); now = time (NULL); g_date_set_time_t (&cal_shell_content->priv->view_start, now); g_date_set_time_t (&cal_shell_content->priv->view_end, now); cal_shell_content->priv->view_start_range_day_offset = (guint32) -1; cal_shell_content->priv->previous_selected_start_time = -1; cal_shell_content->priv->previous_selected_end_time = -1; } void e_cal_shell_content_type_register (GTypeModule *type_module) { /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration * function, so we have to wrap it with a public function in * order to register types from a separate compilation unit. */ e_cal_shell_content_register_type (type_module); } GtkWidget * e_cal_shell_content_new (EShellView *shell_view) { g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); return g_object_new ( E_TYPE_CAL_SHELL_CONTENT, "shell-view", shell_view, NULL); } GtkNotebook * e_cal_shell_content_get_calendar_notebook (ECalShellContent *cal_shell_content) { g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); return GTK_NOTEBOOK (cal_shell_content->priv->calendar_notebook); } EMemoTable * e_cal_shell_content_get_memo_table (ECalShellContent *cal_shell_content) { g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); return E_MEMO_TABLE (cal_shell_content->priv->memo_table); } ETaskTable * e_cal_shell_content_get_task_table (ECalShellContent *cal_shell_content) { g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); return E_TASK_TABLE (cal_shell_content->priv->task_table); } EShellSearchbar * e_cal_shell_content_get_searchbar (ECalShellContent *cal_shell_content) { EShellView *shell_view; EShellContent *shell_content; GtkWidget *widget; g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); shell_content = E_SHELL_CONTENT (cal_shell_content); shell_view = e_shell_content_get_shell_view (shell_content); widget = e_shell_view_get_searchbar (shell_view); return E_SHELL_SEARCHBAR (widget); } static void cal_shell_content_resubscribe (ECalendarView *cal_view, ECalModel *model) { ECalDataModel *data_model; ECalDataModelSubscriber *subscriber; time_t range_start, range_end; gboolean is_tasks_or_memos; g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); g_return_if_fail (E_IS_CAL_MODEL (model)); data_model = e_cal_model_get_data_model (model); subscriber = E_CAL_DATA_MODEL_SUBSCRIBER (model); is_tasks_or_memos = e_cal_model_get_component_kind (model) == ICAL_VJOURNAL_COMPONENT || e_cal_model_get_component_kind (model) == ICAL_VTODO_COMPONENT; if ((!is_tasks_or_memos && e_calendar_view_get_visible_time_range (cal_view, &range_start, &range_end)) || e_cal_data_model_get_subscriber_range (data_model, subscriber, &range_start, &range_end)) { e_cal_data_model_unsubscribe (data_model, subscriber); e_cal_model_remove_all_objects (model); if (is_tasks_or_memos) e_cal_data_model_subscribe (data_model, subscriber, range_start, range_end); } } void e_cal_shell_content_set_current_view_id (ECalShellContent *cal_shell_content, ECalViewKind view_kind) { time_t start_time = -1, end_time = -1; gint ii; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); g_return_if_fail (view_kind >= E_CAL_VIEW_KIND_DAY && view_kind < E_CAL_VIEW_KIND_LAST); if (cal_shell_content->priv->current_view == view_kind) return; if (cal_shell_content->priv->current_view >= E_CAL_VIEW_KIND_DAY && cal_shell_content->priv->current_view < E_CAL_VIEW_KIND_LAST) { ECalendarView *cal_view = cal_shell_content->priv->views[cal_shell_content->priv->current_view]; if (!e_calendar_view_get_selected_time_range (cal_view, &start_time, &end_time)) { start_time = -1; end_time = -1; } } cal_shell_content->priv->previous_selected_start_time = start_time; cal_shell_content->priv->previous_selected_end_time = end_time; for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) { ECalendarView *cal_view = cal_shell_content->priv->views[ii]; gboolean in_focus = ii == view_kind; gboolean focus_changed; if (!cal_view) { g_warn_if_reached (); continue; } focus_changed = (cal_view->in_focus ? 1 : 0) != (in_focus ? 1 : 0); cal_view->in_focus = in_focus; if (focus_changed && in_focus) { /* Currently focused view changed. Any events within the common time range are not shown in the newly focused view, thus make sure it'll contain all what it should have. */ ECalModel *model; model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); /* This may not cause any queries to backends with events, because the time range should be always within the one shown in the date picker. */ cal_shell_content_resubscribe (cal_view, model); if (cal_shell_content->priv->task_table) { ETaskTable *task_table; task_table = E_TASK_TABLE (cal_shell_content->priv->task_table); cal_shell_content_resubscribe (cal_view, e_task_table_get_model (task_table)); } if (cal_shell_content->priv->memo_table) { EMemoTable *memo_table; memo_table = E_MEMO_TABLE (cal_shell_content->priv->memo_table); cal_shell_content_resubscribe (cal_view, e_memo_table_get_model (memo_table)); } } } cal_shell_content->priv->current_view = view_kind; g_object_notify (G_OBJECT (cal_shell_content), "current-view-id"); gtk_widget_queue_draw (GTK_WIDGET (cal_shell_content->priv->views[cal_shell_content->priv->current_view])); } ECalViewKind e_cal_shell_content_get_current_view_id (ECalShellContent *cal_shell_content) { g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), E_CAL_VIEW_KIND_LAST); return cal_shell_content->priv->current_view; } ECalendarView * e_cal_shell_content_get_calendar_view (ECalShellContent *cal_shell_content, ECalViewKind view_kind) { g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); g_return_val_if_fail (view_kind >= E_CAL_VIEW_KIND_DAY && view_kind < E_CAL_VIEW_KIND_LAST, NULL); return cal_shell_content->priv->views[view_kind]; } ECalendarView * e_cal_shell_content_get_current_calendar_view (ECalShellContent *cal_shell_content) { g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); return e_cal_shell_content_get_calendar_view (cal_shell_content, e_cal_shell_content_get_current_view_id (cal_shell_content)); } void e_cal_shell_content_save_state (ECalShellContent *cal_shell_content) { ECalShellContentPrivate *priv; g_return_if_fail (cal_shell_content != NULL); g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); priv = cal_shell_content->priv; if (priv->task_table != NULL) cal_shell_content_save_table_state ( E_SHELL_CONTENT (cal_shell_content), E_TABLE (priv->task_table)); if (priv->memo_table != NULL) cal_shell_content_save_table_state ( E_SHELL_CONTENT (cal_shell_content), E_TABLE (priv->memo_table)); } void e_cal_shell_content_get_current_range (ECalShellContent *cal_shell_content, time_t *range_start, time_t *range_end) { icaltimezone *zone; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); g_return_if_fail (range_start != NULL); g_return_if_fail (range_end != NULL); zone = e_cal_data_model_get_timezone (e_cal_base_shell_content_get_data_model ( E_CAL_BASE_SHELL_CONTENT (cal_shell_content))); *range_start = cal_comp_gdate_to_timet (&(cal_shell_content->priv->view_start), zone); *range_end = cal_comp_gdate_to_timet (&(cal_shell_content->priv->view_end), zone); } void e_cal_shell_content_get_current_range_dates (ECalShellContent *cal_shell_content, GDate *range_start, GDate *range_end) { g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); g_return_if_fail (range_start != NULL); g_return_if_fail (range_end != NULL); *range_start = cal_shell_content->priv->view_start; *range_end = cal_shell_content->priv->view_end; } static void cal_shell_content_move_view_range_relative (ECalShellContent *cal_shell_content, gint direction) { GDate start, end; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); g_return_if_fail (direction != 0); start = cal_shell_content->priv->view_start; end = cal_shell_content->priv->view_end; switch (cal_shell_content->priv->current_view) { case E_CAL_VIEW_KIND_DAY: if (direction > 0) { g_date_add_days (&start, direction); g_date_add_days (&end, direction); } else { g_date_subtract_days (&start, direction * -1); g_date_subtract_days (&end, direction * -1); } break; case E_CAL_VIEW_KIND_WORKWEEK: case E_CAL_VIEW_KIND_WEEK: if (direction > 0) { g_date_add_days (&start, direction * 7); g_date_add_days (&end, direction * 7); } else { g_date_subtract_days (&start, direction * -7); g_date_subtract_days (&end, direction * -7); } break; case E_CAL_VIEW_KIND_MONTH: case E_CAL_VIEW_KIND_LIST: if (g_date_get_day (&start) != 1) { g_date_add_months (&start, 1); g_date_set_day (&start, 1); } if (direction > 0) g_date_add_months (&start, direction); else g_date_subtract_months (&start, direction * -1); end = start; g_date_set_day (&end, g_date_get_days_in_month (g_date_get_month (&start), g_date_get_year (&start))); g_date_add_days (&end, 6); break; case E_CAL_VIEW_KIND_LAST: return; } e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &start, &end, FALSE); } void e_cal_shell_content_move_view_range (ECalShellContent *cal_shell_content, ECalendarViewMoveType move_type, time_t exact_date) { ECalendar *calendar; ECalDataModel *data_model; EShellSidebar *shell_sidebar; EShellView *shell_view; struct icaltimetype tt; icaltimezone *zone; GDate date; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content)); shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (shell_sidebar)); calendar = e_cal_base_shell_sidebar_get_date_navigator (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar)); g_return_if_fail (E_IS_CALENDAR (calendar)); g_return_if_fail (e_calendar_get_item (calendar) != NULL); data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); zone = e_cal_data_model_get_timezone (data_model); switch (move_type) { case E_CALENDAR_VIEW_MOVE_PREVIOUS: cal_shell_content_move_view_range_relative (cal_shell_content, -1); break; case E_CALENDAR_VIEW_MOVE_NEXT: cal_shell_content_move_view_range_relative (cal_shell_content, +1); break; case E_CALENDAR_VIEW_MOVE_TO_TODAY: tt = icaltime_current_time_with_zone (zone); g_date_set_dmy (&date, tt.day, tt.month, tt.year); /* one-day selection takes care of the view range move with left view kind */ e_calendar_item_set_selection (e_calendar_get_item (calendar), &date, &date); break; case E_CALENDAR_VIEW_MOVE_TO_EXACT_DAY: time_to_gdate_with_zone (&date, exact_date, zone); e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &date, &date, FALSE); break; } } static void cal_shell_content_update_model_filter (ECalDataModel *data_model, ECalModel *model, const gchar *filter, time_t range_start, time_t range_end) { time_t tmp_start, tmp_end; g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model)); g_return_if_fail (E_IS_CAL_MODEL (model)); e_cal_data_model_freeze_views_update (data_model); if (filter != NULL) e_cal_data_model_set_filter (data_model, filter); e_cal_model_set_time_range (model, range_start, range_end); if (!e_cal_data_model_get_subscriber_range (data_model, E_CAL_DATA_MODEL_SUBSCRIBER (model), &tmp_start, &tmp_end)) { e_cal_data_model_subscribe (data_model, E_CAL_DATA_MODEL_SUBSCRIBER (model), range_start, range_end); } e_cal_data_model_thaw_views_update (data_model); } void e_cal_shell_content_update_filters (ECalShellContent *cal_shell_content, const gchar *cal_filter, time_t start_range, time_t end_range) { ECalDataModel *data_model; ECalModel *model; g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); if (!cal_filter) return; data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)); cal_shell_content_update_model_filter (data_model, model, cal_filter, start_range, end_range); if (cal_shell_content->priv->task_table) { ETaskTable *task_table; gchar *hide_completed_tasks_sexp; /* Set the query on the task pad. */ task_table = E_TASK_TABLE (cal_shell_content->priv->task_table); model = e_task_table_get_model (task_table); data_model = e_cal_model_get_data_model (model); hide_completed_tasks_sexp = calendar_config_get_hide_completed_tasks_sexp (FALSE); if (hide_completed_tasks_sexp != NULL) { if (*cal_filter) { gchar *filter; filter = g_strdup_printf ("(and %s %s)", hide_completed_tasks_sexp, cal_filter); cal_shell_content_update_model_filter (data_model, model, filter, 0, 0); g_free (filter); } else { cal_shell_content_update_model_filter (data_model, model, hide_completed_tasks_sexp, 0, 0); } } else { cal_shell_content_update_model_filter (data_model, model, *cal_filter ? cal_filter : "#t", 0, 0); } g_free (hide_completed_tasks_sexp); } if (cal_shell_content->priv->memo_table) { EMemoTable *memo_table; /* Set the query on the memo pad. */ memo_table = E_MEMO_TABLE (cal_shell_content->priv->memo_table); model = e_memo_table_get_model (memo_table); data_model = e_cal_model_get_data_model (model); if (start_range != 0 && end_range != 0) { icaltimezone *zone; const gchar *default_tzloc = NULL; time_t end = end_range; gchar *filter; gchar *iso_start; gchar *iso_end; zone = e_cal_data_model_get_timezone (data_model); if (zone && zone != icaltimezone_get_utc_timezone ()) default_tzloc = icaltimezone_get_location (zone); if (!default_tzloc) default_tzloc = ""; if (start_range != (time_t) 0 && end_range != (time_t) 0) { end = time_day_end_with_zone (end_range, zone); } iso_start = isodate_from_time_t (start_range); iso_end = isodate_from_time_t (end); filter = g_strdup_printf ( "(and (or (not (has-start?)) " "(occur-in-time-range? (make-time \"%s\") " "(make-time \"%s\") \"%s\")) %s)", iso_start, iso_end, default_tzloc, cal_filter); cal_shell_content_update_model_filter (data_model, model, filter, 0, 0); g_free (filter); g_free (iso_start); g_free (iso_end); } else { cal_shell_content_update_model_filter (data_model, model, *cal_filter ? cal_filter : "#t", 0, 0); } } }