1 /*
2  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
3  * Copyright (C) 2014 Red Hat, Inc. (www.redhat.com)
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "evolution-config.h"
19 
20 #include <string.h>
21 #include <math.h>
22 #include <glib/gi18n-lib.h>
23 
24 #include "calendar/gui/calendar-config.h"
25 #include "calendar/gui/calendar-view.h"
26 #include "calendar/gui/comp-util.h"
27 #include "calendar/gui/e-cal-list-view.h"
28 #include "calendar/gui/e-cal-model-calendar.h"
29 #include "calendar/gui/e-cal-model-memos.h"
30 #include "calendar/gui/e-cal-model-tasks.h"
31 #include "calendar/gui/e-calendar-view.h"
32 #include "calendar/gui/e-day-view.h"
33 #include "calendar/gui/e-month-view.h"
34 #include "calendar/gui/e-week-view.h"
35 #include "calendar/gui/itip-utils.h"
36 #include "calendar/gui/tag-calendar.h"
37 
38 #include "e-cal-base-shell-sidebar.h"
39 #include "e-cal-shell-view-private.h"
40 #include "e-cal-shell-content.h"
41 
42 #define E_CAL_SHELL_CONTENT_GET_PRIVATE(obj) \
43 	(G_TYPE_INSTANCE_GET_PRIVATE \
44 	((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentPrivate))
45 
46 struct _ECalShellContentPrivate {
47 	GtkWidget *hpaned;
48 	GtkWidget *vpaned;
49 
50 	GtkWidget *calendar_notebook;
51 	GtkWidget *task_table;
52 	ECalModel *task_model;
53 	ECalDataModel *task_data_model;
54 
55 	GtkWidget *memo_table;
56 	ECalModel *memo_model;
57 	ECalDataModel *memo_data_model;
58 
59 	ECalModel *list_view_model;
60 	ECalDataModel *list_view_data_model;
61 
62 	ETagCalendar *tag_calendar;
63 	gulong datepicker_selection_changed_id;
64 	gulong datepicker_range_moved_id;
65 
66 	ECalViewKind current_view;
67 	ECalendarView *views[E_CAL_VIEW_KIND_LAST];
68 	GDate view_start, view_end;
69 	guint32 view_start_range_day_offset;
70 	GDate last_range_start; /* because "date-range-changed" can be emit with no real change */
71 
72 	time_t previous_selected_start_time;
73 	time_t previous_selected_end_time;
74 
75 	gulong current_view_id_changed_id;
76 
77 	gboolean initialized;
78 };
79 
80 enum {
81 	PROP_0,
82 	PROP_CALENDAR_NOTEBOOK,
83 	PROP_MEMO_TABLE,
84 	PROP_TASK_TABLE,
85 	PROP_CURRENT_VIEW_ID,
86 	PROP_CURRENT_VIEW,
87 	PROP_SHOW_TAG_VPANE
88 };
89 
90 /* Used to indicate who has the focus within the calendar view. */
91 typedef enum {
92 	FOCUS_CALENDAR_NOTEBOOK,
93 	FOCUS_MEMO_TABLE,
94 	FOCUS_TASK_TABLE,
95 	FOCUS_OTHER
96 } FocusLocation;
97 
G_DEFINE_DYNAMIC_TYPE(ECalShellContent,e_cal_shell_content,E_TYPE_CAL_BASE_SHELL_CONTENT)98 G_DEFINE_DYNAMIC_TYPE (ECalShellContent, e_cal_shell_content, E_TYPE_CAL_BASE_SHELL_CONTENT)
99 
100 static time_t
101 convert_to_local_zone (time_t tm,
102 		       ICalTimezone *from_zone)
103 {
104 	ICalTime *itt;
105 	time_t tt;
106 
107 	itt = i_cal_time_new_from_timet_with_zone (tm, FALSE, from_zone);
108 	tt = i_cal_time_as_timet (itt);
109 	g_clear_object (&itt);
110 
111 	return tt;
112 }
113 
114 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)115 cal_shell_content_update_model_and_current_view_times (ECalShellContent *cal_shell_content,
116 						       ECalModel *model,
117 						       ECalendarItem *calitem,
118 						       time_t view_start_tt,
119 						       time_t view_end_tt,
120 						       const GDate *view_start,
121 						       const GDate *view_end)
122 {
123 	ECalendarView *current_view;
124 	EDayView *day_view = NULL;
125 	gint day_view_selection_start_day = -1, day_view_selection_end_day = -1;
126 	gint day_view_selection_start_row = -1, day_view_selection_end_row = -1;
127 	gdouble day_view_scrollbar_position = 0.0;
128 	gint syy, smm, sdd, eyy, emm, edd;
129 	time_t visible_range_start, visible_range_end;
130 	gboolean filters_updated = FALSE;
131 	ICalTimezone *zone;
132 	gchar *cal_filter;
133 
134 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
135 	g_return_if_fail (E_IS_CAL_MODEL (model));
136 	g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
137 
138 	current_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
139 	g_return_if_fail (current_view != NULL);
140 
141 	zone = e_cal_model_get_timezone (model);
142 	cal_filter = e_cal_data_model_dup_filter (e_cal_model_get_data_model (model));
143 
144 	if (E_IS_DAY_VIEW (current_view)) {
145 		GtkAdjustment *adjustment;
146 
147 		day_view = E_DAY_VIEW (current_view);
148 		day_view_selection_start_day = day_view->selection_start_day;
149 		day_view_selection_end_day = day_view->selection_end_day;
150 		day_view_selection_start_row = day_view->selection_start_row;
151 		day_view_selection_end_row = day_view->selection_end_row;
152 
153 		adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->main_canvas));
154 		day_view_scrollbar_position = gtk_adjustment_get_value (adjustment);
155 	}
156 
157 	g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_range_moved_id);
158 	g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_selection_changed_id);
159 
160 	visible_range_start = view_start_tt;
161 	visible_range_end = view_end_tt;
162 
163 	e_calendar_view_precalc_visible_time_range (current_view, view_start_tt, view_end_tt, &visible_range_start, &visible_range_end);
164 	if (view_start_tt != visible_range_start || view_end_tt != visible_range_end) {
165 		time_t cmp_range_start = convert_to_local_zone (visible_range_start, zone);
166 		time_t cmp_range_end = convert_to_local_zone (visible_range_end, zone);
167 
168 		if (view_start_tt != cmp_range_start ||
169 		    view_end_tt != cmp_range_end - 1) {
170 			/* Calendar views update their inner time range during e_cal_model_set_time_range() call,
171 			   while they can change it if needed (like a clamp of a week view with a week start day
172 			   not being Monday */
173 			GDate new_view_start, new_view_end;
174 
175 			/* Midnight means the next day, which is not desired here */
176 			cmp_range_end--;
177 			visible_range_end--;
178 
179 			/* These times are in the correct zone already */
180 			time_to_gdate_with_zone (&new_view_start, cmp_range_start, NULL);
181 			time_to_gdate_with_zone (&new_view_end, cmp_range_end, NULL);
182 
183 			e_calendar_item_set_selection (calitem, &new_view_start, &new_view_end);
184 			e_cal_shell_content_update_filters (cal_shell_content, cal_filter, visible_range_start, visible_range_end);
185 			e_calendar_view_set_selected_time_range (current_view, cmp_range_start, cmp_range_start);
186 			filters_updated = TRUE;
187 			view_start_tt = cmp_range_start;
188 			view_end_tt = cmp_range_end;
189 		}
190 	}
191 
192 	if (!filters_updated) {
193 		e_calendar_item_set_selection (calitem, view_start, view_end);
194 		e_cal_shell_content_update_filters (cal_shell_content, cal_filter, view_start_tt, view_end_tt);
195 		e_calendar_view_set_selected_time_range (current_view, view_start_tt, view_start_tt);
196 	}
197 
198 	if (day_view && day_view_selection_start_day != -1 && day_view_selection_end_day != -1 &&
199 	    day_view_selection_start_row != -1 && day_view_selection_end_row != -1) {
200 		GtkAdjustment *adjustment;
201 
202 		day_view->selection_start_day = day_view_selection_start_day;
203 		day_view->selection_end_day = day_view_selection_end_day;
204 		day_view->selection_start_row = day_view_selection_start_row;
205 		day_view->selection_end_row = day_view_selection_end_row;
206 
207 		/* This is better than e_day_view_ensure_rows_visible(), because it keeps both
208 		   selection and the exact scrollbar position in the main canvas, which may not
209 		   always correspond to each other. */
210 		adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->main_canvas));
211 		gtk_adjustment_set_value (adjustment, day_view_scrollbar_position);
212 	}
213 
214 	gtk_widget_queue_draw (GTK_WIDGET (current_view));
215 
216 	g_free (cal_filter);
217 
218 	g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_range_moved_id);
219 	g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_selection_changed_id);
220 
221 	if (e_calendar_item_get_date_range (calitem, &syy, &smm, &sdd, &eyy, &emm, &edd)) {
222 		GDate range_start;
223 
224 		g_date_set_dmy (&range_start, sdd, smm + 1, syy);
225 
226 		cal_shell_content->priv->view_start_range_day_offset =
227 			g_date_get_julian (&cal_shell_content->priv->view_start) - g_date_get_julian (&range_start);
228 	}
229 }
230 
231 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)232 e_cal_shell_content_change_view (ECalShellContent *cal_shell_content,
233 				 ECalViewKind to_view,
234 				 const GDate *view_start,
235 				 const GDate *view_end,
236 				 gboolean force_change)
237 {
238 	EShellSidebar *shell_sidebar;
239 	EShellView *shell_view;
240 	ECalendar *calendar;
241 	ECalModel *model;
242 	ICalTimezone *zone;
243 	time_t view_start_tt, view_end_tt;
244 	gboolean view_changed = FALSE;
245 	gint selected_days;
246 
247 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
248 	g_return_if_fail (to_view >= E_CAL_VIEW_KIND_DAY && to_view < E_CAL_VIEW_KIND_LAST);
249 	g_return_if_fail (view_start != NULL);
250 	g_return_if_fail (g_date_valid (view_start));
251 	g_return_if_fail (view_end != NULL);
252 	g_return_if_fail (g_date_valid (view_end));
253 
254 	shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
255 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
256 	g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
257 
258 	calendar = e_cal_base_shell_sidebar_get_date_navigator (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
259 	g_return_if_fail (E_IS_CALENDAR (calendar));
260 
261 	model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
262 	zone = e_cal_model_get_timezone (model);
263 	view_start_tt = cal_comp_gdate_to_timet (view_start, zone);
264 	view_end_tt = cal_comp_gdate_to_timet (view_end, zone);
265 
266 	if (to_view != cal_shell_content->priv->current_view) {
267 		g_signal_handler_block (cal_shell_content, cal_shell_content->priv->current_view_id_changed_id);
268 		e_cal_shell_content_set_current_view_id (cal_shell_content, to_view);
269 		g_signal_handler_unblock (cal_shell_content, cal_shell_content->priv->current_view_id_changed_id);
270 		view_changed = TRUE;
271 	}
272 
273 	selected_days = g_date_get_julian (view_end) - g_date_get_julian (view_start) + 1;
274 
275 	if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY) {
276 		EDayView *day_view;
277 
278 		day_view = E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY]);
279 		e_day_view_set_days_shown (day_view, selected_days);
280 	} else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH) {
281 		EWeekView *month_view;
282 
283 		month_view = E_WEEK_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_MONTH]);
284 		e_week_view_set_weeks_shown (month_view, selected_days / 7);
285 	}
286 
287 	if (!force_change &&
288 	    g_date_valid (&cal_shell_content->priv->view_start) &&
289 	    g_date_valid (&cal_shell_content->priv->view_end) &&
290 	    g_date_compare (&cal_shell_content->priv->view_start, view_start) == 0 &&
291 	    g_date_compare (&cal_shell_content->priv->view_end, view_end) == 0) {
292 		ECalendarItem *calitem = e_calendar_get_item (calendar);
293 
294 		if (view_changed)
295 			cal_shell_content_update_model_and_current_view_times (
296 				cal_shell_content, model, calitem, view_start_tt, view_end_tt, view_start, view_end);
297 
298 		g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_range_moved_id);
299 		g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_selection_changed_id);
300 
301 		e_calendar_item_set_selection (calitem, view_start, view_end);
302 
303 		g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_range_moved_id);
304 		g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_selection_changed_id);
305 
306 		return;
307 	}
308 
309 	cal_shell_content->priv->view_start = *view_start;
310 	cal_shell_content->priv->view_end = *view_end;
311 
312 	cal_shell_content_update_model_and_current_view_times (
313 		cal_shell_content, model, e_calendar_get_item (calendar), view_start_tt, view_end_tt, view_start, view_end);
314 }
315 
316 static void
cal_shell_content_clamp_for_whole_weeks(GDateWeekday week_start_day,GDate * sel_start,GDate * sel_end,gboolean saturday_as_sunday)317 cal_shell_content_clamp_for_whole_weeks (GDateWeekday week_start_day,
318 					 GDate *sel_start,
319 					 GDate *sel_end,
320 					 gboolean saturday_as_sunday)
321 {
322 	GDateWeekday wday;
323 	guint32 julian_start, julian_end;
324 
325 	g_return_if_fail (sel_start != NULL);
326 	g_return_if_fail (sel_end != NULL);
327 
328 	wday = g_date_get_weekday (sel_start);
329 
330 	/* This is because the month/week view doesn't split weekends */
331 	if (saturday_as_sunday && wday == G_DATE_SATURDAY && week_start_day == G_DATE_SUNDAY)
332 		wday = G_DATE_SUNDAY;
333 
334 	if (week_start_day > wday) {
335 		g_date_subtract_days (sel_start, wday);
336 		wday = g_date_get_weekday (sel_start);
337 	}
338 
339 	if (week_start_day < wday)
340 		g_date_subtract_days (sel_start, wday - week_start_day);
341 
342 	julian_start = g_date_get_julian (sel_start);
343 	julian_end = g_date_get_julian (sel_end);
344 
345 	if (((julian_end - julian_start + 1) % 7) != 0)
346 		g_date_add_days (sel_end, 7 - ((julian_end - julian_start + 1) % 7));
347 
348 	julian_end = g_date_get_julian (sel_end);
349 
350 	/* Can show only up to 6 weeks */
351 	if ((julian_end - julian_start + 1) / 7 > 6) {
352 		*sel_end = *sel_start;
353 		g_date_add_days (sel_end, (7 * 6) - 1);
354 	}
355 
356 	if (g_date_compare (sel_start, sel_end) == 0)
357 		g_date_add_days (sel_end, 6);
358 }
359 
360 static gboolean
cal_shell_content_weekday_within(GDateWeekday start_wday,GDateWeekday end_wday,GDateWeekday test_wday)361 cal_shell_content_weekday_within (GDateWeekday start_wday,
362 				  GDateWeekday end_wday,
363 				  GDateWeekday test_wday)
364 {
365 	gint ii;
366 
367 	if (start_wday <= end_wday)
368 		return start_wday <= test_wday && test_wday <= end_wday;
369 
370 	for (ii = 0; ii < 7; ii++) {
371 		if (start_wday == test_wday)
372 			return TRUE;
373 
374 		if (start_wday == end_wday)
375 			break;
376 
377 		start_wday = e_weekday_get_next (start_wday);
378 	}
379 
380 	return FALSE;
381 }
382 
383 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)384 cal_shell_content_change_selection_in_current_view (ECalShellContent *cal_shell_content,
385 						    time_t sel_start_tt,
386 						    time_t sel_end_tt,
387 						    ICalTimezone *zone)
388 {
389 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
390 
391 	if (cal_shell_content->priv->current_view >= E_CAL_VIEW_KIND_DAY &&
392 	    cal_shell_content->priv->current_view < E_CAL_VIEW_KIND_LAST) {
393 		ECalendarView *view;
394 
395 		view = cal_shell_content->priv->views[cal_shell_content->priv->current_view];
396 
397 		/* Preserve selected time (change only date) for these views */
398 		if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY ||
399 		    cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK) {
400 			time_t current_sel_start = (time_t) -1, current_sel_end = (time_t) -1;
401 
402 			if (e_calendar_view_get_selected_time_range (view, &current_sel_start, &current_sel_end)) {
403 				ICalTime *itt;
404 
405 				itt = i_cal_time_new_from_timet_with_zone (current_sel_start, 0, zone);
406 				current_sel_start = i_cal_time_as_timet_with_zone (itt, NULL);
407 				g_clear_object (&itt);
408 
409 				itt = i_cal_time_new_from_timet_with_zone (current_sel_end, 0, zone);
410 				current_sel_end = i_cal_time_as_timet_with_zone (itt, NULL);
411 				g_clear_object (&itt);
412 
413 				sel_start_tt += current_sel_start % (24 * 60 * 60);
414 				sel_end_tt += current_sel_end % (24 * 60 * 60);
415 			}
416 		}
417 
418 		e_calendar_view_set_selected_time_range (view, sel_start_tt, sel_end_tt);
419 	}
420 }
421 
422 static void
cal_shell_content_datepicker_selection_changed_cb(ECalendarItem * calitem,ECalShellContent * cal_shell_content)423 cal_shell_content_datepicker_selection_changed_cb (ECalendarItem *calitem,
424 						   ECalShellContent *cal_shell_content)
425 {
426 	GDate sel_start, sel_end;
427 	guint32 selected_days, start_julian, end_julian;
428 	ICalTimezone *zone;
429 	time_t sel_start_tt, sel_end_tt;
430 
431 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
432 	g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
433 
434 	g_date_clear (&sel_start, 1);
435 	g_date_clear (&sel_end, 1);
436 
437 	if (!e_calendar_item_get_selection (calitem, &sel_start, &sel_end))
438 		return;
439 
440 	start_julian = g_date_get_julian (&sel_start);
441 	end_julian = g_date_get_julian (&sel_end);
442 
443 	g_return_if_fail (start_julian <= end_julian);
444 
445 	if (g_date_compare (&cal_shell_content->priv->view_start, &sel_start) == 0 &&
446 	    g_date_compare (&cal_shell_content->priv->view_end, &sel_end) == 0) {
447 		/* No change in the selection range */
448 		return;
449 	}
450 
451 	zone = e_cal_model_get_timezone (e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)));
452 	sel_start_tt = cal_comp_gdate_to_timet (&sel_start, zone);
453 	sel_end_tt = cal_comp_gdate_to_timet (&sel_end, zone);
454 
455 	selected_days = end_julian - start_julian + 1;
456 	if (selected_days == 1) {
457 		GDateWeekday sel_start_wday, sel_end_wday, cur_start_wday, cur_end_wday;
458 
459 		/* Clicked inside currently selected view range; do not do anything,
460 		   just make sure the days are selected again */
461 		if (g_date_compare (&cal_shell_content->priv->view_start, &sel_start) <= 0 &&
462 		    g_date_compare (&sel_start, &cal_shell_content->priv->view_end) <= 0) {
463 			sel_start = cal_shell_content->priv->view_start;
464 			sel_end = cal_shell_content->priv->view_end;
465 
466 			e_calendar_item_set_selection (calitem, &sel_start, &sel_end);
467 
468 			cal_shell_content_change_selection_in_current_view (cal_shell_content, sel_start_tt, sel_end_tt, zone);
469 			return;
470 		}
471 
472 		sel_start_wday = g_date_get_weekday (&sel_start);
473 		sel_end_wday = g_date_get_weekday (&sel_end);
474 		cur_start_wday = g_date_get_weekday (&cal_shell_content->priv->view_start);
475 		cur_end_wday = g_date_get_weekday (&cal_shell_content->priv->view_end);
476 
477 		if ((cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK ||
478 		    (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY &&
479 		    e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY])) != 1)) &&
480 		    cal_shell_content_weekday_within (cur_start_wday, cur_end_wday, sel_start_wday)) {
481 			if (cur_start_wday < sel_start_wday) {
482 				g_date_subtract_days (&sel_start, sel_start_wday - cur_start_wday);
483 			} else if (cur_start_wday > sel_start_wday) {
484 				g_date_subtract_days (&sel_start, 7 - (cur_start_wday - sel_start_wday));
485 			}
486 			sel_end = sel_start;
487 			if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY)
488 				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);
489 			else
490 				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);
491 
492 			e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &sel_start, &sel_end, FALSE);
493 		} else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WEEK &&
494 		    cal_shell_content_weekday_within (cur_start_wday, cur_end_wday, sel_start_wday) &&
495 		    cal_shell_content_weekday_within (cur_start_wday, cur_end_wday, sel_end_wday)) {
496 			if (cur_start_wday < sel_start_wday)
497 				g_date_subtract_days (&sel_start, sel_start_wday - cur_start_wday);
498 			sel_end = sel_start;
499 			cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, TRUE);
500 
501 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WEEK, &sel_start, &sel_end, FALSE);
502 		} else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH ||
503 			   cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_LIST) {
504 			/* whole month */
505 			g_date_set_day (&sel_start, 1);
506 			sel_end = sel_start;
507 			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);
508 			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);
509 
510 			e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &sel_start, &sel_end, FALSE);
511 		} else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK) {
512 			cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, TRUE);
513 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WEEK, &sel_start, &sel_end, FALSE);
514 		} else {
515 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_end, FALSE);
516 		}
517 
518 		cal_shell_content_change_selection_in_current_view (cal_shell_content, sel_start_tt, sel_end_tt, zone);
519 	} else if (selected_days < 7) {
520 		GDateWeekday first_work_wday;
521 
522 		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)));
523 
524 		if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK &&
525 		    first_work_wday == g_date_get_weekday (&sel_start) &&
526 		    selected_days == e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK])))
527 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WORKWEEK, &sel_start, &sel_end, FALSE);
528 		else
529 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_end, FALSE);
530 	} else if (selected_days == 7) {
531 		GDateWeekday sel_start_wday;
532 
533 		sel_start_wday = g_date_get_weekday (&sel_start);
534 
535 		if (sel_start_wday == calitem->week_start_day &&
536 		    cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY &&
537 		    e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY])) == 7) {
538 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_end, FALSE);
539 		} else {
540 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WEEK, &sel_start, &sel_end, FALSE);
541 		}
542 	} else {
543 		if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_LIST) {
544 			/* whole month */
545 			g_date_set_day (&sel_start, 1);
546 			sel_end = sel_start;
547 			g_date_set_day (&sel_end, g_date_get_days_in_month (g_date_get_month (&sel_start), g_date_get_year (&sel_start)));
548 			cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, FALSE);
549 
550 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_LIST, &sel_start, &sel_end, FALSE);
551 		} else {
552 			cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end,
553 				cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH || cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WEEK);
554 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_MONTH, &sel_start, &sel_end, FALSE);
555 		}
556 	}
557 }
558 
559 static void
cal_shell_content_datepicker_range_moved_cb(ECalendarItem * calitem,ECalShellContent * cal_shell_content)560 cal_shell_content_datepicker_range_moved_cb (ECalendarItem *calitem,
561 					     ECalShellContent *cal_shell_content)
562 {
563 	gint start_year, start_month, start_day, end_year, end_month, end_day;
564 	GDate sel_start_date, sel_end_date, range_start_date;
565 
566 	g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
567 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
568 
569 	if (!e_calendar_item_get_date_range (calitem, &start_year, &start_month, &start_day, &end_year, &end_month, &end_day))
570 		return;
571 
572 	g_date_set_dmy (&range_start_date, start_day, start_month + 1, start_year);
573 
574 	if (g_date_valid (&cal_shell_content->priv->last_range_start) &&
575 	    g_date_compare (&cal_shell_content->priv->last_range_start, &range_start_date) == 0) {
576 		return;
577 	}
578 
579 	cal_shell_content->priv->last_range_start = range_start_date;
580 
581 	g_date_clear (&sel_start_date, 1);
582 	g_date_clear (&sel_end_date, 1);
583 
584 	if (cal_shell_content->priv->view_start_range_day_offset == (guint32) -1) {
585 		sel_start_date = cal_shell_content->priv->view_start;
586 		sel_end_date = cal_shell_content->priv->view_end;
587 		cal_shell_content->priv->view_start_range_day_offset =
588 			g_date_get_julian (&cal_shell_content->priv->view_start) - g_date_get_julian (&range_start_date);
589 	} else {
590 		gint view_days;
591 
592 		view_days = g_date_get_julian (&cal_shell_content->priv->view_end) - g_date_get_julian (&cal_shell_content->priv->view_start);
593 
594 		sel_start_date = range_start_date;
595 		g_date_add_days (&sel_start_date, cal_shell_content->priv->view_start_range_day_offset);
596 
597 		sel_end_date = sel_start_date;
598 		g_date_add_days (&sel_end_date, view_days);
599 	}
600 
601 	g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_range_moved_id);
602 
603 	e_calendar_item_set_selection (calitem, &sel_start_date, &sel_end_date);
604 
605 	g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_range_moved_id);
606 }
607 
608 static gboolean
cal_shell_content_datepicker_button_press_cb(ECalendar * calendar,GdkEvent * event,ECalShellContent * cal_shell_content)609 cal_shell_content_datepicker_button_press_cb (ECalendar *calendar,
610 					      GdkEvent *event,
611 					      ECalShellContent *cal_shell_content)
612 {
613 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), FALSE);
614 
615 	if (!event)
616 		return FALSE;
617 
618 	if (event->type == GDK_2BUTTON_PRESS) {
619 		ECalendarItem *calitem = e_calendar_get_item (calendar);
620 		gdouble xwin = 0.0, ywin = 0.0;
621 		GDate sel_start, sel_end;
622 
623 		/* Do that only if the double-click was above a day cell */
624 		if (!gdk_event_get_coords (event, &xwin, &ywin) ||
625 		    !e_calendar_item_convert_position_to_date (calitem, xwin, ywin, &sel_start)) {
626 			return FALSE;
627 		}
628 
629 		g_date_clear (&sel_start, 1);
630 		g_date_clear (&sel_end, 1);
631 
632 		if (!e_calendar_item_get_selection (calitem, &sel_start, &sel_end))
633 			return FALSE;
634 
635 		/* Switch to a day view on a double-click */
636 		e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_start, FALSE);
637 	}
638 
639 	return FALSE;
640 }
641 
642 static void
cal_shell_content_current_view_id_changed_cb(ECalShellContent * cal_shell_content)643 cal_shell_content_current_view_id_changed_cb (ECalShellContent *cal_shell_content)
644 {
645 	GDate sel_start, sel_end;
646 	GDateWeekday work_day_first, week_start_day;
647 	ECalModel *model;
648 	gint ii;
649 
650 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
651 
652 	model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
653 	work_day_first = e_cal_model_get_work_day_first (model);
654 	week_start_day = e_cal_model_get_week_start_day (model);
655 
656 	if (cal_shell_content->priv->previous_selected_start_time != -1 &&
657 	    cal_shell_content->priv->previous_selected_end_time != -1) {
658 		ICalTimezone *zone;
659 
660 		zone = e_cal_model_get_timezone (model);
661 		time_to_gdate_with_zone (&sel_start, cal_shell_content->priv->previous_selected_start_time, zone);
662 		time_to_gdate_with_zone (&sel_end, cal_shell_content->priv->previous_selected_end_time, zone);
663 	} else {
664 		sel_start = cal_shell_content->priv->view_start;
665 		sel_end = cal_shell_content->priv->view_end;
666 	}
667 
668 	switch (cal_shell_content->priv->current_view) {
669 		case E_CAL_VIEW_KIND_DAY:
670 			/* Left the start & end being the current view start */
671 			sel_end = sel_start;
672 			break;
673 		case E_CAL_VIEW_KIND_WORKWEEK:
674 			cal_shell_content_clamp_for_whole_weeks (week_start_day, &sel_start, &sel_end, FALSE);
675 			ii = 0;
676 			while (g_date_get_weekday (&sel_start) != work_day_first && ii < 7) {
677 				g_date_add_days (&sel_start, 1);
678 				ii++;
679 			}
680 
681 			sel_end = sel_start;
682 			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);
683 			break;
684 		case E_CAL_VIEW_KIND_WEEK:
685 			sel_end = sel_start;
686 			cal_shell_content_clamp_for_whole_weeks (week_start_day, &sel_start, &sel_end, TRUE);
687 			break;
688 		case E_CAL_VIEW_KIND_MONTH:
689 		case E_CAL_VIEW_KIND_LIST:
690 			if (!calendar_config_get_month_start_with_current_week ()) {
691 				if (g_date_get_days_in_month (g_date_get_month (&sel_start), g_date_get_year (&sel_start)) - g_date_get_day (&sel_start) <= 7) {
692 					/* Keep the sel_start unchanged, because it's within the last week of the month,
693 					   which can be covered by the mini-calendar. Setting to the first day of the month
694 					   may mean the mini-calendar would go back by one month. */
695 				} else if (g_date_get_day (&sel_start) != 1 &&
696 					   (g_date_get_julian (&sel_end) - g_date_get_julian (&sel_start) + 1) / 7 >= 3 &&
697 					   g_date_get_month (&sel_start) != g_date_get_month (&sel_end)) {
698 					g_date_set_day (&sel_start, 1);
699 					g_date_add_months (&sel_start, 1);
700 				} else {
701 					g_date_set_day (&sel_start, 1);
702 				}
703 			}
704 			sel_end = sel_start;
705 			g_date_add_months (&sel_end, 1);
706 			g_date_subtract_days (&sel_end, 1);
707 			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);
708 			break;
709 		default:
710 			g_warn_if_reached ();
711 			return;
712 	}
713 
714 	/* Ensure a change */
715 	e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &sel_start, &sel_end, TRUE);
716 
717 	/* Try to preserve selection between the views */
718 	if (cal_shell_content->priv->previous_selected_start_time != -1 &&
719 	    cal_shell_content->priv->previous_selected_end_time != -1) {
720 		if (cal_shell_content->priv->current_view >= E_CAL_VIEW_KIND_DAY &&
721 		    cal_shell_content->priv->current_view < E_CAL_VIEW_KIND_LAST) {
722 			ECalendarView *cal_view = cal_shell_content->priv->views[cal_shell_content->priv->current_view];
723 
724 			e_calendar_view_set_selected_time_range (cal_view,
725 				cal_shell_content->priv->previous_selected_start_time,
726 				cal_shell_content->priv->previous_selected_end_time);
727 		}
728 	}
729 
730 	cal_shell_content->priv->previous_selected_start_time = -1;
731 	cal_shell_content->priv->previous_selected_end_time = -1;
732 }
733 
734 static void
cal_shell_content_display_view_cb(ECalShellContent * cal_shell_content,GalView * gal_view)735 cal_shell_content_display_view_cb (ECalShellContent *cal_shell_content,
736                                    GalView *gal_view)
737 {
738 	ECalViewKind view_kind;
739 	GType gal_view_type;
740 
741 	gal_view_type = G_OBJECT_TYPE (gal_view);
742 
743 	if (gal_view_type == GAL_TYPE_VIEW_ETABLE) {
744 		ECalendarView *calendar_view;
745 
746 		view_kind = E_CAL_VIEW_KIND_LIST;
747 		calendar_view = cal_shell_content->priv->views[view_kind];
748 		gal_view_etable_attach_table (
749 			GAL_VIEW_ETABLE (gal_view),
750 			e_cal_list_view_get_table (E_CAL_LIST_VIEW (calendar_view)));
751 
752 	} else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_DAY) {
753 		view_kind = E_CAL_VIEW_KIND_DAY;
754 
755 	} else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_WORK_WEEK) {
756 		view_kind = E_CAL_VIEW_KIND_WORKWEEK;
757 
758 	} else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_WEEK) {
759 		view_kind = E_CAL_VIEW_KIND_WEEK;
760 
761 	} else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_MONTH) {
762 		view_kind = E_CAL_VIEW_KIND_MONTH;
763 
764 	} else {
765 		g_return_if_reached ();
766 	}
767 
768 	if (view_kind != E_CAL_VIEW_KIND_LIST) {
769 		EShellView *shell_view;
770 		EShellWindow *shell_window;
771 
772 		shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
773 		shell_window = e_shell_view_get_shell_window (shell_view);
774 
775 		/* Reset these two filters, because they force the List View */
776 		if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (CALENDAR_FILTER_ACTIVE_APPOINTMENTS))) ||
777 		    gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (ACTION (CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS))))
778 			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (ACTION (CALENDAR_FILTER_ANY_CATEGORY)), TRUE);
779 	}
780 
781 	e_cal_shell_content_set_current_view_id (cal_shell_content, view_kind);
782 }
783 
784 static void
cal_shell_content_notify_view_id_cb(ECalShellContent * cal_shell_content)785 cal_shell_content_notify_view_id_cb (ECalShellContent *cal_shell_content)
786 {
787 	EShellContent *shell_content;
788 	EShellView *shell_view;
789 	GSettings *settings;
790 	GtkWidget *paned;
791 	const gchar *key;
792 	const gchar *view_id;
793 
794 	settings = e_util_ref_settings ("org.gnome.evolution.calendar");
795 	paned = cal_shell_content->priv->hpaned;
796 
797 	shell_content = E_SHELL_CONTENT (cal_shell_content);
798 	shell_view = e_shell_content_get_shell_view (shell_content);
799 	view_id = e_shell_view_get_view_id (shell_view);
800 
801 	if (view_id != NULL && strcmp (view_id, "Month_View") == 0)
802 		key = "month-hpane-position";
803 	else
804 		key = "hpane-position";
805 
806 	g_settings_unbind (paned, "hposition");
807 
808 	g_settings_bind (
809 		settings, key,
810 		paned, "hposition",
811 		G_SETTINGS_BIND_DEFAULT);
812 
813 	g_object_unref (settings);
814 }
815 
816 static void
cal_shell_content_is_editing_changed_cb(gpointer cal_view_tasks_memos_table,GParamSpec * param,EShellView * shell_view)817 cal_shell_content_is_editing_changed_cb (gpointer cal_view_tasks_memos_table,
818                                          GParamSpec *param,
819                                          EShellView *shell_view)
820 {
821 	g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
822 
823 	e_shell_view_update_actions (shell_view);
824 }
825 
826 static gchar *
cal_shell_content_get_pad_state_filename(EShellContent * shell_content,ETable * table)827 cal_shell_content_get_pad_state_filename (EShellContent *shell_content,
828                                           ETable *table)
829 {
830 	EShellBackend *shell_backend;
831 	EShellView *shell_view;
832 	const gchar *config_dir, *nick = NULL;
833 
834 	g_return_val_if_fail (shell_content != NULL, NULL);
835 	g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL);
836 	g_return_val_if_fail (table != NULL, NULL);
837 	g_return_val_if_fail (E_IS_TABLE (table), NULL);
838 
839 	if (E_IS_TASK_TABLE (table))
840 		nick = "TaskPad";
841 	else if (E_IS_MEMO_TABLE (table))
842 		nick = "MemoPad";
843 
844 	g_return_val_if_fail (nick != NULL, NULL);
845 
846 	shell_view = e_shell_content_get_shell_view (shell_content);
847 	shell_backend = e_shell_view_get_shell_backend (shell_view);
848 	config_dir = e_shell_backend_get_config_dir (shell_backend);
849 
850 	return g_build_filename (config_dir, nick, NULL);
851 }
852 
853 static void
cal_shell_content_save_table_state(EShellContent * shell_content,ETable * table)854 cal_shell_content_save_table_state (EShellContent *shell_content,
855                                     ETable *table)
856 {
857 	gchar *filename;
858 
859 	filename = cal_shell_content_get_pad_state_filename (
860 		shell_content, table);
861 	g_return_if_fail (filename != NULL);
862 
863 	e_table_save_state (table, filename);
864 	g_free (filename);
865 }
866 
867 static void
cal_shell_content_load_table_state(EShellContent * shell_content,ETable * table)868 cal_shell_content_load_table_state (EShellContent *shell_content,
869                                     ETable *table)
870 {
871 	gchar *filename;
872 
873 	filename = cal_shell_content_get_pad_state_filename (shell_content, table);
874 	g_return_if_fail (filename != NULL);
875 
876 	e_table_load_state (table, filename);
877 	g_free (filename);
878 }
879 
880 static ICalProperty *
cal_shell_content_get_attendee_prop(ICalComponent * icomp,const gchar * address)881 cal_shell_content_get_attendee_prop (ICalComponent *icomp,
882                                      const gchar *address)
883 {
884 	ICalProperty *prop;
885 
886 	if (address == NULL || *address == '\0')
887 		return NULL;
888 
889 	for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
890 	     prop;
891 	     g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
892 		const gchar *attendee;
893 
894 		attendee = itip_strip_mailto (i_cal_property_get_attendee (prop));
895 
896 		if (attendee && g_ascii_strcasecmp (attendee, address) == 0)
897 			return prop;
898 	}
899 
900 	return NULL;
901 }
902 
903 static gboolean
cal_shell_content_icomp_is_delegated(ICalComponent * icomp,const gchar * user_email)904 cal_shell_content_icomp_is_delegated (ICalComponent *icomp,
905 				      const gchar *user_email)
906 {
907 	ICalProperty *prop;
908 	ICalParameter *param;
909 	gchar *delto = NULL;
910 	gboolean is_delegated = FALSE;
911 
912 	prop = cal_shell_content_get_attendee_prop (icomp, user_email);
913 
914 	if (prop) {
915 		param = i_cal_property_get_first_parameter (prop, I_CAL_DELEGATEDTO_PARAMETER);
916 		if (param) {
917 			delto = g_strdup (itip_strip_mailto (i_cal_parameter_get_delegatedto (param)));
918 			g_object_unref (param);
919 		}
920 
921 		g_object_unref (prop);
922 	} else
923 		return FALSE;
924 
925 	prop = cal_shell_content_get_attendee_prop (icomp, delto);
926 
927 	if (prop) {
928 		gchar *delfrom = NULL;
929 		ICalParameterPartstat partstat = I_CAL_PARTSTAT_NONE;
930 
931 		param = i_cal_property_get_first_parameter (prop, I_CAL_DELEGATEDFROM_PARAMETER);
932 		if (param) {
933 			delfrom = g_strdup (itip_strip_mailto (i_cal_parameter_get_delegatedfrom (param)));
934 			g_object_unref (param);
935 		}
936 
937 		param = i_cal_property_get_first_parameter (prop, I_CAL_PARTSTAT_PARAMETER);
938 		if (param) {
939 			partstat = i_cal_parameter_get_partstat (param);
940 			g_object_unref (param);
941 		}
942 
943 		is_delegated = delfrom && user_email &&
944 			partstat != I_CAL_PARTSTAT_DECLINED &&
945 			g_ascii_strcasecmp (delfrom, user_email) == 0;
946 
947 		g_object_unref (prop);
948 		g_free (delfrom);
949 	}
950 
951 	g_free (delto);
952 
953 	return is_delegated;
954 }
955 
956 static guint32
cal_shell_content_check_state(EShellContent * shell_content)957 cal_shell_content_check_state (EShellContent *shell_content)
958 {
959 	EShell *shell;
960 	EShellView *shell_view;
961 	EShellBackend *shell_backend;
962 	ESourceRegistry *registry;
963 	ECalShellContent *cal_shell_content;
964 	ECalendarView *calendar_view;
965 	gboolean selection_is_editable = FALSE;
966 	gboolean selection_is_instance = FALSE;
967 	gboolean selection_is_meeting = FALSE;
968 	gboolean selection_is_organizer = FALSE;
969 	gboolean selection_is_attendee = FALSE;
970 	gboolean selection_is_recurring = FALSE;
971 	gboolean selection_can_delegate = FALSE;
972 	gboolean this_and_future_supported = FALSE;
973 	guint32 state = 0;
974 	GList *selected;
975 	GList *link;
976 	guint n_selected;
977 
978 	cal_shell_content = E_CAL_SHELL_CONTENT (shell_content);
979 
980 	shell_view = e_shell_content_get_shell_view (shell_content);
981 	shell_backend = e_shell_view_get_shell_backend (shell_view);
982 	shell = e_shell_backend_get_shell (shell_backend);
983 	registry = e_shell_get_registry (shell);
984 
985 	calendar_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
986 
987 	selected = e_calendar_view_get_selected_events (calendar_view);
988 	n_selected = g_list_length (selected);
989 
990 	/* If we have a selection, assume it's
991 	 * editable until we learn otherwise. */
992 	if (n_selected > 0)
993 		selection_is_editable = TRUE;
994 
995 	for (link = selected; link != NULL; link = g_list_next (link)) {
996 		ECalendarViewEvent *event = link->data;
997 		ECalClient *client;
998 		ECalComponent *comp;
999 		gchar *user_email;
1000 		ICalComponent *icomp;
1001 		const gchar *capability;
1002 		gboolean cap_delegate_supported;
1003 		gboolean cap_delegate_to_many;
1004 		gboolean icomp_is_delegated;
1005 		gboolean read_only;
1006 
1007 		if (!is_comp_data_valid (event))
1008 			continue;
1009 
1010 		client = event->comp_data->client;
1011 		icomp = event->comp_data->icalcomp;
1012 
1013 		read_only = e_client_is_readonly (E_CLIENT (client));
1014 		selection_is_editable &= !read_only;
1015 
1016 		selection_is_instance |=
1017 			e_cal_util_component_is_instance (icomp);
1018 
1019 		selection_is_meeting =
1020 			(n_selected == 1) &&
1021 			e_cal_util_component_has_attendee (icomp);
1022 
1023 		selection_is_recurring |=
1024 			e_cal_util_component_is_instance (icomp) ||
1025 			e_cal_util_component_has_recurrences (icomp);
1026 
1027 		/* XXX The rest of this is rather expensive and
1028 		 *     only applies if a single event is selected,
1029 		 *     so continue with the loop iteration if the
1030 		 *     rest of this is not applicable. */
1031 		if (n_selected > 1)
1032 			continue;
1033 
1034 		/* XXX This probably belongs in comp-util.c. */
1035 
1036 		comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp));
1037 		user_email = itip_get_comp_attendee (registry, comp, client);
1038 
1039 		selection_is_organizer =
1040 			e_cal_util_component_has_organizer (icomp) &&
1041 			itip_organizer_is_user (registry, comp, client);
1042 
1043 		capability = E_CAL_STATIC_CAPABILITY_DELEGATE_SUPPORTED;
1044 		cap_delegate_supported =
1045 			e_client_check_capability (
1046 			E_CLIENT (client), capability);
1047 
1048 		capability = E_CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY;
1049 		cap_delegate_to_many =
1050 			e_client_check_capability (
1051 			E_CLIENT (client), capability);
1052 
1053 		this_and_future_supported = !e_client_check_capability (
1054 			E_CLIENT (client), E_CAL_STATIC_CAPABILITY_NO_THISANDFUTURE);
1055 
1056 		icomp_is_delegated = user_email != NULL &&
1057 			cal_shell_content_icomp_is_delegated (icomp, user_email);
1058 
1059 		selection_can_delegate =
1060 			cap_delegate_supported &&
1061 			(cap_delegate_to_many ||
1062 			(!selection_is_organizer &&
1063 			 !icomp_is_delegated));
1064 
1065 		selection_is_attendee = !selection_is_organizer &&
1066 			selection_is_meeting &&
1067 			!icomp_is_delegated &&
1068 			itip_attendee_is_user (registry, comp, client);
1069 
1070 		g_free (user_email);
1071 		g_object_unref (comp);
1072 	}
1073 
1074 	g_list_free (selected);
1075 
1076 	if (n_selected == 1)
1077 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_SINGLE;
1078 	if (n_selected > 1)
1079 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_MULTIPLE;
1080 	if (selection_is_editable)
1081 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_EDITABLE;
1082 	if (selection_is_instance)
1083 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_INSTANCE;
1084 	if (selection_is_meeting)
1085 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_MEETING;
1086 	if (selection_is_organizer)
1087 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_ORGANIZER;
1088 	if (selection_is_attendee)
1089 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_ATTENDEE;
1090 	if (selection_is_recurring)
1091 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_RECURRING;
1092 	if (selection_can_delegate)
1093 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_CAN_DELEGATE;
1094 	if (this_and_future_supported)
1095 		state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_THIS_AND_FUTURE_SUPPORTED;
1096 
1097 	return state;
1098 }
1099 
1100 static void
cal_shell_content_focus_search_results(EShellContent * shell_content)1101 cal_shell_content_focus_search_results (EShellContent *shell_content)
1102 {
1103 	ECalendarView *calendar_view;
1104 
1105 	calendar_view = e_cal_shell_content_get_current_calendar_view (E_CAL_SHELL_CONTENT (shell_content));
1106 
1107 	gtk_widget_grab_focus (GTK_WIDGET (calendar_view));
1108 }
1109 
1110 static time_t
cal_shell_content_get_default_time(ECalModel * model,gpointer user_data)1111 cal_shell_content_get_default_time (ECalModel *model,
1112 				    gpointer user_data)
1113 {
1114 	ECalShellContent *cal_shell_content = user_data;
1115 	ICalTimezone *zone;
1116 	ICalTime *itt;
1117 	time_t tt;
1118 
1119 	g_return_val_if_fail (model != NULL, 0);
1120 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), 0);
1121 
1122 	if (e_cal_shell_content_get_current_view_id (cal_shell_content) != E_CAL_VIEW_KIND_LIST) {
1123 		ECalendarView *cal_view;
1124 		time_t selected_start = (time_t) 0, selected_end = (time_t) 0;
1125 
1126 		cal_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
1127 
1128 		if (cal_view && e_calendar_view_get_selected_time_range (cal_view, &selected_start, &selected_end))
1129 			return selected_start;
1130 	}
1131 
1132 	zone = e_cal_model_get_timezone (model);
1133 	itt = i_cal_time_new_current_with_zone (zone);
1134 	tt = i_cal_time_as_timet_with_zone (itt, zone);
1135 	g_clear_object (&itt);
1136 
1137 	return tt;
1138 }
1139 
1140 static void
update_adjustment(ECalShellContent * cal_shell_content,GtkAdjustment * adjustment,EWeekView * week_view,gboolean move_by_week)1141 update_adjustment (ECalShellContent *cal_shell_content,
1142                    GtkAdjustment *adjustment,
1143                    EWeekView *week_view,
1144 		   gboolean move_by_week)
1145 {
1146 	GDate start_date, end_date;
1147 	GDate first_day_shown;
1148 	ECalModel *model;
1149 	gint week_offset;
1150 	ICalTime *start_tt = NULL;
1151 	ICalTimezone *timezone;
1152 	time_t lower;
1153 	guint32 old_first_day_julian, new_first_day_julian;
1154 	gdouble value;
1155 
1156 	e_week_view_get_first_day_shown (week_view, &first_day_shown);
1157 
1158 	/* If we don't have a valid date set yet, just return. */
1159 	if (!g_date_valid (&first_day_shown))
1160 		return;
1161 
1162 	value = gtk_adjustment_get_value (adjustment);
1163 
1164 	/* Determine the first date shown. */
1165 	start_date = week_view->base_date;
1166 	week_offset = floor (value + 0.5);
1167 
1168 	if (week_offset > 0)
1169 		g_date_add_days (&start_date, week_offset * 7);
1170 	else
1171 		g_date_subtract_days (&start_date, week_offset * (-7));
1172 
1173 	/* Convert the old & new first days shown to julian values. */
1174 	old_first_day_julian = g_date_get_julian (&first_day_shown);
1175 	new_first_day_julian = g_date_get_julian (&start_date);
1176 
1177 	/* If we are already showing the date, just return. */
1178 	if (old_first_day_julian == new_first_day_julian)
1179 		return;
1180 
1181 	/* Convert it to a time_t. */
1182 	start_tt = i_cal_time_new_null_time ();
1183 	i_cal_time_set_date (start_tt,
1184 		g_date_get_year (&start_date),
1185 		g_date_get_month (&start_date),
1186 		g_date_get_day (&start_date));
1187 
1188 	model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
1189 	timezone = e_cal_model_get_timezone (model);
1190 	lower = i_cal_time_as_timet_with_zone (start_tt, timezone);
1191 	g_clear_object (&start_tt);
1192 
1193 	end_date = start_date;
1194 	if (move_by_week) {
1195 		g_date_add_days (&end_date, 7 - 1);
1196 	} else {
1197 		g_date_add_days (&end_date, 7 * e_week_view_get_weeks_shown (week_view) - 1);
1198 	}
1199 
1200 	e_week_view_set_update_base_date (week_view, FALSE);
1201 	e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &start_date, &end_date, FALSE);
1202 	e_calendar_view_set_selected_time_range (E_CALENDAR_VIEW (week_view), lower, lower);
1203 	e_week_view_set_update_base_date (week_view, TRUE);
1204 }
1205 
1206 static void
week_view_adjustment_changed_cb(GtkAdjustment * adjustment,ECalShellContent * cal_shell_content)1207 week_view_adjustment_changed_cb (GtkAdjustment *adjustment,
1208 				 ECalShellContent *cal_shell_content)
1209 {
1210 	ECalendarView *view;
1211 
1212 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
1213 
1214 	view = cal_shell_content->priv->views[E_CAL_VIEW_KIND_WEEK];
1215 	update_adjustment (cal_shell_content, adjustment, E_WEEK_VIEW (view), TRUE);
1216 }
1217 
1218 static void
month_view_adjustment_changed_cb(GtkAdjustment * adjustment,ECalShellContent * cal_shell_content)1219 month_view_adjustment_changed_cb (GtkAdjustment *adjustment,
1220 				  ECalShellContent *cal_shell_content)
1221 {
1222 	ECalendarView *view;
1223 
1224 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
1225 
1226 	view = cal_shell_content->priv->views[E_CAL_VIEW_KIND_MONTH];
1227 	update_adjustment (cal_shell_content, adjustment, E_WEEK_VIEW (view), FALSE);
1228 }
1229 
1230 static void
cal_shell_content_notify_work_day_cb(ECalModel * model,GParamSpec * param,ECalShellContent * cal_shell_content)1231 cal_shell_content_notify_work_day_cb (ECalModel *model,
1232 				      GParamSpec *param,
1233 				      ECalShellContent *cal_shell_content)
1234 {
1235 	GDateWeekday work_day_first, work_day_last;
1236 
1237 	g_return_if_fail (E_IS_CAL_MODEL (model));
1238 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
1239 
1240 	if (cal_shell_content->priv->current_view != E_CAL_VIEW_KIND_WORKWEEK)
1241 		return;
1242 
1243 	work_day_first = e_cal_model_get_work_day_first (model);
1244 	work_day_last = e_cal_model_get_work_day_last (model);
1245 
1246 	if (work_day_first == g_date_get_weekday (&cal_shell_content->priv->view_start) &&
1247 	    work_day_last == g_date_get_weekday (&cal_shell_content->priv->view_end))
1248 		return;
1249 
1250 	cal_shell_content->priv->previous_selected_start_time = -1;
1251 	cal_shell_content->priv->previous_selected_end_time = -1;
1252 
1253 	/* This makes sure that the selection in the datepicker corresponds
1254 	   to the time range used in the Work Week view */
1255 	cal_shell_content_current_view_id_changed_cb (cal_shell_content);
1256 }
1257 
1258 static void
cal_shell_content_notify_week_start_day_cb(ECalModel * model,GParamSpec * param,ECalShellContent * cal_shell_content)1259 cal_shell_content_notify_week_start_day_cb (ECalModel *model,
1260 					    GParamSpec *param,
1261 					    ECalShellContent *cal_shell_content)
1262 {
1263 	g_return_if_fail (E_IS_CAL_MODEL (model));
1264 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
1265 
1266 	cal_shell_content->priv->previous_selected_start_time = -1;
1267 	cal_shell_content->priv->previous_selected_end_time = -1;
1268 
1269 	/* This makes sure that the selection in the datepicker corresponds
1270 	   to the time range used in the current view */
1271 	cal_shell_content_current_view_id_changed_cb (cal_shell_content);
1272 }
1273 
1274 static void
cal_shell_content_move_view_range_cb(ECalendarView * cal_view,ECalendarViewMoveType move_type,gint64 exact_date,ECalShellContent * cal_shell_content)1275 cal_shell_content_move_view_range_cb (ECalendarView *cal_view,
1276 				      ECalendarViewMoveType move_type,
1277 				      gint64 exact_date,
1278 				      ECalShellContent *cal_shell_content)
1279 {
1280 	g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
1281 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
1282 
1283 	if (!cal_view->in_focus)
1284 		return;
1285 
1286 	e_cal_shell_content_move_view_range (cal_shell_content, move_type, (time_t) exact_date);
1287 }
1288 
1289 static void
cal_shell_content_clear_all_in_list_view(ECalShellContent * cal_shell_content)1290 cal_shell_content_clear_all_in_list_view (ECalShellContent *cal_shell_content)
1291 {
1292 	ECalDataModelSubscriber *subscriber;
1293 
1294 	subscriber = E_CAL_DATA_MODEL_SUBSCRIBER (cal_shell_content->priv->list_view_model);
1295 
1296 	e_cal_data_model_unsubscribe (cal_shell_content->priv->list_view_data_model, subscriber);
1297 	e_cal_model_remove_all_objects (cal_shell_content->priv->list_view_model);
1298 	e_cal_data_model_remove_all_clients (cal_shell_content->priv->list_view_data_model);
1299 	e_cal_data_model_subscribe (cal_shell_content->priv->list_view_data_model, subscriber, 0, 0);
1300 }
1301 
1302 static void
cal_shell_content_client_opened_cb(ECalBaseShellSidebar * cal_base_shell_sidebar,EClient * client,gpointer user_data)1303 cal_shell_content_client_opened_cb (ECalBaseShellSidebar *cal_base_shell_sidebar,
1304 				    EClient *client,
1305 				    gpointer user_data)
1306 {
1307 	ECalShellContent *cal_shell_content = user_data;
1308 	ESourceSelector *source_selector;
1309 	ESource *source;
1310 
1311 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
1312 
1313 	if (cal_shell_content->priv->current_view != E_CAL_VIEW_KIND_LIST || !E_IS_CAL_CLIENT (client))
1314 		return;
1315 
1316 	source_selector = e_cal_base_shell_sidebar_get_selector (cal_base_shell_sidebar);
1317 	source = e_source_selector_ref_primary_selection (source_selector);
1318 
1319 	/* It can happen that the previously opening calendar finished its open
1320 	   after the current calendar, in which case this would replace the data,
1321 	   thus ensure the opened calendar is the correct calendar. */
1322 	if (source == e_client_get_source (client)) {
1323 		cal_shell_content_clear_all_in_list_view (cal_shell_content);
1324 		e_cal_data_model_add_client (cal_shell_content->priv->list_view_data_model, E_CAL_CLIENT (client));
1325 	}
1326 
1327 	g_clear_object (&source);
1328 }
1329 
1330 static void
cal_shell_content_update_list_view(ECalShellContent * cal_shell_content)1331 cal_shell_content_update_list_view (ECalShellContent *cal_shell_content)
1332 {
1333 	ECalBaseShellSidebar *cal_base_shell_sidebar;
1334 	ESourceSelector *source_selector;
1335 	ECalClient *client;
1336 	ESource *source;
1337 
1338 	cal_base_shell_sidebar = E_CAL_BASE_SHELL_SIDEBAR (e_shell_view_get_shell_sidebar (
1339 		e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content))));
1340 
1341 	source_selector = e_cal_base_shell_sidebar_get_selector (cal_base_shell_sidebar);
1342 
1343 	source = e_source_selector_ref_primary_selection (source_selector);
1344 	if (!source)
1345 		return;
1346 
1347 	e_cal_model_set_default_source_uid (cal_shell_content->priv->list_view_model, e_source_get_uid (source));
1348 
1349 	client = e_cal_data_model_ref_client (cal_shell_content->priv->list_view_data_model, e_source_get_uid (source));
1350 
1351 	if (!client)
1352 		e_cal_base_shell_sidebar_open_source (cal_base_shell_sidebar, source, cal_shell_content_client_opened_cb, cal_shell_content);
1353 
1354 	g_clear_object (&client);
1355 	g_clear_object (&source);
1356 }
1357 
1358 static void
cal_shell_content_primary_selection_changed_cb(ESourceSelector * selector,gpointer user_data)1359 cal_shell_content_primary_selection_changed_cb (ESourceSelector *selector,
1360 						gpointer user_data)
1361 {
1362 	ECalShellContent *cal_shell_content = user_data;
1363 
1364 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
1365 
1366 	if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_LIST)
1367 		cal_shell_content_update_list_view (cal_shell_content);
1368 }
1369 
1370 static void
cal_shell_content_foreign_client_opened_cb(ECalBaseShellSidebar * cal_base_shell_sidebar,ECalClient * client,ECalModel * model)1371 cal_shell_content_foreign_client_opened_cb (ECalBaseShellSidebar *cal_base_shell_sidebar,
1372 					    ECalClient *client,
1373 					    ECalModel *model)
1374 {
1375 	g_return_if_fail (E_IS_CAL_CLIENT (client));
1376 	g_return_if_fail (E_IS_CAL_MODEL (model));
1377 
1378 	e_cal_data_model_add_client (e_cal_model_get_data_model (model), client);
1379 }
1380 
1381 static void
cal_shell_content_foreign_client_closed_cb(ECalBaseShellSidebar * cal_base_shell_sidebar,ESource * source,ECalModel * model)1382 cal_shell_content_foreign_client_closed_cb (ECalBaseShellSidebar *cal_base_shell_sidebar,
1383 					    ESource *source,
1384 					    ECalModel *model)
1385 {
1386 	g_return_if_fail (E_IS_SOURCE (source));
1387 	g_return_if_fail (E_IS_CAL_MODEL (model));
1388 
1389 	e_cal_data_model_remove_client (e_cal_model_get_data_model (model), e_source_get_uid (source));
1390 }
1391 
1392 static void
cal_shell_content_setup_foreign_sources(EShellWindow * shell_window,const gchar * view_name,const gchar * extension_name,ECalModel * model)1393 cal_shell_content_setup_foreign_sources (EShellWindow *shell_window,
1394 					 const gchar *view_name,
1395 					 const gchar *extension_name,
1396 					 ECalModel *model)
1397 {
1398 	EShellSidebar *foreign_sidebar;
1399 	EShellContent *foreign_content;
1400 	EShellView *foreign_view;
1401 	ECalModel *foreign_model;
1402 	gboolean is_new_view;
1403 
1404 	g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
1405 	g_return_if_fail (E_IS_CAL_MODEL (model));
1406 
1407 	is_new_view = e_shell_window_peek_shell_view (shell_window, view_name) == NULL;
1408 
1409 	foreign_view = e_shell_window_get_shell_view (shell_window, view_name);
1410 	g_return_if_fail (E_IS_SHELL_VIEW (foreign_view));
1411 
1412 	foreign_sidebar = e_shell_view_get_shell_sidebar (foreign_view);
1413 	g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (foreign_sidebar));
1414 
1415 	if (is_new_view) {
1416 		/* Preselect default source, when the view was not created yet */
1417 		ESourceSelector *source_selector;
1418 		ESourceRegistry *registry;
1419 		ESource *source;
1420 
1421 		source_selector = e_cal_base_shell_sidebar_get_selector (E_CAL_BASE_SHELL_SIDEBAR (foreign_sidebar));
1422 		registry = e_source_selector_get_registry (source_selector);
1423 		source = e_source_registry_ref_default_for_extension_name (registry, extension_name);
1424 
1425 		if (source)
1426 			e_source_selector_set_primary_selection (source_selector, source);
1427 
1428 		g_clear_object (&source);
1429 	}
1430 
1431 	g_signal_connect_object (foreign_sidebar, "client-opened",
1432 		G_CALLBACK (cal_shell_content_foreign_client_opened_cb), model, 0);
1433 	g_signal_connect_object (foreign_sidebar, "client-closed",
1434 		G_CALLBACK (cal_shell_content_foreign_client_closed_cb), model, 0);
1435 
1436 	foreign_content = e_shell_view_get_shell_content (foreign_view);
1437 	foreign_model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (foreign_content));
1438 
1439 	e_binding_bind_property (
1440 		foreign_model, "default-source-uid",
1441 		model, "default-source-uid",
1442 		G_BINDING_SYNC_CREATE);
1443 
1444 	g_signal_connect_object (model, "row-appended",
1445 		G_CALLBACK (e_cal_base_shell_view_model_row_appended), foreign_view, G_CONNECT_SWAPPED);
1446 
1447 	/* This makes sure that the local models for memos and tasks
1448 	   in the calendar view get populated with the same sources
1449 	   as those in the respective views. */
1450 
1451 	e_cal_base_shell_sidebar_ensure_sources_open (E_CAL_BASE_SHELL_SIDEBAR (foreign_sidebar));
1452 }
1453 
1454 static void
cal_shell_content_view_created(ECalBaseShellContent * cal_base_shell_content)1455 cal_shell_content_view_created (ECalBaseShellContent *cal_base_shell_content)
1456 {
1457 	ECalShellContent *cal_shell_content;
1458 	EShellView *shell_view;
1459 	EShellWindow *shell_window;
1460 	EShellSidebar *shell_sidebar;
1461 	GalViewInstance *view_instance;
1462 	ECalendar *calendar;
1463 	ECalModel *model;
1464 	ECalDataModel *data_model;
1465 	GDate date;
1466 	time_t today;
1467 
1468 	cal_shell_content = E_CAL_SHELL_CONTENT (cal_base_shell_content);
1469 	cal_shell_content->priv->current_view = E_CAL_VIEW_KIND_DAY;
1470 
1471 	today = time (NULL);
1472 	g_date_clear (&date, 1);
1473 	g_date_set_time_t (&date, today);
1474 
1475 	shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
1476 	shell_window = e_shell_view_get_shell_window (shell_view);
1477 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
1478 	g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
1479 
1480 	calendar = e_cal_base_shell_sidebar_get_date_navigator (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
1481 	g_return_if_fail (E_IS_CALENDAR (calendar));
1482 
1483 	model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
1484 	e_calendar_item_set_selection (e_calendar_get_item (calendar), &date, &date);
1485 	e_cal_model_set_time_range (model, today, today);
1486 
1487 	/* Show everything known by default in the task and memo pads */
1488 	e_cal_model_set_time_range (cal_shell_content->priv->memo_model, 0, 0);
1489 	e_cal_model_set_time_range (cal_shell_content->priv->task_model, 0, 0);
1490 	e_cal_model_set_time_range (cal_shell_content->priv->list_view_model, 0, 0);
1491 
1492 	g_signal_connect (e_cal_base_shell_sidebar_get_selector (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar)), "primary-selection-changed",
1493 		G_CALLBACK (cal_shell_content_primary_selection_changed_cb), cal_shell_content);
1494 
1495 	cal_shell_content->priv->datepicker_selection_changed_id =
1496 		g_signal_connect (e_calendar_get_item (calendar), "selection-changed",
1497 		G_CALLBACK (cal_shell_content_datepicker_selection_changed_cb), cal_shell_content);
1498 	cal_shell_content->priv->datepicker_range_moved_id =
1499 		g_signal_connect (e_calendar_get_item (calendar), "date-range-moved",
1500 		G_CALLBACK (cal_shell_content_datepicker_range_moved_cb), cal_shell_content);
1501 
1502 	g_signal_connect_after (calendar, "button-press-event",
1503 		G_CALLBACK (cal_shell_content_datepicker_button_press_cb), cal_shell_content);
1504 
1505 	data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
1506 	cal_shell_content->priv->tag_calendar = e_tag_calendar_new (calendar);
1507 	e_tag_calendar_subscribe (cal_shell_content->priv->tag_calendar, data_model);
1508 
1509 	/* Intentionally not using e_signal_connect_notify() here, no need to filter
1510 	   out "false" notifications, it's dealt with them in another way */
1511 	cal_shell_content->priv->current_view_id_changed_id = g_signal_connect (
1512 		cal_shell_content, "notify::current-view-id",
1513 		G_CALLBACK (cal_shell_content_current_view_id_changed_cb), NULL);
1514 
1515 	/* List of selected Task/Memo sources is taken from respective views,
1516 	   which are loaded if necessary. */
1517 	cal_shell_content_setup_foreign_sources (shell_window, "memos", E_SOURCE_EXTENSION_MEMO_LIST,
1518 		cal_shell_content->priv->memo_model);
1519 
1520 	cal_shell_content_setup_foreign_sources (shell_window, "tasks", E_SOURCE_EXTENSION_TASK_LIST,
1521 		cal_shell_content->priv->task_model);
1522 
1523 	/* Finally load the view instance */
1524 	view_instance = e_shell_view_get_view_instance (shell_view);
1525 	gal_view_instance_load (view_instance);
1526 
1527 	/* Keep the toolbar view buttons in sync with the calendar. */
1528 	e_binding_bind_property (
1529 		cal_shell_content, "current-view-id",
1530 		ACTION (CALENDAR_VIEW_DAY), "current-value",
1531 		G_BINDING_BIDIRECTIONAL |
1532 		G_BINDING_SYNC_CREATE);
1533 
1534 	e_signal_connect_notify (
1535 		model, "notify::work-day-monday",
1536 		G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
1537 
1538 	e_signal_connect_notify (
1539 		model, "notify::work-day-tuesday",
1540 		G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
1541 
1542 	e_signal_connect_notify (
1543 		model, "notify::work-day-wednesday",
1544 		G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
1545 
1546 	e_signal_connect_notify (
1547 		model, "notify::work-day-thursday",
1548 		G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
1549 
1550 	e_signal_connect_notify (
1551 		model, "notify::work-day-friday",
1552 		G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
1553 
1554 	e_signal_connect_notify (
1555 		model, "notify::work-day-saturday",
1556 		G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
1557 
1558 	e_signal_connect_notify (
1559 		model, "notify::work-day-sunday",
1560 		G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
1561 
1562 	e_signal_connect_notify (
1563 		model, "notify::week-start-day",
1564 		G_CALLBACK (cal_shell_content_notify_week_start_day_cb), cal_shell_content);
1565 
1566 	cal_shell_content->priv->initialized = TRUE;
1567 }
1568 
1569 static void
e_cal_shell_content_create_calendar_views(ECalShellContent * cal_shell_content)1570 e_cal_shell_content_create_calendar_views (ECalShellContent *cal_shell_content)
1571 {
1572 	EShellView *shell_view;
1573 	ECalModel *model;
1574 	ECalendarView *calendar_view;
1575 	GtkAdjustment *adjustment;
1576 	time_t today;
1577 	gint ii;
1578 
1579 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
1580 	g_return_if_fail (cal_shell_content->priv->calendar_notebook != NULL);
1581 	g_return_if_fail (cal_shell_content->priv->views[0] == NULL);
1582 
1583 	model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
1584 
1585 	/* Day View */
1586 	calendar_view = e_day_view_new (model);
1587 	cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY] = calendar_view;
1588 	g_object_ref_sink (calendar_view);
1589 
1590 	/* Work Week View */
1591 	calendar_view = e_day_view_new (model);
1592 	e_day_view_set_work_week_view (E_DAY_VIEW (calendar_view), TRUE);
1593 	e_day_view_set_days_shown (E_DAY_VIEW (calendar_view), 5);
1594 	cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK] = calendar_view;
1595 	g_object_ref_sink (calendar_view);
1596 
1597 	/* Week View */
1598 	calendar_view = e_week_view_new (model);
1599 	cal_shell_content->priv->views[E_CAL_VIEW_KIND_WEEK] = calendar_view;
1600 	g_object_ref_sink (calendar_view);
1601 
1602 	adjustment = gtk_range_get_adjustment (
1603 		GTK_RANGE (E_WEEK_VIEW (calendar_view)->vscrollbar));
1604 	g_signal_connect (
1605 		adjustment, "value-changed",
1606 		G_CALLBACK (week_view_adjustment_changed_cb), cal_shell_content);
1607 
1608 	/* Month View */
1609 	calendar_view = e_month_view_new (model);
1610 	e_week_view_set_multi_week_view (E_WEEK_VIEW (calendar_view), TRUE);
1611 	e_week_view_set_weeks_shown (E_WEEK_VIEW (calendar_view), 6);
1612 	cal_shell_content->priv->views[E_CAL_VIEW_KIND_MONTH] = calendar_view;
1613 	g_object_ref_sink (calendar_view);
1614 
1615 	adjustment = gtk_range_get_adjustment (
1616 		GTK_RANGE (E_WEEK_VIEW (calendar_view)->vscrollbar));
1617 	g_signal_connect (
1618 		adjustment, "value-changed",
1619 		G_CALLBACK (month_view_adjustment_changed_cb), cal_shell_content);
1620 
1621 	/* List View */
1622 	calendar_view = e_cal_list_view_new (cal_shell_content->priv->list_view_model);
1623 	cal_shell_content->priv->views[E_CAL_VIEW_KIND_LIST] = calendar_view;
1624 	g_object_ref_sink (calendar_view);
1625 
1626 	shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
1627 	today = time (NULL);
1628 
1629 	for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) {
1630 		calendar_view = cal_shell_content->priv->views[ii];
1631 
1632 		calendar_view->in_focus = ii == cal_shell_content->priv->current_view;
1633 
1634 		e_calendar_view_set_selected_time_range (calendar_view, today, today);
1635 
1636 		e_signal_connect_notify (
1637 			calendar_view, "notify::is-editing",
1638 			G_CALLBACK (cal_shell_content_is_editing_changed_cb), shell_view);
1639 
1640 		g_signal_connect (
1641 			calendar_view, "move-view-range",
1642 			G_CALLBACK (cal_shell_content_move_view_range_cb), cal_shell_content);
1643 
1644 		gtk_notebook_append_page (
1645 			GTK_NOTEBOOK (cal_shell_content->priv->calendar_notebook),
1646 			GTK_WIDGET (calendar_view), NULL);
1647 		gtk_widget_show (GTK_WIDGET (calendar_view));
1648 	}
1649 }
1650 
1651 static void
cal_shell_content_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1652 cal_shell_content_set_property (GObject *object,
1653 				guint property_id,
1654 				const GValue *value,
1655 				GParamSpec *pspec)
1656 {
1657 	switch (property_id) {
1658 		case PROP_CURRENT_VIEW_ID:
1659 			e_cal_shell_content_set_current_view_id (E_CAL_SHELL_CONTENT (object),
1660 				g_value_get_int (value));
1661 			return;
1662 		case PROP_SHOW_TAG_VPANE:
1663 			e_cal_shell_content_set_show_tag_vpane (E_CAL_SHELL_CONTENT (object),
1664 				g_value_get_boolean (value));
1665 			return;
1666 	}
1667 
1668 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1669 }
1670 
1671 static void
cal_shell_content_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1672 cal_shell_content_get_property (GObject *object,
1673 				guint property_id,
1674 				GValue *value,
1675 				GParamSpec *pspec)
1676 {
1677 	switch (property_id) {
1678 		case PROP_CALENDAR_NOTEBOOK:
1679 			g_value_set_object (
1680 				value, e_cal_shell_content_get_calendar_notebook (
1681 				E_CAL_SHELL_CONTENT (object)));
1682 			return;
1683 
1684 		case PROP_MEMO_TABLE:
1685 			g_value_set_object (
1686 				value, e_cal_shell_content_get_memo_table (
1687 				E_CAL_SHELL_CONTENT (object)));
1688 			return;
1689 
1690 		case PROP_TASK_TABLE:
1691 			g_value_set_object (
1692 				value, e_cal_shell_content_get_task_table (
1693 				E_CAL_SHELL_CONTENT (object)));
1694 			return;
1695 
1696 		case PROP_CURRENT_VIEW_ID:
1697 			g_value_set_int (value,
1698 				e_cal_shell_content_get_current_view_id (E_CAL_SHELL_CONTENT (object)));
1699 			return;
1700 
1701 		case PROP_CURRENT_VIEW:
1702 			g_value_set_object (value,
1703 				e_cal_shell_content_get_current_calendar_view (E_CAL_SHELL_CONTENT (object)));
1704 			return;
1705 
1706 		case PROP_SHOW_TAG_VPANE:
1707 			g_value_set_boolean (value,
1708 				e_cal_shell_content_get_show_tag_vpane (E_CAL_SHELL_CONTENT (object)));
1709 			return;
1710 	}
1711 
1712 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1713 }
1714 
1715 static void
cal_shell_content_dispose(GObject * object)1716 cal_shell_content_dispose (GObject *object)
1717 {
1718 	ECalShellContent *cal_shell_content = E_CAL_SHELL_CONTENT (object);
1719 	gint ii;
1720 
1721 	if (cal_shell_content->priv->task_data_model) {
1722 		e_cal_data_model_set_disposing (cal_shell_content->priv->task_data_model, TRUE);
1723 		e_cal_data_model_unsubscribe (cal_shell_content->priv->task_data_model,
1724 			E_CAL_DATA_MODEL_SUBSCRIBER (cal_shell_content->priv->task_model));
1725 	}
1726 
1727 	if (cal_shell_content->priv->memo_data_model) {
1728 		e_cal_data_model_set_disposing (cal_shell_content->priv->memo_data_model, TRUE);
1729 		e_cal_data_model_unsubscribe (cal_shell_content->priv->memo_data_model,
1730 			E_CAL_DATA_MODEL_SUBSCRIBER (cal_shell_content->priv->memo_model));
1731 	}
1732 
1733 	if (cal_shell_content->priv->list_view_data_model) {
1734 		e_cal_data_model_set_disposing (cal_shell_content->priv->list_view_data_model, TRUE);
1735 		e_cal_data_model_unsubscribe (cal_shell_content->priv->list_view_data_model,
1736 			E_CAL_DATA_MODEL_SUBSCRIBER (cal_shell_content->priv->list_view_model));
1737 	}
1738 
1739 	if (cal_shell_content->priv->tag_calendar) {
1740 		ECalDataModel *data_model;
1741 
1742 		data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
1743 		e_cal_data_model_set_disposing (data_model, TRUE);
1744 		e_tag_calendar_unsubscribe (cal_shell_content->priv->tag_calendar, data_model);
1745 		g_clear_object (&cal_shell_content->priv->tag_calendar);
1746 	}
1747 
1748 	for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) {
1749 		g_clear_object (&(cal_shell_content->priv->views[ii]));
1750 	}
1751 
1752 	g_clear_object (&cal_shell_content->priv->hpaned);
1753 	g_clear_object (&cal_shell_content->priv->vpaned);
1754 	g_clear_object (&cal_shell_content->priv->calendar_notebook);
1755 	g_clear_object (&cal_shell_content->priv->task_table);
1756 	g_clear_object (&cal_shell_content->priv->task_model);
1757 	g_clear_object (&cal_shell_content->priv->task_data_model);
1758 	g_clear_object (&cal_shell_content->priv->memo_table);
1759 	g_clear_object (&cal_shell_content->priv->memo_model);
1760 	g_clear_object (&cal_shell_content->priv->memo_data_model);
1761 	g_clear_object (&cal_shell_content->priv->list_view_model);
1762 	g_clear_object (&cal_shell_content->priv->list_view_data_model);
1763 
1764 	/* Chain up to parent's dispose() method. */
1765 	G_OBJECT_CLASS (e_cal_shell_content_parent_class)->dispose (object);
1766 }
1767 
1768 static void
cal_shell_content_constructed(GObject * object)1769 cal_shell_content_constructed (GObject *object)
1770 {
1771 	ECalShellContent *cal_shell_content;
1772 	EShellContent *shell_content;
1773 	EShellView *shell_view;
1774 	EShellWindow *shell_window;
1775 	EShell *shell;
1776 	GalViewInstance *view_instance;
1777 	GSettings *settings;
1778 	GtkWidget *container;
1779 	GtkWidget *widget;
1780 	gchar *markup;
1781 
1782 	/* Chain up to parent's constructed() method. */
1783 	G_OBJECT_CLASS (e_cal_shell_content_parent_class)->constructed (object);
1784 
1785 	cal_shell_content = E_CAL_SHELL_CONTENT (object);
1786 	shell_content = E_SHELL_CONTENT (cal_shell_content);
1787 	shell_view = e_shell_content_get_shell_view (shell_content);
1788 	shell_window = e_shell_view_get_shell_window (shell_view);
1789 	shell = e_shell_window_get_shell (shell_window);
1790 
1791 	cal_shell_content->priv->memo_data_model =
1792 		e_cal_base_shell_content_create_new_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
1793 	cal_shell_content->priv->memo_model =
1794 		e_cal_model_memos_new (cal_shell_content->priv->memo_data_model, e_shell_get_registry (shell), shell);
1795 
1796 	cal_shell_content->priv->task_data_model =
1797 		e_cal_base_shell_content_create_new_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
1798 	cal_shell_content->priv->task_model =
1799 		e_cal_model_tasks_new (cal_shell_content->priv->task_data_model, e_shell_get_registry (shell), shell);
1800 
1801 	cal_shell_content->priv->list_view_data_model =
1802 		e_cal_base_shell_content_create_new_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
1803 	cal_shell_content->priv->list_view_model =
1804 		e_cal_model_calendar_new (cal_shell_content->priv->list_view_data_model, e_shell_get_registry (shell), shell);
1805 
1806 	e_binding_bind_property (
1807 		cal_shell_content->priv->memo_model, "timezone",
1808 		cal_shell_content->priv->memo_data_model, "timezone",
1809 		G_BINDING_SYNC_CREATE);
1810 
1811 	e_binding_bind_property (
1812 		cal_shell_content->priv->task_model, "timezone",
1813 		cal_shell_content->priv->task_data_model, "timezone",
1814 		G_BINDING_SYNC_CREATE);
1815 
1816 	e_binding_bind_property (
1817 		cal_shell_content->priv->list_view_model, "timezone",
1818 		cal_shell_content->priv->list_view_data_model, "timezone",
1819 		G_BINDING_SYNC_CREATE);
1820 
1821 	/* Build content widgets. */
1822 
1823 	container = GTK_WIDGET (object);
1824 
1825 	widget = e_paned_new (GTK_ORIENTATION_HORIZONTAL);
1826 	gtk_container_add (GTK_CONTAINER (container), widget);
1827 	cal_shell_content->priv->hpaned = g_object_ref (widget);
1828 	gtk_widget_show (widget);
1829 
1830 	container = cal_shell_content->priv->hpaned;
1831 
1832 	widget = gtk_notebook_new ();
1833 	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
1834 	gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
1835 	gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE);
1836 	cal_shell_content->priv->calendar_notebook = g_object_ref (widget);
1837 	gtk_widget_show (widget);
1838 
1839 	widget = e_paned_new (GTK_ORIENTATION_VERTICAL);
1840 	e_paned_set_fixed_resize (E_PANED (widget), FALSE);
1841 	gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, TRUE);
1842 	cal_shell_content->priv->vpaned = g_object_ref (widget);
1843 	gtk_widget_show (widget);
1844 
1845 	container = cal_shell_content->priv->calendar_notebook;
1846 
1847 	e_cal_shell_content_create_calendar_views (cal_shell_content);
1848 
1849 	e_binding_bind_property (
1850 		cal_shell_content, "current-view-id",
1851 		cal_shell_content->priv->calendar_notebook, "page",
1852 		G_BINDING_SYNC_CREATE);
1853 
1854 	container = cal_shell_content->priv->vpaned;
1855 
1856 	widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1857 	gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, TRUE);
1858 	gtk_widget_show (widget);
1859 
1860 	container = widget;
1861 
1862 	widget = gtk_label_new (NULL);
1863 	markup = g_strdup_printf ("<b>%s</b>", _("Tasks"));
1864 	gtk_label_set_markup (GTK_LABEL (widget), markup);
1865 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
1866 	gtk_widget_show (widget);
1867 	g_free (markup);
1868 
1869 	widget = gtk_scrolled_window_new (NULL, NULL);
1870 	gtk_scrolled_window_set_policy (
1871 		GTK_SCROLLED_WINDOW (widget),
1872 		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1873 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
1874 	gtk_widget_show (widget);
1875 
1876 	container = widget;
1877 
1878 	widget = e_task_table_new (shell_view, cal_shell_content->priv->task_model);
1879 	gtk_container_add (GTK_CONTAINER (container), widget);
1880 	cal_shell_content->priv->task_table = g_object_ref (widget);
1881 	gtk_widget_show (widget);
1882 
1883 	cal_shell_content_load_table_state (shell_content, E_TABLE (widget));
1884 
1885 	g_signal_connect_swapped (
1886 		widget, "open-component",
1887 		G_CALLBACK (e_cal_shell_view_taskpad_open_task),
1888 		shell_view);
1889 
1890 	e_signal_connect_notify (
1891 		widget, "notify::is-editing",
1892 		G_CALLBACK (cal_shell_content_is_editing_changed_cb), shell_view);
1893 
1894 	container = cal_shell_content->priv->vpaned;
1895 
1896 	widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1897 	gtk_paned_pack2 (GTK_PANED (container), widget, TRUE, TRUE);
1898 	gtk_widget_show (widget);
1899 
1900 	container = widget;
1901 
1902 	widget = gtk_label_new (NULL);
1903 	markup = g_strdup_printf ("<b>%s</b>", _("Memos"));
1904 	gtk_label_set_markup (GTK_LABEL (widget), markup);
1905 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
1906 	gtk_widget_show (widget);
1907 	g_free (markup);
1908 
1909 	widget = gtk_scrolled_window_new (NULL, NULL);
1910 	gtk_scrolled_window_set_policy (
1911 		GTK_SCROLLED_WINDOW (widget),
1912 		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1913 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
1914 	gtk_widget_show (widget);
1915 
1916 	container = widget;
1917 
1918 	widget = e_memo_table_new (shell_view, cal_shell_content->priv->memo_model);
1919 	gtk_container_add (GTK_CONTAINER (container), widget);
1920 	cal_shell_content->priv->memo_table = g_object_ref (widget);
1921 	gtk_widget_show (widget);
1922 
1923 	cal_shell_content_load_table_state (shell_content, E_TABLE (widget));
1924 
1925 	e_cal_model_set_default_time_func (cal_shell_content->priv->memo_model, cal_shell_content_get_default_time, cal_shell_content);
1926 
1927 	g_signal_connect_swapped (
1928 		widget, "open-component",
1929 		G_CALLBACK (e_cal_shell_view_memopad_open_memo),
1930 		shell_view);
1931 
1932 	e_signal_connect_notify (
1933 		widget, "notify::is-editing",
1934 		G_CALLBACK (cal_shell_content_is_editing_changed_cb), shell_view);
1935 
1936 	/* Prepare the view instance. */
1937 
1938 	view_instance = e_shell_view_new_view_instance (shell_view, NULL);
1939 	g_signal_connect_swapped (
1940 		view_instance, "display-view",
1941 		G_CALLBACK (cal_shell_content_display_view_cb),
1942 		object);
1943 	/* Actual load happens at cal_shell_content_view_created() */
1944 	e_shell_view_set_view_instance (shell_view, view_instance);
1945 	g_object_unref (view_instance);
1946 
1947 	e_signal_connect_notify_swapped (
1948 		shell_view, "notify::view-id",
1949 		G_CALLBACK (cal_shell_content_notify_view_id_cb),
1950 		cal_shell_content);
1951 
1952 	settings = e_util_ref_settings ("org.gnome.evolution.calendar");
1953 
1954 	g_settings_bind (
1955 		settings, "tag-vpane-position",
1956 		cal_shell_content->priv->vpaned, "proportion",
1957 		G_SETTINGS_BIND_DEFAULT);
1958 
1959 	g_settings_bind (
1960 		settings, "show-tag-vpane",
1961 		cal_shell_content, "show-tag-vpane",
1962 		G_SETTINGS_BIND_DEFAULT);
1963 
1964 	g_object_unref (settings);
1965 
1966 	/* Cannot access shell sidebar here, thus rely on cal_shell_content_view_created()
1967 	   with exact widget settings which require it */
1968 }
1969 
1970 static void
e_cal_shell_content_class_init(ECalShellContentClass * class)1971 e_cal_shell_content_class_init (ECalShellContentClass *class)
1972 {
1973 	GObjectClass *object_class;
1974 	EShellContentClass *shell_content_class;
1975 	ECalBaseShellContentClass *cal_base_shell_content_class;
1976 
1977 	g_type_class_add_private (class, sizeof (ECalShellContentPrivate));
1978 
1979 	object_class = G_OBJECT_CLASS (class);
1980 	object_class->set_property = cal_shell_content_set_property;
1981 	object_class->get_property = cal_shell_content_get_property;
1982 	object_class->dispose = cal_shell_content_dispose;
1983 	object_class->constructed = cal_shell_content_constructed;
1984 
1985 	shell_content_class = E_SHELL_CONTENT_CLASS (class);
1986 	shell_content_class->check_state = cal_shell_content_check_state;
1987 	shell_content_class->focus_search_results = cal_shell_content_focus_search_results;
1988 
1989 	cal_base_shell_content_class = E_CAL_BASE_SHELL_CONTENT_CLASS (class);
1990 	cal_base_shell_content_class->new_cal_model = e_cal_model_calendar_new;
1991 	cal_base_shell_content_class->view_created = cal_shell_content_view_created;
1992 
1993 	g_object_class_install_property (
1994 		object_class,
1995 		PROP_CALENDAR_NOTEBOOK,
1996 		g_param_spec_object (
1997 			"calendar-notebook",
1998 			NULL,
1999 			NULL,
2000 			GTK_TYPE_NOTEBOOK,
2001 			G_PARAM_READABLE));
2002 
2003 	g_object_class_install_property (
2004 		object_class,
2005 		PROP_MEMO_TABLE,
2006 		g_param_spec_object (
2007 			"memo-table",
2008 			NULL,
2009 			NULL,
2010 			E_TYPE_MEMO_TABLE,
2011 			G_PARAM_READABLE));
2012 
2013 	g_object_class_install_property (
2014 		object_class,
2015 		PROP_TASK_TABLE,
2016 		g_param_spec_object (
2017 			"task-table",
2018 			NULL,
2019 			NULL,
2020 			E_TYPE_TASK_TABLE,
2021 			G_PARAM_READABLE));
2022 
2023 	g_object_class_install_property (
2024 		object_class,
2025 		PROP_CURRENT_VIEW_ID,
2026 		g_param_spec_int (
2027 			"current-view-id",
2028 			"Current Calendar View ID",
2029 			NULL,
2030 			E_CAL_VIEW_KIND_DAY,
2031 			E_CAL_VIEW_KIND_LAST - 1,
2032 			E_CAL_VIEW_KIND_DAY,
2033 			G_PARAM_READWRITE));
2034 
2035 	g_object_class_install_property (
2036 		object_class,
2037 		PROP_CURRENT_VIEW,
2038 		g_param_spec_object (
2039 			"current-view",
2040 			"Current Calendar View",
2041 			NULL,
2042 			E_TYPE_CALENDAR_VIEW,
2043 			G_PARAM_READABLE));
2044 
2045 	g_object_class_install_property (
2046 		object_class,
2047 		PROP_SHOW_TAG_VPANE,
2048 		g_param_spec_boolean (
2049 			"show-tag-vpane",
2050 			NULL,
2051 			NULL,
2052 			TRUE,
2053 			G_PARAM_READWRITE));
2054 }
2055 
2056 static void
e_cal_shell_content_class_finalize(ECalShellContentClass * class)2057 e_cal_shell_content_class_finalize (ECalShellContentClass *class)
2058 {
2059 }
2060 
2061 static void
e_cal_shell_content_init(ECalShellContent * cal_shell_content)2062 e_cal_shell_content_init (ECalShellContent *cal_shell_content)
2063 {
2064 	time_t now;
2065 
2066 	cal_shell_content->priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (cal_shell_content);
2067 	g_date_clear (&cal_shell_content->priv->view_start, 1);
2068 	g_date_clear (&cal_shell_content->priv->view_end, 1);
2069 	g_date_clear (&cal_shell_content->priv->last_range_start, 1);
2070 
2071 	now = time (NULL);
2072 	g_date_set_time_t (&cal_shell_content->priv->view_start, now);
2073 	g_date_set_time_t (&cal_shell_content->priv->view_end, now);
2074 
2075 	cal_shell_content->priv->view_start_range_day_offset = (guint32) -1;
2076 	cal_shell_content->priv->previous_selected_start_time = -1;
2077 	cal_shell_content->priv->previous_selected_end_time = -1;
2078 	cal_shell_content->priv->initialized = FALSE;
2079 }
2080 
2081 void
e_cal_shell_content_type_register(GTypeModule * type_module)2082 e_cal_shell_content_type_register (GTypeModule *type_module)
2083 {
2084 	/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
2085 	 *     function, so we have to wrap it with a public function in
2086 	 *     order to register types from a separate compilation unit. */
2087 	e_cal_shell_content_register_type (type_module);
2088 }
2089 
2090 GtkWidget *
e_cal_shell_content_new(EShellView * shell_view)2091 e_cal_shell_content_new (EShellView *shell_view)
2092 {
2093 	g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
2094 
2095 	return g_object_new (
2096 		E_TYPE_CAL_SHELL_CONTENT,
2097 		"shell-view", shell_view, NULL);
2098 }
2099 
2100 gboolean
e_cal_shell_content_get_initialized(ECalShellContent * cal_shell_content)2101 e_cal_shell_content_get_initialized (ECalShellContent *cal_shell_content)
2102 {
2103 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), FALSE);
2104 
2105 	return cal_shell_content->priv->initialized;
2106 }
2107 
2108 GtkNotebook *
e_cal_shell_content_get_calendar_notebook(ECalShellContent * cal_shell_content)2109 e_cal_shell_content_get_calendar_notebook (ECalShellContent *cal_shell_content)
2110 {
2111 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
2112 
2113 	return GTK_NOTEBOOK (cal_shell_content->priv->calendar_notebook);
2114 }
2115 
2116 EMemoTable *
e_cal_shell_content_get_memo_table(ECalShellContent * cal_shell_content)2117 e_cal_shell_content_get_memo_table (ECalShellContent *cal_shell_content)
2118 {
2119 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
2120 
2121 	return E_MEMO_TABLE (cal_shell_content->priv->memo_table);
2122 }
2123 
2124 ETaskTable *
e_cal_shell_content_get_task_table(ECalShellContent * cal_shell_content)2125 e_cal_shell_content_get_task_table (ECalShellContent *cal_shell_content)
2126 {
2127 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
2128 
2129 	return E_TASK_TABLE (cal_shell_content->priv->task_table);
2130 }
2131 
2132 EShellSearchbar *
e_cal_shell_content_get_searchbar(ECalShellContent * cal_shell_content)2133 e_cal_shell_content_get_searchbar (ECalShellContent *cal_shell_content)
2134 {
2135 	EShellView *shell_view;
2136 	EShellContent *shell_content;
2137 	GtkWidget *widget;
2138 
2139 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
2140 
2141 	shell_content = E_SHELL_CONTENT (cal_shell_content);
2142 	shell_view = e_shell_content_get_shell_view (shell_content);
2143 	widget = e_shell_view_get_searchbar (shell_view);
2144 
2145 	return E_SHELL_SEARCHBAR (widget);
2146 }
2147 
2148 static void
cal_shell_content_resubscribe(ECalendarView * cal_view,ECalModel * model)2149 cal_shell_content_resubscribe (ECalendarView *cal_view,
2150 			       ECalModel *model)
2151 {
2152 	ECalDataModel *data_model;
2153 	ECalDataModelSubscriber *subscriber;
2154 	time_t range_start, range_end;
2155 	gboolean is_tasks_or_memos;
2156 
2157 	g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
2158 	g_return_if_fail (E_IS_CAL_MODEL (model));
2159 
2160 	data_model = e_cal_model_get_data_model (model);
2161 	subscriber = E_CAL_DATA_MODEL_SUBSCRIBER (model);
2162 	is_tasks_or_memos = e_cal_model_get_component_kind (model) == I_CAL_VJOURNAL_COMPONENT ||
2163 		e_cal_model_get_component_kind (model) == I_CAL_VTODO_COMPONENT;
2164 
2165 	if ((!is_tasks_or_memos && e_calendar_view_get_visible_time_range (cal_view, &range_start, &range_end)) ||
2166 	    e_cal_data_model_get_subscriber_range (data_model, subscriber, &range_start, &range_end)) {
2167 		e_cal_data_model_unsubscribe (data_model, subscriber);
2168 		e_cal_model_remove_all_objects (model);
2169 
2170 		if (is_tasks_or_memos)
2171 			e_cal_data_model_subscribe (data_model, subscriber, range_start, range_end);
2172 	}
2173 }
2174 
2175 /* Only helper function */
2176 static void
cal_shell_content_switch_list_view(ECalShellContent * cal_shell_content,ECalViewKind from_view_kind,ECalViewKind to_view_kind)2177 cal_shell_content_switch_list_view (ECalShellContent *cal_shell_content,
2178 				    ECalViewKind from_view_kind,
2179 				    ECalViewKind to_view_kind)
2180 {
2181 	EShellView *shell_view;
2182 	EShellSidebar *shell_sidebar;
2183 	ECalBaseShellSidebar *cal_base_shell_sidebar;
2184 	ECalendar *date_navigator;
2185 	ESourceSelector *source_selector;
2186 	ECalModel *model;
2187 	gchar *cal_filter;
2188 
2189 	g_return_if_fail (from_view_kind != to_view_kind);
2190 
2191 	if (to_view_kind != E_CAL_VIEW_KIND_LIST &&
2192 	    from_view_kind != E_CAL_VIEW_KIND_LIST)
2193 		return;
2194 
2195 	shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
2196 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
2197 	cal_base_shell_sidebar = E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar);
2198 	date_navigator = e_cal_base_shell_sidebar_get_date_navigator (cal_base_shell_sidebar);
2199 	source_selector = e_cal_base_shell_sidebar_get_selector (cal_base_shell_sidebar);
2200 
2201 	gtk_widget_set_visible (GTK_WIDGET (date_navigator), to_view_kind != E_CAL_VIEW_KIND_LIST);
2202 	e_source_selector_set_show_toggles (source_selector, to_view_kind != E_CAL_VIEW_KIND_LIST);
2203 
2204 	model = e_calendar_view_get_model (cal_shell_content->priv->views[from_view_kind]);
2205 	cal_filter = e_cal_data_model_dup_filter (e_cal_model_get_data_model (model));
2206 	if (cal_filter) {
2207 		model = e_calendar_view_get_model (cal_shell_content->priv->views[to_view_kind]);
2208 		e_cal_data_model_set_filter (e_cal_model_get_data_model (model), cal_filter);
2209 		g_free (cal_filter);
2210 	}
2211 
2212 	/* The list view is activated */
2213 	if (to_view_kind == E_CAL_VIEW_KIND_LIST) {
2214 		cal_shell_content_update_list_view (cal_shell_content);
2215 	/* The list view is deactivated */
2216 	} else if (from_view_kind == E_CAL_VIEW_KIND_LIST) {
2217 		cal_shell_content_clear_all_in_list_view (cal_shell_content);
2218 		e_cal_base_shell_sidebar_ensure_sources_open (cal_base_shell_sidebar);
2219 	}
2220 }
2221 
2222 void
e_cal_shell_content_set_current_view_id(ECalShellContent * cal_shell_content,ECalViewKind view_kind)2223 e_cal_shell_content_set_current_view_id (ECalShellContent *cal_shell_content,
2224 					 ECalViewKind view_kind)
2225 {
2226 	EShellView *shell_view;
2227 	time_t start_time = -1, end_time = -1;
2228 	gint ii;
2229 
2230 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
2231 	g_return_if_fail (view_kind >= E_CAL_VIEW_KIND_DAY && view_kind < E_CAL_VIEW_KIND_LAST);
2232 
2233 	if (cal_shell_content->priv->current_view == view_kind)
2234 		return;
2235 
2236 	if (cal_shell_content->priv->current_view >= E_CAL_VIEW_KIND_DAY &&
2237 	    cal_shell_content->priv->current_view < E_CAL_VIEW_KIND_LAST) {
2238 		ECalendarView *cal_view = cal_shell_content->priv->views[cal_shell_content->priv->current_view];
2239 
2240 		if (!e_calendar_view_get_selected_time_range (cal_view, &start_time, &end_time)) {
2241 			start_time = -1;
2242 			end_time = -1;
2243 		}
2244 
2245 		e_calendar_view_destroy_tooltip	(cal_view);
2246 	}
2247 
2248 	cal_shell_content->priv->previous_selected_start_time = start_time;
2249 	cal_shell_content->priv->previous_selected_end_time = end_time;
2250 
2251 	for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) {
2252 		ECalendarView *cal_view = cal_shell_content->priv->views[ii];
2253 		gboolean in_focus = ii == view_kind;
2254 		gboolean focus_changed;
2255 
2256 		if (!cal_view) {
2257 			g_warn_if_reached ();
2258 			continue;
2259 		}
2260 
2261 		focus_changed = (cal_view->in_focus ? 1 : 0) != (in_focus ? 1 : 0);
2262 
2263 		cal_view->in_focus = in_focus;
2264 
2265 		if (ii == E_CAL_VIEW_KIND_LIST)
2266 			continue;
2267 
2268 		if (focus_changed && in_focus) {
2269 			/* Currently focused view changed. Any events within the common time
2270 			   range are not shown in the newly focused view, thus make sure it'll
2271 			   contain all what it should have. */
2272 			ECalModel *model;
2273 
2274 			model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
2275 
2276 			/* This may not cause any queries to backends with events,
2277 			   because the time range should be always within the one
2278 			   shown in the date picker. */
2279 			cal_shell_content_resubscribe (cal_view, model);
2280 
2281 			if (cal_shell_content->priv->task_table) {
2282 				ETaskTable *task_table;
2283 
2284 				task_table = E_TASK_TABLE (cal_shell_content->priv->task_table);
2285 				cal_shell_content_resubscribe (cal_view, e_task_table_get_model (task_table));
2286 			}
2287 
2288 			if (cal_shell_content->priv->memo_table) {
2289 				EMemoTable *memo_table;
2290 
2291 				memo_table = E_MEMO_TABLE (cal_shell_content->priv->memo_table);
2292 				cal_shell_content_resubscribe (cal_view, e_memo_table_get_model (memo_table));
2293 			}
2294 		}
2295 	}
2296 
2297 	cal_shell_content_switch_list_view (cal_shell_content, cal_shell_content->priv->current_view, view_kind);
2298 
2299 	cal_shell_content->priv->current_view = view_kind;
2300 
2301 	g_object_notify (G_OBJECT (cal_shell_content), "current-view-id");
2302 
2303 	gtk_widget_queue_draw (GTK_WIDGET (cal_shell_content->priv->views[cal_shell_content->priv->current_view]));
2304 
2305 	shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
2306 	e_shell_view_update_actions (shell_view);
2307 	e_cal_shell_view_update_sidebar (E_CAL_SHELL_VIEW (shell_view));
2308 }
2309 
2310 ECalViewKind
e_cal_shell_content_get_current_view_id(ECalShellContent * cal_shell_content)2311 e_cal_shell_content_get_current_view_id (ECalShellContent *cal_shell_content)
2312 {
2313 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), E_CAL_VIEW_KIND_LAST);
2314 
2315 	return cal_shell_content->priv->current_view;
2316 }
2317 
2318 ECalendarView *
e_cal_shell_content_get_calendar_view(ECalShellContent * cal_shell_content,ECalViewKind view_kind)2319 e_cal_shell_content_get_calendar_view (ECalShellContent *cal_shell_content,
2320 				       ECalViewKind view_kind)
2321 {
2322 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
2323 	g_return_val_if_fail (view_kind >= E_CAL_VIEW_KIND_DAY && view_kind < E_CAL_VIEW_KIND_LAST, NULL);
2324 
2325 	return cal_shell_content->priv->views[view_kind];
2326 }
2327 
2328 ECalendarView *
e_cal_shell_content_get_current_calendar_view(ECalShellContent * cal_shell_content)2329 e_cal_shell_content_get_current_calendar_view (ECalShellContent *cal_shell_content)
2330 {
2331 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
2332 
2333 	return e_cal_shell_content_get_calendar_view (cal_shell_content,
2334 		e_cal_shell_content_get_current_view_id (cal_shell_content));
2335 }
2336 
2337 void
e_cal_shell_content_save_state(ECalShellContent * cal_shell_content)2338 e_cal_shell_content_save_state (ECalShellContent *cal_shell_content)
2339 {
2340 	ECalShellContentPrivate *priv;
2341 
2342 	g_return_if_fail (cal_shell_content != NULL);
2343 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
2344 
2345 	priv = cal_shell_content->priv;
2346 
2347 	if (priv->task_table != NULL)
2348 		cal_shell_content_save_table_state (
2349 			E_SHELL_CONTENT (cal_shell_content),
2350 			E_TABLE (priv->task_table));
2351 
2352 	if (priv->memo_table != NULL)
2353 		cal_shell_content_save_table_state (
2354 			E_SHELL_CONTENT (cal_shell_content),
2355 			E_TABLE (priv->memo_table));
2356 }
2357 
2358 void
e_cal_shell_content_get_current_range(ECalShellContent * cal_shell_content,time_t * range_start,time_t * range_end)2359 e_cal_shell_content_get_current_range (ECalShellContent *cal_shell_content,
2360 				       time_t *range_start,
2361 				       time_t *range_end)
2362 {
2363 	ICalTimezone *zone;
2364 
2365 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
2366 	g_return_if_fail (range_start != NULL);
2367 	g_return_if_fail (range_end != NULL);
2368 
2369 	zone = e_cal_data_model_get_timezone (e_cal_base_shell_content_get_data_model (
2370 		E_CAL_BASE_SHELL_CONTENT (cal_shell_content)));
2371 
2372 	*range_start = cal_comp_gdate_to_timet (&(cal_shell_content->priv->view_start), zone);
2373 	*range_end = cal_comp_gdate_to_timet (&(cal_shell_content->priv->view_end), zone);
2374 }
2375 
2376 void
e_cal_shell_content_get_current_range_dates(ECalShellContent * cal_shell_content,GDate * range_start,GDate * range_end)2377 e_cal_shell_content_get_current_range_dates (ECalShellContent *cal_shell_content,
2378 					     GDate *range_start,
2379 					     GDate *range_end)
2380 {
2381 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
2382 	g_return_if_fail (range_start != NULL);
2383 	g_return_if_fail (range_end != NULL);
2384 
2385 	*range_start = cal_shell_content->priv->view_start;
2386 	*range_end = cal_shell_content->priv->view_end;
2387 }
2388 
2389 static void
cal_shell_content_move_view_range_relative(ECalShellContent * cal_shell_content,gint direction)2390 cal_shell_content_move_view_range_relative (ECalShellContent *cal_shell_content,
2391 					    gint direction)
2392 {
2393 	GDate start, end;
2394 
2395 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
2396 	g_return_if_fail (direction != 0);
2397 
2398 	start = cal_shell_content->priv->view_start;
2399 	end = cal_shell_content->priv->view_end;
2400 
2401 	switch (cal_shell_content->priv->current_view) {
2402 		case E_CAL_VIEW_KIND_DAY:
2403 			if (direction > 0) {
2404 				g_date_add_days (&start, direction);
2405 				g_date_add_days (&end, direction);
2406 			} else {
2407 				g_date_subtract_days (&start, direction * -1);
2408 				g_date_subtract_days (&end, direction * -1);
2409 			}
2410 			break;
2411 		case E_CAL_VIEW_KIND_WORKWEEK:
2412 		case E_CAL_VIEW_KIND_WEEK:
2413 			if (direction > 0) {
2414 				g_date_add_days (&start, direction * 7);
2415 				g_date_add_days (&end, direction * 7);
2416 			} else {
2417 				g_date_subtract_days (&start, direction * -7);
2418 				g_date_subtract_days (&end, direction * -7);
2419 			}
2420 			break;
2421 		case E_CAL_VIEW_KIND_MONTH:
2422 		case E_CAL_VIEW_KIND_LIST:
2423 			if (g_date_get_day (&start) != 1) {
2424 				g_date_add_months (&start, 1);
2425 				g_date_set_day (&start, 1);
2426 			}
2427 			if (direction > 0)
2428 				g_date_add_months (&start, direction);
2429 			else
2430 				g_date_subtract_months (&start, direction * -1);
2431 			end = start;
2432 			g_date_set_day (&end, g_date_get_days_in_month (g_date_get_month (&start), g_date_get_year (&start)));
2433 			g_date_add_days (&end, 6);
2434 			break;
2435 		case E_CAL_VIEW_KIND_LAST:
2436 			return;
2437 	}
2438 
2439 	e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &start, &end, FALSE);
2440 }
2441 
2442 void
e_cal_shell_content_move_view_range(ECalShellContent * cal_shell_content,ECalendarViewMoveType move_type,time_t exact_date)2443 e_cal_shell_content_move_view_range (ECalShellContent *cal_shell_content,
2444 				     ECalendarViewMoveType move_type,
2445 				     time_t exact_date)
2446 {
2447 	ECalendar *calendar;
2448 	ECalDataModel *data_model;
2449 	EShellSidebar *shell_sidebar;
2450 	EShellView *shell_view;
2451 	ICalTime *tt;
2452 	ICalTimezone *zone;
2453 	GDate date;
2454 
2455 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
2456 
2457 	shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
2458 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
2459 	g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
2460 
2461 	calendar = e_cal_base_shell_sidebar_get_date_navigator (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
2462 	g_return_if_fail (E_IS_CALENDAR (calendar));
2463 	g_return_if_fail (e_calendar_get_item (calendar) != NULL);
2464 
2465 	data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
2466 	zone = e_cal_data_model_get_timezone (data_model);
2467 
2468 	switch (move_type) {
2469 		case E_CALENDAR_VIEW_MOVE_PREVIOUS:
2470 			cal_shell_content_move_view_range_relative (cal_shell_content, -1);
2471 			break;
2472 		case E_CALENDAR_VIEW_MOVE_NEXT:
2473 			cal_shell_content_move_view_range_relative (cal_shell_content, +1);
2474 			break;
2475 		case E_CALENDAR_VIEW_MOVE_TO_TODAY:
2476 			tt = i_cal_time_new_current_with_zone (zone);
2477 			g_date_set_dmy (&date, i_cal_time_get_day (tt), i_cal_time_get_month (tt), i_cal_time_get_year (tt));
2478 			g_clear_object (&tt);
2479 			/* one-day selection takes care of the view range move with left view kind */
2480 			e_calendar_item_set_selection (e_calendar_get_item (calendar), &date, &date);
2481 			break;
2482 		case E_CALENDAR_VIEW_MOVE_TO_EXACT_DAY:
2483 			time_to_gdate_with_zone (&date, exact_date, zone);
2484 			e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &date, &date, FALSE);
2485 			break;
2486 	}
2487 }
2488 
2489 static void
cal_shell_content_update_model_filter(ECalDataModel * data_model,ECalModel * model,const gchar * filter,time_t range_start,time_t range_end)2490 cal_shell_content_update_model_filter (ECalDataModel *data_model,
2491 				       ECalModel *model,
2492 				       const gchar *filter,
2493 				       time_t range_start,
2494 				       time_t range_end)
2495 {
2496 	time_t tmp_start, tmp_end;
2497 
2498 	g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model));
2499 	g_return_if_fail (E_IS_CAL_MODEL (model));
2500 
2501 	e_cal_data_model_freeze_views_update (data_model);
2502 	if (filter != NULL)
2503 		e_cal_data_model_set_filter (data_model, filter);
2504 	e_cal_model_set_time_range (model, range_start, range_end);
2505 
2506 	if (!e_cal_data_model_get_subscriber_range (data_model, E_CAL_DATA_MODEL_SUBSCRIBER (model), &tmp_start, &tmp_end)) {
2507 		e_cal_data_model_subscribe (data_model, E_CAL_DATA_MODEL_SUBSCRIBER (model), range_start, range_end);
2508 	}
2509 
2510 	e_cal_data_model_thaw_views_update (data_model);
2511 }
2512 
2513 void
e_cal_shell_content_update_tasks_filter(ECalShellContent * cal_shell_content,const gchar * cal_filter)2514 e_cal_shell_content_update_tasks_filter (ECalShellContent *cal_shell_content,
2515 					 const gchar *cal_filter)
2516 {
2517 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
2518 
2519 	if (cal_shell_content->priv->task_table) {
2520 		ETaskTable *task_table;
2521 		ECalDataModel *data_model;
2522 		ECalModel *model;
2523 		gchar *hide_completed_tasks_sexp;
2524 		gboolean hide_cancelled_tasks;
2525 
2526 		/* Set the query on the task pad. */
2527 
2528 		task_table = E_TASK_TABLE (cal_shell_content->priv->task_table);
2529 		model = e_task_table_get_model (task_table);
2530 		data_model = e_cal_model_get_data_model (model);
2531 
2532 		hide_completed_tasks_sexp = calendar_config_get_hide_completed_tasks_sexp (FALSE);
2533 		hide_cancelled_tasks = calendar_config_get_hide_cancelled_tasks ();
2534 
2535 		if (hide_completed_tasks_sexp != NULL) {
2536 			if (cal_filter && *cal_filter) {
2537 				gchar *filter;
2538 
2539 				filter = g_strdup_printf ("(and %s %s%s%s)", hide_completed_tasks_sexp,
2540 					hide_cancelled_tasks ? CALENDAR_CONFIG_NOT_CANCELLED_TASKS_SEXP : "",
2541 					hide_cancelled_tasks ? " " : "",
2542 					cal_filter);
2543 				cal_shell_content_update_model_filter (data_model, model, filter, 0, 0);
2544 				g_free (filter);
2545 			} else if (hide_cancelled_tasks) {
2546 				gchar *filter;
2547 
2548 				filter = g_strdup_printf ("(and %s %s)", hide_completed_tasks_sexp,
2549 					CALENDAR_CONFIG_NOT_CANCELLED_TASKS_SEXP);
2550 				cal_shell_content_update_model_filter (data_model, model, filter, 0, 0);
2551 				g_free (filter);
2552 			} else {
2553 				cal_shell_content_update_model_filter (data_model, model, hide_completed_tasks_sexp, 0, 0);
2554 			}
2555 		} else if (hide_cancelled_tasks) {
2556 			if (cal_filter && *cal_filter) {
2557 				gchar *filter;
2558 
2559 				filter = g_strdup_printf ("(and %s %s)", CALENDAR_CONFIG_NOT_CANCELLED_TASKS_SEXP, cal_filter);
2560 				cal_shell_content_update_model_filter (data_model, model, filter, 0, 0);
2561 				g_free (filter);
2562 			} else {
2563 				cal_shell_content_update_model_filter (data_model, model, CALENDAR_CONFIG_NOT_CANCELLED_TASKS_SEXP, 0, 0);
2564 			}
2565 		} else {
2566 			cal_shell_content_update_model_filter (data_model, model, (cal_filter && *cal_filter) ? cal_filter : "#t", 0, 0);
2567 		}
2568 
2569 		g_free (hide_completed_tasks_sexp);
2570 	}
2571 }
2572 
2573 void
e_cal_shell_content_update_filters(ECalShellContent * cal_shell_content,const gchar * cal_filter,time_t start_range,time_t end_range)2574 e_cal_shell_content_update_filters (ECalShellContent *cal_shell_content,
2575 				    const gchar *cal_filter,
2576 				    time_t start_range,
2577 				    time_t end_range)
2578 {
2579 	ECalDataModel *data_model;
2580 	ECalModel *model;
2581 
2582 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
2583 
2584 	if (!cal_filter)
2585 		return;
2586 
2587 	if (e_cal_shell_content_get_current_view_id (cal_shell_content) == E_CAL_VIEW_KIND_LIST) {
2588 		data_model = cal_shell_content->priv->list_view_data_model;
2589 		model = cal_shell_content->priv->list_view_model;
2590 		start_range = 0;
2591 		end_range = 0;
2592 	} else {
2593 		data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
2594 		model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
2595 	}
2596 
2597 	cal_shell_content_update_model_filter (data_model, model, cal_filter, start_range, end_range);
2598 	e_cal_shell_content_update_tasks_filter (cal_shell_content, cal_filter);
2599 
2600 	if (cal_shell_content->priv->memo_table) {
2601 		EMemoTable *memo_table;
2602 
2603 		/* Set the query on the memo pad. */
2604 
2605 		memo_table = E_MEMO_TABLE (cal_shell_content->priv->memo_table);
2606 		model = e_memo_table_get_model (memo_table);
2607 		data_model = e_cal_model_get_data_model (model);
2608 
2609 		if (start_range != 0 && end_range != 0) {
2610 			ICalTimezone *zone;
2611 			const gchar *default_tzloc = NULL;
2612 			time_t end = end_range;
2613 			gchar *filter;
2614 			gchar *iso_start;
2615 			gchar *iso_end;
2616 
2617 			zone = e_cal_data_model_get_timezone (data_model);
2618 			if (zone && zone != i_cal_timezone_get_utc_timezone ())
2619 				default_tzloc = i_cal_timezone_get_location (zone);
2620 			if (!default_tzloc)
2621 				default_tzloc = "";
2622 
2623 			if (start_range != (time_t) 0 && end_range != (time_t) 0) {
2624 				end = time_day_end_with_zone (end_range, zone);
2625 			}
2626 
2627 			iso_start = isodate_from_time_t (start_range);
2628 			iso_end = isodate_from_time_t (end);
2629 
2630 			filter = g_strdup_printf (
2631 				"(and (or (not (has-start?)) "
2632 				"(occur-in-time-range? (make-time \"%s\") "
2633 				"(make-time \"%s\") \"%s\")) %s)",
2634 				iso_start, iso_end, default_tzloc, cal_filter);
2635 
2636 			cal_shell_content_update_model_filter (data_model, model, filter, 0, 0);
2637 
2638 			g_free (filter);
2639 			g_free (iso_start);
2640 			g_free (iso_end);
2641 		} else {
2642 			cal_shell_content_update_model_filter (data_model, model, *cal_filter ? cal_filter : "#t", 0, 0);
2643 		}
2644 	}
2645 }
2646 
2647 ECalDataModel *
e_cal_shell_content_get_list_view_data_model(ECalShellContent * cal_shell_content)2648 e_cal_shell_content_get_list_view_data_model (ECalShellContent *cal_shell_content)
2649 {
2650 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
2651 
2652 	return cal_shell_content->priv->list_view_data_model;
2653 }
2654 
2655 void
e_cal_shell_content_set_show_tag_vpane(ECalShellContent * cal_shell_content,gboolean show)2656 e_cal_shell_content_set_show_tag_vpane (ECalShellContent *cal_shell_content,
2657 					gboolean show)
2658 {
2659 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
2660 
2661 	if ((gtk_widget_get_visible (cal_shell_content->priv->vpaned) ? 1 : 0) == (show ? 1 : 0))
2662 		return;
2663 
2664 	gtk_widget_set_visible (cal_shell_content->priv->vpaned, show);
2665 
2666 	if (show) {
2667 		if (cal_shell_content->priv->task_data_model)
2668 			e_cal_data_model_thaw_views_update (cal_shell_content->priv->task_data_model);
2669 
2670 		if (cal_shell_content->priv->memo_data_model)
2671 			e_cal_data_model_thaw_views_update (cal_shell_content->priv->memo_data_model);
2672 	} else {
2673 		if (cal_shell_content->priv->task_data_model)
2674 			e_cal_data_model_freeze_views_update (cal_shell_content->priv->task_data_model);
2675 
2676 		if (cal_shell_content->priv->memo_data_model)
2677 			e_cal_data_model_freeze_views_update (cal_shell_content->priv->memo_data_model);
2678 	}
2679 
2680 	g_object_notify (G_OBJECT (cal_shell_content), "show-tag-vpane");
2681 }
2682 
2683 gboolean
e_cal_shell_content_get_show_tag_vpane(ECalShellContent * cal_shell_content)2684 e_cal_shell_content_get_show_tag_vpane (ECalShellContent *cal_shell_content)
2685 {
2686 	g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), FALSE);
2687 
2688 	return gtk_widget_get_visible (cal_shell_content->priv->vpaned);
2689 }
2690