/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "evolution-config.h"
#include <string.h>
#include <math.h>
#include <glib/gi18n-lib.h>
#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 ("<b>%s</b>", _("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 ("<b>%s</b>", _("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);
}
}
}