1 /*
2  * This program is free software; you can redistribute it and/or modify it
3  * under the terms of the GNU Lesser General Public License as published by
4  * the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful, but
7  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
9  * for more details.
10  *
11  * You should have received a copy of the GNU Lesser General Public License
12  * along with this program; if not, see <http://www.gnu.org/licenses/>.
13  *
14  *
15  * Authors:
16  *		Damon Chaplin <damon@ximian.com>
17  *		Rodrigo Moya <rodrigo@ximian.com>
18  *
19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20  *
21  */
22 
23 /*
24  * EWeekView - displays the Week & Month views of the calendar.
25  */
26 
27 #include "evolution-config.h"
28 
29 #include "e-week-view.h"
30 
31 #include <math.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <glib/gi18n.h>
34 #include <libgnomecanvas/libgnomecanvas.h>
35 
36 #include "calendar-config.h"
37 #include "comp-util.h"
38 #include "e-cal-dialogs.h"
39 #include "e-cal-model-calendar.h"
40 #include "e-cal-ops.h"
41 #include "e-week-view-event-item.h"
42 #include "e-week-view-layout.h"
43 #include "e-week-view-main-item.h"
44 #include "e-week-view-titles-item.h"
45 #include "ea-calendar.h"
46 #include "itip-utils.h"
47 #include "misc.h"
48 #include "print.h"
49 #include "ea-week-view.h"
50 
51 /* Images */
52 #include "data/xpm/jump.xpm"
53 
54 #define E_WEEK_VIEW_GET_PRIVATE(obj) \
55 	(G_TYPE_INSTANCE_GET_PRIVATE \
56 	((obj), E_TYPE_WEEK_VIEW, EWeekViewPrivate))
57 
58 #define E_WEEK_VIEW_SMALL_FONT_PTSIZE 7
59 
60 #define E_WEEK_VIEW_JUMP_BUTTON_WIDTH	16
61 #define E_WEEK_VIEW_JUMP_BUTTON_HEIGHT	8
62 
63 #define E_WEEK_VIEW_JUMP_BUTTON_X_PAD	3
64 #define E_WEEK_VIEW_JUMP_BUTTON_Y_PAD	3
65 
66 #define E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS -1
67 
68 /* The timeout before we do a layout, so we don't do a layout for each event
69  * we get from the server. */
70 #define E_WEEK_VIEW_LAYOUT_TIMEOUT	100
71 
72 struct _EWeekViewPrivate {
73 	/* The first day shown in the view. */
74 	GDate first_day_shown;
75 
76 	/* If we are displaying multiple weeks in rows.  If this is
77 	 * FALSE only one week is shown, with a different layout. */
78 	gboolean multi_week_view;
79 
80 	/* How many weeks we are showing.  This is only relevant if
81 	 * multi_week_view is TRUE. */
82 	gint weeks_shown;
83 
84 	/* If Sat & Sun are compressed.  Only applicable in month view,
85 	 * since they are always compressed into 1 cell in week view. */
86 	gboolean compress_weekend;
87 
88 	/* Whether we show event end times. */
89 	gboolean show_event_end_times;
90 
91 	/* Whether to update the base date when the time range changes. */
92 	gboolean update_base_date;
93 
94 	/* The first day of the week we display.  This will usually be
95 	 * week_start_day, but if the Sat & Sun are compressed and the
96 	 * week starts on Sunday then we have to use Saturday. */
97 	GDateWeekday display_start_day;
98 
99 	gulong notify_week_start_day_id;
100 
101 	gboolean show_icons_month_view;
102 	gboolean draw_flat_events;
103 	gboolean days_left_to_right;
104 };
105 
106 typedef struct {
107 	EWeekView *week_view;
108 	ECalModelComponent *comp_data;
109 } AddEventData;
110 
111 static void e_week_view_set_colors (EWeekView *week_view);
112 static void e_week_view_recalc_cell_sizes (EWeekView *week_view);
113 static gboolean e_week_view_get_next_tab_event (EWeekView *week_view,
114 						GtkDirectionType direction,
115 						gint current_event_num,
116 						gint current_span_num,
117 						gint *next_event_num,
118 						gint *next_span_num);
119 static void e_week_view_update_query (EWeekView *week_view);
120 
121 static gboolean e_week_view_on_button_press (GtkWidget *widget,
122 					     GdkEvent *button_event,
123 					     EWeekView *week_view);
124 static gboolean e_week_view_on_button_release (GtkWidget *widget,
125 					       GdkEvent *button_event,
126 					       EWeekView *week_view);
127 static gboolean e_week_view_on_scroll (GtkWidget *widget,
128 				       GdkEventScroll *scroll,
129 				       EWeekView *week_view);
130 static gboolean e_week_view_on_motion (GtkWidget *widget,
131 				       GdkEventMotion *event,
132 				       EWeekView *week_view);
133 static gint e_week_view_convert_position_to_day (EWeekView *week_view,
134 						 gint x,
135 						 gint y);
136 static void e_week_view_update_selection (EWeekView *week_view,
137 					  gint day);
138 
139 static void e_week_view_free_events (EWeekView *week_view);
140 static void e_week_view_add_event (ECalClient *client,
141 				   ECalComponent *comp,
142 				   time_t start,
143 				   time_t end,
144 				   gboolean prepend,
145 				   gpointer data);
146 static void e_week_view_check_layout (EWeekView *week_view);
147 static void e_week_view_ensure_events_sorted (EWeekView *week_view);
148 static void e_week_view_reshape_events (EWeekView *week_view);
149 static void e_week_view_reshape_event_span (EWeekView *week_view,
150 					    gint event_num,
151 					    gint span_num);
152 static void e_week_view_recalc_day_starts (EWeekView *week_view,
153 					   time_t lower);
154 static void e_week_view_on_editing_started (EWeekView *week_view,
155 					    GnomeCanvasItem *item);
156 static void e_week_view_on_editing_stopped (EWeekView *week_view,
157 					    GnomeCanvasItem *item);
158 static gboolean e_week_view_find_event_from_uid (EWeekView	  *week_view,
159 						 ECalClient             *client,
160 						 const gchar	  *uid,
161 						 const gchar      *rid,
162 						 gint		  *event_num_return);
163 typedef gboolean (* EWeekViewForeachEventCallback) (EWeekView *week_view,
164 						    gint event_num,
165 						    gpointer data);
166 static void e_week_view_foreach_event_with_uid (EWeekView *week_view,
167 						const gchar *uid,
168 						EWeekViewForeachEventCallback callback,
169 						gpointer data);
170 static gboolean e_week_view_on_text_item_event (GnomeCanvasItem *item,
171 						GdkEvent *event,
172 						EWeekView *week_view);
173 static gboolean e_week_view_event_move (ECalendarView *cal_view, ECalViewMoveDirection direction);
174 static gint e_week_view_get_day_offset_of_event (EWeekView *week_view, time_t event_time);
175 static void e_week_view_change_event_time (EWeekView *week_view, time_t start_dt, time_t end_dt, gboolean is_all_day);
176 static gboolean e_week_view_on_jump_button_event (GnomeCanvasItem *item,
177 						  GdkEvent *event,
178 						  EWeekView *week_view);
179 static gboolean e_week_view_do_key_press (GtkWidget *widget,
180 					  GdkEventKey *event);
181 static gint e_week_view_get_adjust_days_for_move_up (EWeekView *week_view, gint
182 current_day);
183 static gint e_week_view_get_adjust_days_for_move_down (EWeekView *week_view,gint current_day);
184 static gint e_week_view_get_adjust_days_for_move_left (EWeekView *week_view,gint current_day);
185 static gint e_week_view_get_adjust_days_for_move_right (EWeekView *week_view,gint current_day);
186 
187 static gboolean e_week_view_remove_event_cb (EWeekView *week_view,
188 					     gint event_num,
189 					     gpointer data);
190 static gboolean e_week_view_recalc_display_start_day	(EWeekView	*week_view);
191 
192 static void e_week_view_queue_layout (EWeekView *week_view);
193 static void e_week_view_cancel_layout (EWeekView *week_view);
194 static gboolean e_week_view_layout_timeout_cb (gpointer data);
195 
196 G_DEFINE_TYPE (EWeekView, e_week_view, E_TYPE_CALENDAR_VIEW)
197 
198 enum {
199 	PROP_0,
200 	PROP_COMPRESS_WEEKEND,
201 	PROP_DRAW_FLAT_EVENTS,
202 	PROP_DAYS_LEFT_TO_RIGHT,
203 	PROP_SHOW_EVENT_END_TIMES,
204 	PROP_SHOW_ICONS_MONTH_VIEW,
205 	PROP_IS_EDITING
206 };
207 
208 static gint map_left[] = {0, 1, 2, 0, 1, 2, 2};
209 static gint map_right[] = {3, 4, 5, 3, 4, 5, 6};
210 
211 static void
e_week_view_set_popup_event(EWeekView * week_view,gint event_num)212 e_week_view_set_popup_event (EWeekView *week_view,
213 			     gint event_num)
214 {
215 	if (week_view->popup_event_num != event_num) {
216 		week_view->popup_event_num = event_num;
217 
218 		g_signal_emit_by_name (week_view, "selection-changed");
219 	}
220 }
221 
222 static void
week_view_process_component(EWeekView * week_view,ECalModelComponent * comp_data)223 week_view_process_component (EWeekView *week_view,
224                              ECalModelComponent *comp_data)
225 {
226 	ECalComponent *comp = NULL;
227 	AddEventData add_event_data;
228 
229 	/* If we don't have a valid date set yet, just return. */
230 	if (!g_date_valid (&week_view->priv->first_day_shown))
231 		return;
232 
233 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (comp_data->icalcomp));
234 	if (!comp) {
235 		g_message (G_STRLOC ": Could not set ICalComponent on ECalComponent");
236 		return;
237 	}
238 
239 	/* Add the object */
240 	add_event_data.week_view = week_view;
241 	add_event_data.comp_data = comp_data;
242 	e_week_view_add_event (comp_data->client, comp, comp_data->instance_start, comp_data->instance_end, FALSE, &add_event_data);
243 
244 	g_object_unref (comp);
245 }
246 
247 static void
week_view_update_row(EWeekView * week_view,gint row)248 week_view_update_row (EWeekView *week_view,
249                       gint row)
250 {
251 	ECalModelComponent *comp_data;
252 	ECalModel *model;
253 	gint event_num;
254 	const gchar *uid;
255 	gchar *rid;
256 
257 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
258 	comp_data = e_cal_model_get_component_at (model, row);
259 	g_return_if_fail (comp_data != NULL);
260 
261 	uid = i_cal_component_get_uid (comp_data->icalcomp);
262 	rid = e_cal_util_component_get_recurid_as_string (comp_data->icalcomp);
263 
264 	if (e_week_view_find_event_from_uid (week_view, comp_data->client, uid, rid, &event_num))
265 		e_week_view_remove_event_cb (week_view, event_num, NULL);
266 
267 	g_free (rid);
268 
269 	week_view_process_component (week_view, comp_data);
270 
271 	gtk_widget_queue_draw (week_view->main_canvas);
272 	e_week_view_queue_layout (week_view);
273 }
274 
275 static void
week_view_model_cell_changed_cb(EWeekView * week_view,gint col,gint row)276 week_view_model_cell_changed_cb (EWeekView *week_view,
277                                  gint col,
278                                  gint row)
279 {
280 	if (!E_CALENDAR_VIEW (week_view)->in_focus) {
281 		e_week_view_free_events (week_view);
282 		week_view->requires_update = TRUE;
283 		return;
284 	}
285 
286 	week_view_update_row (week_view, row);
287 }
288 
289 static void
week_view_model_comps_deleted_cb(EWeekView * week_view,gpointer data)290 week_view_model_comps_deleted_cb (EWeekView *week_view,
291                                   gpointer data)
292 {
293 	GSList *l, *list = data;
294 
295 	/* FIXME Stop editing? */
296 	if (!E_CALENDAR_VIEW (week_view)->in_focus) {
297 		e_week_view_free_events (week_view);
298 		week_view->requires_update = TRUE;
299 		return;
300 	}
301 
302 	for (l = list; l != NULL; l = g_slist_next (l)) {
303 		gint event_num;
304 		const gchar *uid;
305 		gchar *rid;
306 		ECalModelComponent *comp_data = l->data;
307 
308 		uid = i_cal_component_get_uid (comp_data->icalcomp);
309 		rid = e_cal_util_component_get_recurid_as_string (comp_data->icalcomp);
310 
311 		if (e_week_view_find_event_from_uid (week_view, comp_data->client, uid, rid, &event_num))
312 			e_week_view_remove_event_cb (week_view, event_num, NULL);
313 
314 		g_free (rid);
315 	}
316 
317 	gtk_widget_queue_draw (week_view->main_canvas);
318 	e_week_view_queue_layout (week_view);
319 }
320 
321 static void
week_view_model_row_changed_cb(EWeekView * week_view,gint row)322 week_view_model_row_changed_cb (EWeekView *week_view,
323                                 gint row)
324 {
325 	if (!E_CALENDAR_VIEW (week_view)->in_focus) {
326 		e_week_view_free_events (week_view);
327 		week_view->requires_update = TRUE;
328 		return;
329 	}
330 
331 	week_view_update_row (week_view, row);
332 }
333 
334 static void
week_view_model_rows_inserted_cb(EWeekView * week_view,gint row,gint count)335 week_view_model_rows_inserted_cb (EWeekView *week_view,
336                                   gint row,
337                                   gint count)
338 {
339 	ECalModel *model;
340 	gint i;
341 
342 	if (!E_CALENDAR_VIEW (week_view)->in_focus) {
343 		e_week_view_free_events (week_view);
344 		week_view->requires_update = TRUE;
345 		return;
346 	}
347 
348 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
349 
350 	for (i = 0; i < count; i++) {
351 		ECalModelComponent *comp_data;
352 
353 		comp_data = e_cal_model_get_component_at (model, row + i);
354 		if (comp_data == NULL) {
355 			g_warning ("comp_data is NULL\n");
356 			continue;
357 		}
358 		week_view_process_component (week_view, comp_data);
359 	}
360 
361 	gtk_widget_queue_draw (week_view->main_canvas);
362 	e_week_view_queue_layout (week_view);
363 }
364 
365 static void
e_week_view_precalc_visible_time_range(ECalendarView * cal_view,time_t in_start_time,time_t in_end_time,time_t * out_start_time,time_t * out_end_time)366 e_week_view_precalc_visible_time_range (ECalendarView *cal_view,
367 					time_t in_start_time,
368 					time_t in_end_time,
369 					time_t *out_start_time,
370 					time_t *out_end_time)
371 {
372 	EWeekView *week_view;
373 	GDate date, base_date;
374 	GDateWeekday weekday;
375 	GDateWeekday display_start_day;
376 	guint day_offset, week_start_offset;
377 	gint num_days;
378 	ICalTimezone *zone;
379 
380 	g_return_if_fail (E_IS_WEEK_VIEW (cal_view));
381 	g_return_if_fail (out_start_time != NULL);
382 	g_return_if_fail (out_end_time != NULL);
383 
384 	week_view = E_WEEK_VIEW (cal_view);
385 	zone = e_calendar_view_get_timezone (cal_view);
386 
387 	time_to_gdate_with_zone (&date, in_start_time, zone);
388 
389 	weekday = g_date_get_weekday (&date);
390 	display_start_day = e_week_view_get_display_start_day (week_view);
391 
392 	/* Convert it to an offset from the start of the display. */
393 	week_start_offset = e_weekday_get_days_between (display_start_day, weekday);
394 
395 	/* Set the day_offset to the result, so we move back to the
396 	 * start of the week. */
397 	day_offset = week_start_offset;
398 
399 	/* Calculate the base date, i.e. the first day shown when the
400 	 * scrollbar adjustment value is 0. */
401 	base_date = date;
402 	g_date_subtract_days (&base_date, day_offset);
403 
404 	num_days = e_week_view_get_weeks_shown (week_view) * 7;
405 
406 	/* See if we need to update the first day shown. */
407 	if (!g_date_valid (&week_view->priv->first_day_shown)
408 	    || g_date_compare (&week_view->priv->first_day_shown, &base_date)) {
409 		GDate end_date, in_end_date;
410 		gint day;
411 
412 		end_date = date;
413 		g_date_add_days (&end_date, num_days);
414 		g_date_subtract_days (&end_date, day_offset);
415 
416 		time_to_gdate_with_zone (&in_end_date, in_end_time, zone);
417 
418 		while (g_date_days_between (&end_date, &in_end_date) >= 6) {
419 			g_date_add_days (&end_date, 7);
420 			num_days += 7;
421 		}
422 
423 		in_start_time = time_add_day_with_zone (in_start_time, -((gint) day_offset), zone);
424 		in_start_time = time_day_begin_with_zone (in_start_time, zone);
425 
426 		*out_start_time = in_start_time;
427 		*out_end_time = in_start_time;
428 
429 		for (day = 1; day <= num_days; day++) {
430 			*out_end_time = time_add_day_with_zone (*out_end_time, 1, zone);
431 		}
432 	} else {
433 		*out_start_time = week_view->day_starts[0];
434 		*out_end_time = week_view->day_starts[num_days];
435 	}
436 }
437 
438 static void
week_view_time_range_changed_cb(EWeekView * week_view,gint64 i64_start_time,gint64 i64_end_time,ECalModel * model)439 week_view_time_range_changed_cb (EWeekView *week_view,
440                                  gint64 i64_start_time,
441                                  gint64 i64_end_time,
442                                  ECalModel *model)
443 {
444 	time_t start_time = (time_t) i64_start_time;
445 	GDate date, base_date;
446 	GDateWeekday weekday;
447 	GDateWeekday display_start_day;
448 	guint day_offset, week_start_offset;
449 	gint num_days;
450 	gboolean update_adjustment_value = FALSE;
451 
452 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
453 
454 	time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
455 
456 	weekday = g_date_get_weekday (&date);
457 	display_start_day = e_week_view_get_display_start_day (week_view);
458 
459 	/* Convert it to an offset from the start of the display. */
460 	week_start_offset = e_weekday_get_days_between (
461 		display_start_day, weekday);
462 
463 	/* Set the day_offset to the result, so we move back to the
464 	 * start of the week. */
465 	day_offset = week_start_offset;
466 
467 	/* Calculate the base date, i.e. the first day shown when the
468 	 * scrollbar adjustment value is 0. */
469 	base_date = date;
470 	g_date_subtract_days (&base_date, day_offset);
471 
472 	/* See if we need to update the base date. */
473 	if (!g_date_valid (&week_view->base_date)
474 	    || e_week_view_get_update_base_date (week_view)) {
475 		week_view->base_date = base_date;
476 		update_adjustment_value = TRUE;
477 	}
478 
479 	/* See if we need to update the first day shown. */
480 	if (!g_date_valid (&week_view->priv->first_day_shown)
481 	    || g_date_compare (&week_view->priv->first_day_shown, &base_date)) {
482 		week_view->priv->first_day_shown = base_date;
483 		start_time = time_add_day_with_zone (
484 			start_time, -((gint) day_offset),
485 			e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
486 		start_time = time_day_begin_with_zone (
487 			start_time,
488 			e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
489 		e_week_view_recalc_day_starts (week_view, start_time);
490 	}
491 
492 	/* Reset the adjustment value to 0 if the base address has changed.
493 	 * Note that we do this after updating first_day_shown so that our
494 	 * signal handler will not try to reload the events. */
495 	if (update_adjustment_value) {
496 		GtkRange *range;
497 		GtkAdjustment *adjustment;
498 
499 		range = GTK_RANGE (week_view->vscrollbar);
500 		adjustment = gtk_range_get_adjustment (range);
501 		gtk_adjustment_set_value (adjustment, 0);
502 	}
503 
504 	if (!E_CALENDAR_VIEW (week_view)->in_focus) {
505 		e_week_view_free_events (week_view);
506 		week_view->requires_update = TRUE;
507 		return;
508 	}
509 
510 	gtk_widget_queue_draw (week_view->main_canvas);
511 
512 	num_days = e_week_view_get_weeks_shown (week_view) * 7;
513 
514 	/* FIXME Preserve selection if possible */
515 	if (week_view->selection_start_day == -1 ||
516 	    num_days <= week_view->selection_start_day)
517 		e_calendar_view_set_selected_time_range (
518 			E_CALENDAR_VIEW (week_view), start_time, start_time);
519 }
520 
521 static void
timezone_changed_cb(ECalModel * cal_model,ICalTimezone * old_zone,ICalTimezone * new_zone,gpointer user_data)522 timezone_changed_cb (ECalModel *cal_model,
523 		     ICalTimezone *old_zone,
524 		     ICalTimezone *new_zone,
525 		     gpointer user_data)
526 {
527 	ECalendarView *cal_view = (ECalendarView *) user_data;
528 	GDate *first_day_shown;
529 	ICalTime *tt = NULL;
530 	time_t lower;
531 	EWeekView *week_view = (EWeekView *) cal_view;
532 
533 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
534 
535 	first_day_shown = &week_view->priv->first_day_shown;
536 
537 	if (!cal_view->in_focus) {
538 		e_week_view_free_events (week_view);
539 		week_view->requires_update = TRUE;
540 		return;
541 	}
542 
543 	/* If we don't have a valid date set yet, just return. */
544 	if (!g_date_valid (first_day_shown))
545 		return;
546 
547 	tt = i_cal_time_new_null_time ();
548 
549 	/* Recalculate the new start of the first week. We just use exactly
550 	 * the same time, but with the new timezone. */
551 	i_cal_time_set_date (tt,
552 		g_date_get_year (first_day_shown),
553 		g_date_get_month (first_day_shown),
554 		g_date_get_day (first_day_shown));
555 
556 	lower = i_cal_time_as_timet_with_zone (tt, new_zone);
557 
558 	g_clear_object (&tt);
559 
560 	e_week_view_recalc_day_starts (week_view, lower);
561 	e_week_view_update_query (week_view);
562 }
563 
564 static void
week_view_notify_week_start_day_cb(EWeekView * week_view)565 week_view_notify_week_start_day_cb (EWeekView *week_view)
566 {
567 	GDate *first_day_shown;
568 
569 	first_day_shown = &week_view->priv->first_day_shown;
570 
571 	e_week_view_recalc_display_start_day (week_view);
572 
573 	/* Recalculate the days shown and reload if necessary. */
574 	if (g_date_valid (first_day_shown))
575 		e_week_view_set_first_day_shown (week_view, first_day_shown);
576 
577 	gtk_widget_queue_draw (week_view->titles_canvas);
578 	gtk_widget_queue_draw (week_view->main_canvas);
579 }
580 
581 static void
month_scroll_by_week_changed_cb(GSettings * settings,const gchar * key,gpointer user_data)582 month_scroll_by_week_changed_cb (GSettings *settings,
583                                  const gchar *key,
584                                  gpointer user_data)
585 {
586 	EWeekView *week_view = user_data;
587 
588 	g_return_if_fail (week_view != NULL);
589 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
590 
591 	if (e_week_view_get_multi_week_view (week_view) &&
592 	    week_view->month_scroll_by_week != calendar_config_get_month_scroll_by_week ()) {
593 		week_view->priv->multi_week_view = FALSE;
594 		e_week_view_set_multi_week_view (week_view, TRUE);
595 	}
596 }
597 
598 /* FIXME: This is also needed in e-day-view-time-item.c. We should probably use
599  * pango's approximation function, but it needs a language tag. Find out how to
600  * get one of those properly. */
601 static gint
get_digit_width(PangoLayout * layout)602 get_digit_width (PangoLayout *layout)
603 {
604 	gint digit;
605 	gint max_digit_width = 1;
606 
607 	for (digit = '0'; digit <= '9'; digit++) {
608 		gchar digit_char;
609 		gint  digit_width;
610 
611 		digit_char = digit;
612 
613 		pango_layout_set_text (layout, &digit_char, 1);
614 		pango_layout_get_pixel_size (layout, &digit_width, NULL);
615 
616 		max_digit_width = MAX (max_digit_width, digit_width);
617 	}
618 
619 	return max_digit_width;
620 }
621 
622 static gint
get_string_width(PangoLayout * layout,const gchar * string)623 get_string_width (PangoLayout *layout,
624                   const gchar *string)
625 {
626 	gint width;
627 
628 	pango_layout_set_text (layout, string, -1);
629 	pango_layout_get_pixel_size (layout, &width, NULL);
630 	return width;
631 }
632 
633 typedef struct {
634 	EWeekView *week_view;
635 	time_t dtstart, dtend;
636 	gchar *initial_text;
637 	gboolean paste_clipboard;
638 } NewEventInRangeData;
639 
640 static void
new_event_in_rage_data_free(gpointer ptr)641 new_event_in_rage_data_free (gpointer ptr)
642 {
643 	NewEventInRangeData *ned = ptr;
644 
645 	if (ned) {
646 		g_clear_object (&ned->week_view);
647 		g_free (ned->initial_text);
648 		g_slice_free (NewEventInRangeData, ned);
649 	}
650 }
651 
652 static void
week_view_new_event_in_selected_range_cb(ECalModel * model,ECalClient * client,ICalComponent * default_component,gpointer user_data)653 week_view_new_event_in_selected_range_cb (ECalModel *model,
654 					  ECalClient *client,
655 					  ICalComponent *default_component,
656 					  gpointer user_data)
657 {
658 	NewEventInRangeData *ned = user_data;
659 	ECalComponent *comp = NULL;
660 	gint event_num;
661 	ECalComponentDateTime *date;
662 	const gchar *uid;
663 	AddEventData add_event_data;
664 	EWeekViewEvent *wvevent;
665 	EWeekViewEventSpan *span;
666 	ICalTimezone *zone;
667 
668 	/* Check if the client is read only */
669 	if (e_client_is_readonly (E_CLIENT (client)))
670 		goto exit;
671 
672 	/* Add a new event covering the selected range. */
673 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (default_component));
674 	g_return_if_fail (comp != NULL);
675 
676 	uid = i_cal_component_get_uid (default_component);
677 
678 	zone = e_cal_model_get_timezone (model);
679 
680 	/* We use DATE values now, so we don't need the timezone. */
681 	/*date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));*/
682 
683 	date = e_cal_component_datetime_new_take (i_cal_time_new_from_timet_with_zone (ned->dtstart, TRUE, zone), NULL);
684 	e_cal_component_set_dtstart (comp, date);
685 	e_cal_component_datetime_free (date);
686 
687 	date = e_cal_component_datetime_new_take (i_cal_time_new_from_timet_with_zone (ned->dtend, TRUE, zone), NULL);
688 	e_cal_component_set_dtend (comp, date);
689 	e_cal_component_datetime_free (date);
690 
691 	/* Editor default in week/month view */
692 	e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
693 
694 	/* We add the event locally and start editing it. We don't send it
695 	 * to the server until the user finishes editing it. */
696 	add_event_data.week_view = ned->week_view;
697 	add_event_data.comp_data = NULL;
698 	e_week_view_add_event (client, comp, ned->dtstart, ned->dtend, TRUE, &add_event_data);
699 	e_week_view_check_layout (ned->week_view);
700 	gtk_widget_queue_draw (ned->week_view->main_canvas);
701 
702 	if (!e_week_view_find_event_from_uid (ned->week_view, client, uid, NULL, &event_num)) {
703 		g_warning ("Couldn't find event to start editing.\n");
704 		goto exit;
705 	}
706 
707 	if (!is_array_index_in_bounds (ned->week_view->events, event_num))
708 		goto exit;
709 
710 	wvevent = &g_array_index (ned->week_view->events, EWeekViewEvent, event_num);
711 
712 	if (!is_array_index_in_bounds (ned->week_view->spans, wvevent->spans_index + 0))
713 		goto exit;
714 
715 	span = &g_array_index (ned->week_view->spans, EWeekViewEventSpan, wvevent->spans_index + 0);
716 
717 	/* If the event can't be fit on the screen, don't try to edit it. */
718 	if (!span->text_item) {
719 		e_week_view_foreach_event_with_uid (ned->week_view, uid, e_week_view_remove_event_cb, NULL);
720 		goto exit;
721 	}
722 
723 	e_week_view_start_editing_event (ned->week_view, event_num, 0, ned->initial_text);
724 
725 	if (ned->paste_clipboard) {
726 		wvevent = &g_array_index (ned->week_view->events, EWeekViewEvent, ned->week_view->editing_event_num);
727 
728 		if (!is_array_index_in_bounds (ned->week_view->spans, wvevent->spans_index + ned->week_view->editing_span_num))
729 			return;
730 
731 		span = &g_array_index (ned->week_view->spans, EWeekViewEventSpan, wvevent->spans_index + ned->week_view->editing_span_num);
732 
733 		if (span->text_item &&
734 		    E_IS_TEXT (span->text_item) &&
735 		    E_TEXT (span->text_item)->editing) {
736 			e_text_paste_clipboard (E_TEXT (span->text_item));
737 		}
738 	}
739 
740  exit:
741 	g_clear_object (&comp);
742 }
743 
744 static void
e_week_view_add_new_event_in_selected_range(EWeekView * week_view,const gchar * initial_text,gboolean paste_clipboard)745 e_week_view_add_new_event_in_selected_range (EWeekView *week_view,
746                                              const gchar *initial_text,
747 					     gboolean paste_clipboard)
748 {
749 	NewEventInRangeData *ned;
750 	ECalModel *model;
751 	const gchar *source_uid;
752 
753 	ned = g_slice_new0 (NewEventInRangeData);
754 	ned->week_view = g_object_ref (week_view);
755 	ned->initial_text = g_strdup (initial_text);
756 	ned->dtstart = week_view->day_starts[week_view->selection_start_day];
757 	ned->dtend = week_view->day_starts[week_view->selection_end_day + 1];
758 	ned->paste_clipboard = paste_clipboard;
759 
760 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
761 	source_uid = e_cal_model_get_default_source_uid (model);
762 
763 	e_cal_ops_get_default_component	 (model, source_uid, TRUE,
764 		week_view_new_event_in_selected_range_cb, ned, new_event_in_rage_data_free);
765 }
766 
767 static void
week_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)768 week_view_set_property (GObject *object,
769                         guint property_id,
770                         const GValue *value,
771                         GParamSpec *pspec)
772 {
773 	switch (property_id) {
774 		case PROP_COMPRESS_WEEKEND:
775 			e_week_view_set_compress_weekend (
776 				E_WEEK_VIEW (object),
777 				g_value_get_boolean (value));
778 			return;
779 
780 		case PROP_DAYS_LEFT_TO_RIGHT:
781 			e_week_view_set_days_left_to_right (
782 				E_WEEK_VIEW (object),
783 				g_value_get_boolean (value));
784 			return;
785 
786 		case PROP_DRAW_FLAT_EVENTS:
787 			e_week_view_set_draw_flat_events (
788 				E_WEEK_VIEW (object),
789 				g_value_get_boolean (value));
790 			return;
791 
792 		case PROP_SHOW_EVENT_END_TIMES:
793 			e_week_view_set_show_event_end_times (
794 				E_WEEK_VIEW (object),
795 				g_value_get_boolean (value));
796 			return;
797 
798 		case PROP_SHOW_ICONS_MONTH_VIEW:
799 			e_week_view_set_show_icons_month_view (
800 				E_WEEK_VIEW (object),
801 				g_value_get_boolean (value));
802 			return;
803 	}
804 
805 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
806 }
807 
808 static void
week_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)809 week_view_get_property (GObject *object,
810                         guint property_id,
811                         GValue *value,
812                         GParamSpec *pspec)
813 {
814 	switch (property_id) {
815 		case PROP_COMPRESS_WEEKEND:
816 			g_value_set_boolean (
817 				value,
818 				e_week_view_get_compress_weekend (
819 				E_WEEK_VIEW (object)));
820 			return;
821 
822 		case PROP_DAYS_LEFT_TO_RIGHT:
823 			g_value_set_boolean (
824 				value,
825 				e_week_view_get_days_left_to_right (
826 				E_WEEK_VIEW (object)));
827 			return;
828 
829 		case PROP_DRAW_FLAT_EVENTS:
830 			g_value_set_boolean (
831 				value,
832 				e_week_view_get_draw_flat_events (
833 				E_WEEK_VIEW (object)));
834 			return;
835 
836 		case PROP_SHOW_EVENT_END_TIMES:
837 			g_value_set_boolean (
838 				value,
839 				e_week_view_get_show_event_end_times (
840 				E_WEEK_VIEW (object)));
841 			return;
842 
843 		case PROP_SHOW_ICONS_MONTH_VIEW:
844 			g_value_set_boolean (
845 				value,
846 				e_week_view_get_show_icons_month_view (
847 				E_WEEK_VIEW (object)));
848 			return;
849 
850 		case PROP_IS_EDITING:
851 			g_value_set_boolean (value, e_week_view_is_editing (E_WEEK_VIEW (object)));
852 			return;
853 	}
854 
855 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
856 }
857 
858 static void
week_view_dispose(GObject * object)859 week_view_dispose (GObject *object)
860 {
861 	EWeekView *week_view;
862 	ECalModel *model;
863 
864 	week_view = E_WEEK_VIEW (object);
865 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (object));
866 
867 	e_week_view_cancel_layout (week_view);
868 
869 	if (model) {
870 		g_signal_handlers_disconnect_by_data (model, object);
871 		e_signal_disconnect_notify_handler (model, &week_view->priv->notify_week_start_day_id);
872 	}
873 
874 	if (week_view->events) {
875 		e_week_view_free_events (week_view);
876 		g_array_free (week_view->events, TRUE);
877 		week_view->events = NULL;
878 	}
879 
880 	g_clear_pointer (&week_view->small_font_desc, pango_font_description_free);
881 	g_clear_object (&week_view->normal_cursor);
882 	g_clear_object (&week_view->move_cursor);
883 	g_clear_object (&week_view->resize_width_cursor);
884 
885 	calendar_config_remove_notification (
886 		month_scroll_by_week_changed_cb, week_view);
887 
888 	/* Chain up to parent's dispose() method. */
889 	G_OBJECT_CLASS (e_week_view_parent_class)->dispose (object);
890 }
891 
892 static void
week_view_constructed(GObject * object)893 week_view_constructed (GObject *object)
894 {
895 	EWeekView *week_view;
896 	ECalModel *model;
897 	ECalendarView *calendar_view;
898 	PangoContext *pango_context;
899 
900 	/* Chain up to parent's constructed() method. */
901 	G_OBJECT_CLASS (e_week_view_parent_class)->constructed (object);
902 
903 	week_view = E_WEEK_VIEW (object);
904 	calendar_view = E_CALENDAR_VIEW (object);
905 	model = e_calendar_view_get_model (calendar_view);
906 
907 	pango_context = gtk_widget_get_pango_context (GTK_WIDGET (week_view));
908 	g_warn_if_fail (pango_context != NULL);
909 	week_view->small_font_desc = pango_font_description_copy (pango_context_get_font_description (pango_context));
910 	pango_font_description_set_size (
911 		week_view->small_font_desc,
912 		E_WEEK_VIEW_SMALL_FONT_PTSIZE * PANGO_SCALE);
913 
914 	e_week_view_recalc_display_start_day (E_WEEK_VIEW (object));
915 
916 	week_view->priv->notify_week_start_day_id = e_signal_connect_notify_swapped (
917 		model, "notify::week-start-day",
918 		G_CALLBACK (week_view_notify_week_start_day_cb), object);
919 
920 	g_signal_connect_swapped (
921 		model, "comps-deleted",
922 		G_CALLBACK (week_view_model_comps_deleted_cb), object);
923 
924 	g_signal_connect_swapped (
925 		model, "model-cell-changed",
926 		G_CALLBACK (week_view_model_cell_changed_cb), object);
927 
928 	g_signal_connect_swapped (
929 		model, "model-row-changed",
930 		G_CALLBACK (week_view_model_row_changed_cb), object);
931 
932 	g_signal_connect_swapped (
933 		model, "model-rows-inserted",
934 		G_CALLBACK (week_view_model_rows_inserted_cb), object);
935 
936 	g_signal_connect_swapped (
937 		model, "time-range-changed",
938 		G_CALLBACK (week_view_time_range_changed_cb), object);
939 
940 	g_signal_connect (
941 		model, "timezone-changed",
942 		G_CALLBACK (timezone_changed_cb), object);
943 }
944 
945 static GdkColor
e_week_view_get_text_color(EWeekView * week_view,EWeekViewEvent * event)946 e_week_view_get_text_color (EWeekView *week_view,
947                             EWeekViewEvent *event)
948 {
949 	GdkColor color;
950 	GdkRGBA rgba;
951 
952 	if (is_comp_data_valid (event) &&
953 	    e_cal_model_get_rgba_for_component (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), event->comp_data, &rgba)) {
954 	} else {
955 		gdouble	cc = 65535.0;
956 
957 		rgba.red = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].red / cc;
958 		rgba.green = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].green / cc;
959 		rgba.blue = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].blue / cc;
960 		rgba.alpha = 1.0;
961 
962 	}
963 
964 	rgba = e_utils_get_text_color_for_background (&rgba);
965 	e_rgba_to_color (&rgba, &color);
966 
967 	return color;
968 }
969 
970 static void
week_view_update_style_settings(EWeekView * week_view)971 week_view_update_style_settings (EWeekView *week_view)
972 {
973 	gint day, day_width, max_day_width, max_abbr_day_width;
974 	gint month, month_width, max_month_width, max_abbr_month_width;
975 	gint span_num;
976 	const gchar *name;
977 	PangoFontDescription *font_desc;
978 	PangoContext *pango_context;
979 	PangoFontMetrics *font_metrics;
980 	PangoLayout *layout;
981 	PangoAttrList *tnum;
982 	PangoAttribute *attr;
983 	EWeekViewEventSpan *span;
984 
985 	e_week_view_set_colors (week_view);
986 	e_week_view_check_layout (week_view);
987 
988 	if (week_view->spans) {
989 		for (span_num = 0; span_num < week_view->spans->len; span_num++) {
990 			span = &g_array_index (week_view->spans, EWeekViewEventSpan, span_num);
991 			if (span->text_item && span->background_item) {
992 				gint event_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (span->background_item), "event-num"));
993 				EWeekViewEvent *event = NULL;
994 
995 				if (is_array_index_in_bounds (week_view->events, event_num))
996 					event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
997 
998 				if (event) {
999 					GdkColor text_color;
1000 
1001 					text_color = e_week_view_get_text_color (week_view, event);
1002 
1003 					gnome_canvas_item_set (
1004 						span->text_item,
1005 						"fill_color_gdk", &text_color,
1006 						NULL);
1007 				}
1008 			}
1009 		}
1010 	}
1011 
1012 	/* Set up Pango prerequisites */
1013 	pango_context = gtk_widget_get_pango_context (GTK_WIDGET (week_view));
1014 	font_desc = pango_font_description_copy (pango_context_get_font_description (pango_context));
1015 	font_metrics = pango_context_get_metrics (
1016 		pango_context, font_desc,
1017 		pango_context_get_language (pango_context));
1018 	layout = pango_layout_new (pango_context);
1019 	tnum = pango_attr_list_new ();
1020 	attr = pango_attr_font_features_new ("tnum=1");
1021 	pango_attr_list_insert_before (tnum, attr);
1022 	pango_layout_set_attributes (layout, tnum);
1023 	pango_attr_list_unref (tnum);
1024 
1025 	/* Recalculate the height of each row based on the font size. */
1026 	week_view->row_height = PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1027 		PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) +
1028 		E_WEEK_VIEW_EVENT_BORDER_HEIGHT * 2 + E_WEEK_VIEW_EVENT_TEXT_Y_PAD * 2;
1029 	week_view->row_height = MAX (week_view->row_height, E_WEEK_VIEW_ICON_HEIGHT + E_WEEK_VIEW_ICON_Y_PAD + E_WEEK_VIEW_EVENT_BORDER_HEIGHT * 2);
1030 
1031 	/* Check that the small font is smaller than the default font.
1032 	 * If it isn't, we won't use it. */
1033 	if (week_view->small_font_desc) {
1034 		if (PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1035 		    PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
1036 		    <= E_WEEK_VIEW_SMALL_FONT_PTSIZE)
1037 			week_view->use_small_font = FALSE;
1038 	}
1039 
1040 	/* Set the height of the top canvas. */
1041 	gtk_widget_set_size_request (
1042 		week_view->titles_canvas, -1,
1043 		PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1044 		PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) + 5);
1045 
1046 	/* Save the sizes of various strings in the font, so we can quickly
1047 	 * decide which date formats to use. */
1048 
1049 	max_day_width = 0;
1050 	max_abbr_day_width = 0;
1051 	for (day = 0; day < 7; day++) {
1052 		name = e_get_weekday_name (day + 1, FALSE);
1053 		day_width = get_string_width (layout, name);
1054 		week_view->day_widths[day] = day_width;
1055 		max_day_width = MAX (max_day_width, day_width);
1056 
1057 		name = e_get_weekday_name (day + 1, TRUE);
1058 		day_width = get_string_width (layout, name);
1059 		week_view->abbr_day_widths[day] = day_width;
1060 		max_abbr_day_width = MAX (max_abbr_day_width, day_width);
1061 	}
1062 
1063 	max_month_width = 0;
1064 	max_abbr_month_width = 0;
1065 	for (month = 0; month < 12; month++) {
1066 		name = e_get_month_name (month + 1, FALSE);
1067 		month_width = get_string_width (layout, name);
1068 		week_view->month_widths[month] = month_width;
1069 		max_month_width = MAX (max_month_width, month_width);
1070 
1071 		name = e_get_month_name (month + 1, TRUE);
1072 		month_width = get_string_width (layout, name);
1073 		week_view->abbr_month_widths[month] = month_width;
1074 		max_abbr_month_width = MAX (max_abbr_month_width, month_width);
1075 	}
1076 
1077 	week_view->space_width = get_string_width (layout, " ");
1078 	week_view->colon_width = get_string_width (layout, ":");
1079 	week_view->slash_width = get_string_width (layout, "/");
1080 	week_view->digit_width = get_digit_width (layout);
1081 	if (week_view->small_font_desc) {
1082 		pango_layout_set_font_description (layout, week_view->small_font_desc);
1083 		week_view->small_digit_width = get_digit_width (layout);
1084 		pango_layout_set_font_description (layout, font_desc);
1085 	}
1086 	week_view->max_day_width = max_day_width;
1087 	week_view->max_abbr_day_width = max_abbr_day_width;
1088 	week_view->max_month_width = max_month_width;
1089 	week_view->max_abbr_month_width = max_abbr_month_width;
1090 
1091 	week_view->am_string_width = get_string_width (
1092 		layout,
1093 		week_view->am_string);
1094 	week_view->pm_string_width = get_string_width (
1095 		layout,
1096 		week_view->pm_string);
1097 
1098 	g_object_unref (layout);
1099 	pango_font_metrics_unref (font_metrics);
1100 	pango_font_description_free (font_desc);
1101 }
1102 
1103 static void
week_view_realize(GtkWidget * widget)1104 week_view_realize (GtkWidget *widget)
1105 {
1106 	EWeekView *week_view;
1107 
1108 	if (GTK_WIDGET_CLASS (e_week_view_parent_class)->realize)
1109 		(*GTK_WIDGET_CLASS (e_week_view_parent_class)->realize)(widget);
1110 
1111 	week_view = E_WEEK_VIEW (widget);
1112 
1113 	week_view_update_style_settings (week_view);
1114 
1115 	/* Create the pixmaps. */
1116 	week_view->reminder_icon =
1117 		e_icon_factory_get_icon ("stock_bell", GTK_ICON_SIZE_MENU);
1118 	week_view->recurrence_icon =
1119 		e_icon_factory_get_icon ("view-refresh", GTK_ICON_SIZE_MENU);
1120 	week_view->timezone_icon =
1121 		e_icon_factory_get_icon ("stock_timezone", GTK_ICON_SIZE_MENU);
1122 	week_view->attach_icon =
1123 		e_icon_factory_get_icon ("mail-attachment", GTK_ICON_SIZE_MENU);
1124 	week_view->meeting_icon =
1125 		e_icon_factory_get_icon ("stock_people", GTK_ICON_SIZE_MENU);
1126 }
1127 
1128 static void
week_view_unrealize(GtkWidget * widget)1129 week_view_unrealize (GtkWidget *widget)
1130 {
1131 	EWeekView *week_view;
1132 
1133 	week_view = E_WEEK_VIEW (widget);
1134 
1135 	g_object_unref (week_view->reminder_icon);
1136 	week_view->reminder_icon = NULL;
1137 	g_object_unref (week_view->recurrence_icon);
1138 	week_view->recurrence_icon = NULL;
1139 	g_object_unref (week_view->timezone_icon);
1140 	week_view->timezone_icon = NULL;
1141 	g_object_unref (week_view->attach_icon);
1142 	week_view->attach_icon = NULL;
1143 	g_object_unref (week_view->meeting_icon);
1144 	week_view->meeting_icon = NULL;
1145 
1146 	if (GTK_WIDGET_CLASS (e_week_view_parent_class)->unrealize)
1147 		(*GTK_WIDGET_CLASS (e_week_view_parent_class)->unrealize)(widget);
1148 }
1149 
1150 static void
week_view_style_updated(GtkWidget * widget)1151 week_view_style_updated (GtkWidget *widget)
1152 {
1153 	if (GTK_WIDGET_CLASS (e_week_view_parent_class)->style_updated)
1154 		(*GTK_WIDGET_CLASS (e_week_view_parent_class)->style_updated) (widget);
1155 
1156 	week_view_update_style_settings (E_WEEK_VIEW (widget));
1157 }
1158 
1159 static void
week_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1160 week_view_size_allocate (GtkWidget *widget,
1161                          GtkAllocation *allocation)
1162 {
1163 	EWeekView *week_view;
1164 	GtkAllocation canvas_allocation;
1165 	gdouble old_x2, old_y2, new_x2, new_y2;
1166 
1167 	week_view = E_WEEK_VIEW (widget);
1168 
1169 	(*GTK_WIDGET_CLASS (e_week_view_parent_class)->size_allocate) (widget, allocation);
1170 
1171 	e_week_view_recalc_cell_sizes (week_view);
1172 
1173 	/* Set the scroll region of the top canvas to its allocated size. */
1174 	gnome_canvas_get_scroll_region (
1175 		GNOME_CANVAS (week_view->titles_canvas),
1176 		NULL, NULL, &old_x2, &old_y2);
1177 	gtk_widget_get_allocation (
1178 		week_view->titles_canvas, &canvas_allocation);
1179 	new_x2 = canvas_allocation.width - 1;
1180 	new_y2 = canvas_allocation.height - 1;
1181 	if (old_x2 != new_x2 || old_y2 != new_y2)
1182 		gnome_canvas_set_scroll_region (
1183 			GNOME_CANVAS (week_view->titles_canvas),
1184 			0, 0, new_x2, new_y2);
1185 
1186 	/* Set the scroll region of the main canvas to its allocated width,
1187 	 * but with the height depending on the number of rows needed. */
1188 	gnome_canvas_get_scroll_region (
1189 		GNOME_CANVAS (week_view->main_canvas),
1190 		NULL, NULL, &old_x2, &old_y2);
1191 	gtk_widget_get_allocation (
1192 		week_view->main_canvas, &canvas_allocation);
1193 	new_x2 = canvas_allocation.width - 1;
1194 	new_y2 = canvas_allocation.height - 1;
1195 	if (old_x2 != new_x2 || old_y2 != new_y2)
1196 		gnome_canvas_set_scroll_region (
1197 			GNOME_CANVAS (week_view->main_canvas),
1198 			0, 0, new_x2, new_y2);
1199 
1200 	/* Flag that we need to reshape the events. */
1201 	if (old_x2 != new_x2 || old_y2 != new_y2) {
1202 		week_view->events_need_reshape = TRUE;
1203 		e_week_view_check_layout (week_view);
1204 	}
1205 }
1206 
1207 static gint
week_view_focus_in(GtkWidget * widget,GdkEventFocus * event)1208 week_view_focus_in (GtkWidget *widget,
1209                     GdkEventFocus *event)
1210 {
1211 	EWeekView *week_view;
1212 
1213 	g_return_val_if_fail (widget != NULL, FALSE);
1214 	g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
1215 	g_return_val_if_fail (event != NULL, FALSE);
1216 
1217 	week_view = E_WEEK_VIEW (widget);
1218 
1219 	/* XXX Can't access flags directly anymore, but is it really needed?
1220 	 *     If so, could we call gtk_widget_send_focus_change() instead? */
1221 #if 0
1222 	GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1223 #endif
1224 
1225 	if (E_CALENDAR_VIEW (week_view)->in_focus && week_view->requires_update) {
1226 		time_t my_start = 0, my_end = 0, model_start = 0, model_end = 0;
1227 
1228 		week_view->requires_update = FALSE;
1229 
1230 		e_cal_model_get_time_range (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), &model_start, &model_end);
1231 
1232 		if (e_calendar_view_get_visible_time_range (E_CALENDAR_VIEW (week_view), &my_start, &my_end) &&
1233 		    model_start == my_start && model_end == my_end) {
1234 			/* update only when the same time range is set in a view and in a model;
1235 			 * otherwise time range change invokes also query update */
1236 			e_week_view_update_query (week_view);
1237 		}
1238 	}
1239 
1240 	gtk_widget_queue_draw (week_view->main_canvas);
1241 
1242 	return FALSE;
1243 }
1244 
1245 static gint
week_view_focus_out(GtkWidget * widget,GdkEventFocus * event)1246 week_view_focus_out (GtkWidget *widget,
1247                      GdkEventFocus *event)
1248 {
1249 	EWeekView *week_view;
1250 
1251 	g_return_val_if_fail (widget != NULL, FALSE);
1252 	g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
1253 	g_return_val_if_fail (event != NULL, FALSE);
1254 
1255 	week_view = E_WEEK_VIEW (widget);
1256 
1257 	/* XXX Can't access flags directly anymore, but is it really needed?
1258 	 *     If so, could we call gtk_widget_send_focus_change() instead? */
1259 #if 0
1260 	GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1261 #endif
1262 
1263 	gtk_widget_queue_draw (week_view->main_canvas);
1264 
1265 	return FALSE;
1266 }
1267 
1268 static gboolean
week_view_key_press(GtkWidget * widget,GdkEventKey * event)1269 week_view_key_press (GtkWidget *widget,
1270                      GdkEventKey *event)
1271 {
1272 	gboolean handled = FALSE;
1273 	handled = e_week_view_do_key_press (widget, event);
1274 
1275 	/* if not handled, try key bindings */
1276 	if (!handled)
1277 		handled = GTK_WIDGET_CLASS (e_week_view_parent_class)->key_press_event (widget, event);
1278 	return handled;
1279 }
1280 
1281 static gboolean
week_view_focus(GtkWidget * widget,GtkDirectionType direction)1282 week_view_focus (GtkWidget *widget,
1283                  GtkDirectionType direction)
1284 {
1285 	EWeekView *week_view;
1286 	gint new_event_num;
1287 	gint new_span_num;
1288 	gint event_loop;
1289 	gboolean editable = FALSE;
1290 	static gint last_focus_event_num = -1, last_focus_span_num = -1;
1291 
1292 	g_return_val_if_fail (widget != NULL, FALSE);
1293 	g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
1294 
1295 	week_view = E_WEEK_VIEW (widget);
1296 
1297 	e_week_view_check_layout (week_view);
1298 
1299 	if (week_view->focused_jump_button == E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS) {
1300 		last_focus_event_num = week_view->editing_event_num;
1301 		last_focus_span_num = week_view->editing_span_num;
1302 	}
1303 
1304 	/* if there is not event, just grab week_view */
1305 	if (week_view->events->len == 0) {
1306 		gtk_widget_grab_focus (widget);
1307 		return TRUE;
1308 	}
1309 
1310 	for (event_loop = 0; event_loop < week_view->events->len;
1311 	     ++event_loop) {
1312 		if (!e_week_view_get_next_tab_event (week_view, direction,
1313 						     last_focus_event_num,
1314 						     last_focus_span_num,
1315 						     &new_event_num,
1316 						     &new_span_num))
1317 			return FALSE;
1318 
1319 		if (new_event_num == -1) {
1320 			/* focus should go to week_view widget
1321 			 */
1322 			gtk_widget_grab_focus (widget);
1323 			return TRUE;
1324 		}
1325 
1326 		editable = e_week_view_start_editing_event (
1327 			week_view,
1328 			new_event_num,
1329 			new_span_num,
1330 			NULL);
1331 		last_focus_event_num = new_event_num;
1332 		last_focus_span_num = new_span_num;
1333 
1334 		if (editable)
1335 			break;
1336 		else {
1337 			/* check if we should go to the jump button */
1338 
1339 			EWeekViewEvent *event;
1340 			EWeekViewEventSpan *span;
1341 			gint current_day;
1342 
1343 			if (!is_array_index_in_bounds (week_view->events, new_event_num))
1344 				break;
1345 
1346 			event = &g_array_index (week_view->events,
1347 						EWeekViewEvent,
1348 						new_event_num);
1349 
1350 			if (!is_array_index_in_bounds (week_view->spans, event->spans_index + new_span_num))
1351 				break;
1352 
1353 			span = &g_array_index (week_view->spans,
1354 					       EWeekViewEventSpan,
1355 					       event->spans_index + new_span_num);
1356 			current_day = span->start_day;
1357 
1358 			if ((week_view->focused_jump_button != current_day) &&
1359 			    e_week_view_is_jump_button_visible (week_view, current_day)) {
1360 
1361 				/* focus go to the jump button */
1362 				e_week_view_stop_editing_event (week_view);
1363 				gnome_canvas_item_grab_focus (week_view->jump_buttons[current_day]);
1364 				return TRUE;
1365 			}
1366 		}
1367 	}
1368 	return editable;
1369 }
1370 
1371 static gboolean
week_view_popup_menu(GtkWidget * widget)1372 week_view_popup_menu (GtkWidget *widget)
1373 {
1374 	EWeekView *week_view = E_WEEK_VIEW (widget);
1375 
1376 	e_week_view_show_popup_menu (
1377 		week_view, NULL,
1378 		week_view->editing_event_num);
1379 
1380 	return TRUE;
1381 }
1382 
1383 static GList *
week_view_get_selected_events(ECalendarView * cal_view)1384 week_view_get_selected_events (ECalendarView *cal_view)
1385 {
1386 	EWeekViewEvent *event = NULL;
1387 	GList *list = NULL;
1388 	EWeekView *week_view = (EWeekView *) cal_view;
1389 
1390 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), NULL);
1391 
1392 	if (week_view->editing_event_num != -1) {
1393 		if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num)) {
1394 			week_view->editing_event_num = -1;
1395 			g_object_notify (G_OBJECT (week_view), "is-editing");
1396 			return NULL;
1397 		}
1398 
1399 		event = &g_array_index (week_view->events, EWeekViewEvent,
1400 					week_view->editing_event_num);
1401 	} else if (week_view->popup_event_num != -1) {
1402 		if (!is_array_index_in_bounds (week_view->events, week_view->popup_event_num))
1403 			return NULL;
1404 
1405 		event = &g_array_index (week_view->events, EWeekViewEvent,
1406 					week_view->popup_event_num);
1407 	}
1408 
1409 	if (event)
1410 		list = g_list_prepend (list, event);
1411 
1412 	return list;
1413 }
1414 
1415 static gboolean
week_view_get_selected_time_range(ECalendarView * cal_view,time_t * start_time,time_t * end_time)1416 week_view_get_selected_time_range (ECalendarView *cal_view,
1417                                    time_t *start_time,
1418                                    time_t *end_time)
1419 {
1420 	gint start_day, end_day;
1421 	EWeekView *week_view = E_WEEK_VIEW (cal_view);
1422 
1423 	start_day = week_view->selection_start_day;
1424 	end_day = week_view->selection_end_day;
1425 
1426 	if (start_day == -1) {
1427 		start_day = 0;
1428 		end_day = 0;
1429 	}
1430 
1431 	if (start_time)
1432 		*start_time = week_view->day_starts[start_day];
1433 
1434 	if (end_time)
1435 		*end_time = week_view->day_starts[end_day + 1];
1436 
1437 	return  TRUE;
1438 }
1439 
1440 /* This sets the selected time range. The EWeekView will show the corresponding
1441  * month and the days between start_time and end_time will be selected.
1442  * To select a single day, use the same value for start_time & end_time. */
1443 static void
week_view_set_selected_time_range(ECalendarView * cal_view,time_t start_time,time_t end_time)1444 week_view_set_selected_time_range (ECalendarView *cal_view,
1445                                    time_t start_time,
1446                                    time_t end_time)
1447 {
1448 	GDate date, end_date;
1449 	gint num_days;
1450 	EWeekView *week_view = E_WEEK_VIEW (cal_view);
1451 
1452 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
1453 
1454 	if (!g_date_valid (&week_view->priv->first_day_shown)) {
1455 		/* This view has not been initialized/shown yet, thus skip this. */
1456 		return;
1457 	}
1458 
1459 	time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
1460 
1461 	/* Set the selection to the given days. */
1462 	week_view->selection_start_day = g_date_get_julian (&date)
1463 		- g_date_get_julian (&week_view->priv->first_day_shown);
1464 	if (end_time == start_time
1465 	    || end_time <= time_add_day_with_zone (start_time, 1,
1466 						   e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view))))
1467 		week_view->selection_end_day = week_view->selection_start_day;
1468 	else {
1469 		time_to_gdate_with_zone (&end_date, end_time - 60, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
1470 		week_view->selection_end_day = g_date_get_julian (&end_date)
1471 			- g_date_get_julian (&week_view->priv->first_day_shown);
1472 	}
1473 
1474 	/* Make sure the selection is valid. */
1475 	num_days = (e_week_view_get_weeks_shown (week_view) * 7) - 1;
1476 	week_view->selection_start_day = CLAMP (
1477 		week_view->selection_start_day, 0, num_days);
1478 	week_view->selection_end_day = CLAMP (
1479 		week_view->selection_end_day,
1480 		week_view->selection_start_day,
1481 		num_days);
1482 
1483 	gtk_widget_queue_draw (week_view->main_canvas);
1484 }
1485 
1486 static gboolean
week_view_get_visible_time_range(ECalendarView * cal_view,time_t * start_time,time_t * end_time)1487 week_view_get_visible_time_range (ECalendarView *cal_view,
1488                                   time_t *start_time,
1489                                   time_t *end_time)
1490 {
1491 	gint num_days;
1492 	EWeekView *week_view = E_WEEK_VIEW (cal_view);
1493 
1494 	/* If we don't have a valid date set yet, return FALSE. */
1495 	if (!g_date_valid (&week_view->priv->first_day_shown))
1496 		return FALSE;
1497 
1498 	num_days = e_week_view_get_weeks_shown (week_view) * 7;
1499 
1500 	*start_time = week_view->day_starts[0];
1501 	*end_time = week_view->day_starts[num_days];
1502 
1503 	return TRUE;
1504 }
1505 
1506 static void
week_view_paste_text(ECalendarView * cal_view)1507 week_view_paste_text (ECalendarView *cal_view)
1508 {
1509 	EWeekViewEvent *event;
1510 	EWeekViewEventSpan *span;
1511 	EWeekView *week_view;
1512 
1513 	g_return_if_fail (E_IS_WEEK_VIEW (cal_view));
1514 
1515 	week_view = E_WEEK_VIEW (cal_view);
1516 
1517 	if (week_view->editing_event_num == -1) {
1518 		e_week_view_add_new_event_in_selected_range (week_view, NULL, TRUE);
1519 		return;
1520 	}
1521 
1522 	if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
1523 		return;
1524 
1525 	event = &g_array_index (week_view->events, EWeekViewEvent,
1526 				week_view->editing_event_num);
1527 
1528 	if (!is_array_index_in_bounds (week_view->spans, event->spans_index + week_view->editing_span_num))
1529 		return;
1530 
1531 	span = &g_array_index (week_view->spans, EWeekViewEventSpan,
1532 			       event->spans_index + week_view->editing_span_num);
1533 
1534 	if (span->text_item &&
1535 	    E_IS_TEXT (span->text_item) &&
1536 	    E_TEXT (span->text_item)->editing) {
1537 		e_text_paste_clipboard (E_TEXT (span->text_item));
1538 	}
1539 }
1540 
1541 static void
week_view_cursor_key_up(EWeekView * week_view)1542 week_view_cursor_key_up (EWeekView *week_view)
1543 {
1544 	if (week_view->selection_start_day == -1)
1545 		return;
1546 
1547 	week_view->selection_start_day--;
1548 
1549 	if (week_view->selection_start_day < 0) {
1550 		e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_UP);
1551 		week_view->selection_start_day = 6;
1552 	}
1553 
1554 	week_view->selection_end_day = week_view->selection_start_day;
1555 	g_signal_emit_by_name (week_view, "selected_time_changed");
1556 	gtk_widget_queue_draw (week_view->main_canvas);
1557 }
1558 
1559 static void
week_view_cursor_key_down(EWeekView * week_view)1560 week_view_cursor_key_down (EWeekView *week_view)
1561 {
1562 	if (week_view->selection_start_day == -1)
1563 		return;
1564 
1565 	week_view->selection_start_day++;
1566 
1567 	if (week_view->selection_start_day > 6) {
1568 		e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_DOWN);
1569 		week_view->selection_start_day = 0;
1570 	}
1571 
1572 	week_view->selection_end_day = week_view->selection_start_day;
1573 	g_signal_emit_by_name (week_view, "selected_time_changed");
1574 	gtk_widget_queue_draw (week_view->main_canvas);
1575 }
1576 
1577 static void
week_view_cursor_key_left(EWeekView * week_view)1578 week_view_cursor_key_left (EWeekView *week_view)
1579 {
1580 	if (week_view->selection_start_day == -1)
1581 		return;
1582 
1583 	week_view->selection_start_day = map_left[week_view->selection_start_day];
1584 	week_view->selection_end_day = week_view->selection_start_day;
1585 	g_signal_emit_by_name (week_view, "selected_time_changed");
1586 	gtk_widget_queue_draw (week_view->main_canvas);
1587 }
1588 
1589 static void
week_view_cursor_key_right(EWeekView * week_view)1590 week_view_cursor_key_right (EWeekView *week_view)
1591 {
1592 	if (week_view->selection_start_day == -1)
1593 		return;
1594 
1595 	week_view->selection_start_day = map_right[week_view->selection_start_day];
1596 	week_view->selection_end_day = week_view->selection_start_day;
1597 	g_signal_emit_by_name (week_view, "selected_time_changed");
1598 	gtk_widget_queue_draw (week_view->main_canvas);
1599 }
1600 
1601 static void
e_week_view_class_init(EWeekViewClass * class)1602 e_week_view_class_init (EWeekViewClass *class)
1603 {
1604 	GObjectClass *object_class;
1605 	GtkWidgetClass *widget_class;
1606 	ECalendarViewClass *view_class;
1607 
1608 	g_type_class_add_private (class, sizeof (EWeekViewPrivate));
1609 
1610 	object_class = G_OBJECT_CLASS (class);
1611 	object_class->set_property = week_view_set_property;
1612 	object_class->get_property = week_view_get_property;
1613 	object_class->dispose = week_view_dispose;
1614 	object_class->constructed = week_view_constructed;
1615 
1616 	widget_class = GTK_WIDGET_CLASS (class);
1617 	widget_class->realize = week_view_realize;
1618 	widget_class->unrealize = week_view_unrealize;
1619 	widget_class->style_updated = week_view_style_updated;
1620 	widget_class->size_allocate = week_view_size_allocate;
1621 	widget_class->focus_in_event = week_view_focus_in;
1622 	widget_class->focus_out_event = week_view_focus_out;
1623 	widget_class->key_press_event = week_view_key_press;
1624 	widget_class->focus = week_view_focus;
1625 	widget_class->popup_menu = week_view_popup_menu;
1626 
1627 	view_class = E_CALENDAR_VIEW_CLASS (class);
1628 	view_class->get_selected_events = week_view_get_selected_events;
1629 	view_class->get_selected_time_range = week_view_get_selected_time_range;
1630 	view_class->set_selected_time_range = week_view_set_selected_time_range;
1631 	view_class->get_visible_time_range = week_view_get_visible_time_range;
1632 	view_class->precalc_visible_time_range = e_week_view_precalc_visible_time_range;
1633 	view_class->paste_text = week_view_paste_text;
1634 
1635 	class->cursor_key_up = week_view_cursor_key_up;
1636 	class->cursor_key_down = week_view_cursor_key_down;
1637 	class->cursor_key_left = week_view_cursor_key_left;
1638 	class->cursor_key_right = week_view_cursor_key_right;
1639 
1640 	/* XXX This property really belongs in EMonthView,
1641 	 *     but too much drawing code is tied to it. */
1642 	g_object_class_install_property (
1643 		object_class,
1644 		PROP_COMPRESS_WEEKEND,
1645 		g_param_spec_boolean (
1646 			"compress-weekend",
1647 			"Compress Weekend",
1648 			NULL,
1649 			TRUE,
1650 			G_PARAM_READWRITE |
1651 			G_PARAM_STATIC_STRINGS));
1652 
1653 	g_object_class_install_property (
1654 		object_class,
1655 		PROP_DAYS_LEFT_TO_RIGHT,
1656 		g_param_spec_boolean (
1657 			"days-left-to-right",
1658 			"Days Left To Right",
1659 			NULL,
1660 			FALSE,
1661 			G_PARAM_READWRITE |
1662 			G_PARAM_STATIC_STRINGS));
1663 
1664 	g_object_class_install_property (
1665 		object_class,
1666 		PROP_DRAW_FLAT_EVENTS,
1667 		g_param_spec_boolean (
1668 			"draw-flat-events",
1669 			"Draw Flat Events",
1670 			NULL,
1671 			TRUE,
1672 			G_PARAM_READWRITE |
1673 			G_PARAM_STATIC_STRINGS));
1674 
1675 	g_object_class_install_property (
1676 		object_class,
1677 		PROP_SHOW_EVENT_END_TIMES,
1678 		g_param_spec_boolean (
1679 			"show-event-end-times",
1680 			"Show Event End Times",
1681 			NULL,
1682 			TRUE,
1683 			G_PARAM_READWRITE |
1684 			G_PARAM_STATIC_STRINGS));
1685 
1686 	g_object_class_install_property (
1687 		object_class,
1688 		PROP_SHOW_ICONS_MONTH_VIEW,
1689 		g_param_spec_boolean (
1690 			"show-icons-month-view",
1691 			"Show Icons Month View",
1692 			NULL,
1693 			FALSE,
1694 			G_PARAM_READWRITE |
1695 			G_PARAM_STATIC_STRINGS));
1696 
1697 	g_object_class_override_property (
1698 		object_class,
1699 		PROP_IS_EDITING,
1700 		"is-editing");
1701 
1702 	/* init the accessibility support for e_week_view */
1703 	gtk_widget_class_set_accessible_type (widget_class, EA_TYPE_WEEK_VIEW);
1704 }
1705 
1706 static void
e_week_view_init(EWeekView * week_view)1707 e_week_view_init (EWeekView *week_view)
1708 {
1709 	GnomeCanvasGroup *canvas_group;
1710 	GtkAdjustment *adjustment;
1711 	GdkPixbuf *pixbuf;
1712 	gint i;
1713 
1714 	week_view->priv = E_WEEK_VIEW_GET_PRIVATE (week_view);
1715 	week_view->priv->weeks_shown = 6;
1716 	week_view->priv->compress_weekend = TRUE;
1717 	week_view->priv->days_left_to_right = FALSE;
1718 	week_view->priv->draw_flat_events = TRUE;
1719 	week_view->priv->show_event_end_times = TRUE;
1720 	week_view->priv->update_base_date = TRUE;
1721 	week_view->priv->display_start_day = G_DATE_MONDAY;
1722 
1723 	gtk_widget_set_can_focus (GTK_WIDGET (week_view), TRUE);
1724 
1725 	week_view->event_destroyed = FALSE;
1726 	week_view->events = g_array_new (
1727 		FALSE, FALSE,
1728 		sizeof (EWeekViewEvent));
1729 	week_view->events_sorted = TRUE;
1730 	week_view->events_need_layout = FALSE;
1731 	week_view->events_need_reshape = FALSE;
1732 
1733 	week_view->layout_timeout_id = 0;
1734 
1735 	week_view->spans = NULL;
1736 
1737 	week_view->month_scroll_by_week = FALSE;
1738 	week_view->scroll_by_week_notif_id = 0;
1739 	week_view->rows = 6;
1740 	week_view->columns = 2;
1741 
1742 	g_date_clear (&week_view->base_date, 1);
1743 	g_date_clear (&week_view->priv->first_day_shown, 1);
1744 
1745 	week_view->row_height = 10;
1746 	week_view->rows_per_cell = 1;
1747 
1748 	week_view->selection_start_day = -1;
1749 	week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
1750 
1751 	week_view->pressed_event_num = -1;
1752 	week_view->editing_event_num = -1;
1753 
1754 	week_view->last_edited_comp_string = NULL;
1755 
1756 	/* Create the small font in constructed. */
1757 	week_view->use_small_font = TRUE;
1758 	week_view->small_font_desc = NULL;
1759 
1760 	/* String to use in 12-hour time format for times in the morning. */
1761 	week_view->am_string = _("am");
1762 
1763 	/* String to use in 12-hour time format for times in the afternoon. */
1764 	week_view->pm_string = _("pm");
1765 
1766 	week_view->bc_event_time = 0;
1767 	week_view->before_click_dtstart = 0;
1768 	week_view->before_click_dtend = 0;
1769 
1770 	gtk_widget_set_margin_top (GTK_WIDGET (week_view), 1);
1771 
1772 	/*
1773 	 * Titles Canvas. Note that we don't show it is only shown in the
1774 	 * Month view.
1775 	 */
1776 	week_view->titles_canvas = e_canvas_new ();
1777 	gtk_grid_attach (GTK_GRID (week_view), week_view->titles_canvas, 1, 0, 1, 1);
1778 	g_object_set (G_OBJECT (week_view->titles_canvas),
1779 		"hexpand", TRUE,
1780 		"vexpand", FALSE,
1781 		"halign", GTK_ALIGN_FILL,
1782 		"valign", GTK_ALIGN_FILL,
1783 		NULL);
1784 
1785 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->titles_canvas)->root);
1786 
1787 	week_view->titles_canvas_item =
1788 		gnome_canvas_item_new (
1789 			canvas_group,
1790 			e_week_view_titles_item_get_type (),
1791 			"EWeekViewTitlesItem::week_view", week_view,
1792 			NULL);
1793 
1794 	/*
1795 	 * Main Canvas
1796 	 */
1797 	week_view->main_canvas = e_canvas_new ();
1798 	gtk_grid_attach (GTK_GRID (week_view), week_view->main_canvas, 1, 1, 1, 1);
1799 	g_object_set (G_OBJECT (week_view->main_canvas),
1800 		"hexpand", TRUE,
1801 		"vexpand", TRUE,
1802 		"halign", GTK_ALIGN_FILL,
1803 		"valign", GTK_ALIGN_FILL,
1804 		NULL);
1805 	gtk_widget_show (week_view->main_canvas);
1806 
1807 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root);
1808 
1809 	week_view->main_canvas_item =
1810 		gnome_canvas_item_new (
1811 			canvas_group,
1812 			e_week_view_main_item_get_type (),
1813 			"EWeekViewMainItem::week_view", week_view,
1814 			NULL);
1815 
1816 	g_signal_connect_after (
1817 		week_view->main_canvas, "button_press_event",
1818 		G_CALLBACK (e_week_view_on_button_press), week_view);
1819 	g_signal_connect (
1820 		week_view->main_canvas, "button_release_event",
1821 		G_CALLBACK (e_week_view_on_button_release), week_view);
1822 	g_signal_connect (
1823 		week_view->main_canvas, "scroll_event",
1824 		G_CALLBACK (e_week_view_on_scroll), week_view);
1825 	g_signal_connect (
1826 		week_view->main_canvas, "motion_notify_event",
1827 		G_CALLBACK (e_week_view_on_motion), week_view);
1828 
1829 	/* Create the buttons to jump to each days. */
1830 	pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) jump_xpm);
1831 
1832 	for (i = 0; i < E_WEEK_VIEW_MAX_WEEKS * 7; i++) {
1833 		week_view->jump_buttons[i] = gnome_canvas_item_new
1834 			(canvas_group,
1835 			 gnome_canvas_pixbuf_get_type (),
1836 			 "GnomeCanvasPixbuf::pixbuf", pixbuf,
1837 			 NULL);
1838 
1839 		g_signal_connect (
1840 			week_view->jump_buttons[i], "event",
1841 			G_CALLBACK (e_week_view_on_jump_button_event), week_view);
1842 	}
1843 	week_view->focused_jump_button = E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS;
1844 
1845 	g_object_unref (pixbuf);
1846 
1847 	/*
1848 	 * Scrollbar.
1849 	 */
1850 	adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, -52, 52, 1, 1, 1));
1851 
1852 	week_view->vscrollbar = gtk_scrollbar_new (
1853 		GTK_ORIENTATION_VERTICAL, adjustment);
1854 	gtk_grid_attach (GTK_GRID (week_view), week_view->vscrollbar, 2, 1, 1, 1);
1855 	g_object_set (G_OBJECT (week_view->vscrollbar),
1856 		"hexpand", FALSE,
1857 		"vexpand", TRUE,
1858 		"halign", GTK_ALIGN_START,
1859 		"valign", GTK_ALIGN_FILL,
1860 		NULL);
1861 	gtk_widget_show (week_view->vscrollbar);
1862 
1863 	/* Create the cursors. */
1864 	week_view->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR);
1865 	week_view->move_cursor = gdk_cursor_new (GDK_FLEUR);
1866 	week_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
1867 	week_view->last_cursor_set = NULL;
1868 
1869 	week_view->requires_update = FALSE;
1870 }
1871 
1872 /**
1873  * e_week_view_new:
1874  * @Returns: a new #EWeekView.
1875  *
1876  * Creates a new #EWeekView.
1877  **/
1878 ECalendarView *
e_week_view_new(ECalModel * model)1879 e_week_view_new (ECalModel *model)
1880 {
1881 	g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1882 
1883 	return g_object_new (E_TYPE_WEEK_VIEW, "model", model, NULL);
1884 }
1885 
1886 static GdkColor
color_inc(GdkColor c,gint amount)1887 color_inc (GdkColor c,
1888            gint amount)
1889 {
1890 	#define dec(x) \
1891 		if (x + amount >= 0 \
1892 		    && x + amount <= 0xFFFF) \
1893 			x += amount; \
1894 		else if (amount <= 0) \
1895 			x = 0; \
1896 		else \
1897 			x = 0xFFFF;
1898 
1899 	dec (c.red);
1900 	dec (c.green);
1901 	dec (c.blue);
1902 
1903 	#undef dec
1904 
1905 	return c;
1906 }
1907 
1908 static void
e_week_view_set_colors(EWeekView * week_view)1909 e_week_view_set_colors (EWeekView *week_view)
1910 {
1911 	GtkWidget *widget = GTK_WIDGET (week_view);
1912 	GdkRGBA base_bg, bg_bg, text_fg, selected_bg, selected_fg, unfocused_selected_bg, dark_bg, light_bg;
1913 
1914 	e_utils_get_theme_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &base_bg);
1915 	e_utils_get_theme_color (widget, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR, &bg_bg);
1916 	e_utils_get_theme_color (widget, "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR, &text_fg);
1917 	e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &selected_bg);
1918 	e_utils_get_theme_color (widget, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR, &selected_fg);
1919 	e_utils_get_theme_color (widget, "theme_unfocused_selected_bg_color,theme_selected_bg_color", E_UTILS_DEFAULT_THEME_UNFOCUSED_SELECTED_BG_COLOR, &unfocused_selected_bg);
1920 
1921 	e_utils_shade_color (&bg_bg, &dark_bg, E_UTILS_DARKNESS_MULT);
1922 	e_utils_shade_color (&bg_bg, &light_bg, E_UTILS_LIGHTNESS_MULT);
1923 
1924 	e_rgba_to_color (&bg_bg, &week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS]);
1925 	e_rgba_to_color (&base_bg, &week_view->colors[E_WEEK_VIEW_COLOR_ODD_MONTHS]);
1926 	e_rgba_to_color (&base_bg, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND]);
1927 	e_rgba_to_color (&dark_bg, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER]);
1928 	e_rgba_to_color (&text_fg, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_TEXT]);
1929 	e_rgba_to_color (&dark_bg, &week_view->colors[E_WEEK_VIEW_COLOR_GRID]);
1930 	e_rgba_to_color (&selected_bg, &week_view->colors[E_WEEK_VIEW_COLOR_SELECTED]);
1931 	e_rgba_to_color (&unfocused_selected_bg, &week_view->colors[E_WEEK_VIEW_COLOR_SELECTED_UNFOCUSSED]);
1932 	e_rgba_to_color (&text_fg, &week_view->colors[E_WEEK_VIEW_COLOR_DATES]);
1933 	e_rgba_to_color (&selected_fg, &week_view->colors[E_WEEK_VIEW_COLOR_DATES_SELECTED]);
1934 	e_rgba_to_color (&selected_bg, &week_view->colors[E_WEEK_VIEW_COLOR_TODAY]);
1935 
1936 	week_view->colors[E_WEEK_VIEW_COLOR_TODAY_BACKGROUND] = get_today_background (week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND]);
1937 	week_view->colors[E_WEEK_VIEW_COLOR_MONTH_NONWORKING_DAY] = color_inc (week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS], -0x0A0A);
1938 }
1939 
1940 static void
e_week_view_recalc_cell_sizes(EWeekView * week_view)1941 e_week_view_recalc_cell_sizes (EWeekView *week_view)
1942 {
1943 	gfloat canvas_width, canvas_height, offset;
1944 	gint row, col;
1945 	GtkAllocation allocation;
1946 	GtkWidget *widget;
1947 	gint width, height, time_width;
1948 	PangoContext *pango_context;
1949 	PangoFontMetrics *font_metrics;
1950 
1951 	if (e_week_view_get_multi_week_view (week_view)) {
1952 		week_view->rows =
1953 			e_week_view_get_weeks_shown (week_view) * 2;
1954 		week_view->columns =
1955 			e_week_view_get_compress_weekend (week_view) ? 6 : 7;
1956 	} else {
1957 		week_view->rows = 6;
1958 		week_view->columns = 2;
1959 	}
1960 
1961 	gtk_widget_get_allocation (week_view->main_canvas, &allocation);
1962 
1963 	/* Calculate the column sizes, using floating point so that pixels
1964 	 * get divided evenly. Note that we use one more element than the
1965 	 * number of columns, to make it easy to get the column widths.
1966 	 * We also add one to the width so that the right border of the last
1967 	 * column is off the edge of the displayed area. */
1968 	canvas_width = allocation.width + 1;
1969 	canvas_width /= week_view->columns;
1970 	offset = 0;
1971 	for (col = 0; col <= week_view->columns; col++) {
1972 		week_view->col_offsets[col] = floor (offset + 0.5);
1973 		offset += canvas_width;
1974 	}
1975 
1976 	/* Calculate the cell widths based on the offsets. */
1977 	for (col = 0; col < week_view->columns; col++) {
1978 		week_view->col_widths[col] = week_view->col_offsets[col + 1]
1979 			- week_view->col_offsets[col];
1980 	}
1981 
1982 	/* Now do the same for the row heights. */
1983 	canvas_height = allocation.height + 1;
1984 	canvas_height /= week_view->rows;
1985 	offset = 0;
1986 	for (row = 0; row <= week_view->rows; row++) {
1987 		week_view->row_offsets[row] = floor (offset + 0.5);
1988 		offset += canvas_height;
1989 	}
1990 
1991 	/* Calculate the cell heights based on the offsets. */
1992 	for (row = 0; row < week_view->rows; row++) {
1993 		week_view->row_heights[row] = week_view->row_offsets[row + 1]
1994 			- week_view->row_offsets[row];
1995 	}
1996 
1997 	widget = GTK_WIDGET (week_view);
1998 
1999 	pango_context = gtk_widget_get_pango_context (widget);
2000 	if (!pango_context)
2001 		return;
2002 
2003 	font_metrics = pango_context_get_metrics (
2004 		pango_context, NULL,
2005 		pango_context_get_language (pango_context));
2006 
2007 	/* Calculate the number of rows of events in each cell, for the large
2008 	 * cells and the compressed weekend cells. */
2009 	if (e_week_view_get_multi_week_view (week_view)) {
2010 		week_view->events_y_offset = E_WEEK_VIEW_DATE_T_PAD
2011 			+ PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
2012 			+ PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
2013 			+ E_WEEK_VIEW_DATE_B_PAD;
2014 	} else {
2015 		week_view->events_y_offset = E_WEEK_VIEW_DATE_T_PAD
2016 			+ PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
2017 			+ PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
2018 			+ E_WEEK_VIEW_DATE_LINE_T_PAD + 1
2019 			+ E_WEEK_VIEW_DATE_LINE_B_PAD;
2020 	}
2021 
2022 	height = week_view->row_heights[0];
2023 	week_view->rows_per_cell =
2024 		(height * 2 - week_view->events_y_offset) /
2025 		(week_view->row_height + E_WEEK_VIEW_EVENT_Y_SPACING);
2026 	week_view->rows_per_cell = MIN (
2027 		week_view->rows_per_cell,
2028 		E_WEEK_VIEW_MAX_ROWS_PER_CELL);
2029 
2030 	week_view->rows_per_compressed_cell =
2031 		(height - week_view->events_y_offset) /
2032 		(week_view->row_height + E_WEEK_VIEW_EVENT_Y_SPACING);
2033 	week_view->rows_per_compressed_cell = MIN (
2034 		week_view->rows_per_compressed_cell,
2035 		E_WEEK_VIEW_MAX_ROWS_PER_CELL);
2036 
2037 	/* Determine which time format to use, based on the width of the cells.
2038 	 * We only allow the time to take up about half of the width. */
2039 	width = week_view->col_widths[0];
2040 
2041 	time_width = e_week_view_get_time_string_width (week_view);
2042 
2043 	week_view->time_format = E_WEEK_VIEW_TIME_NONE;
2044 	if (week_view->use_small_font && week_view->small_font_desc) {
2045 		if (e_week_view_get_show_event_end_times (week_view) &&
2046 		    width / 2 > time_width * 2 + E_WEEK_VIEW_EVENT_TIME_SPACING)
2047 			week_view->time_format = E_WEEK_VIEW_TIME_BOTH_SMALL_MIN;
2048 		else if (width / 2 > time_width)
2049 			week_view->time_format = E_WEEK_VIEW_TIME_START_SMALL_MIN;
2050 	} else {
2051 		if (e_week_view_get_show_event_end_times (week_view) &&
2052 		    width / 2 > time_width * 2 + E_WEEK_VIEW_EVENT_TIME_SPACING)
2053 			week_view->time_format = E_WEEK_VIEW_TIME_BOTH;
2054 		else if (width / 2 > time_width)
2055 			week_view->time_format = E_WEEK_VIEW_TIME_START;
2056 	}
2057 
2058 	pango_font_metrics_unref (font_metrics);
2059 }
2060 
2061 /**
2062  * e_week_view_get_next_tab_event
2063  * @week_view: the week_view widget operate on
2064  * @direction: GTK_DIR_TAB_BACKWARD or GTK_DIR_TAB_FORWARD.
2065  * @current_event_num and @current_span_num: current status.
2066  * @next_event_num: the event number focus should go next.
2067  *                  -1 indicates focus should go to week_view widget.
2068  * @next_span_num: always return 0.
2069  **/
2070 static gboolean
e_week_view_get_next_tab_event(EWeekView * week_view,GtkDirectionType direction,gint current_event_num,gint current_span_num,gint * next_event_num,gint * next_span_num)2071 e_week_view_get_next_tab_event (EWeekView *week_view,
2072                                 GtkDirectionType direction,
2073                                 gint current_event_num,
2074                                 gint current_span_num,
2075                                 gint *next_event_num,
2076                                 gint *next_span_num)
2077 {
2078 	gint event_num;
2079 
2080 	g_return_val_if_fail (week_view != NULL, FALSE);
2081 	g_return_val_if_fail (next_event_num != NULL, FALSE);
2082 	g_return_val_if_fail (next_span_num != NULL, FALSE);
2083 
2084 	if (week_view->events->len <= 0)
2085 		return FALSE;
2086 
2087 	/* we only tab through events not spans */
2088 	*next_span_num = 0;
2089 
2090 	switch (direction) {
2091 	case GTK_DIR_TAB_BACKWARD:
2092 		event_num = current_event_num - 1;
2093 		break;
2094 	case GTK_DIR_TAB_FORWARD:
2095 		event_num = current_event_num + 1;
2096 		break;
2097 	default:
2098 		return FALSE;
2099 	}
2100 
2101 	if (event_num == -1)
2102 		/* backward, out of event range, go to week view widget
2103 		 */
2104 		*next_event_num = -1;
2105 	else if (event_num < -1)
2106 		/* backward from week_view, go to the last event
2107 		 */
2108 		*next_event_num = week_view->events->len - 1;
2109 	else if (event_num >= week_view->events->len)
2110 		/* forward, out of event range, go to week view widget
2111 		 */
2112 		*next_event_num = -1;
2113 	else
2114 		*next_event_num = event_num;
2115 	return TRUE;
2116 }
2117 
2118 /* Restarts a query for the week view */
2119 static void
e_week_view_update_query(EWeekView * week_view)2120 e_week_view_update_query (EWeekView *week_view)
2121 {
2122 	ECalModel *cal_model;
2123 	gint rows, r;
2124 
2125 	if (!E_CALENDAR_VIEW (week_view)->in_focus) {
2126 		e_week_view_free_events (week_view);
2127 		week_view->requires_update = TRUE;
2128 		return;
2129 	}
2130 
2131 	gtk_widget_queue_draw (week_view->main_canvas);
2132 	e_week_view_free_events (week_view);
2133 	e_week_view_queue_layout (week_view);
2134 
2135 	cal_model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
2136 	rows = e_table_model_row_count (E_TABLE_MODEL (cal_model));
2137 
2138 	for (r = 0; r < rows; r++) {
2139 		ECalModelComponent *comp_data;
2140 
2141 		comp_data = e_cal_model_get_component_at (cal_model, r);
2142 		if (comp_data == NULL) {
2143 			g_warning ("comp_data is NULL\n");
2144 			continue;
2145 		}
2146 		week_view_process_component (week_view, comp_data);
2147 	}
2148 }
2149 
2150 void
e_week_view_set_selected_time_range_visible(EWeekView * week_view,time_t start_time,time_t end_time)2151 e_week_view_set_selected_time_range_visible (EWeekView *week_view,
2152                                              time_t start_time,
2153                                              time_t end_time)
2154 {
2155 	GDate *first_day_shown;
2156 	GDate date, end_date;
2157 	gint num_days;
2158 
2159 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2160 
2161 	first_day_shown = &week_view->priv->first_day_shown;
2162 
2163 	time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
2164 
2165 	/* Set the selection to the given days. */
2166 	week_view->selection_start_day =
2167 		g_date_get_julian (&date) -
2168 		g_date_get_julian (first_day_shown);
2169 	if (end_time == start_time
2170 	    || end_time <= time_add_day_with_zone (start_time, 1,
2171 						   e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view))))
2172 		week_view->selection_end_day = week_view->selection_start_day;
2173 	else {
2174 		time_to_gdate_with_zone (&end_date, end_time - 60, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
2175 		week_view->selection_end_day =
2176 			g_date_get_julian (&end_date) -
2177 			g_date_get_julian (first_day_shown);
2178 	}
2179 
2180 	/* Make sure the selection is valid. */
2181 	num_days = (e_week_view_get_weeks_shown (week_view) * 7) - 1;
2182 	week_view->selection_start_day = CLAMP (
2183 		week_view->selection_start_day, 0, num_days);
2184 	week_view->selection_end_day = CLAMP (
2185 		week_view->selection_end_day,
2186 		week_view->selection_start_day,
2187 		num_days);
2188 
2189 	gtk_widget_queue_draw (week_view->main_canvas);
2190 }
2191 
2192 /* Note that the returned date may be invalid if no date has been set yet. */
2193 void
e_week_view_get_first_day_shown(EWeekView * week_view,GDate * date)2194 e_week_view_get_first_day_shown (EWeekView *week_view,
2195                                  GDate *date)
2196 {
2197 	*date = week_view->priv->first_day_shown;
2198 }
2199 
2200 /* This sets the first day shown in the view. It will be rounded down to the
2201  * nearest week. */
2202 void
e_week_view_set_first_day_shown(EWeekView * week_view,GDate * date)2203 e_week_view_set_first_day_shown (EWeekView *week_view,
2204                                  GDate *date)
2205 {
2206 	GDate base_date;
2207 	GDateWeekday weekday;
2208 	GDateWeekday display_start_day;
2209 	guint day_offset;
2210 	gint num_days;
2211 	gboolean update_adjustment_value = FALSE;
2212 	guint32 old_selection_start_julian = 0, old_selection_end_julian = 0;
2213 
2214 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2215 
2216 	/* Calculate the old selection range. */
2217 	if (week_view->selection_start_day != -1) {
2218 		old_selection_start_julian =
2219 			g_date_get_julian (&week_view->base_date)
2220 			+ week_view->selection_start_day;
2221 		old_selection_end_julian =
2222 			g_date_get_julian (&week_view->base_date)
2223 			+ week_view->selection_end_day;
2224 	}
2225 
2226 	weekday = g_date_get_weekday (date);
2227 	display_start_day = e_week_view_get_display_start_day (week_view);
2228 
2229 	/* Convert it to an offset from the start of the display. */
2230 	day_offset = e_weekday_get_days_between (display_start_day, weekday);
2231 
2232 	/* Calculate the base date, i.e. the first day shown when the
2233 	 * scrollbar adjustment value is 0. */
2234 	base_date = *date;
2235 	g_date_subtract_days (&base_date, day_offset);
2236 
2237 	/* See if we need to update the base date. */
2238 	if (!g_date_valid (&week_view->base_date)
2239 	    || g_date_compare (&week_view->base_date, &base_date)) {
2240 		week_view->base_date = base_date;
2241 		update_adjustment_value = TRUE;
2242 	}
2243 
2244 	/* See if we need to update the first day shown. */
2245 	if (!g_date_valid (&week_view->priv->first_day_shown)
2246 	    || g_date_compare (&week_view->priv->first_day_shown, &base_date)) {
2247 		ICalTime *start_tt;
2248 		time_t start_time;
2249 
2250 		week_view->priv->first_day_shown = base_date;
2251 
2252 		start_tt = i_cal_time_new_null_time ();
2253 		i_cal_time_set_date (start_tt,
2254 			g_date_get_year (&base_date),
2255 			g_date_get_month (&base_date),
2256 			g_date_get_day (&base_date));
2257 
2258 		start_time = i_cal_time_as_timet_with_zone (
2259 			start_tt,
2260 			e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
2261 
2262 		g_clear_object (&start_tt);
2263 
2264 		e_week_view_recalc_day_starts (week_view, start_time);
2265 		e_week_view_update_query (week_view);
2266 	}
2267 
2268 	/* Try to keep the previous selection, but if it is no longer shown
2269 	 * just select the first day. */
2270 	if (week_view->selection_start_day != -1) {
2271 		week_view->selection_start_day = old_selection_start_julian
2272 			- g_date_get_julian (&base_date);
2273 		week_view->selection_end_day = old_selection_end_julian
2274 			- g_date_get_julian (&base_date);
2275 
2276 		/* Make sure the selection is valid. */
2277 		num_days = (e_week_view_get_weeks_shown (week_view) * 7) - 1;
2278 		week_view->selection_start_day = CLAMP (
2279 			week_view->selection_start_day, 0, num_days);
2280 		week_view->selection_end_day = CLAMP (
2281 			week_view->selection_end_day,
2282 			week_view->selection_start_day,
2283 			num_days);
2284 	}
2285 
2286 	/* Reset the adjustment value to 0 if the base address has changed.
2287 	 * Note that we do this after updating first_day_shown so that our
2288 	 * signal handler will not try to reload the events. */
2289 	if (update_adjustment_value) {
2290 		GtkRange *range;
2291 		GtkAdjustment *adjustment;
2292 
2293 		range = GTK_RANGE (week_view->vscrollbar);
2294 		adjustment = gtk_range_get_adjustment (range);
2295 		gtk_adjustment_set_value (adjustment, 0);
2296 	}
2297 
2298 	e_week_view_update_query (week_view);
2299 	gtk_widget_queue_draw (week_view->main_canvas);
2300 }
2301 
2302 GDateWeekday
e_week_view_get_display_start_day(EWeekView * week_view)2303 e_week_view_get_display_start_day (EWeekView *week_view)
2304 {
2305 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), G_DATE_BAD_WEEKDAY);
2306 
2307 	return week_view->priv->display_start_day;
2308 }
2309 
2310 /* Recalculates the time_t corresponding to the start of each day. */
2311 static void
e_week_view_recalc_day_starts(EWeekView * week_view,time_t lower)2312 e_week_view_recalc_day_starts (EWeekView *week_view,
2313                                time_t lower)
2314 {
2315 	gint num_days, day;
2316 	time_t tmp_time;
2317 
2318 	num_days = E_WEEK_VIEW_MAX_WEEKS * 7;
2319 
2320 	tmp_time = lower;
2321 	week_view->day_starts[0] = tmp_time;
2322 	for (day = 1; day <= num_days; day++) {
2323 		tmp_time = time_add_day_with_zone (
2324 			tmp_time, 1,
2325 			e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
2326 		week_view->day_starts[day] = tmp_time;
2327 	}
2328 }
2329 
2330 gboolean
e_week_view_get_multi_week_view(EWeekView * week_view)2331 e_week_view_get_multi_week_view (EWeekView *week_view)
2332 {
2333 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2334 
2335 	return week_view->priv->multi_week_view;
2336 }
2337 
2338 void
e_week_view_set_multi_week_view(EWeekView * week_view,gboolean multi_week_view)2339 e_week_view_set_multi_week_view (EWeekView *week_view,
2340                                  gboolean multi_week_view)
2341 {
2342 	GtkRange *range;
2343 	GtkAdjustment *adjustment;
2344 	gint page_increment, page_size;
2345 
2346 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2347 
2348 	if (multi_week_view == week_view->priv->multi_week_view)
2349 		return;
2350 
2351 	week_view->priv->multi_week_view = multi_week_view;
2352 
2353 	if (multi_week_view) {
2354 		gtk_widget_show (week_view->titles_canvas);
2355 		week_view->month_scroll_by_week = calendar_config_get_month_scroll_by_week ();
2356 
2357 		calendar_config_add_notification_month_scroll_by_week (
2358 			month_scroll_by_week_changed_cb, week_view);
2359 
2360 		if (week_view->month_scroll_by_week) {
2361 			page_increment = 1;
2362 			page_size = 5;
2363 		} else {
2364 			page_increment = 4;
2365 			page_size = 5;
2366 		}
2367 	} else {
2368 		gtk_widget_hide (week_view->titles_canvas);
2369 		page_increment = page_size = 1;
2370 
2371 		if (week_view->scroll_by_week_notif_id) {
2372 			calendar_config_remove_notification (
2373 				month_scroll_by_week_changed_cb, week_view);
2374 			week_view->scroll_by_week_notif_id = 0;
2375 		}
2376 	}
2377 
2378 	range = GTK_RANGE (week_view->vscrollbar);
2379 	adjustment = gtk_range_get_adjustment (range);
2380 	gtk_adjustment_set_page_increment (adjustment, page_increment);
2381 	gtk_adjustment_set_page_size (adjustment, page_size);
2382 
2383 	e_week_view_recalc_display_start_day (week_view);
2384 	e_week_view_recalc_cell_sizes (week_view);
2385 
2386 	if (g_date_valid (&week_view->priv->first_day_shown))
2387 		e_week_view_set_first_day_shown (
2388 			week_view,
2389 			&week_view->priv->first_day_shown);
2390 }
2391 
2392 gboolean
e_week_view_get_update_base_date(EWeekView * week_view)2393 e_week_view_get_update_base_date (EWeekView *week_view)
2394 {
2395 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2396 
2397 	return week_view->priv->update_base_date;
2398 }
2399 
2400 void
e_week_view_set_update_base_date(EWeekView * week_view,gboolean update_base_date)2401 e_week_view_set_update_base_date (EWeekView *week_view,
2402                                   gboolean update_base_date)
2403 {
2404 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2405 
2406 	week_view->priv->update_base_date = update_base_date;
2407 }
2408 
2409 gint
e_week_view_get_weeks_shown(EWeekView * week_view)2410 e_week_view_get_weeks_shown (EWeekView *week_view)
2411 {
2412 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), 1);
2413 
2414 	/* Give a sensible answer for single-week view. */
2415 	if (!e_week_view_get_multi_week_view (week_view))
2416 		return 1;
2417 
2418 	return week_view->priv->weeks_shown;
2419 }
2420 
2421 void
e_week_view_set_weeks_shown(EWeekView * week_view,gint weeks_shown)2422 e_week_view_set_weeks_shown (EWeekView *week_view,
2423                              gint weeks_shown)
2424 {
2425 	GtkRange *range;
2426 	GtkAdjustment *adjustment;
2427 	gint page_increment, page_size;
2428 
2429 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2430 
2431 	weeks_shown = MIN (weeks_shown, E_WEEK_VIEW_MAX_WEEKS);
2432 
2433 	if (weeks_shown == week_view->priv->weeks_shown)
2434 		return;
2435 
2436 	week_view->priv->weeks_shown = weeks_shown;
2437 
2438 	if (e_week_view_get_multi_week_view (week_view)) {
2439 		if (week_view->month_scroll_by_week) {
2440 			page_increment = 1;
2441 			page_size = 5;
2442 		} else {
2443 			page_increment = 4;
2444 			page_size = 5;
2445 		}
2446 
2447 		range = GTK_RANGE (week_view->vscrollbar);
2448 		adjustment = gtk_range_get_adjustment (range);
2449 		gtk_adjustment_set_page_increment (adjustment, page_increment);
2450 		gtk_adjustment_set_page_size (adjustment, page_size);
2451 
2452 		e_week_view_recalc_cell_sizes (week_view);
2453 
2454 		if (g_date_valid (&week_view->priv->first_day_shown))
2455 			e_week_view_set_first_day_shown (
2456 				week_view,
2457 				&week_view->priv->first_day_shown);
2458 
2459 		e_week_view_update_query (week_view);
2460 	}
2461 }
2462 
2463 gboolean
e_week_view_get_compress_weekend(EWeekView * week_view)2464 e_week_view_get_compress_weekend (EWeekView *week_view)
2465 {
2466 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2467 
2468 	return week_view->priv->compress_weekend;
2469 }
2470 
2471 void
e_week_view_set_compress_weekend(EWeekView * week_view,gboolean compress_weekend)2472 e_week_view_set_compress_weekend (EWeekView *week_view,
2473                                   gboolean compress_weekend)
2474 {
2475 	gboolean need_reload = FALSE;
2476 
2477 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2478 
2479 	if (compress_weekend == week_view->priv->compress_weekend)
2480 		return;
2481 
2482 	week_view->priv->compress_weekend = compress_weekend;
2483 
2484 	/* The option only affects the month view. */
2485 	if (!e_week_view_get_multi_week_view (week_view))
2486 		return;
2487 
2488 	e_week_view_recalc_cell_sizes (week_view);
2489 
2490 	need_reload = e_week_view_recalc_display_start_day (week_view);
2491 
2492 	/* If the display_start_day has changed we need to recalculate the
2493 	 * date range shown and reload all events, otherwise we only need to
2494 	 * do a reshape. */
2495 	if (need_reload) {
2496 		/* Recalculate the days shown and reload if necessary. */
2497 		if (g_date_valid (&week_view->priv->first_day_shown))
2498 			e_week_view_set_first_day_shown (
2499 				week_view,
2500 				&week_view->priv->first_day_shown);
2501 	} else {
2502 		week_view->events_need_reshape = TRUE;
2503 		e_week_view_check_layout (week_view);
2504 	}
2505 
2506 	gtk_widget_queue_draw (week_view->titles_canvas);
2507 	gtk_widget_queue_draw (week_view->main_canvas);
2508 
2509 	g_object_notify (G_OBJECT (week_view), "compress-weekend");
2510 }
2511 
2512 gboolean
e_week_view_get_draw_flat_events(EWeekView * week_view)2513 e_week_view_get_draw_flat_events (EWeekView *week_view)
2514 {
2515 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2516 
2517 	return week_view->priv->draw_flat_events;
2518 }
2519 
2520 void
e_week_view_set_draw_flat_events(EWeekView * week_view,gboolean draw_flat_events)2521 e_week_view_set_draw_flat_events (EWeekView *week_view,
2522 				  gboolean draw_flat_events)
2523 {
2524 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2525 
2526 	if ((week_view->priv->draw_flat_events ? 1 : 0) == (draw_flat_events ? 1 : 0))
2527 		return;
2528 
2529 	week_view->priv->draw_flat_events = draw_flat_events;
2530 
2531 	gtk_widget_queue_draw (week_view->titles_canvas);
2532 	gtk_widget_queue_draw (week_view->main_canvas);
2533 
2534 	g_object_notify (G_OBJECT (week_view), "draw-flat-events");
2535 }
2536 
2537 gboolean
e_week_view_get_days_left_to_right(EWeekView * week_view)2538 e_week_view_get_days_left_to_right (EWeekView *week_view)
2539 {
2540 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2541 
2542 	return week_view->priv->days_left_to_right;
2543 }
2544 
2545 void
e_week_view_set_days_left_to_right(EWeekView * week_view,gboolean days_left_to_right)2546 e_week_view_set_days_left_to_right (EWeekView *week_view,
2547 				    gboolean days_left_to_right)
2548 {
2549 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2550 
2551 	if ((week_view->priv->days_left_to_right ? 1 : 0) == (days_left_to_right ? 1 : 0))
2552 		return;
2553 
2554 	week_view->priv->days_left_to_right = days_left_to_right;
2555 
2556 	week_view->events_need_layout = TRUE;
2557 	week_view->events_need_reshape = TRUE;
2558 
2559 	gtk_widget_queue_draw (week_view->main_canvas);
2560 	e_week_view_queue_layout (week_view);
2561 
2562 	g_object_notify (G_OBJECT (week_view), "days-left-to-right");
2563 }
2564 
2565 /* Whether we display event end times. */
2566 gboolean
e_week_view_get_show_event_end_times(EWeekView * week_view)2567 e_week_view_get_show_event_end_times (EWeekView *week_view)
2568 {
2569 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), TRUE);
2570 
2571 	return week_view->priv->show_event_end_times;
2572 }
2573 
2574 void
e_week_view_set_show_event_end_times(EWeekView * week_view,gboolean show_event_end_times)2575 e_week_view_set_show_event_end_times (EWeekView *week_view,
2576                                       gboolean show_event_end_times)
2577 {
2578 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2579 
2580 	if (show_event_end_times == week_view->priv->show_event_end_times)
2581 		return;
2582 
2583 	week_view->priv->show_event_end_times = show_event_end_times;
2584 	e_week_view_recalc_cell_sizes (week_view);
2585 	week_view->events_need_reshape = TRUE;
2586 	e_week_view_check_layout (week_view);
2587 
2588 	gtk_widget_queue_draw (week_view->titles_canvas);
2589 	gtk_widget_queue_draw (week_view->main_canvas);
2590 
2591 	g_object_notify (G_OBJECT (week_view), "show-event-end-times");
2592 }
2593 
2594 gboolean
e_week_view_get_show_icons_month_view(EWeekView * week_view)2595 e_week_view_get_show_icons_month_view (EWeekView *week_view)
2596 {
2597 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), TRUE);
2598 
2599 	return week_view->priv->show_icons_month_view;
2600 }
2601 
2602 void
e_week_view_set_show_icons_month_view(EWeekView * week_view,gboolean show_icons_month_view)2603 e_week_view_set_show_icons_month_view (EWeekView *week_view,
2604 				       gboolean show_icons_month_view)
2605 {
2606 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2607 
2608 	if (show_icons_month_view == week_view->priv->show_icons_month_view)
2609 		return;
2610 
2611 	week_view->priv->show_icons_month_view = show_icons_month_view;
2612 
2613 	if (e_week_view_get_multi_week_view (week_view)) {
2614 		e_week_view_recalc_cell_sizes (week_view);
2615 		week_view->events_need_reshape = TRUE;
2616 		e_week_view_check_layout (week_view);
2617 
2618 		gtk_widget_queue_draw (week_view->titles_canvas);
2619 		gtk_widget_queue_draw (week_view->main_canvas);
2620 	}
2621 
2622 	g_object_notify (G_OBJECT (week_view), "show-icons-month-view");
2623 }
2624 
2625 static gboolean
e_week_view_recalc_display_start_day(EWeekView * week_view)2626 e_week_view_recalc_display_start_day (EWeekView *week_view)
2627 {
2628 	ECalModel *model;
2629 	GDateWeekday week_start_day;
2630 	GDateWeekday display_start_day;
2631 	gboolean changed;
2632 
2633 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
2634 	week_start_day = e_cal_model_get_week_start_day (model);
2635 
2636 	/* The display start day defaults to week_start_day, but we have
2637 	 * to use Saturday if the weekend is compressed and week_start_day
2638 	 * is Sunday. */
2639 	display_start_day = week_start_day;
2640 
2641 	if (display_start_day == G_DATE_SUNDAY &&
2642 	    e_week_view_get_multi_week_view (week_view) &&
2643 	    e_week_view_get_compress_weekend (week_view)) {
2644 		display_start_day = G_DATE_SATURDAY;
2645 	}
2646 
2647 	changed = (display_start_day != week_view->priv->display_start_day);
2648 
2649 	week_view->priv->display_start_day = display_start_day;
2650 
2651 	return changed;
2652 }
2653 
2654 /* Checks if the users participation status is NEEDS-ACTION and shows the summary as bold text */
2655 static void
set_style_from_attendee(EWeekViewEvent * event,EWeekViewEventSpan * span,ESourceRegistry * registry)2656 set_style_from_attendee (EWeekViewEvent *event,
2657 			 EWeekViewEventSpan *span,
2658 			 ESourceRegistry *registry)
2659 {
2660 	ECalComponent *comp;
2661 	GSList *attendees = NULL, *l;
2662 	gchar *address;
2663 	ICalParameterPartstat partstat = I_CAL_PARTSTAT_NONE;
2664 
2665 	if (!is_comp_data_valid (event))
2666 		return;
2667 
2668 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
2669 	if (!comp)
2670 		return;
2671 
2672 	address = itip_get_comp_attendee (
2673 		registry, comp, event->comp_data->client);
2674 	attendees = e_cal_component_get_attendees (comp);
2675 	for (l = attendees; l && address; l = l->next) {
2676 		ECalComponentAttendee *attendee = l->data;
2677 		const gchar *value, *sentby;
2678 
2679 		if (!attendee)
2680 			continue;
2681 
2682 		value = e_cal_component_attendee_get_value (attendee);
2683 		if (value)
2684 			value = itip_strip_mailto (value);
2685 		sentby = e_cal_component_attendee_get_sentby (attendee);
2686 		if (sentby)
2687 			sentby = itip_strip_mailto (sentby);
2688 		if ((value && g_ascii_strcasecmp (value, address) == 0) ||
2689 		    (sentby && g_ascii_strcasecmp (sentby, address) == 0)) {
2690 			partstat = e_cal_component_attendee_get_partstat (attendee);
2691 			break;
2692 		}
2693 	}
2694 
2695 	if (i_cal_component_get_status (event->comp_data->icalcomp) == I_CAL_STATUS_CANCELLED)
2696 		gnome_canvas_item_set (span->text_item, "strikeout", TRUE, NULL);
2697 
2698 	/* The attendee has not yet accepted the meeting, display the summary as bolded.
2699 	 * If the attendee is not present, it might have come through a mailing list.
2700 	 * In that case, we never show the meeting as bold even if it is unaccepted. */
2701 	if (partstat == I_CAL_PARTSTAT_NEEDSACTION)
2702 		gnome_canvas_item_set (span->text_item, "bold", TRUE, NULL);
2703 	else if (partstat == I_CAL_PARTSTAT_DECLINED)
2704 		gnome_canvas_item_set (span->text_item, "strikeout", TRUE, NULL);
2705 	else if (partstat == I_CAL_PARTSTAT_TENTATIVE)
2706 		gnome_canvas_item_set (span->text_item, "italic", TRUE, NULL);
2707 	else if (partstat == I_CAL_PARTSTAT_DELEGATED)
2708 		gnome_canvas_item_set (span->text_item, "italic", TRUE, "strikeout", TRUE, NULL);
2709 
2710 	g_slist_free_full (attendees, e_cal_component_attendee_free);
2711 	g_free (address);
2712 	g_object_unref (comp);
2713 }
2714 
2715 /* This calls a given function for each event instance that matches the given
2716  * uid. Note that it is safe for the callback to remove the event (since we
2717  * step backwards through the arrays). */
2718 static void
e_week_view_foreach_event_with_uid(EWeekView * week_view,const gchar * uid,EWeekViewForeachEventCallback callback,gpointer data)2719 e_week_view_foreach_event_with_uid (EWeekView *week_view,
2720                                     const gchar *uid,
2721                                     EWeekViewForeachEventCallback callback,
2722                                     gpointer data)
2723 {
2724 	EWeekViewEvent *event;
2725 	gint event_num;
2726 
2727 	for (event_num = week_view->events->len - 1;
2728 	     event_num >= 0;
2729 	     event_num--) {
2730 		const gchar *u;
2731 
2732 		event = &g_array_index (week_view->events, EWeekViewEvent,
2733 					event_num);
2734 
2735 		if (!is_comp_data_valid (event))
2736 			continue;
2737 
2738 		u = i_cal_component_get_uid (event->comp_data->icalcomp);
2739 		if (u && !strcmp (uid, u)) {
2740 			if (!(*callback) (week_view, event_num, data))
2741 				return;
2742 		}
2743 	}
2744 }
2745 
2746 static gboolean
e_week_view_remove_event_cb(EWeekView * week_view,gint event_num,gpointer data)2747 e_week_view_remove_event_cb (EWeekView *week_view,
2748                              gint event_num,
2749                              gpointer data)
2750 {
2751 	EWeekViewEvent *event;
2752 	EWeekViewEventSpan *span;
2753 	gint span_num;
2754 
2755 	if (!is_array_index_in_bounds (week_view->events, event_num))
2756 		return TRUE;
2757 
2758 	event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
2759 
2760 	/* If we were editing this event, set editing_event_num to -1 so
2761 	 * on_editing_stopped doesn't try to update the event. */
2762 	if (week_view->editing_event_num == event_num) {
2763 		week_view->editing_event_num = -1;
2764 		g_object_notify (G_OBJECT (week_view), "is-editing");
2765 	}
2766 
2767 	if (week_view->popup_event_num == event_num)
2768 		e_week_view_set_popup_event (week_view, -1);
2769 
2770 	if (is_comp_data_valid (event))
2771 		g_object_unref (event->comp_data);
2772 	event->comp_data = NULL;
2773 
2774 	if (week_view->spans) {
2775 		/* We leave the span elements in the array, but set the canvas item
2776 		 * pointers to NULL. */
2777 		for (span_num = 0; span_num < event->num_spans; span_num++) {
2778 			if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
2779 				break;
2780 
2781 			span = &g_array_index (week_view->spans, EWeekViewEventSpan,
2782 					       event->spans_index + span_num);
2783 
2784 			if (span->text_item) {
2785 				g_object_run_dispose (G_OBJECT (span->text_item));
2786 				span->text_item = NULL;
2787 			}
2788 			if (span->background_item) {
2789 				g_object_run_dispose (G_OBJECT (span->background_item));
2790 				span->background_item = NULL;
2791 			}
2792 		}
2793 
2794 		/* Update event_num numbers for already created spans with event_num higher than our event_num */
2795 		for (span_num = 0; span_num < week_view->spans->len; span_num++) {
2796 			span = &g_array_index (week_view->spans, EWeekViewEventSpan, span_num);
2797 
2798 			if (span && span->background_item && E_IS_WEEK_VIEW_EVENT_ITEM (span->background_item)) {
2799 				EWeekViewEventItem *wveitem = E_WEEK_VIEW_EVENT_ITEM (span->background_item);
2800 				gint wveitem_event_num;
2801 
2802 				wveitem_event_num =
2803 					e_week_view_event_item_get_event_num (wveitem);
2804 
2805 				if (wveitem_event_num > event_num)
2806 					e_week_view_event_item_set_event_num (
2807 						wveitem, wveitem_event_num - 1);
2808 			}
2809 		}
2810 	}
2811 
2812 	g_array_remove_index (week_view->events, event_num);
2813 
2814 	week_view->events_need_layout = TRUE;
2815 
2816 	return TRUE;
2817 }
2818 
2819 void
e_week_view_get_day_position(EWeekView * week_view,gint day,gint * day_x,gint * day_y,gint * day_w,gint * day_h)2820 e_week_view_get_day_position (EWeekView *week_view,
2821                               gint day,
2822                               gint *day_x,
2823                               gint *day_y,
2824                               gint *day_w,
2825                               gint *day_h)
2826 {
2827 	gint cell_x, cell_y, cell_h;
2828 
2829 	e_week_view_layout_get_day_position (
2830 		day,
2831 		e_week_view_get_multi_week_view (week_view),
2832 		e_week_view_get_weeks_shown (week_view),
2833 		e_week_view_get_display_start_day (week_view),
2834 		e_week_view_get_compress_weekend (week_view),
2835 		&cell_x, &cell_y, &cell_h);
2836 
2837 	*day_x = week_view->col_offsets[cell_x];
2838 	*day_y = week_view->row_offsets[cell_y];
2839 
2840 	*day_w = week_view->col_widths[cell_x];
2841 	*day_h = week_view->row_heights[cell_y];
2842 
2843 	while (cell_h > 1) {
2844 		*day_h += week_view->row_heights[cell_y + 1];
2845 		cell_h--;
2846 		cell_y++;
2847 	}
2848 }
2849 
2850 /* Returns the bounding box for a span of an event. Usually this can easily
2851  * be determined by the start & end days and row of the span, which are set in
2852  * e_week_view_layout_event (). Though we need a special case for the weekends
2853  * when they are compressed, since the span may not fit.
2854  * The bounding box includes the entire width of the days in the view (but
2855  * not the vertical line down the right of the last day), though the displayed
2856  * event doesn't normally extend to the edges of the day.
2857  * It returns FALSE if the span isn't visible. */
2858 gboolean
e_week_view_get_span_position(EWeekView * week_view,gint event_num,gint span_num,gint * span_x,gint * span_y,gint * span_w)2859 e_week_view_get_span_position (EWeekView *week_view,
2860                                gint event_num,
2861                                gint span_num,
2862                                gint *span_x,
2863                                gint *span_y,
2864                                gint *span_w)
2865 {
2866 	EWeekViewEvent *event;
2867 	EWeekViewEventSpan *span;
2868 	gint num_days;
2869 	gint start_x, start_y, start_w, start_h;
2870 	gint end_x, end_y, end_w, end_h;
2871 
2872 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2873 	g_return_val_if_fail (event_num < week_view->events->len, FALSE);
2874 
2875 	event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
2876 
2877 	g_return_val_if_fail (span_num < event->num_spans, FALSE);
2878 
2879 	if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
2880 		return FALSE;
2881 
2882 	span = &g_array_index (week_view->spans, EWeekViewEventSpan,
2883 			       event->spans_index + span_num);
2884 
2885 	if (!e_week_view_layout_get_span_position (
2886 		event, span,
2887 		week_view->rows_per_cell,
2888 		week_view->rows_per_compressed_cell,
2889 		e_week_view_get_display_start_day (week_view),
2890 		e_week_view_get_multi_week_view (week_view),
2891 		e_week_view_get_compress_weekend (week_view),
2892 		&num_days)) {
2893 		return FALSE;
2894 	}
2895 
2896 	e_week_view_get_day_position (
2897 		week_view, span->start_day,
2898 		&start_x, &start_y, &start_w, &start_h);
2899 	*span_y = start_y + week_view->events_y_offset
2900 		+ span->row * (week_view->row_height
2901 			       + E_WEEK_VIEW_EVENT_Y_SPACING);
2902 	if (num_days == 1) {
2903 		*span_x = start_x;
2904 		*span_w = start_w - 1;
2905 	} else {
2906 		e_week_view_get_day_position (
2907 			week_view,
2908 			span->start_day + num_days - 1,
2909 			&end_x, &end_y, &end_w, &end_h);
2910 		*span_x = start_x;
2911 		*span_w = end_x + end_w - start_x - 1;
2912 	}
2913 
2914 	return TRUE;
2915 }
2916 
2917 static gboolean
ewv_pass_gdkevent_to_etext(EWeekView * week_view,GdkEvent * gevent)2918 ewv_pass_gdkevent_to_etext (EWeekView *week_view,
2919                             GdkEvent *gevent)
2920 {
2921 	g_return_val_if_fail (week_view != NULL, FALSE);
2922 	g_return_val_if_fail (gevent != NULL, FALSE);
2923 
2924 	if (week_view->editing_event_num != -1 && week_view->editing_span_num != -1) {
2925 		EWeekViewEvent *event;
2926 		EWeekViewEventSpan *span;
2927 
2928 		if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
2929 			return FALSE;
2930 
2931 		event = &g_array_index (week_view->events, EWeekViewEvent, week_view->editing_event_num);
2932 
2933 		if (!is_array_index_in_bounds (week_view->spans, event->spans_index + week_view->editing_span_num))
2934 			return FALSE;
2935 
2936 		span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + week_view->editing_span_num);
2937 
2938 		if (span->text_item && E_IS_TEXT (span->text_item)) {
2939 			gdouble x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0, ex = 0.0, ey = 0.0;
2940 
2941 			gdk_event_get_coords (gevent, &ex, &ey);
2942 
2943 			gnome_canvas_item_get_bounds (span->text_item, &x1, &y1, &x2, &y2);
2944 
2945 			if (ex >= x1 && ex <= x2 && ey >= y1 && ey <= y2) {
2946 				GNOME_CANVAS_ITEM_GET_CLASS (span->text_item)->event (span->text_item, gevent);
2947 				return TRUE;
2948 			}
2949 		}
2950 	}
2951 
2952 	return FALSE;
2953 }
2954 
2955 static gboolean
e_week_view_on_button_press(GtkWidget * widget,GdkEvent * button_event,EWeekView * week_view)2956 e_week_view_on_button_press (GtkWidget *widget,
2957                              GdkEvent *button_event,
2958                              EWeekView *week_view)
2959 {
2960 	GnomeCanvasItem *item;
2961 	gint event_num = -1, span_num = -1;
2962 	guint event_button = 0;
2963 	gdouble event_x_win = 0;
2964 	gdouble event_y_win = 0;
2965 	gint x, y, day;
2966 
2967 	gdk_event_get_button (button_event, &event_button);
2968 	gdk_event_get_coords (button_event, &event_x_win, &event_y_win);
2969 
2970 	/* Convert the mouse position to a week & day. */
2971 	x = (gint) event_x_win;
2972 	y = (gint) event_y_win;
2973 	day = e_week_view_convert_position_to_day (week_view, x, y);
2974 	if (day == -1)
2975 		return FALSE;
2976 
2977 	if (ewv_pass_gdkevent_to_etext (week_view, button_event))
2978 		return TRUE;
2979 
2980 	/* If an event is pressed just return. */
2981 	if (week_view->pressed_event_num != -1) {
2982 		e_week_view_set_popup_event (week_view, week_view->pressed_event_num);
2983 		return FALSE;
2984 	}
2985 
2986 	e_week_view_stop_editing_event (week_view);
2987 
2988 	item = gnome_canvas_get_item_at (GNOME_CANVAS (widget), x, y);
2989 	if (!item || !e_week_view_find_event_from_item (week_view, item, &event_num, &span_num))
2990 		event_num = -1;
2991 
2992 	e_week_view_set_popup_event (week_view, event_num);
2993 
2994 	if (event_button == 1 && button_event->type == GDK_2BUTTON_PRESS) {
2995 		time_t dtstart, dtend;
2996 
2997 		e_calendar_view_get_selected_time_range ((ECalendarView *) week_view, &dtstart, &dtend);
2998 		if (dtstart < week_view->before_click_dtend && dtend > week_view->before_click_dtstart) {
2999 			e_calendar_view_set_selected_time_range (
3000 				E_CALENDAR_VIEW (week_view),
3001 				week_view->before_click_dtstart,
3002 				week_view->before_click_dtend);
3003 		}
3004 		e_calendar_view_new_appointment (E_CALENDAR_VIEW (week_view),
3005 			(calendar_config_get_prefer_meeting () ? E_NEW_APPOINTMENT_FLAG_MEETING : 0));
3006 		return TRUE;
3007 	}
3008 
3009 	if (event_button == 1) {
3010 		GdkGrabStatus grab_status;
3011 		GdkWindow *window;
3012 		GdkDevice *event_device;
3013 		guint32 event_time;
3014 
3015 		/* Start the selection drag. */
3016 		if (!gtk_widget_has_focus (GTK_WIDGET (week_view)) && !gtk_widget_has_focus (GTK_WIDGET (week_view->main_canvas)))
3017 			gtk_widget_grab_focus (GTK_WIDGET (week_view));
3018 
3019 		window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
3020 
3021 		event_device = gdk_event_get_device (button_event);
3022 		event_time = gdk_event_get_time (button_event);
3023 
3024 		grab_status = gdk_device_grab (
3025 			event_device,
3026 			window,
3027 			GDK_OWNERSHIP_NONE,
3028 			FALSE,
3029 			GDK_POINTER_MOTION_MASK |
3030 			GDK_BUTTON_RELEASE_MASK,
3031 			NULL,
3032 			event_time);
3033 
3034 		if (grab_status == GDK_GRAB_SUCCESS) {
3035 			if (event_time - week_view->bc_event_time > 250)
3036 				e_calendar_view_get_selected_time_range (
3037 					E_CALENDAR_VIEW (week_view),
3038 					&week_view->before_click_dtstart,
3039 					&week_view->before_click_dtend);
3040 			week_view->bc_event_time = event_time;
3041 			week_view->selection_start_day = day;
3042 			week_view->selection_end_day = day;
3043 			week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_END;
3044 			g_signal_emit_by_name (week_view, "selected_time_changed");
3045 
3046 			/* FIXME: Optimise? */
3047 			gtk_widget_queue_draw (week_view->main_canvas);
3048 		}
3049 	} else if (event_button == 3) {
3050 		if (!gtk_widget_has_focus (GTK_WIDGET (week_view)))
3051 			gtk_widget_grab_focus (GTK_WIDGET (week_view));
3052 
3053 		if (day < week_view->selection_start_day || day > week_view->selection_end_day) {
3054 			week_view->selection_start_day = day;
3055 			week_view->selection_end_day = day;
3056 			week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
3057 
3058 			/* FIXME: Optimise? */
3059 			gtk_widget_queue_draw (week_view->main_canvas);
3060 		}
3061 
3062 		e_week_view_show_popup_menu (week_view, button_event, event_num);
3063 	}
3064 
3065 	return TRUE;
3066 }
3067 
3068 static gboolean
e_week_view_on_button_release(GtkWidget * widget,GdkEvent * button_event,EWeekView * week_view)3069 e_week_view_on_button_release (GtkWidget *widget,
3070                                GdkEvent *button_event,
3071                                EWeekView *week_view)
3072 {
3073 	GdkDevice *event_device;
3074 	guint32 event_time;
3075 
3076 	event_device = gdk_event_get_device (button_event);
3077 	event_time = gdk_event_get_time (button_event);
3078 
3079 	if (week_view->selection_drag_pos != E_WEEK_VIEW_DRAG_NONE) {
3080 		week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
3081 		gdk_device_ungrab (event_device, event_time);
3082 	} else {
3083 		ewv_pass_gdkevent_to_etext (week_view, button_event);
3084 	}
3085 
3086 	return FALSE;
3087 }
3088 
3089 static gboolean
e_week_view_on_scroll(GtkWidget * widget,GdkEventScroll * scroll,EWeekView * week_view)3090 e_week_view_on_scroll (GtkWidget *widget,
3091                        GdkEventScroll *scroll,
3092                        EWeekView *week_view)
3093 {
3094 	GtkRange *range;
3095 	GtkAdjustment *adjustment;
3096 	gdouble page_increment;
3097 	gdouble new_value;
3098 	gdouble page_size;
3099 	gdouble lower;
3100 	gdouble upper;
3101 	gdouble value;
3102 	GtkWidget *tool_window = g_object_get_data (G_OBJECT (week_view), "tooltip-window");
3103 	guint timeout;
3104 
3105 	timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
3106 	if (timeout) {
3107 		g_source_remove (timeout);
3108 		g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
3109 	}
3110 
3111 	if (tool_window) {
3112 		gtk_widget_destroy (tool_window);
3113 		g_object_set_data (G_OBJECT (week_view), "tooltip-window", NULL);
3114 	}
3115 
3116 	range = GTK_RANGE (week_view->vscrollbar);
3117 	adjustment = gtk_range_get_adjustment (range);
3118 
3119 	page_increment = gtk_adjustment_get_page_increment (adjustment);
3120 	page_size = gtk_adjustment_get_page_size (adjustment);
3121 	lower = gtk_adjustment_get_lower (adjustment);
3122 	upper = gtk_adjustment_get_upper (adjustment);
3123 	value = gtk_adjustment_get_value (adjustment);
3124 
3125 	switch (scroll->direction) {
3126 		case GDK_SCROLL_UP:
3127 			new_value = value - page_increment;
3128 			break;
3129 		case GDK_SCROLL_DOWN:
3130 			new_value = value + page_increment;
3131 			break;
3132 		case GDK_SCROLL_SMOOTH:
3133 			if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) {
3134 				new_value = value + scroll->delta_y * page_increment;
3135 				break;
3136 			}
3137 			return FALSE;
3138 		default:
3139 			return FALSE;
3140 	}
3141 
3142 	new_value = CLAMP (new_value, lower, upper - page_size);
3143 	gtk_adjustment_set_value (adjustment, new_value);
3144 
3145 	week_view->events_need_layout = TRUE;
3146 	e_week_view_check_layout (week_view);
3147 
3148 	return TRUE;
3149 }
3150 
3151 static gboolean
e_week_view_on_motion(GtkWidget * widget,GdkEventMotion * mevent,EWeekView * week_view)3152 e_week_view_on_motion (GtkWidget *widget,
3153                        GdkEventMotion *mevent,
3154                        EWeekView *week_view)
3155 {
3156 	gint x, y, day;
3157 
3158 	/* Convert the mouse position to a week & day. */
3159 	x = mevent->x;
3160 	y = mevent->y;
3161 	day = e_week_view_convert_position_to_day (week_view, x, y);
3162 	if (day == -1)
3163 		return FALSE;
3164 
3165 	if (week_view->selection_drag_pos != E_WEEK_VIEW_DRAG_NONE) {
3166 		e_week_view_update_selection (week_view, day);
3167 		return TRUE;
3168 	}
3169 
3170 	ewv_pass_gdkevent_to_etext (week_view, (GdkEvent *) mevent);
3171 
3172 	return FALSE;
3173 }
3174 
3175 /* Converts a position in the canvas window to a day offset from the first
3176  * day displayed. Returns -1 if the position is outside the grid. */
3177 static gint
e_week_view_convert_position_to_day(EWeekView * week_view,gint x,gint y)3178 e_week_view_convert_position_to_day (EWeekView *week_view,
3179                                      gint x,
3180                                      gint y)
3181 {
3182 	GDateWeekday display_start_day;
3183 	gint col, row, grid_x = -1, grid_y = -1, week, day;
3184 	gint weekend_col;
3185 
3186 	display_start_day = e_week_view_get_display_start_day (week_view);
3187 
3188 	/* First we convert it to a grid position. */
3189 	for (col = 0; col <= week_view->columns; col++) {
3190 		if (x < week_view->col_offsets[col]) {
3191 			grid_x = col - 1;
3192 			break;
3193 		}
3194 	}
3195 
3196 	for (row = 0; row <= week_view->rows; row++) {
3197 		if (y < week_view->row_offsets[row]) {
3198 			grid_y = row - 1;
3199 			break;
3200 		}
3201 	}
3202 
3203 	/* If the mouse is outside the grid return FALSE. */
3204 	if (grid_x == -1 || grid_y == -1)
3205 		return -1;
3206 
3207 	/* Now convert the grid position to a week and day. */
3208 	if (e_week_view_get_multi_week_view (week_view)) {
3209 		week = grid_y / 2;
3210 		day = grid_x;
3211 
3212 		if (e_week_view_get_compress_weekend (week_view)) {
3213 			weekend_col = e_weekday_get_days_between (
3214 				display_start_day, G_DATE_SATURDAY);
3215 			if (grid_x > weekend_col
3216 			    || (grid_x == weekend_col && grid_y % 2 == 1))
3217 				day++;
3218 		}
3219 	} else {
3220 		week = 0;
3221 
3222 		for (day = 0; day < 7; day++) {
3223 			gint day_x = 0, day_y = 0, rows = 0;
3224 			e_week_view_layout_get_day_position (
3225 				day, FALSE, 1,
3226 				e_week_view_get_display_start_day (week_view),
3227 				e_week_view_get_compress_weekend (week_view),
3228 				&day_x, &day_y, &rows);
3229 
3230 			if (grid_x == day_x && grid_y >= day_y && grid_y < day_y + rows)
3231 				break;
3232 		}
3233 
3234 		if (day == 7)
3235 			return -1;
3236 	}
3237 
3238 	return week * 7 + day;
3239 }
3240 
3241 static void
e_week_view_update_selection(EWeekView * week_view,gint day)3242 e_week_view_update_selection (EWeekView *week_view,
3243                               gint day)
3244 {
3245 	gint tmp_day;
3246 	gboolean need_redraw = FALSE;
3247 
3248 	if (week_view->selection_drag_pos == E_WEEK_VIEW_DRAG_START) {
3249 		if (day != week_view->selection_start_day) {
3250 			need_redraw = TRUE;
3251 			week_view->selection_start_day = day;
3252 		}
3253 	} else {
3254 		if (day != week_view->selection_end_day) {
3255 			need_redraw = TRUE;
3256 			week_view->selection_end_day = day;
3257 		}
3258 	}
3259 
3260 	/* Switch the drag position if necessary. */
3261 	if (week_view->selection_start_day > week_view->selection_end_day) {
3262 		tmp_day = week_view->selection_start_day;
3263 		week_view->selection_start_day = week_view->selection_end_day;
3264 		week_view->selection_end_day = tmp_day;
3265 		if (week_view->selection_drag_pos == E_WEEK_VIEW_DRAG_START)
3266 			week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_END;
3267 		else
3268 			week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_START;
3269 	}
3270 
3271 	/* FIXME: Optimise? */
3272 	if (need_redraw) {
3273 		gtk_widget_queue_draw (week_view->main_canvas);
3274 	}
3275 }
3276 
3277 static void
e_week_view_free_events(EWeekView * week_view)3278 e_week_view_free_events (EWeekView *week_view)
3279 {
3280 	EWeekViewEvent *event;
3281 	EWeekViewEventSpan *span;
3282 	gint event_num, span_num, num_days, day;
3283 	gboolean did_editing = week_view->editing_event_num != -1;
3284 	guint timeout;
3285 
3286 	/* Reset all our indices. */
3287 	week_view->pressed_event_num = -1;
3288 	week_view->pressed_span_num = -1;
3289 	week_view->editing_event_num = -1;
3290 	week_view->editing_span_num = -1;
3291 	week_view->popup_event_num = -1;
3292 
3293 	for (event_num = 0; event_num < week_view->events->len; event_num++) {
3294 		event = &g_array_index (week_view->events, EWeekViewEvent,
3295 					event_num);
3296 
3297 		if (is_comp_data_valid (event))
3298 			g_object_unref (event->comp_data);
3299 	}
3300 
3301 	g_array_set_size (week_view->events, 0);
3302 
3303 	/* Destroy all the old canvas items. */
3304 	if (week_view->spans) {
3305 		for (span_num = 0; span_num < week_view->spans->len;
3306 		     span_num++) {
3307 			span = &g_array_index (week_view->spans,
3308 					       EWeekViewEventSpan, span_num);
3309 			if (span->background_item)
3310 				g_object_run_dispose (G_OBJECT (span->background_item));
3311 			if (span->text_item)
3312 				g_object_run_dispose (G_OBJECT (span->text_item));
3313 		}
3314 		g_array_free (week_view->spans, TRUE);
3315 		week_view->spans = NULL;
3316 	}
3317 
3318 	/* Clear the number of rows used per day. */
3319 	num_days = e_week_view_get_weeks_shown (week_view) * 7;
3320 	for (day = 0; day <= num_days; day++) {
3321 		week_view->rows_per_day[day] = 0;
3322 	}
3323 
3324 	/* Hide all the jump buttons. */
3325 	for (day = 0; day < E_WEEK_VIEW_MAX_WEEKS * 7; day++) {
3326 		gnome_canvas_item_hide (week_view->jump_buttons[day]);
3327 	}
3328 
3329 	if (did_editing)
3330 		g_object_notify (G_OBJECT (week_view), "is-editing");
3331 
3332 	timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
3333 	if (timeout) {
3334 		g_source_remove (timeout);
3335 		g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
3336 	}
3337 }
3338 
3339 /* This adds one event to the view, adding it to the appropriate array. */
3340 static void
e_week_view_add_event(ECalClient * client,ECalComponent * comp,time_t start,time_t end,gboolean prepend,gpointer data)3341 e_week_view_add_event (ECalClient *client,
3342 		       ECalComponent *comp,
3343                        time_t start,
3344                        time_t end,
3345                        gboolean prepend,
3346                        gpointer data)
3347 
3348 {
3349 	AddEventData *add_event_data;
3350 	EWeekViewEvent event;
3351 	gint num_days;
3352 	ICalTime *start_tt, *end_tt;
3353 
3354 	add_event_data = data;
3355 
3356 	/* Check that the event times are valid. */
3357 	num_days = e_week_view_get_weeks_shown (add_event_data->week_view) * 7;
3358 
3359 	g_return_if_fail (start <= end);
3360 	g_return_if_fail (start < add_event_data->week_view->day_starts[num_days]);
3361 
3362 	if (end != start || end < add_event_data->week_view->day_starts[0])
3363 		g_return_if_fail (end > add_event_data->week_view->day_starts[0]);
3364 
3365 	start_tt = i_cal_time_new_from_timet_with_zone (
3366 		start, FALSE,
3367 		e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->week_view)));
3368 	end_tt = i_cal_time_new_from_timet_with_zone (
3369 		end, FALSE,
3370 		e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->week_view)));
3371 
3372 	if (add_event_data->comp_data) {
3373 		event.comp_data = g_object_ref (add_event_data->comp_data);
3374 	} else {
3375 		event.comp_data = g_object_new (E_TYPE_CAL_MODEL_COMPONENT, NULL);
3376 		event.comp_data->is_new_component = TRUE;
3377 		event.comp_data->client = g_object_ref (client);
3378 		e_cal_component_abort_sequence (comp);
3379 		event.comp_data->icalcomp = i_cal_component_clone (e_cal_component_get_icalcomponent (comp));
3380 	}
3381 
3382 	event.start = start;
3383 	event.end = end;
3384 	event.tooltip = NULL;
3385 	event.timeout = -1;
3386 	event.color = NULL;
3387 	event.spans_index = 0;
3388 	event.num_spans = 0;
3389 	event.comp_data->instance_start = start;
3390 	event.comp_data->instance_end = end;
3391 
3392 	event.start_minute = i_cal_time_get_hour (start_tt) * 60 + i_cal_time_get_minute (start_tt);
3393 	event.end_minute = i_cal_time_get_hour (end_tt) * 60 + i_cal_time_get_minute (end_tt);
3394 	if (event.end_minute == 0 && start != end)
3395 		event.end_minute = 24 * 60;
3396 
3397 	event.different_timezone = FALSE;
3398 	if (!cal_comp_util_compare_event_timezones (
3399 		    comp,
3400 		    event.comp_data->client,
3401 		    e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->week_view))))
3402 		event.different_timezone = TRUE;
3403 
3404 	if (prepend)
3405 		g_array_prepend_val (add_event_data->week_view->events, event);
3406 	else
3407 		g_array_append_val (add_event_data->week_view->events, event);
3408 	add_event_data->week_view->events_sorted = FALSE;
3409 	add_event_data->week_view->events_need_layout = TRUE;
3410 
3411 	g_clear_object (&start_tt);
3412 	g_clear_object (&end_tt);
3413 }
3414 
3415 /* This lays out the events, or reshapes them, as necessary. */
3416 static void
e_week_view_check_layout(EWeekView * week_view)3417 e_week_view_check_layout (EWeekView *week_view)
3418 {
3419 	/* Don't bother if we aren't visible. */
3420 	if (!E_CALENDAR_VIEW (week_view)->in_focus) {
3421 		e_week_view_free_events (week_view);
3422 		week_view->requires_update = TRUE;
3423 		return;
3424 	}
3425 
3426 	/* Make sure the events are sorted (by start and size). */
3427 	e_week_view_ensure_events_sorted (week_view);
3428 
3429 	if (week_view->events_need_layout)
3430 		week_view->spans = e_week_view_layout_events (
3431 			week_view->events,
3432 			week_view->spans,
3433 			e_week_view_get_multi_week_view (week_view),
3434 			e_week_view_get_weeks_shown (week_view),
3435 			e_week_view_get_compress_weekend (week_view),
3436 			e_week_view_get_display_start_day (week_view),
3437 			week_view->day_starts,
3438 			week_view->rows_per_day);
3439 
3440 	if (week_view->events_need_layout || week_view->events_need_reshape)
3441 		e_week_view_reshape_events (week_view);
3442 
3443 	week_view->events_need_layout = FALSE;
3444 	week_view->events_need_reshape = FALSE;
3445 }
3446 
3447 static void
e_week_view_ensure_events_sorted(EWeekView * week_view)3448 e_week_view_ensure_events_sorted (EWeekView *week_view)
3449 {
3450 	if (!week_view->events_sorted) {
3451 		qsort (
3452 			week_view->events->data,
3453 			week_view->events->len,
3454 			sizeof (EWeekViewEvent),
3455 			e_week_view_event_sort_func);
3456 		week_view->events_sorted = TRUE;
3457 	}
3458 }
3459 
3460 gint
e_week_view_event_sort_func(gconstpointer arg1,gconstpointer arg2)3461 e_week_view_event_sort_func (gconstpointer arg1,
3462                              gconstpointer arg2)
3463 {
3464 	EWeekViewEvent *event1, *event2;
3465 
3466 	event1 = (EWeekViewEvent *) arg1;
3467 	event2 = (EWeekViewEvent *) arg2;
3468 
3469 	if (event1->start < event2->start)
3470 		return -1;
3471 	if (event1->start > event2->start)
3472 		return 1;
3473 
3474 	if (event1->end > event2->end)
3475 		return -1;
3476 	if (event1->end < event2->end)
3477 		return 1;
3478 
3479 	return 0;
3480 }
3481 
3482 static void
e_week_view_reshape_events(EWeekView * week_view)3483 e_week_view_reshape_events (EWeekView *week_view)
3484 {
3485 	EWeekViewEvent *event;
3486 	GDateWeekday display_start_day;
3487 	gint event_num, span_num;
3488 	gint num_days, day, day_x, day_y, day_w, day_h, max_rows;
3489 	gboolean is_weekend;
3490 
3491 	for (event_num = 0; event_num < week_view->events->len; event_num++) {
3492 		event = &g_array_index (week_view->events, EWeekViewEvent,
3493 					event_num);
3494 		if (!is_comp_data_valid (event))
3495 			continue;
3496 
3497 		for (span_num = 0; span_num < event->num_spans; span_num++) {
3498 			gchar *current_comp_string;
3499 
3500 			e_week_view_reshape_event_span (
3501 				week_view, event_num, span_num);
3502 			if (week_view->last_edited_comp_string == NULL)
3503 				continue;
3504 			current_comp_string = i_cal_component_as_ical_string (event->comp_data->icalcomp);
3505 			if (strncmp (current_comp_string, week_view->last_edited_comp_string, 50) == 0) {
3506 				EWeekViewEventSpan *span;
3507 
3508 				if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num)) {
3509 					g_free (current_comp_string);
3510 					continue;
3511 				}
3512 
3513 				span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num);
3514 				e_canvas_item_grab_focus (span->text_item, TRUE);
3515 				g_free (week_view->last_edited_comp_string);
3516 				week_view->last_edited_comp_string = NULL;
3517 			}
3518 			g_free (current_comp_string);
3519 		}
3520 	}
3521 
3522 	/* Reshape the jump buttons and show/hide them as appropriate. */
3523 	num_days = e_week_view_get_weeks_shown (week_view) * 7;
3524 	display_start_day = e_week_view_get_display_start_day (week_view);
3525 	for (day = 0; day < num_days; day++) {
3526 		switch (e_weekday_add_days (display_start_day, day)) {
3527 			case G_DATE_SATURDAY:
3528 			case G_DATE_SUNDAY:
3529 				is_weekend = TRUE;
3530 				break;
3531 			default:
3532 				is_weekend = FALSE;
3533 				break;
3534 		}
3535 
3536 		if (!is_weekend || (
3537 		    e_week_view_get_multi_week_view (week_view)
3538 		    && !e_week_view_get_compress_weekend (week_view)))
3539 			max_rows = week_view->rows_per_cell;
3540 		else
3541 			max_rows = week_view->rows_per_compressed_cell;
3542 
3543 		/* Determine whether the jump button should be shown. */
3544 		if (week_view->rows_per_day[day] <= max_rows) {
3545 			gnome_canvas_item_hide (week_view->jump_buttons[day]);
3546 		} else {
3547 			cairo_matrix_t matrix;
3548 
3549 			e_week_view_get_day_position (
3550 				week_view, day,
3551 				&day_x, &day_y,
3552 				&day_w, &day_h);
3553 
3554 			cairo_matrix_init_translate (
3555 				&matrix,
3556 				day_x + day_w - E_WEEK_VIEW_JUMP_BUTTON_X_PAD - E_WEEK_VIEW_JUMP_BUTTON_WIDTH,
3557 				day_y + day_h - E_WEEK_VIEW_JUMP_BUTTON_Y_PAD - E_WEEK_VIEW_JUMP_BUTTON_HEIGHT);
3558 			gnome_canvas_item_set_matrix (week_view->jump_buttons[day], &matrix);
3559 
3560 			gnome_canvas_item_show (week_view->jump_buttons[day]);
3561 			gnome_canvas_item_raise_to_top (week_view->jump_buttons[day]);
3562 		}
3563 	}
3564 
3565 	for (day = num_days; day < E_WEEK_VIEW_MAX_WEEKS * 7; day++) {
3566 		gnome_canvas_item_hide (week_view->jump_buttons[day]);
3567 	}
3568 }
3569 
3570 static EWeekViewEvent *
tooltip_get_view_event(EWeekView * week_view,gint day,gint event_num)3571 tooltip_get_view_event (EWeekView *week_view,
3572                         gint day,
3573                         gint event_num)
3574 {
3575 	EWeekViewEvent *pevent;
3576 
3577 	if (!is_array_index_in_bounds (week_view->events, event_num))
3578 		return NULL;
3579 
3580 	pevent = &g_array_index (week_view->events, EWeekViewEvent, event_num);
3581 
3582 	return pevent;
3583 }
3584 
3585 static void
tooltip_destroy(EWeekView * week_view,GnomeCanvasItem * item)3586 tooltip_destroy (EWeekView *week_view,
3587                  GnomeCanvasItem *item)
3588 {
3589 	gint event_num;
3590 	EWeekViewEvent *pevent;
3591 	guint timeout;
3592 
3593 	e_week_view_check_layout (week_view);
3594 
3595 	event_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "event-num"));
3596 
3597 	timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
3598 	if (timeout) {
3599 		g_source_remove (timeout);
3600 		g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
3601 	}
3602 
3603 	pevent = tooltip_get_view_event (week_view, -1, event_num);
3604 	if (pevent) {
3605 		if (pevent->tooltip && g_object_get_data (G_OBJECT (week_view), "tooltip-window")) {
3606 			gtk_widget_destroy (pevent->tooltip);
3607 			pevent->tooltip = NULL;
3608 		}
3609 
3610 		g_object_set_data (G_OBJECT (week_view), "tooltip-window", NULL);
3611 	}
3612 }
3613 
3614 static gboolean
e_week_view_handle_tooltip_timeout(gpointer user_data)3615 e_week_view_handle_tooltip_timeout (gpointer user_data)
3616 {
3617 	ECalendarViewEventData *data = user_data;
3618 
3619 	g_return_val_if_fail (data != NULL, FALSE);
3620 
3621 	return e_calendar_view_get_tooltips (data);
3622 }
3623 
3624 static void
e_week_view_destroy_tooltip_timeout_data(gpointer ptr)3625 e_week_view_destroy_tooltip_timeout_data (gpointer ptr)
3626 {
3627 	ECalendarViewEventData *data = ptr;
3628 
3629 	if (data) {
3630 		g_object_set_data ((GObject *) data->cal_view, "tooltip-timeout", NULL);
3631 		g_object_unref (data->cal_view);
3632 		g_free (data);
3633 	}
3634 }
3635 
3636 static gboolean
tooltip_event_cb(GnomeCanvasItem * item,GdkEvent * event,EWeekView * view)3637 tooltip_event_cb (GnomeCanvasItem *item,
3638                   GdkEvent *event,
3639                   EWeekView *view)
3640 {
3641 	gint event_num;
3642 	guint event_button = 0;
3643 	EWeekViewEvent *pevent;
3644 
3645 	e_week_view_check_layout (view);
3646 
3647 	event_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "event-num"));
3648 	pevent = tooltip_get_view_event (view, -1, event_num);
3649 
3650 	switch (event->type) {
3651 		case GDK_ENTER_NOTIFY:
3652 		if (view->editing_event_num == -1) {
3653 			ECalendarViewEventData *data;
3654 
3655 			g_return_val_if_fail (pevent != NULL, FALSE);
3656 			data = g_malloc (sizeof (ECalendarViewEventData));
3657 
3658 			pevent->x = ((GdkEventCrossing *) event)->x_root;
3659 			pevent->y = ((GdkEventCrossing *) event)->y_root;
3660 			pevent->tooltip = NULL;
3661 
3662 			data->cal_view = E_CALENDAR_VIEW (g_object_ref (view));
3663 			data->day = -1;
3664 			data->event_num = event_num;
3665 			data->get_view_event = (ECalendarViewEvent * (*)(ECalendarView *, int, gint)) tooltip_get_view_event;
3666 			pevent->timeout = e_named_timeout_add_full (
3667 				G_PRIORITY_DEFAULT, 500,
3668 				e_week_view_handle_tooltip_timeout,
3669 				data, e_week_view_destroy_tooltip_timeout_data);
3670 			g_object_set_data ((GObject *) view, "tooltip-timeout", GUINT_TO_POINTER (pevent->timeout));
3671 
3672 			return TRUE;
3673 		} else {
3674 			return FALSE;
3675 		}
3676 		case GDK_MOTION_NOTIFY:
3677 			g_return_val_if_fail (pevent != NULL, FALSE);
3678 
3679 			pevent->x = ((GdkEventMotion *) event)->x_root;
3680 			pevent->y = ((GdkEventMotion *) event)->y_root;
3681 			pevent->tooltip = (GtkWidget *) g_object_get_data (G_OBJECT (view), "tooltip-window");
3682 
3683 			if (pevent->tooltip) {
3684 				e_calendar_view_move_tip (pevent->tooltip, pevent->x, pevent->y);
3685 			}
3686 
3687 			return TRUE;
3688 		case GDK_LEAVE_NOTIFY:
3689 		case GDK_KEY_PRESS:
3690 		case GDK_BUTTON_PRESS:
3691 			tooltip_destroy (view, item);
3692 			if (gdk_event_get_button (event, &event_button) && event_button == 1)
3693 				e_week_view_set_popup_event (view, event_num);
3694 
3695 			return FALSE;
3696 		default:
3697 			return FALSE;
3698 	}
3699 }
3700 
3701 static gchar *
dup_comp_summary(ECalClient * client,ICalComponent * icomp)3702 dup_comp_summary (ECalClient *client,
3703 		  ICalComponent *icomp)
3704 {
3705 	const gchar *location;
3706 	gchar *summary, *my_summary;
3707 
3708 	g_return_val_if_fail (icomp != NULL, NULL);
3709 
3710 	my_summary = e_calendar_view_dup_component_summary (icomp);
3711 
3712 	location = i_cal_component_get_location (icomp);
3713 
3714 	if (location && *location) {
3715 		/* Translators: the first '%s' is replaced with a component summary;
3716 		   the second '%s' is replaced with an event location.
3717 		   Example: "Meet John Doe (Central Park)" */
3718 		summary = g_strdup_printf (C_("SummaryWithLocation", "%s (%s)"), my_summary ? my_summary : "", location);
3719 
3720 		g_free (my_summary);
3721 	} else {
3722 		summary = my_summary;
3723 	}
3724 
3725 	return summary;
3726 }
3727 
3728 static void
e_week_view_on_text_item_notify_text_width(GObject * etext,GParamSpec * param,gpointer user_data)3729 e_week_view_on_text_item_notify_text_width (GObject *etext,
3730 					    GParamSpec *param,
3731 					    gpointer user_data)
3732 {
3733 	EWeekView *week_view = user_data;
3734 	gint event_num = 0, span_num;
3735 
3736 	g_return_if_fail (E_IS_WEEK_VIEW (week_view));
3737 
3738 	if (!e_week_view_find_event_from_item (week_view, GNOME_CANVAS_ITEM (etext), &event_num, &span_num))
3739 		return;
3740 
3741 	e_week_view_reshape_event_span (week_view, event_num, span_num);
3742 }
3743 
3744 static void
e_week_view_reshape_event_span(EWeekView * week_view,gint event_num,gint span_num)3745 e_week_view_reshape_event_span (EWeekView *week_view,
3746                                 gint event_num,
3747                                 gint span_num)
3748 {
3749 	ECalendarView *cal_view;
3750 	ECalModel *model;
3751 	ESourceRegistry *registry;
3752 	EWeekViewEvent *event;
3753 	EWeekViewEventSpan *span;
3754 	gint span_x, span_y, span_w, num_icons, icons_width, time_width;
3755 	gint min_text_x, max_text_w, width;
3756 	gboolean show_icons = TRUE, use_max_width = FALSE;
3757 	gboolean one_day_event;
3758 	ECalComponent *comp;
3759 	gdouble text_x, text_y, text_w, text_h;
3760 	gchar *text, *end_of_line;
3761 	gint line_len, text_width;
3762 	PangoContext *pango_context;
3763 	PangoFontMetrics *font_metrics;
3764 	PangoLayout *layout;
3765 
3766 	cal_view = E_CALENDAR_VIEW (week_view);
3767 	model = e_calendar_view_get_model (cal_view);
3768 
3769 	registry = e_cal_model_get_registry (model);
3770 
3771 	if (!is_array_index_in_bounds (week_view->events, event_num))
3772 		return;
3773 
3774 	event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
3775 
3776 	if (!is_comp_data_valid (event))
3777 		return;
3778 
3779 	if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
3780 		return;
3781 
3782 	span = &g_array_index (week_view->spans, EWeekViewEventSpan,
3783 			       event->spans_index + span_num);
3784 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
3785 	if (!comp)
3786 		return;
3787 
3788 	one_day_event = e_week_view_is_one_day_event (week_view, event_num);
3789 
3790 	/* If the span will not be visible destroy the canvas items and
3791 	 * return. */
3792 	if (!e_week_view_get_span_position (week_view, event_num, span_num,
3793 					    &span_x, &span_y, &span_w)) {
3794 		if (span->background_item)
3795 			g_object_run_dispose (G_OBJECT (span->background_item));
3796 		if (span->text_item)
3797 			g_object_run_dispose (G_OBJECT (span->text_item));
3798 		span->background_item = NULL;
3799 		span->text_item = NULL;
3800 
3801 		g_object_unref (comp);
3802 		return;
3803 	}
3804 
3805 	/* Set up Pango prerequisites */
3806 	pango_context = gtk_widget_get_pango_context (GTK_WIDGET (week_view));
3807 	font_metrics = pango_context_get_metrics (
3808 		pango_context, NULL,
3809 		pango_context_get_language (pango_context));
3810 	layout = pango_layout_new (pango_context);
3811 
3812 	/* If we are editing a long event we don't show the icons and the EText
3813 	 * item uses the maximum width available. */
3814 	if (!one_day_event && week_view->editing_event_num == event_num
3815 	    && week_view->editing_span_num == span_num) {
3816 		show_icons = FALSE;
3817 		use_max_width = TRUE;
3818 	} else if (e_week_view_get_multi_week_view (week_view)) {
3819 		show_icons = e_week_view_get_show_icons_month_view (week_view);
3820 	}
3821 
3822 	/* Calculate how many icons we need to show. */
3823 	num_icons = 0;
3824 	if (show_icons) {
3825 		if (e_cal_component_has_alarms (comp))
3826 			num_icons++;
3827 		if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp))
3828 			num_icons++;
3829 		if (e_cal_component_has_attachments (comp))
3830 			num_icons++;
3831 		if (e_cal_component_has_attendees (comp))
3832 			num_icons++;
3833 		if (event->different_timezone)
3834 			num_icons++;
3835 		num_icons += cal_comp_util_get_n_icons (comp, NULL);
3836 	}
3837 
3838 	/* Create the background canvas item if necessary. */
3839 	if (!span->background_item) {
3840 		span->background_item =
3841 			gnome_canvas_item_new (
3842 				GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root),
3843 				e_week_view_event_item_get_type (),
3844 				NULL);
3845 	}
3846 
3847 	g_object_set_data ((GObject *) span->background_item, "event-num", GINT_TO_POINTER (event_num));
3848 	g_signal_connect (
3849 		span->background_item, "event",
3850 		G_CALLBACK (tooltip_event_cb), week_view);
3851 
3852 	gnome_canvas_item_set (
3853 		span->background_item,
3854 		"event_num", event_num,
3855 		"span_num", span_num,
3856 		NULL);
3857 
3858 	/* Create the text item if necessary. */
3859 	if (!span->text_item) {
3860 		gchar *summary;
3861 		GdkColor color;
3862 
3863 		color = e_week_view_get_text_color (week_view, event);
3864 		summary = dup_comp_summary (event->comp_data->client, event->comp_data->icalcomp);
3865 
3866 		span->text_item =
3867 			gnome_canvas_item_new (
3868 				GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root),
3869 				e_text_get_type (),
3870 				"clip", TRUE,
3871 				"max_lines", 1,
3872 				"editable", TRUE,
3873 				"text", summary ? summary : "",
3874 				"use_ellipsis", TRUE,
3875 				"fill_color_gdk", &color,
3876 				"im_context", E_CANVAS (week_view->main_canvas)->im_context,
3877 				NULL);
3878 
3879 		g_free (summary);
3880 
3881 		if (e_cal_util_component_has_attendee (event->comp_data->icalcomp))
3882 			set_style_from_attendee (event, span, registry);
3883 		else if (i_cal_component_get_status (event->comp_data->icalcomp) == I_CAL_STATUS_CANCELLED)
3884 			gnome_canvas_item_set (span->text_item, "strikeout", TRUE, NULL);
3885 
3886 		g_signal_connect (
3887 			span->text_item, "event",
3888 			G_CALLBACK (e_week_view_on_text_item_event), week_view);
3889 		g_signal_connect (
3890 			span->text_item, "notify::text-width",
3891 			G_CALLBACK (e_week_view_on_text_item_notify_text_width), week_view);
3892 		g_signal_emit_by_name (
3893 			G_OBJECT (week_view),
3894 			"event_added", event);
3895 
3896 	}
3897 
3898 	g_object_set_data (G_OBJECT (span->text_item), "event-num", GINT_TO_POINTER (event_num));
3899 
3900 	/* Calculate the position of the text item.
3901 	 * For events < 1 day it starts after the times & icons and ends at the
3902 	 * right edge of the span.
3903 	 * For events >= 1 day we need to determine whether times are shown at
3904 	 * the start and end of the span, then try to center the text item with
3905 	 * the icons in the middle, but making sure we don't go over the times.
3906 	*/
3907 
3908 	/* Calculate the space necessary to display a time, e.g. "13:00". */
3909 	time_width = e_week_view_get_time_string_width (week_view);
3910 
3911 	/* Calculate the space needed for the icons. */
3912 	if (num_icons > 0) {
3913 		icons_width = (E_WEEK_VIEW_ICON_WIDTH + E_WEEK_VIEW_ICON_X_PAD)
3914 			* num_icons - E_WEEK_VIEW_ICON_X_PAD + E_WEEK_VIEW_ICON_R_PAD;
3915 	} else {
3916 		icons_width = 0;
3917 	}
3918 
3919 	/* The y position and height are the same for both event types. */
3920 	text_y = span_y + E_WEEK_VIEW_EVENT_BORDER_HEIGHT
3921 		+ E_WEEK_VIEW_EVENT_TEXT_Y_PAD;
3922 
3923 	text_h =
3924 		PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
3925 		PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
3926 
3927 	if (one_day_event) {
3928 		gboolean hide_end_time;
3929 
3930 		hide_end_time = event->start_minute == event->end_minute;
3931 
3932 		/* Note that 1-day events don't have a border. Although we
3933 		 * still use the border height to position the events
3934 		 * vertically so they still line up neatly (see above),
3935 		 * we don't use the border width or edge padding at all. */
3936 		text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD;
3937 
3938 		switch (week_view->time_format) {
3939 		case E_WEEK_VIEW_TIME_BOTH_SMALL_MIN:
3940 		case E_WEEK_VIEW_TIME_BOTH:
3941 			/* These have 2 time strings with a small space between
3942 			 * them and some space before the EText item. */
3943 			text_x += time_width * (hide_end_time ? 1 : 2)
3944 				+ (hide_end_time ? 0 : E_WEEK_VIEW_EVENT_TIME_SPACING)
3945 				+ E_WEEK_VIEW_EVENT_TIME_X_PAD;
3946 			break;
3947 		case E_WEEK_VIEW_TIME_START_SMALL_MIN:
3948 		case E_WEEK_VIEW_TIME_START:
3949 			/* These have just 1 time string with some space
3950 			 * before the EText item. */
3951 			text_x += time_width + E_WEEK_VIEW_EVENT_TIME_X_PAD;
3952 			break;
3953 		case E_WEEK_VIEW_TIME_NONE:
3954 			break;
3955 		}
3956 
3957 		/* The icons_width includes space on the right of the icons. */
3958 		text_x += icons_width;
3959 
3960 		/* The width of the EText item extends right to the edge of the
3961 		 * event, just inside the border. */
3962 		text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD - text_x;
3963 
3964 	} else {
3965 		if (use_max_width) {
3966 			/* When we are editing the event we use all the
3967 			 * available width. */
3968 			text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD
3969 				+ E_WEEK_VIEW_EVENT_BORDER_WIDTH
3970 				+ E_WEEK_VIEW_EVENT_EDGE_X_PAD;
3971 			text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD
3972 				- E_WEEK_VIEW_EVENT_BORDER_WIDTH
3973 				- E_WEEK_VIEW_EVENT_EDGE_X_PAD - text_x;
3974 		} else {
3975 			gdouble item_text_width = 0.0;
3976 
3977 			g_object_get (span->text_item, "text-width", &item_text_width, NULL);
3978 			text_width = (gint) item_text_width;
3979 
3980 			if (text_width <= 0) {
3981 				text = NULL;
3982 				/* Get the width of the text of the event. This is a
3983 				 * bit of a hack. It would be better if EText could
3984 				 * tell us this. */
3985 				g_object_get (span->text_item, "text", &text, NULL);
3986 				text_width = 0;
3987 				if (text) {
3988 					/* It should only have one line of text in it.
3989 					 * I'm not sure we need this any more. */
3990 					end_of_line = strchr (text, '\n');
3991 					if (end_of_line)
3992 						line_len = end_of_line - text;
3993 					else
3994 						line_len = strlen (text);
3995 
3996 					pango_layout_set_text (layout, text, line_len);
3997 					pango_layout_get_pixel_size (layout, &text_width, NULL);
3998 					g_free (text);
3999 				}
4000 			}
4001 
4002 			/* Add on the width of the icons and find the default
4003 			 * position, which centers the icons + text. */
4004 			width = text_width + icons_width;
4005 			text_x = span_x + (span_w - width) / 2;
4006 
4007 			/* Now calculate the left-most valid position, and make
4008 			 * sure we don't go to the left of that. */
4009 			min_text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD
4010 				+ E_WEEK_VIEW_EVENT_BORDER_WIDTH
4011 				+ E_WEEK_VIEW_EVENT_EDGE_X_PAD;
4012 			/* See if we will want to display the start time, and
4013 			 * if so take that into account. */
4014 			if (event->start > week_view->day_starts[span->start_day])
4015 				min_text_x += time_width
4016 					+ E_WEEK_VIEW_EVENT_TIME_X_PAD;
4017 
4018 			/* Now make sure we don't go to the left of the minimum
4019 			 * position. */
4020 			text_x = MAX (text_x, min_text_x);
4021 
4022 			/* Now calculate the largest valid width, using the
4023 			 * calculated x position, and make sure we don't
4024 			 * exceed that. */
4025 			max_text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD
4026 				- E_WEEK_VIEW_EVENT_BORDER_WIDTH
4027 				- E_WEEK_VIEW_EVENT_EDGE_X_PAD - text_x;
4028 			if (event->end < week_view->day_starts[span->start_day
4029 							      + span->num_days])
4030 				max_text_w -= time_width
4031 					+ E_WEEK_VIEW_EVENT_TIME_X_PAD;
4032 
4033 			text_w = MIN (width, max_text_w);
4034 
4035 			/* Now take out the space for the icons. */
4036 			text_x += icons_width;
4037 			text_w -= icons_width;
4038 		}
4039 	}
4040 
4041 	/* Make sure we don't try to use a negative width. */
4042 	text_w = MAX (text_w, 0);
4043 
4044 	gnome_canvas_item_set (
4045 		span->text_item,
4046 		"clip_width", (gdouble) text_w,
4047 		"clip_height", (gdouble) text_h,
4048 		NULL);
4049 	e_canvas_item_move_absolute (span->text_item, text_x, text_y);
4050 	gnome_canvas_item_request_update (span->background_item);
4051 
4052 	g_object_unref (comp);
4053 	g_object_unref (layout);
4054 	pango_font_metrics_unref (font_metrics);
4055 }
4056 
4057 gboolean
e_week_view_start_editing_event(EWeekView * week_view,gint event_num,gint span_num,gchar * initial_text)4058 e_week_view_start_editing_event (EWeekView *week_view,
4059                                  gint event_num,
4060                                  gint span_num,
4061                                  gchar *initial_text)
4062 {
4063 	EWeekViewEvent *event;
4064 	EWeekViewEventSpan *span;
4065 	ETextEventProcessor *event_processor = NULL;
4066 	ETextEventProcessorCommand command;
4067 	ECalModelComponent *comp_data;
4068 	const gchar *summary;
4069 
4070 	/* If we are already editing the event, just return. */
4071 	if (event_num == week_view->editing_event_num
4072 	    && span_num == week_view->editing_span_num)
4073 		return TRUE;
4074 
4075 	if (!is_array_index_in_bounds (week_view->events, event_num))
4076 		return FALSE;
4077 
4078 	event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4079 
4080 	if (!is_comp_data_valid (event))
4081 		return FALSE;
4082 
4083 	if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4084 		return FALSE;
4085 
4086 	span = &g_array_index (week_view->spans, EWeekViewEventSpan,
4087 			       event->spans_index + span_num);
4088 
4089 	if (e_client_is_readonly (E_CLIENT (event->comp_data->client)) ||
4090 	    (!initial_text && !e_calendar_view_get_allow_direct_summary_edit (E_CALENDAR_VIEW (week_view))))
4091 		return FALSE;
4092 
4093 	/* If the event is not shown, don't try to edit it. */
4094 	if (!span->text_item)
4095 		return FALSE;
4096 
4097 	if (week_view->editing_event_num >= 0) {
4098 		EWeekViewEvent *editing;
4099 
4100 		if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
4101 			return FALSE;
4102 
4103 		editing = &g_array_index (week_view->events, EWeekViewEvent, week_view->editing_event_num);
4104 
4105 		/* do not change to other part of same component - the event is spread into more days */
4106 		if (editing && editing->comp_data == event->comp_data)
4107 			return FALSE;
4108 	}
4109 
4110 	summary = i_cal_component_get_summary (event->comp_data->icalcomp);
4111 	if (!summary)
4112 		summary = "";
4113 
4114 	gnome_canvas_item_set (
4115 		span->text_item,
4116 		"text", initial_text ? initial_text : summary,
4117 		NULL);
4118 
4119 	/* Save the comp_data value because we use that as our invariant */
4120 	comp_data = event->comp_data;
4121 
4122 	e_canvas_item_grab_focus (span->text_item, TRUE);
4123 
4124 	/* If the above focus caused things to redraw, then find the
4125 	 * the event and the span again */
4126 	if (event_num < week_view->events->len)
4127 		event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4128 
4129 	if (event_num >= week_view->events->len || event->comp_data != comp_data) {
4130 		/* When got in because of other comp_data, then be sure we go through all events */
4131 		event_num = week_view->events->len;
4132 
4133 		/* Unfocussing can cause a removal but not a new
4134 		 * addition so just run backwards through the
4135 		 * events */
4136 		for (event_num--; event_num >= 0; event_num--) {
4137 			event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4138 			if (event->comp_data == comp_data)
4139 				break;
4140 		}
4141 		g_return_val_if_fail (event_num >= 0, FALSE);
4142 	}
4143 
4144 	if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4145 		return FALSE;
4146 
4147 	span = &g_array_index (week_view->spans, EWeekViewEventSpan,  event->spans_index + span_num);
4148 
4149 	/* Try to move the cursor to the end of the text. */
4150 	g_object_get (span->text_item, "event_processor", &event_processor, NULL);
4151 	if (event_processor) {
4152 		command.action = E_TEP_MOVE;
4153 		command.position = E_TEP_END_OF_BUFFER;
4154 		g_signal_emit_by_name (
4155 			event_processor,
4156 			"command", &command);
4157 	}
4158 	return TRUE;
4159 }
4160 
4161 /* This stops any current edit. */
4162 void
e_week_view_stop_editing_event(EWeekView * week_view)4163 e_week_view_stop_editing_event (EWeekView *week_view)
4164 {
4165 	GtkWidget *toplevel;
4166 
4167 	/* Check we are editing an event. */
4168 	if (week_view->editing_event_num == -1)
4169 		return;
4170 
4171 	/* Set focus to the toplevel so the item loses focus. */
4172 	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (week_view));
4173 	if (toplevel && GTK_IS_WINDOW (toplevel))
4174 		gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
4175 }
4176 
4177 /* Cancels the current edition by resetting the appointment's text to its original value */
4178 static void
cancel_editing(EWeekView * week_view)4179 cancel_editing (EWeekView *week_view)
4180 {
4181 	gint event_num, span_num;
4182 	EWeekViewEvent *event;
4183 	EWeekViewEventSpan *span;
4184 	const gchar *summary;
4185 
4186 	event_num = week_view->editing_event_num;
4187 	span_num = week_view->editing_span_num;
4188 
4189 	g_return_if_fail (event_num != -1);
4190 
4191 	if (!is_array_index_in_bounds (week_view->events, event_num))
4192 		return;
4193 
4194 	event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4195 
4196 	if (!is_comp_data_valid (event))
4197 		return;
4198 
4199 	if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4200 		return;
4201 
4202 	span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num);
4203 
4204 	/* Reset the text to what was in the component */
4205 
4206 	summary = i_cal_component_get_summary (event->comp_data->icalcomp);
4207 	g_object_set (span->text_item, "text", summary ? summary : "", NULL);
4208 
4209 	/* Stop editing */
4210 	e_week_view_stop_editing_event (week_view);
4211 }
4212 
4213 static gboolean
e_week_view_on_text_item_event(GnomeCanvasItem * item,GdkEvent * gdk_event,EWeekView * week_view)4214 e_week_view_on_text_item_event (GnomeCanvasItem *item,
4215                                 GdkEvent *gdk_event,
4216                                 EWeekView *week_view)
4217 {
4218 	EWeekViewEvent *event;
4219 	gint event_num, span_num;
4220 	gint nevent;
4221 	EWeekViewEvent *pevent;
4222 	guint event_button = 0;
4223 	guint event_keyval = 0;
4224 	gdouble event_x_root = 0;
4225 	gdouble event_y_root = 0;
4226 
4227 	e_week_view_check_layout (week_view);
4228 
4229 	nevent = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "event-num"));
4230 	pevent = tooltip_get_view_event (week_view, -1, nevent);
4231 
4232 	switch (gdk_event->type) {
4233 	case GDK_KEY_PRESS:
4234 		tooltip_destroy (week_view, item);
4235 		gdk_event_get_keyval (gdk_event, &event_keyval);
4236 
4237 		if (!E_TEXT (item)->preedit_len && (event_keyval == GDK_KEY_Return || event_keyval == GDK_KEY_KP_Enter)) {
4238 			/* We set the keyboard focus to the EDayView, so the
4239 			 * EText item loses it and stops the edit. */
4240 			gtk_widget_grab_focus (GTK_WIDGET (week_view));
4241 
4242 			/* Stop the signal last or we will also stop any
4243 			 * other events getting to the EText item. */
4244 			g_signal_stop_emission_by_name (item, "event");
4245 			return TRUE;
4246 		} else if (event_keyval == GDK_KEY_Escape) {
4247 			cancel_editing (week_view);
4248 			g_signal_stop_emission_by_name (item, "event");
4249 			/* focus should go to week view when stop editing */
4250 			gtk_widget_grab_focus (GTK_WIDGET (week_view));
4251 			return TRUE;
4252 		}
4253 		break;
4254 	case GDK_2BUTTON_PRESS:
4255 		if (!e_week_view_find_event_from_item (week_view, item,
4256 						       &event_num, &span_num))
4257 			return FALSE;
4258 
4259 		if (!is_array_index_in_bounds (week_view->events, event_num))
4260 			return FALSE;
4261 
4262 		event = &g_array_index (week_view->events, EWeekViewEvent,
4263 					event_num);
4264 
4265 		if (!is_comp_data_valid (event))
4266 			return FALSE;
4267 
4268 		/* if we started to editing new item on the canvas, then do not open editing dialog until it's saved,
4269 		 * because the save of the event recalculates event numbers and you can edit different one */
4270 		if (event->comp_data->is_new_component)
4271 			return TRUE;
4272 
4273 		e_calendar_view_edit_appointment (
4274 			E_CALENDAR_VIEW (week_view),
4275 			event->comp_data->client,
4276 			event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT);
4277 
4278 		g_signal_stop_emission_by_name (item, "event");
4279 		return TRUE;
4280 	case GDK_BUTTON_PRESS:
4281 		tooltip_destroy (week_view, item);
4282 		if (!e_week_view_find_event_from_item (week_view, item,
4283 						       &event_num, &span_num))
4284 			return FALSE;
4285 
4286 		gdk_event_get_button (gdk_event, &event_button);
4287 		if (event_button == 3) {
4288 			EWeekViewEvent *e;
4289 
4290 			if (E_TEXT (item)->editing) {
4291 				e_week_view_stop_editing_event (week_view);
4292 				gtk_widget_grab_focus (GTK_WIDGET (week_view));
4293 				return FALSE;
4294 			}
4295 
4296 			if (!is_array_index_in_bounds (week_view->events, event_num))
4297 				return FALSE;
4298 
4299 			e = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4300 
4301 			if (!gtk_widget_has_focus (GTK_WIDGET (week_view)))
4302 				gtk_widget_grab_focus (GTK_WIDGET (week_view));
4303 
4304 			e_week_view_set_selected_time_range_visible (week_view, e->start, e->end);
4305 
4306 			e_week_view_show_popup_menu (
4307 				week_view, gdk_event, event_num);
4308 
4309 			g_signal_stop_emission_by_name (
4310 				item->canvas, "button_press_event");
4311 			return TRUE;
4312 		}
4313 
4314 		if (event_button != 3) {
4315 			week_view->pressed_event_num = event_num;
4316 			week_view->pressed_span_num = span_num;
4317 			e_week_view_set_popup_event (week_view, week_view->pressed_event_num);
4318 		}
4319 
4320 		/* Only let the EText handle the event while editing. */
4321 		if (!E_TEXT (item)->editing) {
4322 			gdouble event_x_win = 0;
4323 			gdouble event_y_win = 0;
4324 
4325 			g_signal_stop_emission_by_name (item, "event");
4326 
4327 			gdk_event_get_coords (
4328 				gdk_event, &event_x_win, &event_y_win);
4329 
4330 			week_view->drag_event_x = (gint) event_x_win;
4331 			week_view->drag_event_y = (gint) event_y_win;
4332 
4333 			/* FIXME: Remember the day offset from the start of
4334 			 * the event, for DnD. */
4335 
4336 			return TRUE;
4337 		}
4338 		break;
4339 	case GDK_BUTTON_RELEASE:
4340 		if (!E_TEXT (item)->editing) {
4341 			/* This shouldn't ever happen. */
4342 			if (!e_week_view_find_event_from_item (week_view,
4343 							       item,
4344 							       &event_num,
4345 							       &span_num))
4346 				return FALSE;
4347 
4348 			if (week_view->pressed_event_num != -1
4349 			    && week_view->pressed_event_num == event_num
4350 			    && week_view->pressed_span_num == span_num) {
4351 				e_week_view_start_editing_event (
4352 					week_view,
4353 					event_num,
4354 					span_num,
4355 					NULL);
4356 				week_view->pressed_event_num = -1;
4357 			}
4358 
4359 			/* Stop the signal last or we will also stop any
4360 			 * other events getting to the EText item. */
4361 			g_signal_stop_emission_by_name (item, "event");
4362 			return TRUE;
4363 		}
4364 		week_view->pressed_event_num = -1;
4365 		break;
4366 	case GDK_ENTER_NOTIFY:
4367 	{
4368 		ECalendarViewEventData *data;
4369 		gint nspan;
4370 
4371 		if (week_view->editing_event_num != -1
4372 		    || !e_week_view_find_event_from_item (week_view, item, &nevent, &nspan))
4373 			return FALSE;
4374 
4375 		g_object_set_data ((GObject *) item, "event-num", GINT_TO_POINTER (nevent));
4376 
4377 		pevent = tooltip_get_view_event (week_view, -1, nevent);
4378 		g_return_val_if_fail (pevent != NULL, FALSE);
4379 
4380 		data = g_malloc (sizeof (ECalendarViewEventData));
4381 
4382 		gdk_event_get_root_coords (
4383 			gdk_event, &event_x_root, &event_y_root);
4384 
4385 		pevent->x = (gint) event_x_root;
4386 		pevent->y = (gint) event_y_root;
4387 		pevent->tooltip = NULL;
4388 
4389 		data->cal_view = E_CALENDAR_VIEW (g_object_ref (week_view));
4390 		data->day = -1;
4391 		data->event_num = nevent;
4392 		data->get_view_event = (ECalendarViewEvent * (*)(ECalendarView *, int, gint)) tooltip_get_view_event;
4393 		pevent->timeout = e_named_timeout_add_full (
4394 			G_PRIORITY_DEFAULT, 500,
4395 			e_week_view_handle_tooltip_timeout,
4396 			data, e_week_view_destroy_tooltip_timeout_data);
4397 		g_object_set_data ((GObject *) week_view, "tooltip-timeout", GUINT_TO_POINTER (pevent->timeout));
4398 
4399 		return TRUE;
4400 	}
4401 	case GDK_LEAVE_NOTIFY:
4402 		tooltip_destroy (week_view, item);
4403 
4404 		return FALSE;
4405 	case GDK_MOTION_NOTIFY:
4406 		g_return_val_if_fail (pevent != NULL, FALSE);
4407 
4408 		gdk_event_get_root_coords (
4409 			gdk_event, &event_x_root, &event_y_root);
4410 
4411 		pevent->x = (gint) event_x_root;
4412 		pevent->y = (gint) event_y_root;
4413 		pevent->tooltip = (GtkWidget *) g_object_get_data (G_OBJECT (week_view), "tooltip-window");
4414 
4415 		if (pevent->tooltip) {
4416 			e_calendar_view_move_tip (pevent->tooltip, pevent->x, pevent->y);
4417 		}
4418 		return TRUE;
4419 	case GDK_FOCUS_CHANGE:
4420 		if (gdk_event->focus_change.in) {
4421 			e_week_view_on_editing_started (week_view, item);
4422 		} else {
4423 			e_week_view_on_editing_stopped (week_view, item);
4424 		}
4425 
4426 		return FALSE;
4427 	default:
4428 		break;
4429 	}
4430 
4431 	return FALSE;
4432 }
4433 
4434 static gboolean
e_week_view_event_move(ECalendarView * cal_view,ECalViewMoveDirection direction)4435 e_week_view_event_move (ECalendarView *cal_view,
4436                         ECalViewMoveDirection direction)
4437 {
4438 	EWeekViewEvent *event;
4439 	gint event_num, adjust_days, current_start_day, current_end_day;
4440 	time_t start_dt, end_dt;
4441 	ICalTime *start_time, *end_time;
4442 	EWeekView *week_view = E_WEEK_VIEW (cal_view);
4443 	gboolean is_all_day = FALSE;
4444 
4445 	event_num = week_view->editing_event_num;
4446 	adjust_days = 0;
4447 
4448 	/* If no item is being edited, just return. */
4449 	if (event_num == -1)
4450 		return FALSE;
4451 
4452 	if (!is_array_index_in_bounds (week_view->events, event_num))
4453 		return FALSE;
4454 
4455 	event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4456 
4457 	if (!is_comp_data_valid (event))
4458 		return FALSE;
4459 
4460 	end_dt = event->end;
4461 	start_time = i_cal_component_get_dtstart (event->comp_data->icalcomp);
4462 	end_time = i_cal_component_get_dtend (event->comp_data->icalcomp);
4463 
4464 	if (i_cal_time_is_date (start_time) && i_cal_time_is_date (end_time))
4465 		is_all_day = TRUE;
4466 
4467 	current_end_day = e_week_view_get_day_offset_of_event (week_view,end_dt);
4468 
4469 	switch (direction) {
4470 	case E_CAL_VIEW_MOVE_UP:
4471 		adjust_days = e_week_view_get_adjust_days_for_move_up (week_view,current_end_day);
4472 		break;
4473 	case E_CAL_VIEW_MOVE_DOWN:
4474 		adjust_days = e_week_view_get_adjust_days_for_move_down (week_view,current_end_day);
4475 		break;
4476 	case E_CAL_VIEW_MOVE_LEFT:
4477 		adjust_days = e_week_view_get_adjust_days_for_move_left (week_view,current_end_day);
4478 		break;
4479 	case E_CAL_VIEW_MOVE_RIGHT:
4480 		adjust_days = e_week_view_get_adjust_days_for_move_right (week_view,current_end_day);
4481 		break;
4482 	default:
4483 		break;
4484 	}
4485 
4486 	i_cal_time_adjust (start_time, adjust_days, 0, 0, 0);
4487 	i_cal_time_adjust (end_time, adjust_days, 0, 0, 0);
4488 	start_dt = i_cal_time_as_timet_with_zone (
4489 		start_time,
4490 		e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
4491 	end_dt = i_cal_time_as_timet_with_zone (
4492 		end_time,
4493 		e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
4494 
4495 	g_clear_object (&start_time);
4496 	g_clear_object (&end_time);
4497 
4498 	current_start_day = e_week_view_get_day_offset_of_event (week_view,start_dt);
4499 	current_end_day = e_week_view_get_day_offset_of_event (week_view,end_dt);
4500 	if (is_all_day)
4501 		current_end_day--;
4502 
4503 	if (current_start_day < 0)
4504 		return TRUE;
4505 
4506 	if (current_end_day >= e_week_view_get_weeks_shown (week_view) * 7)
4507 		return TRUE;
4508 
4509 	e_week_view_change_event_time (week_view, start_dt, end_dt, is_all_day);
4510 
4511 	return TRUE;
4512 }
4513 
4514 static gint
e_week_view_get_day_offset_of_event(EWeekView * week_view,time_t event_time)4515 e_week_view_get_day_offset_of_event (EWeekView *week_view,
4516                                      time_t event_time)
4517 {
4518 	time_t first_day = week_view->day_starts[0];
4519 
4520 	if (event_time - first_day < 0)
4521 		return -1;
4522 	else
4523 		return (event_time - first_day) / (24 * 60 * 60);
4524 }
4525 
4526 void
e_week_view_scroll_a_step(EWeekView * week_view,ECalViewMoveDirection direction)4527 e_week_view_scroll_a_step (EWeekView *week_view,
4528                            ECalViewMoveDirection direction)
4529 {
4530 	GtkAdjustment *adjustment;
4531 	GtkRange *range;
4532 	gdouble step_increment;
4533 	gdouble page_size;
4534 	gdouble new_value;
4535 	gdouble lower;
4536 	gdouble upper;
4537 	gdouble value;
4538 
4539 	range = GTK_RANGE (week_view->vscrollbar);
4540 	adjustment = gtk_range_get_adjustment (range);
4541 
4542 	step_increment = gtk_adjustment_get_step_increment (adjustment);
4543 	page_size = gtk_adjustment_get_page_size (adjustment);
4544 	lower = gtk_adjustment_get_lower (adjustment);
4545 	upper = gtk_adjustment_get_upper (adjustment);
4546 	value = gtk_adjustment_get_value (adjustment);
4547 
4548 	switch (direction) {
4549 		case E_CAL_VIEW_MOVE_UP:
4550 			new_value = value - step_increment;
4551 			break;
4552 		case E_CAL_VIEW_MOVE_DOWN:
4553 			new_value = value + step_increment;
4554 			break;
4555 		case E_CAL_VIEW_MOVE_PAGE_UP:
4556 			new_value = value - page_size;
4557 			break;
4558 		case E_CAL_VIEW_MOVE_PAGE_DOWN:
4559 			new_value = value + page_size;
4560 			break;
4561 		default:
4562 			return;
4563 	}
4564 
4565 	new_value = CLAMP (new_value, lower, upper - page_size);
4566 	gtk_adjustment_set_value (adjustment, new_value);
4567 }
4568 
4569 static void
e_week_view_change_event_time(EWeekView * week_view,time_t start_dt,time_t end_dt,gboolean is_all_day)4570 e_week_view_change_event_time (EWeekView *week_view,
4571                                time_t start_dt,
4572                                time_t end_dt,
4573                                gboolean is_all_day)
4574 {
4575 	EWeekViewEvent *event;
4576 	gint event_num;
4577 	ECalComponent *comp;
4578 	ECalComponentDateTime *date;
4579 	ECalClient *client;
4580 	ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
4581 	ICalTimezone *zone;
4582 
4583 	event_num = week_view->editing_event_num;
4584 
4585 	/* If no item is being edited, just return. */
4586 	if (event_num == -1)
4587 		return;
4588 
4589 	if (!is_array_index_in_bounds (week_view->events, event_num))
4590 		return;
4591 
4592 	event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4593 
4594 	if (!is_comp_data_valid (event))
4595 		return;
4596 
4597 	client = event->comp_data->client;
4598 
4599 	/* We use a temporary shallow copy of the ico since we don't want to
4600 	 * change the original ico here. Otherwise we would not detect that
4601 	 * the event's time had changed in the "update_event" callback. */
4602 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
4603 
4604 	zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view));
4605 
4606 	date = e_cal_component_datetime_new_take (i_cal_time_new_from_timet_with_zone (start_dt, is_all_day, zone),
4607 		zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
4608 	cal_comp_set_dtstart_with_oldzone (client, comp, date);
4609 	e_cal_component_datetime_free (date);
4610 
4611 	date = e_cal_component_datetime_new_take (i_cal_time_new_from_timet_with_zone (end_dt, is_all_day, zone),
4612 		zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
4613 	cal_comp_set_dtend_with_oldzone (client, comp, date);
4614 	e_cal_component_datetime_free (date);
4615 
4616 	e_cal_component_commit_sequence (comp);
4617 
4618 	g_clear_pointer (&week_view->last_edited_comp_string, g_free);
4619 
4620 	week_view->last_edited_comp_string = e_cal_component_get_as_string (comp);
4621 
4622 	if (e_cal_component_has_recurrences (comp)) {
4623 		if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
4624 			gtk_widget_queue_draw (week_view->main_canvas);
4625 			goto out;
4626 		}
4627 
4628 		if (mod == E_CAL_OBJ_MOD_THIS) {
4629 			e_cal_component_set_rdates (comp, NULL);
4630 			e_cal_component_set_rrules (comp, NULL);
4631 			e_cal_component_set_exdates (comp, NULL);
4632 			e_cal_component_set_exrules (comp, NULL);
4633 		}
4634 	} else if (e_cal_component_is_instance (comp))
4635 		mod = E_CAL_OBJ_MOD_THIS;
4636 
4637 	e_cal_component_commit_sequence (comp);
4638 
4639 	e_cal_ops_modify_component (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)),
4640 		client, e_cal_component_get_icalcomponent (comp),
4641 		mod, E_CAL_OPS_SEND_FLAG_ASK | E_CAL_OPS_SEND_FLAG_IS_NEW_COMPONENT);
4642 
4643 out:
4644 	g_object_unref (comp);
4645 }
4646 
4647 static void
e_week_view_on_editing_started(EWeekView * week_view,GnomeCanvasItem * item)4648 e_week_view_on_editing_started (EWeekView *week_view,
4649                                 GnomeCanvasItem *item)
4650 {
4651 	gint event_num = -1, span_num = -1;
4652 
4653 	if (!e_week_view_find_event_from_item (week_view, item,
4654 					       &event_num, &span_num))
4655 		return;
4656 
4657 	week_view->editing_event_num = event_num;
4658 	week_view->editing_span_num = span_num;
4659 
4660 	/* We need to reshape long events so the whole width is used while
4661 	 * editing. */
4662 	if (!e_week_view_is_one_day_event (week_view, event_num)) {
4663 		e_week_view_reshape_event_span (
4664 			week_view, event_num, span_num);
4665 	}
4666 
4667 	if (event_num != -1) {
4668 		EWeekViewEvent *event;
4669 		EWeekViewEventSpan *span;
4670 
4671 		if (is_array_index_in_bounds (week_view->events, event_num)) {
4672 			event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4673 
4674 			if (is_comp_data_valid (event) &&
4675 			    is_array_index_in_bounds (week_view->spans, event->spans_index + span_num)) {
4676 				const gchar *summary;
4677 
4678 				span = &g_array_index (week_view->spans, EWeekViewEventSpan,
4679 						       event->spans_index + span_num);
4680 
4681 				summary = i_cal_component_get_summary (event->comp_data->icalcomp);
4682 
4683 				gnome_canvas_item_set (
4684 					span->text_item,
4685 					"text", summary ? summary : "",
4686 					NULL);
4687 			}
4688 		}
4689 	}
4690 
4691 	g_signal_emit_by_name (week_view, "selection_changed");
4692 
4693 	g_object_notify (G_OBJECT (week_view), "is-editing");
4694 }
4695 
4696 static void
e_week_view_on_editing_stopped(EWeekView * week_view,GnomeCanvasItem * item)4697 e_week_view_on_editing_stopped (EWeekView *week_view,
4698                                 GnomeCanvasItem *item)
4699 {
4700 	gint event_num, span_num;
4701 	EWeekViewEvent *event;
4702 	EWeekViewEventSpan *span;
4703 	gchar *text = NULL;
4704 	ECalComponent *comp;
4705 	ECalComponentText *summary = NULL;
4706 	ECalClient *client;
4707 	const gchar *uid;
4708 	gboolean on_server;
4709 
4710 	/* Note: the item we are passed here isn't reliable, so we just stop
4711 	 * the edit of whatever item was being edited. We also receive this
4712 	 * event twice for some reason. */
4713 	event_num = week_view->editing_event_num;
4714 	span_num = week_view->editing_span_num;
4715 
4716 	/* If no item is being edited, just return. */
4717 	if (event_num == -1)
4718 		return;
4719 
4720 	if (!is_array_index_in_bounds (week_view->events, event_num))
4721 		return;
4722 
4723 	event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4724 
4725 	if (!is_comp_data_valid (event))
4726 		return;
4727 
4728 	if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4729 		return;
4730 
4731 	span = &g_array_index (week_view->spans, EWeekViewEventSpan,
4732 			       event->spans_index + span_num);
4733 
4734 	/* Reset the edit fields. */
4735 	week_view->editing_event_num = -1;
4736 
4737 	/* Check that the event is still valid. */
4738 	uid = i_cal_component_get_uid (event->comp_data->icalcomp);
4739 	if (!uid) {
4740 		g_object_notify (G_OBJECT (week_view), "is-editing");
4741 		return;
4742 	}
4743 
4744 	text = NULL;
4745 	g_object_set (span->text_item, "handle_popup", FALSE, NULL);
4746 	g_object_get (span->text_item, "text", &text, NULL);
4747 
4748 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
4749 	if (!comp) {
4750 		g_free (text);
4751 		g_object_notify (G_OBJECT (week_view), "is-editing");
4752 		return;
4753 	}
4754 
4755 	client = event->comp_data->client;
4756 	on_server = !event->comp_data->is_new_component;
4757 
4758 	if (string_is_empty (text) && !on_server) {
4759 		uid = e_cal_component_get_uid (comp);
4760 		g_signal_handlers_disconnect_by_func (item, e_week_view_on_text_item_event, week_view);
4761 		e_week_view_foreach_event_with_uid (week_view, uid,
4762 						    e_week_view_remove_event_cb, NULL);
4763 		week_view->event_destroyed = TRUE;
4764 		gtk_widget_queue_draw (week_view->main_canvas);
4765 		e_week_view_check_layout (week_view);
4766 		goto out;
4767 	}
4768 
4769 	/* Only update the summary if necessary. */
4770 	summary = e_cal_component_get_summary (comp);
4771 	if (summary && !g_strcmp0 (e_cal_component_text_get_value (summary), text)) {
4772 		gchar *summary_txt;
4773 
4774 		summary_txt = dup_comp_summary (event->comp_data->client, event->comp_data->icalcomp);
4775 		g_object_set (span->text_item, "text", summary_txt ? summary_txt : "", NULL);
4776 
4777 		g_free (summary_txt);
4778 
4779 		if (!e_week_view_is_one_day_event (week_view, event_num))
4780 			e_week_view_reshape_event_span (week_view, event_num, span_num);
4781 	} else if ((summary && e_cal_component_text_get_value (summary)) || !string_is_empty (text)) {
4782 		ICalComponent *icomp = e_cal_component_get_icalcomponent (comp);
4783 
4784 		e_cal_component_text_free (summary);
4785 		summary = e_cal_component_text_new (text, NULL);
4786 
4787 		e_cal_component_set_summary (comp, summary);
4788 		e_cal_component_commit_sequence (comp);
4789 
4790 		e_cal_component_text_free (summary);
4791 		summary = NULL;
4792 
4793 		if (!on_server) {
4794 			e_cal_ops_create_component (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), client, icomp,
4795 				e_calendar_view_component_created_cb, g_object_ref (week_view), g_object_unref);
4796 
4797 			/* we remove the object since we either got the update from the server or failed */
4798 			e_week_view_remove_event_cb (week_view, event_num, NULL);
4799 		} else {
4800 			ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
4801 
4802 			if (e_cal_component_has_recurrences (comp)) {
4803 				if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
4804 					goto out;
4805 				}
4806 
4807 				if (mod == E_CAL_OBJ_MOD_THIS) {
4808 					ECalComponentDateTime *dt;
4809 					ICalTime *tt = NULL;
4810 					gchar *tzid;
4811 
4812 					dt = e_cal_component_get_dtstart (comp);
4813 					if (dt)
4814 						tt = e_cal_component_datetime_get_value (dt);
4815 					if (dt && tt && i_cal_time_get_timezone (tt)) {
4816 						tt = i_cal_time_new_from_timet_with_zone (
4817 							event->comp_data->instance_start,
4818 							i_cal_time_is_date (tt),
4819 							i_cal_time_get_timezone (tt));
4820 					} else {
4821 						tt = i_cal_time_new_from_timet_with_zone (
4822 							event->comp_data->instance_start,
4823 							(dt && tt) ? i_cal_time_is_date (tt) : FALSE,
4824 							e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
4825 					}
4826 					tzid = dt ? g_strdup (e_cal_component_datetime_get_tzid (dt)) : NULL;
4827 					e_cal_component_datetime_free (dt);
4828 
4829 					dt = e_cal_component_datetime_new_take (tt, tzid);
4830 					e_cal_component_set_dtstart (comp, dt);
4831 					e_cal_component_datetime_free (dt);
4832 
4833 					dt = e_cal_component_get_dtend (comp);
4834 					if (dt)
4835 						tt = e_cal_component_datetime_get_value (dt);
4836 					if (dt && tt && i_cal_time_get_timezone (tt)) {
4837 						tt = i_cal_time_new_from_timet_with_zone (
4838 							event->comp_data->instance_end,
4839 							i_cal_time_is_date (tt),
4840 							i_cal_time_get_timezone (tt));
4841 					} else {
4842 						tt = i_cal_time_new_from_timet_with_zone (
4843 							event->comp_data->instance_end,
4844 							(dt && tt) ? i_cal_time_is_date (tt) : FALSE,
4845 							e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
4846 					}
4847 					tzid = dt ? g_strdup (e_cal_component_datetime_get_tzid (dt)) : NULL;
4848 					e_cal_component_datetime_free (dt);
4849 
4850 					dt = e_cal_component_datetime_new_take (tt, tzid);
4851 					e_cal_component_set_dtend (comp, dt);
4852 					e_cal_component_datetime_free (dt);
4853 
4854 					e_cal_component_set_rdates (comp, NULL);
4855 					e_cal_component_set_rrules (comp, NULL);
4856 					e_cal_component_set_exdates (comp, NULL);
4857 					e_cal_component_set_exrules (comp, NULL);
4858 				}
4859 			} else if (e_cal_component_is_instance (comp))
4860 				mod = E_CAL_OBJ_MOD_THIS;
4861 
4862 			e_cal_component_commit_sequence (comp);
4863 			e_cal_ops_modify_component (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)),
4864 				client, e_cal_component_get_icalcomponent (comp), mod, E_CAL_OPS_SEND_FLAG_ASK);
4865 		}
4866 	}
4867 
4868  out:
4869 
4870 	e_cal_component_text_free (summary);
4871 	g_free (text);
4872 	g_object_unref (comp);
4873 
4874 	g_signal_emit_by_name (week_view, "selection_changed");
4875 
4876 	g_object_notify (G_OBJECT (week_view), "is-editing");
4877 }
4878 
4879 gboolean
e_week_view_find_event_from_item(EWeekView * week_view,GnomeCanvasItem * item,gint * event_num_return,gint * span_num_return)4880 e_week_view_find_event_from_item (EWeekView *week_view,
4881                                   GnomeCanvasItem *item,
4882                                   gint *event_num_return,
4883                                   gint *span_num_return)
4884 {
4885 	EWeekViewEvent *event;
4886 	EWeekViewEventSpan *span;
4887 	gint event_num, span_num, num_events;
4888 
4889 	num_events = week_view->events->len;
4890 	for (event_num = 0; event_num < num_events; event_num++) {
4891 		event = &g_array_index (week_view->events, EWeekViewEvent,
4892 					event_num);
4893 		for (span_num = 0; span_num < event->num_spans; span_num++) {
4894 			if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4895 				continue;
4896 
4897 			span = &g_array_index (week_view->spans,
4898 					       EWeekViewEventSpan,
4899 					       event->spans_index + span_num);
4900 			if (span->text_item == item) {
4901 				*event_num_return = event_num;
4902 				*span_num_return = span_num;
4903 				return TRUE;
4904 			}
4905 		}
4906 	}
4907 
4908 	return FALSE;
4909 }
4910 
4911 /* Finds the index of the event with the given uid.
4912  * Returns TRUE if an event with the uid was found.
4913  * Note that for recurring events there may be several EWeekViewEvents, one
4914  * for each instance, all with the same iCalObject and uid. So only use this
4915  * function if you know the event doesn't recur or you are just checking to
4916  * see if any events with the uid exist. */
4917 static gboolean
e_week_view_find_event_from_uid(EWeekView * week_view,ECalClient * client,const gchar * uid,const gchar * rid,gint * event_num_return)4918 e_week_view_find_event_from_uid (EWeekView *week_view,
4919                                  ECalClient *client,
4920                                  const gchar *uid,
4921                                  const gchar *rid,
4922                                  gint *event_num_return)
4923 {
4924 	EWeekViewEvent *event;
4925 	gint event_num, num_events;
4926 
4927 	*event_num_return = -1;
4928 	if (!uid)
4929 		return FALSE;
4930 
4931 	num_events = week_view->events->len;
4932 	for (event_num = 0; event_num < num_events; event_num++) {
4933 		const gchar *u;
4934 		gchar *r = NULL;
4935 
4936 		event = &g_array_index (week_view->events, EWeekViewEvent,
4937 					event_num);
4938 
4939 		if (!is_comp_data_valid (event))
4940 			continue;
4941 
4942 		if (event->comp_data->client != client)
4943 			continue;
4944 
4945 		u = i_cal_component_get_uid (event->comp_data->icalcomp);
4946 		if (u && !strcmp (uid, u)) {
4947 			if (rid && *rid) {
4948 				r = e_cal_util_component_get_recurid_as_string (event->comp_data->icalcomp);
4949 				if (!r || !*r || strcmp (rid, r) != 0) {
4950 					g_free (r);
4951 					continue;
4952 				}
4953 
4954 				g_free (r);
4955 			}
4956 
4957 			*event_num_return = event_num;
4958 			return TRUE;
4959 		}
4960 	}
4961 
4962 	return FALSE;
4963 }
4964 
4965 gboolean
e_week_view_is_one_day_event(EWeekView * week_view,gint event_num)4966 e_week_view_is_one_day_event (EWeekView *week_view,
4967                               gint event_num)
4968 {
4969 	EWeekViewEvent *event;
4970 	EWeekViewEventSpan *span;
4971 
4972 	if (!is_array_index_in_bounds (week_view->events, event_num))
4973 		return FALSE;
4974 
4975 	event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4976 	if (event->num_spans != 1)
4977 		return FALSE;
4978 
4979 	if (!is_array_index_in_bounds (week_view->spans, event->spans_index))
4980 		return FALSE;
4981 
4982 	span = &g_array_index (week_view->spans, EWeekViewEventSpan,
4983 			       event->spans_index);
4984 
4985 	if (event->start == week_view->day_starts[span->start_day]
4986 	    && event->end == week_view->day_starts[span->start_day + 1])
4987 		return FALSE;
4988 
4989 	if (span->num_days == 1
4990 	    && event->start >= week_view->day_starts[span->start_day]
4991 	    && event->end <= week_view->day_starts[span->start_day + 1])
4992 		return TRUE;
4993 
4994 	return FALSE;
4995 }
4996 
4997 static void
e_week_view_cursor_key_up(EWeekView * week_view)4998 e_week_view_cursor_key_up (EWeekView *week_view)
4999 {
5000 	EWeekViewClass *week_view_class;
5001 
5002 	week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
5003 	g_return_if_fail (week_view_class->cursor_key_up != NULL);
5004 
5005 	week_view_class->cursor_key_up (week_view);
5006 }
5007 
5008 static void
e_week_view_cursor_key_down(EWeekView * week_view)5009 e_week_view_cursor_key_down (EWeekView *week_view)
5010 {
5011 	EWeekViewClass *week_view_class;
5012 
5013 	week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
5014 	g_return_if_fail (week_view_class->cursor_key_down != NULL);
5015 
5016 	week_view_class->cursor_key_down (week_view);
5017 }
5018 
5019 static void
e_week_view_cursor_key_left(EWeekView * week_view)5020 e_week_view_cursor_key_left (EWeekView *week_view)
5021 {
5022 	EWeekViewClass *week_view_class;
5023 
5024 	week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
5025 	g_return_if_fail (week_view_class->cursor_key_left != NULL);
5026 
5027 	week_view_class->cursor_key_left (week_view);
5028 }
5029 
5030 static void
e_week_view_cursor_key_right(EWeekView * week_view)5031 e_week_view_cursor_key_right (EWeekView *week_view)
5032 {
5033 	EWeekViewClass *week_view_class;
5034 
5035 	week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
5036 	g_return_if_fail (week_view_class->cursor_key_right != NULL);
5037 
5038 	week_view_class->cursor_key_right (week_view);
5039 }
5040 
5041 static gboolean
e_week_view_do_key_press(GtkWidget * widget,GdkEventKey * event)5042 e_week_view_do_key_press (GtkWidget *widget,
5043                           GdkEventKey *event)
5044 {
5045 	EWeekView *week_view;
5046 	gchar *initial_text;
5047 	guint keyval;
5048 	gboolean stop_emission;
5049 
5050 	g_return_val_if_fail (widget != NULL, FALSE);
5051 	g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
5052 	g_return_val_if_fail (event != NULL, FALSE);
5053 
5054 	week_view = E_WEEK_VIEW (widget);
5055 	keyval = event->keyval;
5056 
5057 	/* The Escape key aborts a resize operation. */
5058 #if 0
5059 	if (week_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
5060 		if (event->keyval == GDK_KEY_Escape) {
5061 			e_week_view_abort_resize (week_view, event->time);
5062 		}
5063 		return FALSE;
5064 	}
5065 #endif
5066 
5067 	/* Handle the cursor keys for moving the selection */
5068 	stop_emission = FALSE;
5069 	if (!(event->state & GDK_SHIFT_MASK)
5070 		&& !(event->state & GDK_MOD1_MASK)) {
5071 		stop_emission = TRUE;
5072 		switch (keyval) {
5073 		case GDK_KEY_Page_Up:
5074 			if (!e_week_view_get_multi_week_view (week_view))
5075 				e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_UP);
5076 			else
5077 				e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_PAGE_UP);
5078 			break;
5079 		case GDK_KEY_Page_Down:
5080 			if (!e_week_view_get_multi_week_view (week_view))
5081 				e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_DOWN);
5082 			else
5083 				e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_PAGE_DOWN);
5084 			break;
5085 		case GDK_KEY_Up:
5086 			e_week_view_cursor_key_up (week_view);
5087 			break;
5088 		case GDK_KEY_Down:
5089 			e_week_view_cursor_key_down (week_view);
5090 			break;
5091 		case GDK_KEY_Left:
5092 			e_week_view_cursor_key_left (week_view);
5093 			break;
5094 		case GDK_KEY_Right:
5095 			e_week_view_cursor_key_right (week_view);
5096 			break;
5097 		default:
5098 			stop_emission = FALSE;
5099 			break;
5100 		}
5101 	}
5102 	if (stop_emission)
5103 		return TRUE;
5104 
5105 	/*Navigation through days with arrow keys*/
5106 	if (((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK)
5107 		&&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
5108 		&&((event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK)) {
5109 		if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up)
5110 			return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_UP);
5111 		else if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
5112 			return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_DOWN);
5113 		else if (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left)
5114 			return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_LEFT);
5115 		else if (keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right)
5116 			return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_RIGHT);
5117 	}
5118 
5119 	if (week_view->selection_start_day == -1)
5120 		return FALSE;
5121 
5122 	/* We only want to start an edit with a return key or a simple
5123 	 * character. */
5124 	if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
5125 		initial_text = NULL;
5126 	} else if (((event->keyval >= 0x20) && (event->keyval <= 0xFF)
5127 		    && (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
5128 		   || (event->length == 0)
5129 		   || (event->keyval == GDK_KEY_Tab)
5130 		   || (event->keyval == GDK_KEY_Escape)
5131 		   || (event->keyval == GDK_KEY_Delete)
5132 		   || (event->keyval == GDK_KEY_KP_Delete)) {
5133 		return FALSE;
5134 	} else
5135 		initial_text = e_utf8_from_gtk_event_key (widget, event->keyval, event->string);
5136 
5137 	e_week_view_add_new_event_in_selected_range (week_view, initial_text, FALSE);
5138 
5139 	g_free (initial_text);
5140 
5141 	return TRUE;
5142 }
5143 
5144 static gint
e_week_view_get_adjust_days_for_move_up(EWeekView * week_view,gint current_day)5145 e_week_view_get_adjust_days_for_move_up (EWeekView *week_view,
5146                                          gint current_day)
5147 {
5148 	return e_week_view_get_multi_week_view (week_view) ? -7 : 0;
5149 }
5150 
5151 static gint
e_week_view_get_adjust_days_for_move_down(EWeekView * week_view,gint current_day)5152 e_week_view_get_adjust_days_for_move_down (EWeekView *week_view,
5153                                            gint current_day)
5154 {
5155 	return e_week_view_get_multi_week_view (week_view) ? 7 : 0;
5156 }
5157 
5158 static gint
e_week_view_get_adjust_days_for_move_left(EWeekView * week_view,gint current_day)5159 e_week_view_get_adjust_days_for_move_left (EWeekView *week_view,
5160                                            gint current_day)
5161 {
5162 	return -1;
5163 }
5164 
5165 static gint
e_week_view_get_adjust_days_for_move_right(EWeekView * week_view,gint current_day)5166 e_week_view_get_adjust_days_for_move_right (EWeekView *week_view,
5167                                             gint current_day)
5168 {
5169 	return 1;
5170 }
5171 
5172 void
e_week_view_show_popup_menu(EWeekView * week_view,GdkEvent * button_event,gint event_num)5173 e_week_view_show_popup_menu (EWeekView *week_view,
5174                              GdkEvent *button_event,
5175                              gint event_num)
5176 {
5177 	guint timeout;
5178 
5179 	timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
5180 	if (timeout) {
5181 		g_source_remove (timeout);
5182 		g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
5183 	}
5184 
5185 	e_week_view_set_popup_event (week_view, event_num);
5186 
5187 	e_calendar_view_popup_event (E_CALENDAR_VIEW (week_view), button_event);
5188 }
5189 
5190 void
e_week_view_jump_to_button_item(EWeekView * week_view,GnomeCanvasItem * item)5191 e_week_view_jump_to_button_item (EWeekView *week_view,
5192                                  GnomeCanvasItem *item)
5193 {
5194 	gint day;
5195 
5196 	for (day = 0; day < E_WEEK_VIEW_MAX_WEEKS * 7; ++day) {
5197 		if (item == week_view->jump_buttons[day]) {
5198 			e_calendar_view_move_view_range (E_CALENDAR_VIEW (week_view), E_CALENDAR_VIEW_MOVE_TO_EXACT_DAY, week_view->day_starts[day]);
5199 			return;
5200 		}
5201 	}
5202 }
5203 
5204 static gboolean
e_week_view_on_jump_button_event(GnomeCanvasItem * item,GdkEvent * event,EWeekView * week_view)5205 e_week_view_on_jump_button_event (GnomeCanvasItem *item,
5206                                   GdkEvent *event,
5207                                   EWeekView *week_view)
5208 {
5209 	gint day;
5210 
5211 	if (event->type == GDK_BUTTON_PRESS) {
5212 		e_week_view_jump_to_button_item (week_view, item);
5213 		return TRUE;
5214 	}
5215 	else if (event->type == GDK_KEY_PRESS) {
5216 		/* return, if Tab, Control or Alt is pressed */
5217 		if ((event->key.keyval == GDK_KEY_Tab) ||
5218 		    (event->key.state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
5219 			return FALSE;
5220 		/* with a return key or a simple character (from 0x20 to 0xff),
5221 		 * jump to the day
5222 		 */
5223 		if ((event->key.keyval == GDK_KEY_Return || event->key.keyval == GDK_KEY_KP_Enter) ||
5224 		    ((event->key.keyval >= 0x20) &&
5225 		     (event->key.keyval <= 0xFF))) {
5226 			e_week_view_jump_to_button_item (week_view, item);
5227 			return TRUE;
5228 		}
5229 	}
5230 	else if (event->type == GDK_FOCUS_CHANGE) {
5231 		GdkEventFocus *focus_event = (GdkEventFocus *) event;
5232 		GdkPixbuf *pixbuf = NULL;
5233 
5234 		for (day = 0; day < E_WEEK_VIEW_MAX_WEEKS * 7; day++) {
5235 			if (item == week_view->jump_buttons[day])
5236 				break;
5237 		}
5238 
5239 		if (day >= E_WEEK_VIEW_MAX_WEEKS * 7) {
5240 			g_warn_if_reached ();
5241 			return FALSE;
5242 		}
5243 
5244 		if (focus_event->in) {
5245 			week_view->focused_jump_button = day;
5246 			pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) jump_xpm_focused);
5247 			gnome_canvas_item_set (
5248 				week_view->jump_buttons[day],
5249 				"GnomeCanvasPixbuf::pixbuf",
5250 				pixbuf, NULL);
5251 		}
5252 		else {
5253 			week_view->focused_jump_button = E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS;
5254 			pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) jump_xpm);
5255 			gnome_canvas_item_set (
5256 				week_view->jump_buttons[day],
5257 				"GnomeCanvasPixbuf::pixbuf",
5258 				pixbuf, NULL);
5259 		}
5260 		if (pixbuf)
5261 			g_object_unref (pixbuf);
5262 	}
5263 
5264 	return FALSE;
5265 }
5266 
5267 /* Converts an hour from 0-23 to the preferred time format, and returns the
5268  * suffix to add and the width of it in the normal font. */
5269 void
e_week_view_convert_time_to_display(EWeekView * week_view,gint hour,gint * display_hour,const gchar ** suffix,gint * suffix_width)5270 e_week_view_convert_time_to_display (EWeekView *week_view,
5271                                      gint hour,
5272                                      gint *display_hour,
5273                                      const gchar **suffix,
5274                                      gint *suffix_width)
5275 {
5276 	ECalModel *model;
5277 
5278 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
5279 
5280 	/* Calculate the actual hour number to display. For 12-hour
5281 	 * format we convert 0-23 to 12-11am/12-11pm. */
5282 	*display_hour = hour;
5283 	if (e_cal_model_get_use_24_hour_format (model)) {
5284 		*suffix = "";
5285 		*suffix_width = 0;
5286 	} else {
5287 		if (hour < 12) {
5288 			*suffix = week_view->am_string;
5289 			*suffix_width = week_view->am_string_width;
5290 		} else {
5291 			*display_hour -= 12;
5292 			*suffix = week_view->pm_string;
5293 			*suffix_width = week_view->pm_string_width;
5294 		}
5295 
5296 		/* 12-hour uses 12:00 rather than 0:00. */
5297 		if (*display_hour == 0)
5298 			*display_hour = 12;
5299 	}
5300 }
5301 
5302 gint
e_week_view_get_time_string_width(EWeekView * week_view)5303 e_week_view_get_time_string_width (EWeekView *week_view)
5304 {
5305 	ECalModel *model;
5306 	gint time_width;
5307 
5308 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
5309 
5310 	if (week_view->use_small_font && week_view->small_font_desc)
5311 		time_width = week_view->digit_width * 2
5312 			+ week_view->small_digit_width * 2;
5313 	else
5314 		time_width = week_view->digit_width * 4
5315 			+ week_view->colon_width;
5316 
5317 	if (!e_cal_model_get_use_24_hour_format (model))
5318 		time_width += MAX (week_view->am_string_width,
5319 				   week_view->pm_string_width);
5320 
5321 	return time_width;
5322 }
5323 
5324 /* Queues a layout, unless one is already queued. */
5325 static void
e_week_view_queue_layout(EWeekView * week_view)5326 e_week_view_queue_layout (EWeekView *week_view)
5327 {
5328 	if (week_view->layout_timeout_id == 0) {
5329 		week_view->layout_timeout_id = e_named_timeout_add (
5330 			E_WEEK_VIEW_LAYOUT_TIMEOUT,
5331 			e_week_view_layout_timeout_cb, week_view);
5332 	}
5333 }
5334 
5335 /* Removes any queued layout. */
5336 static void
e_week_view_cancel_layout(EWeekView * week_view)5337 e_week_view_cancel_layout (EWeekView *week_view)
5338 {
5339 	if (week_view->layout_timeout_id != 0) {
5340 		g_source_remove (week_view->layout_timeout_id);
5341 		week_view->layout_timeout_id = 0;
5342 	}
5343 }
5344 
5345 static gboolean
e_week_view_layout_timeout_cb(gpointer data)5346 e_week_view_layout_timeout_cb (gpointer data)
5347 {
5348 	EWeekView *week_view = E_WEEK_VIEW (data);
5349 
5350 	gtk_widget_queue_draw (week_view->main_canvas);
5351 	e_week_view_check_layout (week_view);
5352 
5353 	week_view->layout_timeout_id = 0;
5354 	return FALSE;
5355 }
5356 
5357 /* Returns the number of selected events (0 or 1 at present). */
5358 gint
e_week_view_get_num_events_selected(EWeekView * week_view)5359 e_week_view_get_num_events_selected (EWeekView *week_view)
5360 {
5361 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), 0);
5362 
5363 	return (week_view->editing_event_num != -1) ? 1 : 0;
5364 }
5365 
5366 gboolean
e_week_view_is_jump_button_visible(EWeekView * week_view,gint day)5367 e_week_view_is_jump_button_visible (EWeekView *week_view,
5368                                     gint day)
5369 {
5370 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
5371 
5372 	if ((day >= 0) && (day < E_WEEK_VIEW_MAX_WEEKS * 7))
5373 		return week_view->jump_buttons[day]->flags & GNOME_CANVAS_ITEM_VISIBLE;
5374 	return FALSE;
5375 }
5376 
5377 gboolean
e_week_view_is_editing(EWeekView * week_view)5378 e_week_view_is_editing (EWeekView *week_view)
5379 {
5380 	g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
5381 
5382 	return week_view->editing_event_num != -1;
5383 }
5384