1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * EDayView - displays the Day & Work-Week views of the calendar.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors:
18  *      Damon Chaplin <damon@ximian.com>
19  *      Rodrigo Moya <rodrigo@ximian.com>
20  *
21  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22  */
23 
24 #include "evolution-config.h"
25 
26 #include "e-day-view.h"
27 
28 #include <math.h>
29 #include <time.h>
30 
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 
34 #include "libgnomecanvas/libgnomecanvas.h"
35 
36 #include "e-cal-dialogs.h"
37 #include "e-util/e-util.h"
38 
39 #include "calendar-config.h"
40 #include "comp-util.h"
41 #include "e-cal-ops.h"
42 #include "e-cal-model-calendar.h"
43 #include "e-day-view-layout.h"
44 #include "e-day-view-main-item.h"
45 #include "e-day-view-time-item.h"
46 #include "e-day-view-top-item.h"
47 #include "ea-calendar.h"
48 #include "itip-utils.h"
49 #include "misc.h"
50 #include "print.h"
51 #include "ea-day-view.h"
52 
53 #define E_DAY_VIEW_GET_PRIVATE(obj) \
54 	(G_TYPE_INSTANCE_GET_PRIVATE \
55 	((obj), E_TYPE_DAY_VIEW, EDayViewPrivate))
56 
57 /* The minimum amount of space wanted on each side of the date string. */
58 #define E_DAY_VIEW_DATE_X_PAD	4
59 
60 #define E_DAY_VIEW_LARGE_FONT_PTSIZE 18
61 #define E_DAY_VIEW_SMALL_FONT_PTSIZE 10
62 
63 /* The offset from the top/bottom of the canvas before auto-scrolling starts.*/
64 #define E_DAY_VIEW_AUTO_SCROLL_OFFSET	16
65 
66 /* The time between each auto-scroll, in milliseconds. */
67 #define E_DAY_VIEW_AUTO_SCROLL_TIMEOUT	50
68 
69 /* The number of timeouts we skip before we start scrolling. */
70 #define E_DAY_VIEW_AUTO_SCROLL_DELAY	5
71 
72 /* The amount we scroll the main canvas when the Page Up/Down keys are pressed,
73  * as a fraction of the page size. */
74 #define E_DAY_VIEW_PAGE_STEP		0.5
75 
76 /* The amount we scroll the main canvas when the mouse wheel buttons are
77  * pressed, as a fraction of the page size. */
78 #define E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE	0.25
79 
80 /* The timeout before we do a layout, so we don't do a layout for each event
81  * we get from the server. */
82 #define E_DAY_VIEW_LAYOUT_TIMEOUT	100
83 
84 /* How many rows can be shown at a top_canvas; there will be always + 2 for
85  * caption item and DnD space */
86 #define E_DAY_VIEW_MAX_ROWS_AT_TOP     6
87 
88 struct _EDayViewPrivate {
89 	ECalModel *model;
90 	gulong notify_work_day_monday_handler_id;
91 	gulong notify_work_day_tuesday_handler_id;
92 	gulong notify_work_day_wednesday_handler_id;
93 	gulong notify_work_day_thursday_handler_id;
94 	gulong notify_work_day_friday_handler_id;
95 	gulong notify_work_day_saturday_handler_id;
96 	gulong notify_work_day_sunday_handler_id;
97 	gulong notify_week_start_day_handler_id;
98 	gulong notify_work_day_start_hour_handler_id;
99 	gulong notify_work_day_start_minute_handler_id;
100 	gulong notify_work_day_end_hour_handler_id;
101 	gulong notify_work_day_end_minute_handler_id;
102 	gulong notify_work_day_start_mon_handler_id;
103 	gulong notify_work_day_end_mon_handler_id;
104 	gulong notify_work_day_start_tue_handler_id;
105 	gulong notify_work_day_end_tue_handler_id;
106 	gulong notify_work_day_start_wed_handler_id;
107 	gulong notify_work_day_end_wed_handler_id;
108 	gulong notify_work_day_start_thu_handler_id;
109 	gulong notify_work_day_end_thu_handler_id;
110 	gulong notify_work_day_start_fri_handler_id;
111 	gulong notify_work_day_end_fri_handler_id;
112 	gulong notify_work_day_start_sat_handler_id;
113 	gulong notify_work_day_end_sat_handler_id;
114 	gulong notify_work_day_start_sun_handler_id;
115 	gulong notify_work_day_end_sun_handler_id;
116 	gulong time_range_changed_handler_id;
117 	gulong model_row_changed_handler_id;
118 	gulong model_cell_changed_handler_id;
119 	gulong model_rows_inserted_handler_id;
120 	gulong comps_deleted_handler_id;
121 	gulong timezone_changed_handler_id;
122 
123 	/* "top_canvas" signal handlers */
124 	gulong top_canvas_button_press_event_handler_id;
125 	gulong top_canvas_button_release_event_handler_id;
126 	gulong top_canvas_scroll_event_handler_id;
127 	gulong top_canvas_motion_notify_event_handler_id;
128 	gulong top_canvas_drag_motion_handler_id;
129 	gulong top_canvas_drag_leave_handler_id;
130 	gulong top_canvas_drag_begin_handler_id;
131 	gulong top_canvas_drag_end_handler_id;
132 	gulong top_canvas_drag_data_get_handler_id;
133 	gulong top_canvas_drag_data_received_handler_id;
134 
135 	/* "main_canvas" signal handlers */
136 	gulong main_canvas_realize_handler_id;
137 	gulong main_canvas_button_press_event_handler_id;
138 	gulong main_canvas_button_release_event_handler_id;
139 	gulong main_canvas_scroll_event_handler_id;
140 	gulong main_canvas_motion_notify_event_handler_id;
141 	gulong main_canvas_drag_motion_handler_id;
142 	gulong main_canvas_drag_leave_handler_id;
143 	gulong main_canvas_drag_begin_handler_id;
144 	gulong main_canvas_drag_end_handler_id;
145 	gulong main_canvas_drag_data_get_handler_id;
146 	gulong main_canvas_drag_data_received_handler_id;
147 
148 	/* "time_canvas" signal handlers */
149 	gulong time_canvas_scroll_event_handler_id;
150 
151 	/* Whether we are showing the work-week view. */
152 	gboolean work_week_view;
153 
154 	/* The number of days we are shoing.  Usually 1 or 5, but can be
155 	 * up to E_DAY_VIEW_MAX_DAYS, e.g. when the user selects a range
156 	 * of days in the date navigator. */
157 	gint days_shown;
158 
159 	/* Work days.  Indices are based on GDateWeekday.
160 	 * The first element (G_DATE_BAD_WEEKDAY) is unused. */
161 	gboolean work_days[G_DATE_SUNDAY + 1];
162 
163 	/* Whether we show the Marcus Bains Line in the main
164 	 * canvas and time canvas, and the colors for each. */
165 	gboolean marcus_bains_show_line;
166 	gchar *marcus_bains_day_view_color;
167 	gchar *marcus_bains_time_bar_color;
168 
169 	GtkWidget *timezone_name_1_label; /* not referenced */
170 	GtkWidget *timezone_name_2_label; /* not referenced */
171 
172 	GdkDragContext *drag_context;
173 
174 	gboolean draw_flat_events;
175 };
176 
177 typedef struct {
178 	EDayView *day_view;
179 	ECalModelComponent *comp_data;
180 } AddEventData;
181 
182 /* Drag and Drop stuff. */
183 static GtkTargetEntry target_table[] = {
184 	{ (gchar *) "application/x-e-calendar-event", 0, 0 }
185 };
186 
187 static void e_day_view_set_colors (EDayView *day_view);
188 static gboolean e_day_view_update_scroll_regions (EDayView *day_view);
189 static gboolean e_day_view_get_next_tab_event (EDayView *day_view,
190 					       GtkDirectionType direction,
191 					       gint *day, gint *event_num);
192 static gboolean e_day_view_get_extreme_long_event (EDayView *day_view,
193 						   gboolean first,
194 						   gint *day_out,
195 						   gint *event_num_out);
196 static gboolean e_day_view_get_extreme_event (EDayView *day_view,
197 					      gint start_day,
198 					      gint end_day,
199 					      gboolean first,
200 					      gint *day_out,
201 					      gint *event_num_out);
202 static gboolean e_day_view_do_key_press (GtkWidget *widget,
203 					 GdkEventKey *event);
204 static void e_day_view_update_query (EDayView *day_view);
205 static void e_day_view_goto_start_of_work_day (EDayView *day_view);
206 static void e_day_view_goto_end_of_work_day (EDayView *day_view);
207 static void e_day_view_change_duration_to_start_of_work_day (EDayView *day_view);
208 static void e_day_view_change_duration_to_end_of_work_day (EDayView *day_view);
209 static void e_day_view_cursor_key_up_shifted (EDayView *day_view,
210 					      GdkEventKey *event);
211 static void e_day_view_cursor_key_down_shifted (EDayView *day_view,
212 						GdkEventKey *event);
213 static void e_day_view_cursor_key_left_shifted (EDayView *day_view,
214 						GdkEventKey *event);
215 static void e_day_view_cursor_key_right_shifted (EDayView *day_view,
216 						 GdkEventKey *event);
217 static void e_day_view_cursor_key_up (EDayView *day_view,
218 				      GdkEventKey *event);
219 static void e_day_view_cursor_key_down (EDayView *day_view,
220 					GdkEventKey *event);
221 static void e_day_view_cursor_key_left (EDayView *day_view,
222 					GdkEventKey *event);
223 static void e_day_view_cursor_key_right (EDayView *day_view,
224 					 GdkEventKey *event);
225 static void e_day_view_scroll	(EDayView	*day_view,
226 				 gfloat		 pages_to_scroll);
227 
228 static void e_day_view_top_scroll (EDayView	*day_view,
229 				   gfloat	 pages_to_scroll);
230 
231 static void e_day_view_update_top_scroll (EDayView *day_view, gboolean scroll_to_top);
232 
233 static void e_day_view_on_canvas_realized (GtkWidget *widget,
234 					   EDayView *day_view);
235 
236 static gboolean e_day_view_on_top_canvas_button_press (GtkWidget *widget,
237 						       GdkEvent *button_event,
238 						       EDayView *day_view);
239 static gboolean e_day_view_on_top_canvas_button_release (GtkWidget *widget,
240 							 GdkEvent *button_event,
241 							 EDayView *day_view);
242 static gboolean e_day_view_on_top_canvas_motion (GtkWidget *widget,
243 						 GdkEventMotion *event,
244 						 EDayView *day_view);
245 
246 static gboolean e_day_view_on_main_canvas_button_press (GtkWidget *widget,
247 							GdkEvent *button_event,
248 							EDayView *day_view);
249 static gboolean e_day_view_on_main_canvas_button_release (GtkWidget *widget,
250 							  GdkEvent *button_event,
251 							  EDayView *day_view);
252 
253 static gboolean e_day_view_on_top_canvas_scroll (GtkWidget *widget,
254 						 GdkEventScroll *scroll,
255 						 EDayView *day_view);
256 
257 static gboolean e_day_view_on_main_canvas_scroll (GtkWidget *widget,
258 						  GdkEventScroll *scroll,
259 						  EDayView *day_view);
260 static gboolean e_day_view_on_time_canvas_scroll (GtkWidget *widget,
261 						  GdkEventScroll *scroll,
262 						  EDayView *day_view);
263 static gboolean e_day_view_on_main_canvas_motion (GtkWidget *widget,
264 						  GdkEventMotion *event,
265 						  EDayView *day_view);
266 static gboolean e_day_view_convert_event_coords (EDayView *day_view,
267 						 GdkEvent *event,
268 						 GdkWindow *window,
269 						 gint *x_return,
270 						 gint *y_return);
271 static void e_day_view_update_long_event_resize (EDayView *day_view,
272 						 gint day);
273 static void e_day_view_update_resize (EDayView *day_view,
274 				      gint row);
275 static void e_day_view_finish_long_event_resize (EDayView *day_view);
276 static void e_day_view_finish_resize (EDayView *day_view);
277 static void e_day_view_abort_resize (EDayView *day_view);
278 
279 static gboolean e_day_view_on_long_event_button_press (EDayView		*day_view,
280 						       gint		 event_num,
281 						       GdkEvent *button_event,
282 						       ECalendarViewPosition  pos,
283 						       gint		 event_x,
284 						       gint		 event_y);
285 static gboolean e_day_view_on_event_button_press (EDayView	 *day_view,
286 						  gint		  day,
287 						  gint		  event_num,
288 						  GdkEvent *button_event,
289 						  ECalendarViewPosition pos,
290 						  gint		  event_x,
291 						  gint		  event_y);
292 static void e_day_view_on_long_event_click (EDayView *day_view,
293 					    gint event_num,
294 					    GdkEvent *button_event,
295 					    ECalendarViewPosition pos,
296 					    gint	     event_x,
297 					    gint	     event_y);
298 static void e_day_view_on_event_click (EDayView *day_view,
299 				       gint day,
300 				       gint event_num,
301 				       GdkEvent *button_event,
302 				       ECalendarViewPosition pos,
303 				       gint		event_x,
304 				       gint		event_y);
305 static void e_day_view_on_event_double_click (EDayView *day_view,
306 					      gint day,
307 					      gint event_num);
308 static void e_day_view_on_event_right_click (EDayView *day_view,
309 					     GdkEvent *button_event,
310 					     gint day,
311 					     gint event_num);
312 static void e_day_view_show_popup_menu (EDayView *day_view,
313 					GdkEvent *button_event,
314 					gint day,
315 					gint event_num);
316 
317 static void e_day_view_recalc_day_starts (EDayView *day_view,
318 					  time_t start_time);
319 static void e_day_view_recalc_num_rows	(EDayView	*day_view);
320 static void e_day_view_recalc_cell_sizes	(EDayView	*day_view);
321 
322 static ECalendarViewPosition e_day_view_convert_position_in_top_canvas (EDayView *day_view,
323 								   gint x,
324 								   gint y,
325 								   gint *day_return,
326 								   gint *event_num_return);
327 static ECalendarViewPosition e_day_view_convert_position_in_main_canvas (EDayView *day_view,
328 								    gint x,
329 								    gint y,
330 								    gint *day_return,
331 								    gint *row_return,
332 								    gint *event_num_return);
333 static gboolean e_day_view_find_event_from_uid (EDayView *day_view,
334 						ECalClient *client,
335 						const gchar *uid,
336 						const gchar *rid,
337 						gint *day_return,
338 						gint *event_num_return);
339 
340 typedef gboolean (* EDayViewForeachEventCallback) (EDayView *day_view,
341 						   gint day,
342 						   gint event_num,
343 						   gpointer data);
344 
345 static void e_day_view_foreach_event		(EDayView	*day_view,
346 						 EDayViewForeachEventCallback callback,
347 						 gpointer	 data);
348 static void e_day_view_foreach_event_with_uid (EDayView *day_view,
349 					       const gchar *uid,
350 					       EDayViewForeachEventCallback callback,
351 					       gpointer data);
352 
353 static void e_day_view_free_events (EDayView *day_view);
354 static void e_day_view_free_event_array (EDayView *day_view,
355 					 GArray *array);
356 static void e_day_view_add_event (ESourceRegistry *registry,
357 				 ECalClient *client,
358 				 ECalComponent *comp,
359 				 time_t	  start,
360 				 time_t	  end,
361 				 gpointer data);
362 static void e_day_view_update_event_label (EDayView *day_view,
363 					   gint day,
364 					   gint event_num);
365 static void e_day_view_update_long_event_label (EDayView *day_view,
366 						gint event_num);
367 
368 static void e_day_view_reshape_long_events (EDayView *day_view);
369 static void e_day_view_reshape_long_event (EDayView *day_view,
370 					   gint event_num);
371 static void e_day_view_reshape_day_events (EDayView *day_view,
372 					   gint day);
373 static void e_day_view_reshape_day_event (EDayView *day_view,
374 					  gint	day,
375 					  gint	event_num);
376 static void e_day_view_reshape_main_canvas_resize_bars (EDayView *day_view);
377 
378 static void e_day_view_ensure_events_sorted (EDayView *day_view);
379 
380 static void e_day_view_start_editing_event (EDayView *day_view,
381 					    gint day,
382 					    gint event_num,
383 					    GdkEventKey *key_event);
384 static void e_day_view_stop_editing_event (EDayView *day_view);
385 static void cancel_editing (EDayView *day_view);
386 static gboolean e_day_view_on_text_item_event (GnomeCanvasItem *item,
387 					       GdkEvent *event,
388 					       EDayView *day_view);
389 static gboolean e_day_view_event_move (ECalendarView *cal_view, ECalViewMoveDirection direction);
390 static void e_day_view_change_event_time (EDayView *day_view, time_t start_dt,
391 time_t end_dt);
392 static void e_day_view_change_event_end_time_up (EDayView *day_view);
393 static void e_day_view_change_event_end_time_down (EDayView *day_view);
394 static void e_day_view_on_editing_started (EDayView *day_view,
395 					   GnomeCanvasItem *item);
396 static void e_day_view_on_editing_stopped (EDayView *day_view,
397 					   GnomeCanvasItem *item);
398 
399 static time_t e_day_view_convert_grid_position_to_time (EDayView *day_view,
400 							gint col,
401 							gint row);
402 static gboolean e_day_view_convert_time_to_grid_position (EDayView *day_view,
403 							  time_t time,
404 							  gint *col,
405 							  gint *row);
406 
407 static void e_day_view_start_auto_scroll (EDayView *day_view,
408 					  gboolean scroll_up);
409 static gboolean e_day_view_auto_scroll_handler (gpointer data);
410 
411 static gboolean e_day_view_on_top_canvas_drag_motion (GtkWidget      *widget,
412 						      GdkDragContext *context,
413 						      gint            x,
414 						      gint            y,
415 						      guint           time,
416 						      EDayView	  *day_view);
417 static void e_day_view_update_top_canvas_drag (EDayView *day_view,
418 					       gint day);
419 static void e_day_view_reshape_top_canvas_drag_item (EDayView *day_view);
420 static gboolean e_day_view_on_main_canvas_drag_motion (GtkWidget      *widget,
421 						       GdkDragContext *context,
422 						       gint            x,
423 						       gint            y,
424 						       guint           time,
425 						       EDayView	  *day_view);
426 static void e_day_view_reshape_main_canvas_drag_item (EDayView *day_view);
427 static void e_day_view_update_main_canvas_drag (EDayView *day_view,
428 						gint row,
429 						gint day);
430 static void e_day_view_on_top_canvas_drag_leave (GtkWidget      *widget,
431 						 GdkDragContext *context,
432 						 guint           time,
433 						 EDayView	     *day_view);
434 static void e_day_view_on_main_canvas_drag_leave (GtkWidget      *widget,
435 						  GdkDragContext *context,
436 						  guint           time,
437 						  EDayView	 *day_view);
438 static void e_day_view_on_drag_begin (GtkWidget      *widget,
439 				      GdkDragContext *context,
440 				      EDayView	   *day_view);
441 static void e_day_view_on_drag_end (GtkWidget      *widget,
442 				    GdkDragContext *context,
443 				    EDayView       *day_view);
444 static void e_day_view_on_drag_data_get (GtkWidget          *widget,
445 					 GdkDragContext     *context,
446 					 GtkSelectionData   *selection_data,
447 					 guint               info,
448 					 guint               time,
449 					 EDayView		*day_view);
450 static void e_day_view_on_top_canvas_drag_data_received (GtkWidget	*widget,
451 							 GdkDragContext *context,
452 							 gint            x,
453 							 gint            y,
454 							 GtkSelectionData *data,
455 							 guint           info,
456 							 guint           time,
457 							 EDayView	*day_view);
458 static void e_day_view_on_main_canvas_drag_data_received (GtkWidget      *widget,
459 							  GdkDragContext *context,
460 							  gint            x,
461 							  gint            y,
462 							  GtkSelectionData *data,
463 							  guint           info,
464 							  guint           time,
465 							  EDayView	 *day_view);
466 
467 static gboolean e_day_view_remove_event_cb (EDayView *day_view,
468 					    gint day,
469 					    gint event_num,
470 					    gpointer data);
471 static void e_day_view_normalize_selection (EDayView *day_view);
472 static gboolean e_day_view_set_show_times_cb	(EDayView	*day_view,
473 						 gint		 day,
474 						 gint		 event_num,
475 						 gpointer	 data);
476 static time_t e_day_view_find_work_week_start	(EDayView	*day_view,
477 						 time_t		 start_time);
478 static void e_day_view_recalc_work_week		(EDayView	*day_view);
479 static void e_day_view_recalc_work_week_days_shown	(EDayView	*day_view);
480 
481 static void e_day_view_precalc_visible_time_range (ECalendarView *cal_view,
482 						   time_t in_start_time,
483 						   time_t in_end_time,
484 						   time_t *out_start_time,
485 						   time_t *out_end_time);
486 static void e_day_view_queue_layout (EDayView *day_view);
487 static void e_day_view_cancel_layout (EDayView *day_view);
488 static gboolean e_day_view_layout_timeout_cb (gpointer data);
489 static void tooltip_destroy (EDayView *day_view, GnomeCanvasItem *item);
490 static EDayViewEvent *tooltip_get_view_event (EDayView *day_view, gint day, gint event_num);
491 
492 enum {
493 	PROP_0,
494 	PROP_DRAW_FLAT_EVENTS,
495 	PROP_MARCUS_BAINS_SHOW_LINE,
496 	PROP_MARCUS_BAINS_DAY_VIEW_COLOR,
497 	PROP_MARCUS_BAINS_TIME_BAR_COLOR,
498 	PROP_IS_EDITING
499 };
500 
G_DEFINE_TYPE(EDayView,e_day_view,E_TYPE_CALENDAR_VIEW)501 G_DEFINE_TYPE (EDayView, e_day_view, E_TYPE_CALENDAR_VIEW)
502 
503 static void
504 e_day_view_set_popup_event (EDayView *day_view,
505 			    gint day,
506 			    gint event_num)
507 {
508 	if (day_view->popup_event_day != day ||
509 	    day_view->popup_event_num != event_num) {
510 		day_view->popup_event_day = day;
511 		day_view->popup_event_num = event_num;
512 
513 		g_signal_emit_by_name (day_view, "selection-changed");
514 	}
515 }
516 
517 static void
day_view_notify_time_divisions_cb(EDayView * day_view)518 day_view_notify_time_divisions_cb (EDayView *day_view)
519 {
520 	gint day;
521 
522 	e_day_view_recalc_num_rows (day_view);
523 
524 	/* If we aren't visible, we'll sort it out later. */
525 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
526 		e_day_view_free_events (day_view);
527 		day_view->requires_update = TRUE;
528 		return;
529 	}
530 
531 	for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
532 		day_view->need_layout[day] = TRUE;
533 
534 	/* We need to update all the day event labels since the start & end
535 	 * times may or may not be on row boundaries any more. */
536 	e_day_view_foreach_event (day_view,
537 				  e_day_view_set_show_times_cb, NULL);
538 
539 	/* We must layout the events before updating the scroll region, since
540 	 * that will result in a redraw which would crash otherwise. */
541 	e_day_view_check_layout (day_view);
542 	gtk_widget_queue_draw (day_view->time_canvas);
543 	gtk_widget_queue_draw (day_view->main_canvas);
544 
545 	e_day_view_update_scroll_regions (day_view);
546 }
547 
548 static void
day_view_notify_week_start_day_cb(EDayView * day_view)549 day_view_notify_week_start_day_cb (EDayView *day_view)
550 {
551 	/* FIXME Write an EWorkWeekView subclass, like EMonthView. */
552 
553 	if (day_view->priv->work_week_view)
554 		e_day_view_recalc_work_week (day_view);
555 }
556 
557 static void
day_view_notify_work_day_cb(ECalModel * model,GParamSpec * pspec,EDayView * day_view)558 day_view_notify_work_day_cb (ECalModel *model,
559                              GParamSpec *pspec,
560                              EDayView *day_view)
561 {
562 	/* FIXME Write an EWorkWeekView subclass, like EMonthView. */
563 
564 	if (day_view->priv->work_week_view)
565 		e_day_view_recalc_work_week (day_view);
566 
567 	/* We have to do this, as the new working days may have no effect on
568 	 * the days shown, but we still want the background color to change. */
569 	gtk_widget_queue_draw (day_view->main_canvas);
570 }
571 
572 static void
e_day_view_get_work_day_range_for_day(EDayView * day_view,gint day,gint * start_hour,gint * start_minute,gint * end_hour,gint * end_minute)573 e_day_view_get_work_day_range_for_day (EDayView *day_view,
574 				       gint day,
575 				       gint *start_hour,
576 				       gint *start_minute,
577 				       gint *end_hour,
578 				       gint *end_minute)
579 {
580 	ECalModel *model;
581 
582 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
583 	g_return_if_fail (start_hour != NULL);
584 	g_return_if_fail (start_minute != NULL);
585 	g_return_if_fail (end_hour != NULL);
586 	g_return_if_fail (end_minute != NULL);
587 
588 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
589 
590 	if (day >= 0 && day < e_day_view_get_days_shown (day_view)) {
591 		GDateWeekday weekday;
592 		ICalTime *tt;
593 
594 		tt = i_cal_time_new_from_timet_with_zone (day_view->day_starts[day], FALSE,
595 			e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
596 
597 		switch (i_cal_time_day_of_week (tt)) {
598 			case 1:
599 				weekday = G_DATE_SUNDAY;
600 				break;
601 			case 2:
602 				weekday = G_DATE_MONDAY;
603 				break;
604 			case 3:
605 				weekday = G_DATE_TUESDAY;
606 				break;
607 			case 4:
608 				weekday = G_DATE_WEDNESDAY;
609 				break;
610 			case 5:
611 				weekday = G_DATE_THURSDAY;
612 				break;
613 			case 6:
614 				weekday = G_DATE_FRIDAY;
615 				break;
616 			case 7:
617 				weekday = G_DATE_SATURDAY;
618 				break;
619 			default:
620 				weekday = G_DATE_BAD_WEEKDAY;
621 				break;
622 		}
623 
624 		g_clear_object (&tt);
625 
626 		e_cal_model_get_work_day_range_for (model, weekday,
627 			start_hour, start_minute,
628 			end_hour, end_minute);
629 	} else {
630 		*start_hour = e_cal_model_get_work_day_start_hour (model);
631 		*start_minute = e_cal_model_get_work_day_start_minute (model);
632 		*end_hour = e_cal_model_get_work_day_end_hour (model);
633 		*end_minute = e_cal_model_get_work_day_end_minute (model);
634 	}
635 }
636 
637 static void
e_day_view_recalc_main_canvas_size(EDayView * day_view)638 e_day_view_recalc_main_canvas_size (EDayView *day_view)
639 {
640 	gint day, scroll_y;
641 	gboolean need_reshape;
642 
643 	/* Set the scroll region of the top canvas */
644 	e_day_view_update_top_scroll (day_view, TRUE);
645 
646 	need_reshape = e_day_view_update_scroll_regions (day_view);
647 
648 	e_day_view_recalc_cell_sizes (day_view);
649 
650 	/* Scroll to the start of the working day, if this is the initial
651 	 * allocation. */
652 	if (day_view->scroll_to_work_day) {
653 		gint work_day_start_hour;
654 		gint work_day_start_minute;
655 		gint work_day_end_hour;
656 		gint work_day_end_minute;
657 
658 		e_day_view_get_work_day_range_for_day (day_view, 0,
659 			&work_day_start_hour, &work_day_start_minute,
660 			&work_day_end_hour, &work_day_end_minute);
661 
662 		scroll_y = e_day_view_convert_time_to_position (
663 			day_view, work_day_start_hour, work_day_start_minute);
664 		gnome_canvas_scroll_to (
665 			GNOME_CANVAS (day_view->main_canvas), 0, scroll_y);
666 		day_view->scroll_to_work_day = FALSE;
667 	}
668 
669 	/* Flag that we need to reshape the events. Note that changes in height
670 	 * don't matter, since the rows are always the same height. */
671 	if (need_reshape) {
672 		day_view->long_events_need_reshape = TRUE;
673 		for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
674 			day_view->need_reshape[day] = TRUE;
675 
676 		e_day_view_check_layout (day_view);
677 	}
678 }
679 
680 static GdkColor
e_day_view_get_text_color(EDayView * day_view,EDayViewEvent * event)681 e_day_view_get_text_color (EDayView *day_view,
682                            EDayViewEvent *event)
683 {
684 	GdkColor color;
685 	GdkRGBA rgba;
686 
687 	if (is_comp_data_valid (event) &&
688 	    e_cal_model_get_rgba_for_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), event->comp_data, &rgba)) {
689 	} else {
690 		gdouble	cc = 65535.0;
691 
692 		rgba.red = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].red / cc;
693 		rgba.green = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].green / cc;
694 		rgba.blue = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].blue / cc;
695 		rgba.alpha = 1.0;
696 	}
697 
698 	rgba = e_utils_get_text_color_for_background (&rgba);
699 	e_rgba_to_color (&rgba, &color);
700 
701 	return color;
702 }
703 
704 /* Returns the selected time range. */
705 static gboolean
day_view_get_selected_time_range(ECalendarView * cal_view,time_t * start_time,time_t * end_time)706 day_view_get_selected_time_range (ECalendarView *cal_view,
707                                   time_t *start_time,
708                                   time_t *end_time)
709 {
710 	gint start_col, start_row, end_col, end_row;
711 	time_t start, end;
712 	EDayView *day_view = E_DAY_VIEW (cal_view);
713 
714 	start_col = day_view->selection_start_day;
715 	start_row = day_view->selection_start_row;
716 	end_col = day_view->selection_end_day;
717 	end_row = day_view->selection_end_row;
718 
719 	if (start_col == -1) {
720 		start_col = 0;
721 		start_row = 0;
722 		end_col = 0;
723 		end_row = 0;
724 	}
725 
726 	/* Check if the selection is only in the top canvas, in which case
727 	 * we can simply use the day_starts array. */
728 	if (day_view->selection_in_top_canvas) {
729 		start = day_view->day_starts[start_col];
730 		end = day_view->day_starts[end_col + 1];
731 	} else {
732 		/* Convert the start col + row into a time. */
733 		start = e_day_view_convert_grid_position_to_time (day_view, start_col, start_row);
734 		end = e_day_view_convert_grid_position_to_time (day_view, end_col, end_row + 1);
735 	}
736 
737 	if (start_time)
738 		*start_time = start;
739 
740 	if (end_time)
741 		*end_time = end;
742 
743 	return TRUE;
744 }
745 
746 typedef struct {
747 	EDayView *day_view;
748 	GdkEventKey *key_event;
749 	time_t dtstart, dtend;
750 	gboolean in_top_canvas;
751 	gboolean paste_clipboard;
752 } NewEventInRangeData;
753 
754 static void
new_event_in_rage_data_free(gpointer ptr)755 new_event_in_rage_data_free (gpointer ptr)
756 {
757 	NewEventInRangeData *ned = ptr;
758 
759 	if (ned) {
760 		g_clear_object (&ned->day_view);
761 		g_slice_free (GdkEventKey, ned->key_event);
762 		g_slice_free (NewEventInRangeData, ned);
763 	}
764 }
765 
766 static void
day_view_new_event_in_selected_range_cb(ECalModel * model,ECalClient * client,ICalComponent * default_component,gpointer user_data)767 day_view_new_event_in_selected_range_cb (ECalModel *model,
768 					 ECalClient *client,
769 					 ICalComponent *default_component,
770 					 gpointer user_data)
771 {
772 	NewEventInRangeData *ned = user_data;
773 	ECalComponent *comp = NULL;
774 	gint day, event_num;
775 	ECalComponentDateTime *start_dt, *end_dt;
776 	ICalTime *start_tt, *end_tt;
777 	const gchar *uid, *use_tzid;
778 	AddEventData add_event_data;
779 	ESourceRegistry *registry;
780 	ICalTimezone *zone;
781 
782 	g_return_if_fail (ned != NULL);
783 	g_return_if_fail (E_IS_CAL_MODEL (model));
784 	g_return_if_fail (E_IS_CAL_CLIENT (client));
785 	g_return_if_fail (default_component != NULL);
786 
787 	/* Check if the client is read only */
788 	if (e_client_is_readonly (E_CLIENT (client)))
789 		return;
790 
791 	registry = e_cal_model_get_registry (model);
792 	zone = e_cal_model_get_timezone (model);
793 	uid = i_cal_component_get_uid (default_component);
794 
795 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (default_component));
796 	g_return_if_fail (comp != NULL);
797 
798 	start_tt = i_cal_time_new_from_timet_with_zone (ned->dtstart, FALSE, zone);
799 	end_tt = i_cal_time_new_from_timet_with_zone (ned->dtend, FALSE, zone);
800 
801 	if (ned->in_top_canvas) {
802 		use_tzid = NULL;
803 		i_cal_time_set_is_date (start_tt, 1);
804 		i_cal_time_set_is_date (end_tt, 1);
805 
806 		/* Editor default in day/work-week view - top canvas */
807 		e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
808 	} else {
809 		use_tzid = i_cal_timezone_get_tzid (zone);
810 
811 		/* Editor default in day/work-week view - main canvas */
812 		e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
813 	}
814 
815 	start_dt = e_cal_component_datetime_new_take (start_tt, g_strdup (use_tzid));
816 	end_dt = e_cal_component_datetime_new_take (end_tt, g_strdup (use_tzid));
817 
818 	e_cal_component_set_dtstart (comp, start_dt);
819 	e_cal_component_set_dtend (comp, end_dt);
820 
821 	e_cal_component_datetime_free (start_dt);
822 	e_cal_component_datetime_free (end_dt);
823 
824 	/* We add the event locally and start editing it. We don't send it
825 	 * to the server until the user finishes editing it. */
826 	add_event_data.day_view = ned->day_view;
827 	add_event_data.comp_data = NULL;
828 	e_day_view_add_event (registry, client, comp, ned->dtstart, ned->dtend, &add_event_data);
829 	e_day_view_check_layout (ned->day_view);
830 	gtk_widget_queue_draw (ned->day_view->top_canvas);
831 	gtk_widget_queue_draw (ned->day_view->main_canvas);
832 
833 	if (!e_day_view_find_event_from_uid (ned->day_view, client, uid, NULL, &day, &event_num)) {
834 		g_warning ("Couldn't find event to start editing.\n");
835 	} else {
836 		e_day_view_start_editing_event (ned->day_view, day, event_num, ned->key_event);
837 
838 		if (ned->paste_clipboard) {
839 			EDayViewEvent *event;
840 
841 			g_clear_object (&comp);
842 
843 			if (ned->day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT) {
844 				if (!is_array_index_in_bounds (ned->day_view->long_events, ned->day_view->editing_event_num))
845 					goto out;
846 
847 				event = &g_array_index (ned->day_view->long_events,
848 							EDayViewEvent,
849 							ned->day_view->editing_event_num);
850 			} else {
851 				if (!is_array_index_in_bounds (ned->day_view->events[ned->day_view->editing_event_day], ned->day_view->editing_event_num))
852 					goto out;
853 
854 				event = &g_array_index (ned->day_view->events[ned->day_view->editing_event_day],
855 							EDayViewEvent,
856 							ned->day_view->editing_event_num);
857 			}
858 
859 			if (event->canvas_item &&
860 			    E_IS_TEXT (event->canvas_item) &&
861 			    E_TEXT (event->canvas_item)->editing) {
862 				e_text_paste_clipboard (E_TEXT (event->canvas_item));
863 			}
864 		}
865 	}
866 
867  out:
868 	g_clear_object (&comp);
869 }
870 
871 static void
e_day_view_add_new_event_in_selected_range(EDayView * day_view,GdkEventKey * key_event,gboolean paste_clipboard)872 e_day_view_add_new_event_in_selected_range (EDayView *day_view,
873                                             GdkEventKey *key_event,
874 					    gboolean paste_clipboard)
875 {
876 	NewEventInRangeData *ned;
877 	ECalModel *model;
878 	const gchar *source_uid;
879 
880 	ned = g_slice_new0 (NewEventInRangeData);
881 	ned->day_view = g_object_ref (day_view);
882 	if (key_event) {
883 		ned->key_event = g_slice_new0 (GdkEventKey);
884 		*ned->key_event = *key_event;
885 	}
886 	day_view_get_selected_time_range (E_CALENDAR_VIEW (day_view), &ned->dtstart, &ned->dtend);
887 	ned->in_top_canvas = day_view->selection_in_top_canvas;
888 	ned->paste_clipboard = paste_clipboard;
889 
890 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
891 	source_uid = e_cal_model_get_default_source_uid (model);
892 
893 	e_cal_ops_get_default_component	 (model, source_uid, ned->in_top_canvas,
894 		day_view_new_event_in_selected_range_cb, ned, new_event_in_rage_data_free);
895 }
896 
897 static void
day_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)898 day_view_set_property (GObject *object,
899                        guint property_id,
900                        const GValue *value,
901                        GParamSpec *pspec)
902 {
903 	switch (property_id) {
904 		case PROP_DRAW_FLAT_EVENTS:
905 			e_day_view_set_draw_flat_events (
906 				E_DAY_VIEW (object),
907 				g_value_get_boolean (value));
908 			return;
909 
910 		case PROP_MARCUS_BAINS_SHOW_LINE:
911 			e_day_view_marcus_bains_set_show_line (
912 				E_DAY_VIEW (object),
913 				g_value_get_boolean (value));
914 			return;
915 
916 		case PROP_MARCUS_BAINS_DAY_VIEW_COLOR:
917 			e_day_view_marcus_bains_set_day_view_color (
918 				E_DAY_VIEW (object),
919 				g_value_get_string (value));
920 			return;
921 
922 		case PROP_MARCUS_BAINS_TIME_BAR_COLOR:
923 			e_day_view_marcus_bains_set_time_bar_color (
924 				E_DAY_VIEW (object),
925 				g_value_get_string (value));
926 			return;
927 	}
928 
929 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
930 }
931 
932 static void
day_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)933 day_view_get_property (GObject *object,
934                        guint property_id,
935                        GValue *value,
936                        GParamSpec *pspec)
937 {
938 	switch (property_id) {
939 		case PROP_DRAW_FLAT_EVENTS:
940 			g_value_set_boolean (
941 				value,
942 				e_day_view_get_draw_flat_events (
943 				E_DAY_VIEW (object)));
944 			return;
945 
946 		case PROP_MARCUS_BAINS_SHOW_LINE:
947 			g_value_set_boolean (
948 				value,
949 				e_day_view_marcus_bains_get_show_line (
950 				E_DAY_VIEW (object)));
951 			return;
952 
953 		case PROP_MARCUS_BAINS_DAY_VIEW_COLOR:
954 			g_value_set_string (
955 				value,
956 				e_day_view_marcus_bains_get_day_view_color (
957 				E_DAY_VIEW (object)));
958 			return;
959 
960 		case PROP_MARCUS_BAINS_TIME_BAR_COLOR:
961 			g_value_set_string (
962 				value,
963 				e_day_view_marcus_bains_get_time_bar_color (
964 				E_DAY_VIEW (object)));
965 			return;
966 
967 		case PROP_IS_EDITING:
968 			g_value_set_boolean (value, e_day_view_is_editing (E_DAY_VIEW (object)));
969 			return;
970 	}
971 
972 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
973 }
974 
975 static void
day_view_dispose(GObject * object)976 day_view_dispose (GObject *object)
977 {
978 	EDayView *day_view;
979 	gint day;
980 
981 	day_view = E_DAY_VIEW (object);
982 
983 	e_day_view_cancel_layout (day_view);
984 
985 	e_day_view_stop_auto_scroll (day_view);
986 
987 	g_clear_pointer (&day_view->large_font_desc, pango_font_description_free);
988 	g_clear_pointer (&day_view->small_font_desc, pango_font_description_free);
989 	g_clear_object (&day_view->normal_cursor);
990 	g_clear_object (&day_view->move_cursor);
991 	g_clear_object (&day_view->resize_width_cursor);
992 	g_clear_object (&day_view->resize_height_cursor);
993 
994 	if (day_view->long_events) {
995 		e_day_view_free_events (day_view);
996 		g_array_free (day_view->long_events, TRUE);
997 		day_view->long_events = NULL;
998 	}
999 
1000 	for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++) {
1001 		if (day_view->events[day]) {
1002 			g_array_free (day_view->events[day], TRUE);
1003 			day_view->events[day] = NULL;
1004 		}
1005 	}
1006 
1007 	if (day_view->grabbed_pointer != NULL) {
1008 		gdk_device_ungrab (
1009 			day_view->grabbed_pointer,
1010 			GDK_CURRENT_TIME);
1011 		g_object_unref (day_view->grabbed_pointer);
1012 		day_view->grabbed_pointer = NULL;
1013 	}
1014 
1015 	#define disconnect_model_handler(x) G_STMT_START { \
1016 		if ((x) > 0) { \
1017 			g_signal_handler_disconnect (day_view->priv->model, (x)); \
1018 			(x) = 0; \
1019 		} \
1020 	} G_STMT_END
1021 
1022 	disconnect_model_handler (day_view->priv->notify_work_day_monday_handler_id);
1023 	disconnect_model_handler (day_view->priv->notify_work_day_tuesday_handler_id);
1024 	disconnect_model_handler (day_view->priv->notify_work_day_wednesday_handler_id);
1025 	disconnect_model_handler (day_view->priv->notify_work_day_thursday_handler_id);
1026 	disconnect_model_handler (day_view->priv->notify_work_day_friday_handler_id);
1027 	disconnect_model_handler (day_view->priv->notify_work_day_saturday_handler_id);
1028 	disconnect_model_handler (day_view->priv->notify_work_day_sunday_handler_id);
1029 	disconnect_model_handler (day_view->priv->notify_week_start_day_handler_id);
1030 	disconnect_model_handler (day_view->priv->notify_work_day_start_hour_handler_id);
1031 	disconnect_model_handler (day_view->priv->notify_work_day_start_minute_handler_id);
1032 	disconnect_model_handler (day_view->priv->notify_work_day_end_hour_handler_id);
1033 	disconnect_model_handler (day_view->priv->notify_work_day_end_minute_handler_id);
1034 	disconnect_model_handler (day_view->priv->notify_work_day_start_mon_handler_id);
1035 	disconnect_model_handler (day_view->priv->notify_work_day_end_mon_handler_id);
1036 	disconnect_model_handler (day_view->priv->notify_work_day_start_tue_handler_id);
1037 	disconnect_model_handler (day_view->priv->notify_work_day_end_tue_handler_id);
1038 	disconnect_model_handler (day_view->priv->notify_work_day_start_wed_handler_id);
1039 	disconnect_model_handler (day_view->priv->notify_work_day_end_wed_handler_id);
1040 	disconnect_model_handler (day_view->priv->notify_work_day_start_thu_handler_id);
1041 	disconnect_model_handler (day_view->priv->notify_work_day_end_thu_handler_id);
1042 	disconnect_model_handler (day_view->priv->notify_work_day_start_fri_handler_id);
1043 	disconnect_model_handler (day_view->priv->notify_work_day_end_fri_handler_id);
1044 	disconnect_model_handler (day_view->priv->notify_work_day_start_sat_handler_id);
1045 	disconnect_model_handler (day_view->priv->notify_work_day_end_sat_handler_id);
1046 	disconnect_model_handler (day_view->priv->notify_work_day_start_sun_handler_id);
1047 	disconnect_model_handler (day_view->priv->notify_work_day_end_sun_handler_id);
1048 	disconnect_model_handler (day_view->priv->time_range_changed_handler_id);
1049 	disconnect_model_handler (day_view->priv->model_row_changed_handler_id);
1050 	disconnect_model_handler (day_view->priv->model_cell_changed_handler_id);
1051 	disconnect_model_handler (day_view->priv->model_rows_inserted_handler_id);
1052 	disconnect_model_handler (day_view->priv->comps_deleted_handler_id);
1053 	disconnect_model_handler (day_view->priv->timezone_changed_handler_id);
1054 
1055 	#undef disconnect_model_handler
1056 
1057 	if (day_view->priv->top_canvas_button_press_event_handler_id > 0) {
1058 		g_signal_handler_disconnect (
1059 			day_view->top_canvas,
1060 			day_view->priv->top_canvas_button_press_event_handler_id);
1061 		day_view->priv->top_canvas_button_press_event_handler_id = 0;
1062 	}
1063 
1064 	if (day_view->priv->top_canvas_button_release_event_handler_id > 0) {
1065 		g_signal_handler_disconnect (
1066 			day_view->top_canvas,
1067 			day_view->priv->top_canvas_button_release_event_handler_id);
1068 		day_view->priv->top_canvas_button_release_event_handler_id = 0;
1069 	}
1070 
1071 	if (day_view->priv->top_canvas_scroll_event_handler_id > 0) {
1072 		g_signal_handler_disconnect (
1073 			day_view->top_canvas,
1074 			day_view->priv->top_canvas_scroll_event_handler_id);
1075 		day_view->priv->top_canvas_scroll_event_handler_id = 0;
1076 	}
1077 
1078 	if (day_view->priv->top_canvas_motion_notify_event_handler_id > 0) {
1079 		g_signal_handler_disconnect (
1080 			day_view->top_canvas,
1081 			day_view->priv->top_canvas_motion_notify_event_handler_id);
1082 		day_view->priv->top_canvas_motion_notify_event_handler_id = 0;
1083 	}
1084 
1085 	if (day_view->priv->top_canvas_drag_motion_handler_id > 0) {
1086 		g_signal_handler_disconnect (
1087 			day_view->top_canvas,
1088 			day_view->priv->top_canvas_drag_motion_handler_id);
1089 		day_view->priv->top_canvas_drag_motion_handler_id = 0;
1090 	}
1091 
1092 	if (day_view->priv->top_canvas_drag_leave_handler_id > 0) {
1093 		g_signal_handler_disconnect (
1094 			day_view->top_canvas,
1095 			day_view->priv->top_canvas_drag_leave_handler_id);
1096 		day_view->priv->top_canvas_drag_leave_handler_id = 0;
1097 	}
1098 
1099 	if (day_view->priv->top_canvas_drag_begin_handler_id > 0) {
1100 		g_signal_handler_disconnect (
1101 			day_view->top_canvas,
1102 			day_view->priv->top_canvas_drag_begin_handler_id);
1103 		day_view->priv->top_canvas_drag_begin_handler_id = 0;
1104 	}
1105 
1106 	if (day_view->priv->top_canvas_drag_end_handler_id > 0) {
1107 		g_signal_handler_disconnect (
1108 			day_view->top_canvas,
1109 			day_view->priv->top_canvas_drag_end_handler_id);
1110 		day_view->priv->top_canvas_drag_end_handler_id = 0;
1111 	}
1112 
1113 	if (day_view->priv->top_canvas_drag_data_get_handler_id > 0) {
1114 		g_signal_handler_disconnect (
1115 			day_view->top_canvas,
1116 			day_view->priv->top_canvas_drag_data_get_handler_id);
1117 		day_view->priv->top_canvas_drag_data_get_handler_id = 0;
1118 	}
1119 
1120 	if (day_view->priv->top_canvas_drag_data_received_handler_id > 0) {
1121 		g_signal_handler_disconnect (
1122 			day_view->top_canvas,
1123 			day_view->priv->top_canvas_drag_data_received_handler_id);
1124 		day_view->priv->top_canvas_drag_data_received_handler_id = 0;
1125 	}
1126 
1127 	if (day_view->priv->main_canvas_realize_handler_id > 0) {
1128 		g_signal_handler_disconnect (
1129 			day_view->main_canvas,
1130 			day_view->priv->main_canvas_realize_handler_id);
1131 		day_view->priv->main_canvas_realize_handler_id = 0;
1132 	}
1133 
1134 	if (day_view->priv->main_canvas_button_press_event_handler_id > 0) {
1135 		g_signal_handler_disconnect (
1136 			day_view->main_canvas,
1137 			day_view->priv->main_canvas_button_press_event_handler_id);
1138 		day_view->priv->main_canvas_button_press_event_handler_id = 0;
1139 	}
1140 
1141 	if (day_view->priv->main_canvas_button_release_event_handler_id > 0) {
1142 		g_signal_handler_disconnect (
1143 			day_view->main_canvas,
1144 			day_view->priv->main_canvas_button_release_event_handler_id);
1145 		day_view->priv->main_canvas_button_release_event_handler_id = 0;
1146 	}
1147 
1148 	if (day_view->priv->main_canvas_scroll_event_handler_id > 0) {
1149 		g_signal_handler_disconnect (
1150 			day_view->main_canvas,
1151 			day_view->priv->main_canvas_scroll_event_handler_id);
1152 		day_view->priv->main_canvas_scroll_event_handler_id = 0;
1153 	}
1154 
1155 	if (day_view->priv->main_canvas_motion_notify_event_handler_id > 0) {
1156 		g_signal_handler_disconnect (
1157 			day_view->main_canvas,
1158 			day_view->priv->main_canvas_motion_notify_event_handler_id);
1159 		day_view->priv->main_canvas_motion_notify_event_handler_id = 0;
1160 	}
1161 
1162 	if (day_view->priv->main_canvas_drag_motion_handler_id > 0) {
1163 		g_signal_handler_disconnect (
1164 			day_view->main_canvas,
1165 			day_view->priv->main_canvas_drag_motion_handler_id);
1166 		day_view->priv->main_canvas_drag_motion_handler_id = 0;
1167 	}
1168 
1169 	if (day_view->priv->main_canvas_drag_leave_handler_id > 0) {
1170 		g_signal_handler_disconnect (
1171 			day_view->main_canvas,
1172 			day_view->priv->main_canvas_drag_leave_handler_id);
1173 		day_view->priv->main_canvas_drag_leave_handler_id = 0;
1174 	}
1175 
1176 	if (day_view->priv->main_canvas_drag_begin_handler_id > 0) {
1177 		g_signal_handler_disconnect (
1178 			day_view->main_canvas,
1179 			day_view->priv->main_canvas_drag_begin_handler_id);
1180 		day_view->priv->main_canvas_drag_begin_handler_id = 0;
1181 	}
1182 
1183 	if (day_view->priv->main_canvas_drag_end_handler_id > 0) {
1184 		g_signal_handler_disconnect (
1185 			day_view->main_canvas,
1186 			day_view->priv->main_canvas_drag_end_handler_id);
1187 		day_view->priv->main_canvas_drag_end_handler_id = 0;
1188 	}
1189 
1190 	if (day_view->priv->main_canvas_drag_data_get_handler_id > 0) {
1191 		g_signal_handler_disconnect (
1192 			day_view->main_canvas,
1193 			day_view->priv->main_canvas_drag_data_get_handler_id);
1194 		day_view->priv->main_canvas_drag_data_get_handler_id = 0;
1195 	}
1196 
1197 	if (day_view->priv->main_canvas_drag_data_received_handler_id > 0) {
1198 		g_signal_handler_disconnect (
1199 			day_view->main_canvas,
1200 			day_view->priv->main_canvas_drag_data_received_handler_id);
1201 		day_view->priv->main_canvas_drag_data_received_handler_id = 0;
1202 	}
1203 
1204 	if (day_view->priv->time_canvas_scroll_event_handler_id > 0) {
1205 		g_signal_handler_disconnect (
1206 			day_view->time_canvas,
1207 			day_view->priv->time_canvas_scroll_event_handler_id);
1208 		day_view->priv->time_canvas_scroll_event_handler_id = 0;
1209 	}
1210 
1211 	g_clear_object (&day_view->top_canvas);
1212 	g_clear_object (&day_view->main_canvas);
1213 	g_clear_object (&day_view->time_canvas);
1214 	g_clear_object (&day_view->priv->model);
1215 	g_clear_object (&day_view->priv->drag_context);
1216 
1217 	g_free (day_view->priv->marcus_bains_day_view_color);
1218 	day_view->priv->marcus_bains_day_view_color = NULL;
1219 
1220 	g_free (day_view->priv->marcus_bains_time_bar_color);
1221 	day_view->priv->marcus_bains_time_bar_color = NULL;
1222 
1223 	/* Chain up to parent's dispose() method. */
1224 	G_OBJECT_CLASS (e_day_view_parent_class)->dispose (object);
1225 }
1226 
1227 static void
day_view_notify(GObject * object,GParamSpec * pspec)1228 day_view_notify (GObject *object,
1229                  GParamSpec *pspec)
1230 {
1231 	/* Don't chain up.  None of our parent classes, not
1232 	 * even GObjectClass itself, implements this method. */
1233 
1234 	if (g_str_equal (pspec->name, "time-divisions"))
1235 		day_view_notify_time_divisions_cb (E_DAY_VIEW (object));
1236 }
1237 
1238 static void
day_view_constructed(GObject * object)1239 day_view_constructed (GObject *object)
1240 {
1241 	EDayView *day_view;
1242 	ECalModel *model;
1243 	gulong handler_id;
1244 
1245 	day_view = E_DAY_VIEW (object);
1246 
1247 	/* Chain up to parent's constructed() method. */
1248 	G_OBJECT_CLASS (e_day_view_parent_class)->constructed (object);
1249 
1250 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
1251 
1252 	/* Keep our own model reference so we can
1253 	 * disconnect signal handlers in dispose(). */
1254 	day_view->priv->model = g_object_ref (model);
1255 
1256 	handler_id = e_signal_connect_notify (
1257 		model, "notify::work-day-monday",
1258 		G_CALLBACK (day_view_notify_work_day_cb), day_view);
1259 	day_view->priv->notify_work_day_monday_handler_id = handler_id;
1260 
1261 	handler_id = e_signal_connect_notify (
1262 		model, "notify::work-day-tuesday",
1263 		G_CALLBACK (day_view_notify_work_day_cb), day_view);
1264 	day_view->priv->notify_work_day_tuesday_handler_id = handler_id;
1265 
1266 	handler_id = e_signal_connect_notify (
1267 		model, "notify::work-day-wednesday",
1268 		G_CALLBACK (day_view_notify_work_day_cb), day_view);
1269 	day_view->priv->notify_work_day_wednesday_handler_id = handler_id;
1270 
1271 	handler_id = e_signal_connect_notify (
1272 		model, "notify::work-day-thursday",
1273 		G_CALLBACK (day_view_notify_work_day_cb), day_view);
1274 	day_view->priv->notify_work_day_thursday_handler_id = handler_id;
1275 
1276 	handler_id = e_signal_connect_notify (
1277 		model, "notify::work-day-friday",
1278 		G_CALLBACK (day_view_notify_work_day_cb), day_view);
1279 	day_view->priv->notify_work_day_friday_handler_id = handler_id;
1280 
1281 	handler_id = e_signal_connect_notify (
1282 		model, "notify::work-day-saturday",
1283 		G_CALLBACK (day_view_notify_work_day_cb), day_view);
1284 	day_view->priv->notify_work_day_saturday_handler_id = handler_id;
1285 
1286 	handler_id = e_signal_connect_notify (
1287 		model, "notify::work-day-sunday",
1288 		G_CALLBACK (day_view_notify_work_day_cb), day_view);
1289 	day_view->priv->notify_work_day_sunday_handler_id = handler_id;
1290 
1291 	handler_id = e_signal_connect_notify_swapped (
1292 		model, "notify::week-start-day",
1293 		G_CALLBACK (day_view_notify_week_start_day_cb), day_view);
1294 	day_view->priv->notify_week_start_day_handler_id = handler_id;
1295 
1296 	handler_id = e_signal_connect_notify_swapped (
1297 		model, "notify::work-day-start-hour",
1298 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1299 	day_view->priv->notify_work_day_start_hour_handler_id = handler_id;
1300 
1301 	handler_id = e_signal_connect_notify_swapped (
1302 		model, "notify::work-day-start-minute",
1303 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1304 	day_view->priv->notify_work_day_start_minute_handler_id = handler_id;
1305 
1306 	handler_id = e_signal_connect_notify_swapped (
1307 		model, "notify::work-day-end-hour",
1308 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1309 	day_view->priv->notify_work_day_end_hour_handler_id = handler_id;
1310 
1311 	handler_id = e_signal_connect_notify_swapped (
1312 		model, "notify::work-day-end-minute",
1313 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1314 	day_view->priv->notify_work_day_end_minute_handler_id = handler_id;
1315 
1316 	handler_id = e_signal_connect_notify_swapped (
1317 		model, "notify::work-day-start-mon",
1318 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1319 	day_view->priv->notify_work_day_start_mon_handler_id = handler_id;
1320 
1321 	handler_id = e_signal_connect_notify_swapped (
1322 		model, "notify::work-day-end-mon",
1323 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1324 	day_view->priv->notify_work_day_end_mon_handler_id = handler_id;
1325 
1326 	handler_id = e_signal_connect_notify_swapped (
1327 		model, "notify::work-day-start-tue",
1328 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1329 	day_view->priv->notify_work_day_start_tue_handler_id = handler_id;
1330 
1331 	handler_id = e_signal_connect_notify_swapped (
1332 		model, "notify::work-day-end-tue",
1333 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1334 	day_view->priv->notify_work_day_end_tue_handler_id = handler_id;
1335 
1336 	handler_id = e_signal_connect_notify_swapped (
1337 		model, "notify::work-day-start-wed",
1338 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1339 	day_view->priv->notify_work_day_start_wed_handler_id = handler_id;
1340 
1341 	handler_id = e_signal_connect_notify_swapped (
1342 		model, "notify::work-day-end-wed",
1343 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1344 	day_view->priv->notify_work_day_end_wed_handler_id = handler_id;
1345 
1346 	handler_id = e_signal_connect_notify_swapped (
1347 		model, "notify::work-day-start-thu",
1348 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1349 	day_view->priv->notify_work_day_start_thu_handler_id = handler_id;
1350 
1351 	handler_id = e_signal_connect_notify_swapped (
1352 		model, "notify::work-day-end-thu",
1353 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1354 	day_view->priv->notify_work_day_end_thu_handler_id = handler_id;
1355 
1356 	handler_id = e_signal_connect_notify_swapped (
1357 		model, "notify::work-day-start-fri",
1358 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1359 	day_view->priv->notify_work_day_start_fri_handler_id = handler_id;
1360 
1361 	handler_id = e_signal_connect_notify_swapped (
1362 		model, "notify::work-day-end-fri",
1363 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1364 	day_view->priv->notify_work_day_end_fri_handler_id = handler_id;
1365 
1366 	handler_id = e_signal_connect_notify_swapped (
1367 		model, "notify::work-day-start-sat",
1368 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1369 	day_view->priv->notify_work_day_start_sat_handler_id = handler_id;
1370 
1371 	handler_id = e_signal_connect_notify_swapped (
1372 		model, "notify::work-day-end-sat",
1373 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1374 	day_view->priv->notify_work_day_end_sat_handler_id = handler_id;
1375 
1376 	handler_id = e_signal_connect_notify_swapped (
1377 		model, "notify::work-day-start-sun",
1378 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1379 	day_view->priv->notify_work_day_start_sun_handler_id = handler_id;
1380 
1381 	handler_id = e_signal_connect_notify_swapped (
1382 		model, "notify::work-day-end-sun",
1383 		G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1384 	day_view->priv->notify_work_day_end_sun_handler_id = handler_id;
1385 
1386 	e_day_view_update_timezone_name_labels (day_view);
1387 }
1388 
1389 static void
day_view_update_style_settings(EDayView * day_view)1390 day_view_update_style_settings (EDayView *day_view)
1391 {
1392 	gint hour;
1393 	gint minute, max_minute_width, i;
1394 	gint month, day, width;
1395 	gint longest_month_width, longest_abbreviated_month_width;
1396 	gint longest_weekday_width, longest_abbreviated_weekday_width;
1397 	gchar buffer[128];
1398 	const gchar *name;
1399 	gint times_width;
1400 	PangoFontDescription *font_desc;
1401 	PangoContext *pango_context;
1402 	PangoFontMetrics *font_metrics;
1403 	PangoLayout *layout;
1404 	gint week_day, event_num;
1405 	GtkAdjustment *adjustment;
1406 	EDayViewEvent *event;
1407 	GdkColor color;
1408 
1409 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
1410 
1411 	e_day_view_set_colors (day_view);
1412 
1413 	for (week_day = 0; week_day < E_DAY_VIEW_MAX_DAYS; week_day++) {
1414 		for (event_num = 0; event_num < day_view->events[week_day]->len; event_num++) {
1415 			event = &g_array_index (day_view->events[week_day], EDayViewEvent, event_num);
1416 			if (event->canvas_item) {
1417 				color = e_day_view_get_text_color (day_view, event);
1418 				gnome_canvas_item_set (
1419 					event->canvas_item,
1420 					"fill_color_gdk", &color,
1421 					NULL);
1422 			}
1423 		}
1424 	}
1425 	for (event_num = 0; event_num < day_view->long_events->len; event_num++) {
1426 		event = &g_array_index (day_view->long_events, EDayViewEvent, event_num);
1427 		if (event->canvas_item) {
1428 			color = e_day_view_get_text_color (day_view, event);
1429 			gnome_canvas_item_set (
1430 				event->canvas_item,
1431 				"fill_color_gdk", &color,
1432 				NULL);
1433 		}
1434 	}
1435 
1436 	/* Set up Pango prerequisites */
1437 	pango_context = gtk_widget_get_pango_context (GTK_WIDGET (day_view));
1438 	font_desc = pango_context_get_font_description (pango_context);
1439 	font_metrics = pango_context_get_metrics (
1440 		pango_context, font_desc,
1441 		pango_context_get_language (pango_context));
1442 	layout = pango_layout_new (pango_context);
1443 
1444 	/* Create the large font. */
1445 	if (day_view->large_font_desc != NULL)
1446 		pango_font_description_free (day_view->large_font_desc);
1447 
1448 	day_view->large_font_desc = pango_font_description_copy (font_desc);
1449 	pango_font_description_set_size (
1450 		day_view->large_font_desc,
1451 		E_DAY_VIEW_LARGE_FONT_PTSIZE * PANGO_SCALE);
1452 
1453 	/* Create the small fonts. */
1454 	if (day_view->small_font_desc != NULL)
1455 		pango_font_description_free (day_view->small_font_desc);
1456 
1457 	day_view->small_font_desc = pango_font_description_copy (font_desc);
1458 	pango_font_description_set_size (
1459 		day_view->small_font_desc,
1460 		E_DAY_VIEW_SMALL_FONT_PTSIZE * PANGO_SCALE);
1461 
1462 	/* Recalculate the height of each row based on the font size. */
1463 	day_view->row_height =
1464 		PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1465 		PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) +
1466 		E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD * 2 + 2 /* FIXME */;
1467 	day_view->row_height = MAX (
1468 		day_view->row_height,
1469 		E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD + 2);
1470 
1471 	adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->main_canvas));
1472 	gtk_adjustment_set_step_increment (adjustment, day_view->row_height);
1473 
1474 	day_view->top_row_height =
1475 		PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1476 		PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) +
1477 		E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT * 2 + E_DAY_VIEW_LONG_EVENT_Y_PAD * 2 +
1478 		E_DAY_VIEW_TOP_CANVAS_Y_GAP;
1479 	day_view->top_row_height =
1480 		MAX (
1481 			day_view->top_row_height,
1482 		E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD + 2 +
1483 		E_DAY_VIEW_TOP_CANVAS_Y_GAP);
1484 
1485 	adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->top_canvas));
1486 	gtk_adjustment_set_step_increment (adjustment, day_view->top_row_height);
1487 	gtk_widget_set_size_request (day_view->top_dates_canvas, -1, day_view->top_row_height - 2);
1488 
1489 	e_day_view_update_top_scroll (day_view, TRUE);
1490 
1491 	/* Find the longest full & abbreviated month names. */
1492 	longest_month_width = 0;
1493 	longest_abbreviated_month_width = 0;
1494 	for (month = 0; month < 12; month++) {
1495 		name = e_get_month_name (month + 1, FALSE);
1496 		pango_layout_set_text (layout, name, -1);
1497 		pango_layout_get_pixel_size (layout, &width, NULL);
1498 
1499 		if (width > longest_month_width) {
1500 			longest_month_width = width;
1501 			day_view->longest_month_name = month;
1502 		}
1503 
1504 		name = e_get_month_name (month + 1, TRUE);
1505 		pango_layout_set_text (layout, name, -1);
1506 		pango_layout_get_pixel_size (layout, &width, NULL);
1507 
1508 		if (width > longest_abbreviated_month_width) {
1509 			longest_abbreviated_month_width = width;
1510 			day_view->longest_abbreviated_month_name = month;
1511 		}
1512 	}
1513 
1514 	/* Find the longest full & abbreviated weekday names. */
1515 	longest_weekday_width = 0;
1516 	longest_abbreviated_weekday_width = 0;
1517 	for (day = 0; day < 7; day++) {
1518 		name = e_get_weekday_name (day + 1, FALSE);
1519 		pango_layout_set_text (layout, name, -1);
1520 		pango_layout_get_pixel_size (layout, &width, NULL);
1521 
1522 		if (width > longest_weekday_width) {
1523 			longest_weekday_width = width;
1524 			day_view->longest_weekday_name = day;
1525 		}
1526 
1527 		name = e_get_weekday_name (day + 1, TRUE);
1528 		pango_layout_set_text (layout, name, -1);
1529 		pango_layout_get_pixel_size (layout, &width, NULL);
1530 
1531 		if (width > longest_abbreviated_weekday_width) {
1532 			longest_abbreviated_weekday_width = width;
1533 			day_view->longest_abbreviated_weekday_name = day;
1534 		}
1535 	}
1536 
1537 	/* Calculate the widths of all the time strings necessary. */
1538 	day_view->max_small_hour_width = 0;
1539 	for (hour = 0; hour < 24; hour++) {
1540 		g_snprintf (buffer, sizeof (buffer), "%02i", hour);
1541 		pango_layout_set_text (layout, buffer, -1);
1542 		pango_layout_get_pixel_size (layout, &day_view->small_hour_widths[hour], NULL);
1543 
1544 		day_view->max_small_hour_width = MAX (day_view->max_small_hour_width, day_view->small_hour_widths[hour]);
1545 	}
1546 
1547 	max_minute_width = 0;
1548 	for (minute = 0, i = 0; minute < 60; minute += 5, i++) {
1549 		gint minute_width;
1550 
1551 		g_snprintf (buffer, sizeof (buffer), "%02i", minute);
1552 		pango_layout_set_text (layout, buffer, -1);
1553 		pango_layout_get_pixel_size (layout, &minute_width, NULL);
1554 
1555 		max_minute_width = MAX (max_minute_width, minute_width);
1556 	}
1557 	day_view->max_minute_width = max_minute_width;
1558 
1559 	pango_layout_set_text (layout, ":", 1);
1560 	pango_layout_get_pixel_size (layout, &day_view->colon_width, NULL);
1561 	pango_layout_set_text (layout, "0", 1);
1562 	pango_layout_get_pixel_size (layout, &day_view->digit_width, NULL);
1563 
1564 	pango_layout_set_text (layout, day_view->am_string, -1);
1565 	pango_layout_get_pixel_size (layout, &day_view->am_string_width, NULL);
1566 	pango_layout_set_text (layout, day_view->pm_string, -1);
1567 	pango_layout_get_pixel_size (layout, &day_view->pm_string_width, NULL);
1568 
1569 	/* Calculate the width of the time column. */
1570 	times_width = e_day_view_time_item_get_column_width (E_DAY_VIEW_TIME_ITEM (day_view->time_canvas_item));
1571 	gtk_widget_set_size_request (day_view->time_canvas, times_width, -1);
1572 
1573 	g_object_unref (layout);
1574 	pango_font_metrics_unref (font_metrics);
1575 }
1576 
1577 static void
day_view_realize(GtkWidget * widget)1578 day_view_realize (GtkWidget *widget)
1579 {
1580 	EDayView *day_view;
1581 
1582 	if (GTK_WIDGET_CLASS (e_day_view_parent_class)->realize)
1583 		(*GTK_WIDGET_CLASS (e_day_view_parent_class)->realize)(widget);
1584 
1585 	day_view = E_DAY_VIEW (widget);
1586 
1587 	day_view_update_style_settings (day_view);
1588 
1589 	/* Create the pixmaps. */
1590 	day_view->reminder_icon = e_icon_factory_get_icon ("stock_bell", GTK_ICON_SIZE_MENU);
1591 	day_view->recurrence_icon = e_icon_factory_get_icon ("view-refresh", GTK_ICON_SIZE_MENU);
1592 	day_view->timezone_icon = e_icon_factory_get_icon ("stock_timezone", GTK_ICON_SIZE_MENU);
1593 	day_view->meeting_icon = e_icon_factory_get_icon ("stock_people", GTK_ICON_SIZE_MENU);
1594 	day_view->attach_icon = e_icon_factory_get_icon ("mail-attachment", GTK_ICON_SIZE_MENU);
1595 
1596 	/* Set the canvas item colors. */
1597 	gnome_canvas_item_set (
1598 		day_view->drag_long_event_rect_item,
1599 		"fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
1600 		"outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
1601 		NULL);
1602 
1603 	gnome_canvas_item_set (
1604 		day_view->drag_rect_item,
1605 		"fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
1606 		"outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
1607 		NULL);
1608 
1609 	gnome_canvas_item_set (
1610 		day_view->drag_bar_item,
1611 		"fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR],
1612 		"outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
1613 		NULL);
1614 }
1615 
1616 static void
day_view_unrealize(GtkWidget * widget)1617 day_view_unrealize (GtkWidget *widget)
1618 {
1619 	EDayView *day_view;
1620 
1621 	day_view = E_DAY_VIEW (widget);
1622 
1623 	g_object_unref (day_view->reminder_icon);
1624 	day_view->reminder_icon = NULL;
1625 	g_object_unref (day_view->recurrence_icon);
1626 	day_view->recurrence_icon = NULL;
1627 	g_object_unref (day_view->timezone_icon);
1628 	day_view->timezone_icon = NULL;
1629 	g_object_unref (day_view->meeting_icon);
1630 	day_view->meeting_icon = NULL;
1631 	g_object_unref (day_view->attach_icon);
1632 	day_view->attach_icon = NULL;
1633 
1634 	if (GTK_WIDGET_CLASS (e_day_view_parent_class)->unrealize)
1635 		(*GTK_WIDGET_CLASS (e_day_view_parent_class)->unrealize)(widget);
1636 }
1637 
1638 static void
day_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1639 day_view_size_allocate (GtkWidget *widget,
1640                         GtkAllocation *allocation)
1641 {
1642 	(*GTK_WIDGET_CLASS (e_day_view_parent_class)->size_allocate) (widget, allocation);
1643 
1644 	e_day_view_recalc_main_canvas_size (E_DAY_VIEW (widget));
1645 }
1646 
1647 static void
day_view_style_updated(GtkWidget * widget)1648 day_view_style_updated (GtkWidget *widget)
1649 {
1650 	if (GTK_WIDGET_CLASS (e_day_view_parent_class)->style_updated)
1651 		(*GTK_WIDGET_CLASS (e_day_view_parent_class)->style_updated) (widget);
1652 
1653 	day_view_update_style_settings (E_DAY_VIEW (widget));
1654 }
1655 
1656 static gboolean
day_view_focus(GtkWidget * widget,GtkDirectionType direction)1657 day_view_focus (GtkWidget *widget,
1658                 GtkDirectionType direction)
1659 {
1660 	EDayView *day_view;
1661 	gint new_day;
1662 	gint new_event_num;
1663 	gint start_row, end_row;
1664 
1665 	g_return_val_if_fail (widget != NULL, FALSE);
1666 	g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
1667 	day_view = E_DAY_VIEW (widget);
1668 
1669 	if (!e_day_view_get_next_tab_event (day_view, direction,
1670 					    &new_day, &new_event_num))
1671 		return FALSE;
1672 
1673 	if ((new_day == -1) && (new_event_num == -1)) {
1674 		/* focus should go to the day view widget itself
1675 		 */
1676 		gtk_widget_grab_focus (GTK_WIDGET (day_view));
1677 		return TRUE;
1678 	}
1679 
1680 	if (new_day != E_DAY_VIEW_LONG_EVENT && new_day != -1) {
1681 		if (e_day_view_get_event_rows (day_view, new_day,
1682 					       new_event_num,
1683 					       &start_row, &end_row))
1684 			/* ensure the event to be seen */
1685 			e_day_view_ensure_rows_visible (
1686 				day_view,
1687 				start_row, end_row);
1688 	} else if (new_day != -1) {
1689 		e_day_view_start_editing_event (
1690 			day_view, new_day,
1691 			new_event_num, NULL);
1692 	}
1693 
1694 	return TRUE;
1695 }
1696 
1697 static gboolean
day_view_key_press(GtkWidget * widget,GdkEventKey * event)1698 day_view_key_press (GtkWidget *widget,
1699                     GdkEventKey *event)
1700 {
1701 	gboolean handled = FALSE;
1702 	handled = e_day_view_do_key_press (widget, event);
1703 
1704 	/* if not handled, try key bindings */
1705 	if (!handled)
1706 		handled = GTK_WIDGET_CLASS (e_day_view_parent_class)->key_press_event (widget, event);
1707 	return handled;
1708 }
1709 
1710 static gint
day_view_focus_in(GtkWidget * widget,GdkEventFocus * event)1711 day_view_focus_in (GtkWidget *widget,
1712                    GdkEventFocus *event)
1713 {
1714 	EDayView *day_view;
1715 
1716 	g_return_val_if_fail (widget != NULL, FALSE);
1717 	g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
1718 	g_return_val_if_fail (event != NULL, FALSE);
1719 
1720 	day_view = E_DAY_VIEW (widget);
1721 
1722 	/* XXX Can't access flags directly anymore, but is it really needed?
1723 	 *     If so, could we call gtk_widget_send_focus_change() instead? */
1724 #if 0
1725 	GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1726 #endif
1727 
1728 	if (E_CALENDAR_VIEW (day_view)->in_focus && day_view->requires_update) {
1729 		time_t my_start = 0, my_end = 0, model_start = 0, model_end = 0;
1730 
1731 		day_view->requires_update = FALSE;
1732 
1733 		e_cal_model_get_time_range (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), &model_start, &model_end);
1734 
1735 		if (e_calendar_view_get_visible_time_range (E_CALENDAR_VIEW (day_view), &my_start, &my_end) &&
1736 		    model_start == my_start && model_end == my_end) {
1737 			/* update only when the same time range is set in a view and in a model;
1738 			 * otherwise time range change invokes also query update */
1739 			e_day_view_recalc_day_starts (day_view, day_view->lower);
1740 			e_day_view_update_query (day_view);
1741 		}
1742 	}
1743 
1744 	gtk_widget_queue_draw (day_view->top_canvas);
1745 	gtk_widget_queue_draw (day_view->main_canvas);
1746 
1747 	return FALSE;
1748 }
1749 
1750 static gint
day_view_focus_out(GtkWidget * widget,GdkEventFocus * event)1751 day_view_focus_out (GtkWidget *widget,
1752                     GdkEventFocus *event)
1753 {
1754 	EDayView *day_view;
1755 
1756 	g_return_val_if_fail (widget != NULL, FALSE);
1757 	g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
1758 	g_return_val_if_fail (event != NULL, FALSE);
1759 
1760 	day_view = E_DAY_VIEW (widget);
1761 
1762 	/* XXX Can't access flags directly anymore, but is it really needed?
1763 	 *     If so, could we call gtk_widget_send_focus_change() instead? */
1764 #if 0
1765 	GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1766 #endif
1767 
1768 	gtk_widget_queue_draw (day_view->top_canvas);
1769 	gtk_widget_queue_draw (day_view->main_canvas);
1770 
1771 	return FALSE;
1772 }
1773 
1774 static gboolean
day_view_popup_menu(GtkWidget * widget)1775 day_view_popup_menu (GtkWidget *widget)
1776 {
1777 	EDayView *day_view = E_DAY_VIEW (widget);
1778 	e_day_view_show_popup_menu (
1779 		day_view, NULL,
1780 		day_view->editing_event_day,
1781 		day_view->editing_event_num);
1782 	return TRUE;
1783 }
1784 
1785 /* Returns the currently-selected event, or NULL if none */
1786 static GList *
day_view_get_selected_events(ECalendarView * cal_view)1787 day_view_get_selected_events (ECalendarView *cal_view)
1788 {
1789 	EDayViewEvent *event = NULL;
1790 	GList *list = NULL;
1791 	EDayView *day_view = (EDayView *) cal_view;
1792 
1793 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), NULL);
1794 
1795 	if (day_view->editing_event_num != -1) {
1796 		if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT) {
1797 			if (!is_array_index_in_bounds (day_view->long_events, day_view->editing_event_num))
1798 				return NULL;
1799 
1800 			event = &g_array_index (day_view->long_events,
1801 						EDayViewEvent,
1802 						day_view->editing_event_num);
1803 		} else {
1804 			if (!is_array_index_in_bounds (day_view->events[day_view->editing_event_day], day_view->editing_event_num))
1805 				return NULL;
1806 
1807 			event = &g_array_index (day_view->events[day_view->editing_event_day],
1808 						EDayViewEvent,
1809 						day_view->editing_event_num);
1810 		}
1811 	} else if (day_view->popup_event_num != -1) {
1812 		if (day_view->popup_event_day == E_DAY_VIEW_LONG_EVENT) {
1813 			if (!is_array_index_in_bounds (day_view->long_events, day_view->popup_event_num))
1814 				return NULL;
1815 
1816 			event = &g_array_index (day_view->long_events,
1817 						EDayViewEvent,
1818 						day_view->popup_event_num);
1819 		} else {
1820 			if (!is_array_index_in_bounds (day_view->events[day_view->popup_event_day], day_view->popup_event_num))
1821 				return NULL;
1822 
1823 			event = &g_array_index (day_view->events[day_view->popup_event_day],
1824 						EDayViewEvent,
1825 						day_view->popup_event_num);
1826 		}
1827 	}
1828 
1829 	if (event)
1830 		list = g_list_append (list, event);
1831 
1832 	return list;
1833 }
1834 
1835 /* This sets the selected time range. If the start_time & end_time are not equal
1836  * and are both visible in the view, then the selection is set to those times,
1837  * otherwise it is set to 1 hour from the start of the working day. */
1838 static void
day_view_set_selected_time_range(ECalendarView * cal_view,time_t start_time,time_t end_time)1839 day_view_set_selected_time_range (ECalendarView *cal_view,
1840                                   time_t start_time,
1841                                   time_t end_time)
1842 {
1843 	EDayView *day_view;
1844 	gint work_day_start_hour;
1845 	gint work_day_start_minute;
1846 	gint work_day_end_hour;
1847 	gint work_day_end_minute;
1848 	gint start_row, start_col, end_row, end_col;
1849 	gboolean need_redraw = FALSE, start_in_grid, end_in_grid;
1850 
1851 	day_view = E_DAY_VIEW (cal_view);
1852 
1853 	if (start_time == end_time)
1854 		end_time += e_calendar_view_get_time_divisions (cal_view) * 60;
1855 
1856 	/* Set the selection. */
1857 	start_in_grid = e_day_view_convert_time_to_grid_position (
1858 		day_view,
1859 		start_time,
1860 		&start_col,
1861 		&start_row);
1862 	end_in_grid = e_day_view_convert_time_to_grid_position (
1863 		day_view,
1864 		end_time - 60,
1865 		&end_col,
1866 		&end_row);
1867 
1868 	e_day_view_get_work_day_range_for_day (day_view, start_col,
1869 		&work_day_start_hour, &work_day_start_minute,
1870 		&work_day_end_hour, &work_day_end_minute);
1871 
1872 	/* If either of the times isn't in the grid, or the selection covers
1873 	 * an entire day, we set the selection to 1 row from the start of the
1874 	 * working day, in the day corresponding to the start time. */
1875 	if (!start_in_grid || !end_in_grid
1876 	    || (start_row == 0 && end_row == day_view->rows - 1)) {
1877 		end_col = start_col;
1878 
1879 		start_row = e_day_view_convert_time_to_row (
1880 			day_view, work_day_start_hour, work_day_start_minute);
1881 		start_row = CLAMP (start_row, 0, day_view->rows - 1);
1882 		end_row = start_row;
1883 	}
1884 
1885 	if (start_row != day_view->selection_start_row
1886 	    || start_col != day_view->selection_start_day) {
1887 		need_redraw = TRUE;
1888 		day_view->selection_in_top_canvas = FALSE;
1889 		day_view->selection_start_row = start_row;
1890 		day_view->selection_start_day = start_col;
1891 	}
1892 
1893 	if (end_row != day_view->selection_end_row
1894 	    || end_col != day_view->selection_end_day) {
1895 		need_redraw = TRUE;
1896 		day_view->selection_in_top_canvas = FALSE;
1897 		day_view->selection_end_row = end_row;
1898 		day_view->selection_end_day = end_col;
1899 	}
1900 
1901 	if (need_redraw) {
1902 		gtk_widget_queue_draw (day_view->top_canvas);
1903 		gtk_widget_queue_draw (day_view->top_dates_canvas);
1904 		gtk_widget_queue_draw (day_view->main_canvas);
1905 
1906 		e_day_view_ensure_rows_visible (day_view, day_view->selection_start_row, day_view->selection_end_row);
1907 	}
1908 }
1909 
1910 /* Gets the visible time range. Returns FALSE if no time range has been set. */
1911 static gboolean
day_view_get_visible_time_range(ECalendarView * cal_view,time_t * start_time,time_t * end_time)1912 day_view_get_visible_time_range (ECalendarView *cal_view,
1913                                  time_t *start_time,
1914                                  time_t *end_time)
1915 {
1916 	EDayView *day_view = E_DAY_VIEW (cal_view);
1917 	gint days_shown;
1918 
1919 	/* If the date isn't set, return FALSE. */
1920 	if (day_view->lower == 0 && day_view->upper == 0)
1921 		return FALSE;
1922 
1923 	days_shown = e_day_view_get_days_shown (day_view);
1924 	if (days_shown <= 0)
1925 		return FALSE;
1926 
1927 	*start_time = day_view->day_starts[0];
1928 	*end_time = day_view->day_starts[days_shown];
1929 
1930 	return TRUE;
1931 }
1932 
1933 static void
day_view_paste_text(ECalendarView * cal_view)1934 day_view_paste_text (ECalendarView *cal_view)
1935 {
1936 	EDayView *day_view;
1937 	EDayViewEvent *event;
1938 
1939 	g_return_if_fail (E_IS_DAY_VIEW (cal_view));
1940 
1941 	day_view = E_DAY_VIEW (cal_view);
1942 
1943 	if (day_view->editing_event_num == -1) {
1944 		e_day_view_add_new_event_in_selected_range (day_view, NULL, TRUE);
1945 		return;
1946 	}
1947 
1948 	if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT) {
1949 		if (!is_array_index_in_bounds (day_view->long_events, day_view->editing_event_num))
1950 			return;
1951 
1952 		event = &g_array_index (day_view->long_events,
1953 					EDayViewEvent,
1954 					day_view->editing_event_num);
1955 	} else {
1956 		if (!is_array_index_in_bounds (day_view->events[day_view->editing_event_day], day_view->editing_event_num))
1957 			return;
1958 
1959 		event = &g_array_index (day_view->events[day_view->editing_event_day],
1960 					EDayViewEvent,
1961 					day_view->editing_event_num);
1962 	}
1963 
1964 	if (event->canvas_item &&
1965 	    E_IS_TEXT (event->canvas_item) &&
1966 	    E_TEXT (event->canvas_item)->editing) {
1967 		e_text_paste_clipboard (E_TEXT (event->canvas_item));
1968 	}
1969 }
1970 
1971 static void
e_day_view_class_init(EDayViewClass * class)1972 e_day_view_class_init (EDayViewClass *class)
1973 {
1974 	GObjectClass *object_class;
1975 	GtkWidgetClass *widget_class;
1976 	ECalendarViewClass *view_class;
1977 
1978 	g_type_class_add_private (class, sizeof (EDayViewPrivate));
1979 
1980 	object_class = G_OBJECT_CLASS (class);
1981 	object_class->set_property = day_view_set_property;
1982 	object_class->get_property = day_view_get_property;
1983 	object_class->constructed = day_view_constructed;
1984 	object_class->dispose = day_view_dispose;
1985 	object_class->notify = day_view_notify;
1986 
1987 	widget_class = GTK_WIDGET_CLASS (class);
1988 	widget_class->realize = day_view_realize;
1989 	widget_class->unrealize = day_view_unrealize;
1990 	widget_class->size_allocate = day_view_size_allocate;
1991 	widget_class->style_updated = day_view_style_updated;
1992 	widget_class->focus = day_view_focus;
1993 	widget_class->key_press_event = day_view_key_press;
1994 	widget_class->focus_in_event = day_view_focus_in;
1995 	widget_class->focus_out_event = day_view_focus_out;
1996 	widget_class->popup_menu = day_view_popup_menu;
1997 
1998 	view_class = E_CALENDAR_VIEW_CLASS (class);
1999 	view_class->get_selected_events = day_view_get_selected_events;
2000 	view_class->get_selected_time_range = day_view_get_selected_time_range;
2001 	view_class->set_selected_time_range = day_view_set_selected_time_range;
2002 	view_class->get_visible_time_range = day_view_get_visible_time_range;
2003 	view_class->precalc_visible_time_range = e_day_view_precalc_visible_time_range;
2004 	view_class->paste_text = day_view_paste_text;
2005 
2006 	g_object_class_install_property (
2007 		object_class,
2008 		PROP_DRAW_FLAT_EVENTS,
2009 		g_param_spec_boolean (
2010 			"draw-flat-events",
2011 			"Draw Flat Events",
2012 			NULL,
2013 			TRUE,
2014 			G_PARAM_READWRITE |
2015 			G_PARAM_CONSTRUCT |
2016 			G_PARAM_STATIC_STRINGS));
2017 
2018 	g_object_class_install_property (
2019 		object_class,
2020 		PROP_MARCUS_BAINS_SHOW_LINE,
2021 		g_param_spec_boolean (
2022 			"marcus-bains-show-line",
2023 			"Marcus Bains Show Line",
2024 			NULL,
2025 			TRUE,
2026 			G_PARAM_READWRITE |
2027 			G_PARAM_CONSTRUCT |
2028 			G_PARAM_STATIC_STRINGS));
2029 
2030 	g_object_class_install_property (
2031 		object_class,
2032 		PROP_MARCUS_BAINS_DAY_VIEW_COLOR,
2033 		g_param_spec_string (
2034 			"marcus-bains-day-view-color",
2035 			"Marcus Bains Day View Color",
2036 			NULL,
2037 			NULL,
2038 			G_PARAM_READWRITE |
2039 			G_PARAM_STATIC_STRINGS));
2040 
2041 	g_object_class_install_property (
2042 		object_class,
2043 		PROP_MARCUS_BAINS_TIME_BAR_COLOR,
2044 		g_param_spec_string (
2045 			"marcus-bains-time-bar-color",
2046 			"Marcus Bains Time Bar Color",
2047 			NULL,
2048 			NULL,
2049 			G_PARAM_READWRITE |
2050 			G_PARAM_STATIC_STRINGS));
2051 
2052 	g_object_class_override_property (
2053 		object_class,
2054 		PROP_IS_EDITING,
2055 		"is-editing");
2056 
2057 	/* init the accessibility support for e_day_view */
2058 	gtk_widget_class_set_accessible_type (widget_class, EA_TYPE_DAY_VIEW);
2059 }
2060 
2061 static void
e_day_view_init(EDayView * day_view)2062 e_day_view_init (EDayView *day_view)
2063 {
2064 	gint day;
2065 	GnomeCanvasGroup *canvas_group;
2066 	GtkAdjustment *adjustment;
2067 	GtkScrollable *scrollable;
2068 	GtkWidget *container;
2069 	GtkWidget *widget;
2070 	gulong handler_id;
2071 
2072 	day_view->priv = E_DAY_VIEW_GET_PRIVATE (day_view);
2073 
2074 	gtk_widget_set_can_focus (GTK_WIDGET (day_view), TRUE);
2075 
2076 	day_view->long_events = g_array_new (
2077 		FALSE, FALSE,
2078 		sizeof (EDayViewEvent));
2079 	day_view->long_events_sorted = TRUE;
2080 	day_view->long_events_need_layout = FALSE;
2081 	day_view->long_events_need_reshape = FALSE;
2082 
2083 	day_view->layout_timeout_id = 0;
2084 
2085 	for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++) {
2086 		day_view->events[day] = g_array_new (
2087 			FALSE, FALSE,
2088 			sizeof (EDayViewEvent));
2089 		day_view->events_sorted[day] = TRUE;
2090 		day_view->need_layout[day] = FALSE;
2091 		day_view->need_reshape[day] = FALSE;
2092 	}
2093 
2094 	/* These indicate that the times haven't been set. */
2095 	day_view->lower = 0;
2096 	day_view->upper = 0;
2097 
2098 	day_view->priv->days_shown = 1;
2099 
2100 	day_view->date_format = E_DAY_VIEW_DATE_FULL;
2101 	day_view->rows_in_top_display = 0;
2102 
2103 	/* Note that these don't work yet. It would need a few fixes to the
2104 	 * way event->start_minute and event->end_minute are used, and there
2105 	 * may be problems with events that go outside the visible times. */
2106 	day_view->first_hour_shown = 0;
2107 	day_view->first_minute_shown = 0;
2108 	day_view->last_hour_shown = 24;
2109 	day_view->last_minute_shown = 0;
2110 
2111 	e_day_view_recalc_num_rows (day_view);
2112 
2113 	day_view->show_event_end_times = TRUE;
2114 	day_view->scroll_to_work_day = TRUE;
2115 
2116 	day_view->editing_event_day = -1;
2117 	day_view->editing_event_num = -1;
2118 
2119 	day_view->resize_event_num = -1;
2120 	day_view->resize_bars_event_day = -1;
2121 	day_view->resize_bars_event_num = -1;
2122 
2123 	day_view->last_edited_comp_string = NULL;
2124 
2125 	day_view->selection_start_row = -1;
2126 	day_view->selection_start_day = -1;
2127 	day_view->selection_end_row = -1;
2128 	day_view->selection_end_day = -1;
2129 	day_view->selection_is_being_dragged = FALSE;
2130 	day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
2131 	day_view->selection_in_top_canvas = FALSE;
2132 	day_view->drag_last_day = -1;
2133 	day_view->drag_event_day = -1;
2134 	day_view->drag_event_num = -1;
2135 	day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
2136 	day_view->priv->drag_context = NULL;
2137 
2138 	day_view->pressed_event_day = -1;
2139 
2140 	day_view->auto_scroll_timeout_id = 0;
2141 
2142 	day_view->large_font_desc = NULL;
2143 	day_view->small_font_desc = NULL;
2144 
2145 	/* String to use in 12-hour time format for times in the morning. */
2146 	day_view->am_string = _("am");
2147 
2148 	/* String to use in 12-hour time format for times in the afternoon. */
2149 	day_view->pm_string = _("pm");
2150 
2151 	day_view->bc_event_time = 0;
2152 	day_view->before_click_dtstart = 0;
2153 	day_view->before_click_dtend = 0;
2154 
2155 	gtk_widget_set_margin_top (GTK_WIDGET (day_view), 1);
2156 
2157 	day_view->week_number_label = gtk_label_new ("");
2158 
2159 	widget = gtk_label_new (NULL);
2160 	gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
2161 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 1.0);
2162 	day_view->priv->timezone_name_1_label = widget;
2163 
2164 	widget = gtk_label_new (NULL);
2165 	gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
2166 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 1.0);
2167 	day_view->priv->timezone_name_2_label = widget;
2168 
2169 	widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
2170 
2171 	gtk_grid_attach (GTK_GRID (day_view), widget, 0, 0, 1, 1);
2172 	g_object_set (G_OBJECT (widget),
2173 		"hexpand", FALSE,
2174 		"vexpand", FALSE,
2175 		"halign", GTK_ALIGN_FILL,
2176 		"valign", GTK_ALIGN_FILL,
2177 		NULL);
2178 
2179 	container = widget;
2180 
2181 	gtk_box_pack_start (GTK_BOX (container), day_view->week_number_label, TRUE, TRUE, 2);
2182 
2183 	widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
2184 	gtk_box_pack_end (GTK_BOX (container), widget, TRUE, TRUE, 2);
2185 
2186 	gtk_widget_show_all (container);
2187 
2188 	container = widget;
2189 
2190 	gtk_box_set_homogeneous (GTK_BOX (container), TRUE);
2191 	gtk_box_pack_start (GTK_BOX (container), day_view->priv->timezone_name_1_label, TRUE, TRUE, 2);
2192 	gtk_box_pack_start (GTK_BOX (container), day_view->priv->timezone_name_2_label, TRUE, TRUE, 2);
2193 
2194 	gtk_widget_show_all (container);
2195 
2196 	/*
2197 	 * Top Canvas
2198 	 */
2199 	widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2200 	gtk_grid_attach (GTK_GRID (day_view), widget, 1, 0, 1, 1);
2201 	g_object_set (G_OBJECT (widget),
2202 		"hexpand", TRUE,
2203 		"vexpand", FALSE,
2204 		"halign", GTK_ALIGN_FILL,
2205 		"valign", GTK_ALIGN_FILL,
2206 		NULL);
2207 	gtk_widget_show (widget);
2208 
2209 	container = widget;
2210 
2211 	widget = e_canvas_new ();
2212 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
2213 	day_view->top_dates_canvas = widget;
2214 	gtk_widget_show (widget);
2215 
2216 	/* Keep our own canvas reference so we can
2217 	 * disconnect signal handlers in dispose(). */
2218 	widget = e_canvas_new ();
2219 	gtk_box_pack_end (GTK_BOX (container), widget, TRUE, TRUE, 0);
2220 	day_view->top_canvas = g_object_ref (widget);
2221 	gtk_widget_show (widget);
2222 
2223 	handler_id = g_signal_connect_after (
2224 		day_view->top_canvas, "button_press_event",
2225 		G_CALLBACK (e_day_view_on_top_canvas_button_press), day_view);
2226 	day_view->priv->top_canvas_button_press_event_handler_id = handler_id;
2227 
2228 	handler_id = g_signal_connect (
2229 		day_view->top_canvas, "button_release_event",
2230 		G_CALLBACK (e_day_view_on_top_canvas_button_release), day_view);
2231 	day_view->priv->top_canvas_button_release_event_handler_id = handler_id;
2232 
2233 	handler_id = g_signal_connect (
2234 		day_view->top_canvas, "scroll_event",
2235 		G_CALLBACK (e_day_view_on_top_canvas_scroll), day_view);
2236 	day_view->priv->top_canvas_scroll_event_handler_id = handler_id;
2237 
2238 	handler_id = g_signal_connect (
2239 		day_view->top_canvas, "motion_notify_event",
2240 		G_CALLBACK (e_day_view_on_top_canvas_motion), day_view);
2241 	day_view->priv->top_canvas_motion_notify_event_handler_id = handler_id;
2242 
2243 	handler_id = g_signal_connect (
2244 		day_view->top_canvas, "drag_motion",
2245 		G_CALLBACK (e_day_view_on_top_canvas_drag_motion), day_view);
2246 	day_view->priv->top_canvas_drag_motion_handler_id = handler_id;
2247 
2248 	handler_id = g_signal_connect (
2249 		day_view->top_canvas, "drag_leave",
2250 		G_CALLBACK (e_day_view_on_top_canvas_drag_leave), day_view);
2251 	day_view->priv->top_canvas_drag_leave_handler_id = handler_id;
2252 
2253 	handler_id = g_signal_connect (
2254 		day_view->top_canvas, "drag_begin",
2255 		G_CALLBACK (e_day_view_on_drag_begin), day_view);
2256 	day_view->priv->top_canvas_drag_begin_handler_id = handler_id;
2257 
2258 	handler_id = g_signal_connect (
2259 		day_view->top_canvas, "drag_end",
2260 		G_CALLBACK (e_day_view_on_drag_end), day_view);
2261 	day_view->priv->top_canvas_drag_end_handler_id = handler_id;
2262 
2263 	handler_id = g_signal_connect (
2264 		day_view->top_canvas, "drag_data_get",
2265 		G_CALLBACK (e_day_view_on_drag_data_get), day_view);
2266 	day_view->priv->top_canvas_drag_data_get_handler_id = handler_id;
2267 
2268 	handler_id = g_signal_connect (
2269 		day_view->top_canvas, "drag_data_received",
2270 		G_CALLBACK (e_day_view_on_top_canvas_drag_data_received), day_view);
2271 	day_view->priv->top_canvas_drag_data_received_handler_id = handler_id;
2272 
2273 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_dates_canvas)->root);
2274 
2275 	day_view->top_dates_canvas_item =
2276 		gnome_canvas_item_new (
2277 			canvas_group,
2278 			e_day_view_top_item_get_type (),
2279 			"EDayViewTopItem::day_view", day_view,
2280 			"EDayViewTopItem::show_dates", TRUE,
2281 			NULL);
2282 	gtk_widget_set_size_request (day_view->top_dates_canvas, -1, day_view->top_row_height);
2283 
2284 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_canvas)->root);
2285 
2286 	day_view->top_canvas_item =
2287 		gnome_canvas_item_new (
2288 			canvas_group,
2289 			e_day_view_top_item_get_type (),
2290 			"EDayViewTopItem::day_view", day_view,
2291 			"EDayViewTopItem::show_dates", FALSE,
2292 			NULL);
2293 
2294 	day_view->drag_long_event_rect_item =
2295 		gnome_canvas_item_new (
2296 			canvas_group,
2297 			gnome_canvas_rect_get_type (),
2298 			"line_width", 1.0,
2299 			NULL);
2300 	gnome_canvas_item_hide (day_view->drag_long_event_rect_item);
2301 
2302 	day_view->drag_long_event_item =
2303 		gnome_canvas_item_new (
2304 			canvas_group,
2305 			e_text_get_type (),
2306 			"line_wrap", TRUE,
2307 			"clip", TRUE,
2308 			"max_lines", 1,
2309 			"editable", TRUE,
2310 			"fill_color_rgba", GNOME_CANVAS_COLOR (0, 0, 0),
2311 			NULL);
2312 	gnome_canvas_item_hide (day_view->drag_long_event_item);
2313 
2314 	/*
2315 	 * Main Canvas
2316 	 */
2317 
2318 	/* Keep our own canvas reference so we can
2319 	 * disconnect signal handlers in dispose(). */
2320 	widget = e_canvas_new ();
2321 	gtk_grid_attach (GTK_GRID (day_view), widget, 1, 1, 1, 1);
2322 	g_object_set (G_OBJECT (widget),
2323 		"hexpand", TRUE,
2324 		"vexpand", TRUE,
2325 		"halign", GTK_ALIGN_FILL,
2326 		"valign", GTK_ALIGN_FILL,
2327 		NULL);
2328 	day_view->main_canvas = g_object_ref (widget);
2329 	gtk_widget_show (widget);
2330 
2331 	handler_id = g_signal_connect (
2332 		day_view->main_canvas, "realize",
2333 		G_CALLBACK (e_day_view_on_canvas_realized), day_view);
2334 	day_view->priv->main_canvas_realize_handler_id = handler_id;
2335 
2336 	handler_id = g_signal_connect (
2337 		day_view->main_canvas, "button_press_event",
2338 		G_CALLBACK (e_day_view_on_main_canvas_button_press), day_view);
2339 	day_view->priv->main_canvas_button_press_event_handler_id = handler_id;
2340 
2341 	handler_id = g_signal_connect (
2342 		day_view->main_canvas, "button_release_event",
2343 		G_CALLBACK (e_day_view_on_main_canvas_button_release),
2344 		day_view);
2345 	day_view->priv->main_canvas_button_release_event_handler_id = handler_id;
2346 
2347 	handler_id = g_signal_connect (
2348 		day_view->main_canvas, "scroll_event",
2349 		G_CALLBACK (e_day_view_on_main_canvas_scroll), day_view);
2350 	day_view->priv->main_canvas_scroll_event_handler_id = handler_id;
2351 
2352 	handler_id = g_signal_connect (
2353 		day_view->main_canvas, "motion_notify_event",
2354 		G_CALLBACK (e_day_view_on_main_canvas_motion), day_view);
2355 	day_view->priv->main_canvas_motion_notify_event_handler_id = handler_id;
2356 
2357 	handler_id = g_signal_connect (
2358 		day_view->main_canvas, "drag_motion",
2359 		G_CALLBACK (e_day_view_on_main_canvas_drag_motion), day_view);
2360 	day_view->priv->main_canvas_drag_motion_handler_id = handler_id;
2361 
2362 	handler_id = g_signal_connect (
2363 		day_view->main_canvas, "drag_leave",
2364 		G_CALLBACK (e_day_view_on_main_canvas_drag_leave), day_view);
2365 	day_view->priv->main_canvas_drag_leave_handler_id = handler_id;
2366 
2367 	handler_id = g_signal_connect (
2368 		day_view->main_canvas, "drag_begin",
2369 		G_CALLBACK (e_day_view_on_drag_begin), day_view);
2370 	day_view->priv->main_canvas_drag_begin_handler_id = handler_id;
2371 
2372 	handler_id = g_signal_connect (
2373 		day_view->main_canvas, "drag_end",
2374 		G_CALLBACK (e_day_view_on_drag_end), day_view);
2375 	day_view->priv->main_canvas_drag_end_handler_id = handler_id;
2376 
2377 	handler_id = g_signal_connect (
2378 		day_view->main_canvas, "drag_data_get",
2379 		G_CALLBACK (e_day_view_on_drag_data_get), day_view);
2380 	day_view->priv->main_canvas_drag_data_get_handler_id = handler_id;
2381 
2382 	handler_id = g_signal_connect (
2383 		day_view->main_canvas, "drag_data_received",
2384 		G_CALLBACK (e_day_view_on_main_canvas_drag_data_received), day_view);
2385 	day_view->priv->main_canvas_drag_data_received_handler_id = handler_id;
2386 
2387 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->main_canvas)->root);
2388 
2389 	day_view->main_canvas_item =
2390 		gnome_canvas_item_new (
2391 			canvas_group,
2392 			e_day_view_main_item_get_type (),
2393 			"EDayViewMainItem::day_view", day_view,
2394 			NULL);
2395 
2396 	day_view->drag_rect_item =
2397 		gnome_canvas_item_new (
2398 			canvas_group,
2399 			gnome_canvas_rect_get_type (),
2400 			"line_width", 1.0,
2401 			NULL);
2402 	gnome_canvas_item_hide (day_view->drag_rect_item);
2403 
2404 	day_view->drag_bar_item =
2405 		gnome_canvas_item_new (
2406 			canvas_group,
2407 			gnome_canvas_rect_get_type (),
2408 			"line_width", 1.0,
2409 			NULL);
2410 	gnome_canvas_item_hide (day_view->drag_bar_item);
2411 
2412 	day_view->drag_item =
2413 		gnome_canvas_item_new (
2414 			canvas_group,
2415 			e_text_get_type (),
2416 			"line_wrap", TRUE,
2417 			"clip", TRUE,
2418 			"editable", TRUE,
2419 			"fill_color_rgba", GNOME_CANVAS_COLOR (0, 0, 0),
2420 			NULL);
2421 	gnome_canvas_item_hide (day_view->drag_item);
2422 
2423 	/*
2424 	 * Times Canvas
2425 	 */
2426 
2427 	/* Keep our own canvas reference so we can
2428 	 * disconnect signal handlers in dispose(). */
2429 	widget = e_canvas_new ();
2430 	scrollable = GTK_SCROLLABLE (day_view->main_canvas);
2431 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
2432 	scrollable = GTK_SCROLLABLE (widget);
2433 	gtk_scrollable_set_vadjustment (scrollable, adjustment);
2434 	gtk_grid_attach (GTK_GRID (day_view), widget, 0, 1, 1, 1);
2435 	g_object_set (G_OBJECT (widget),
2436 		"hexpand", FALSE,
2437 		"vexpand", TRUE,
2438 		"halign", GTK_ALIGN_FILL,
2439 		"valign", GTK_ALIGN_FILL,
2440 		NULL);
2441 	day_view->time_canvas = g_object_ref (widget);
2442 	gtk_widget_show (widget);
2443 
2444 	handler_id = g_signal_connect_after (
2445 		day_view->time_canvas, "scroll_event",
2446 		G_CALLBACK (e_day_view_on_time_canvas_scroll), day_view);
2447 	day_view->priv->time_canvas_scroll_event_handler_id = handler_id;
2448 
2449 	canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->time_canvas)->root);
2450 
2451 	day_view->time_canvas_item =
2452 		gnome_canvas_item_new (
2453 			canvas_group,
2454 			e_day_view_time_item_get_type (),
2455 			"EDayViewTimeItem::day_view", day_view,
2456 			NULL);
2457 
2458 	/*
2459 	 * Scrollbar.
2460 	 */
2461 	scrollable = GTK_SCROLLABLE (day_view->main_canvas);
2462 	adjustment = gtk_scrollable_get_hadjustment (scrollable);
2463 	day_view->mc_hscrollbar = gtk_scrollbar_new (
2464 		GTK_ORIENTATION_HORIZONTAL, adjustment);
2465 	gtk_grid_attach (GTK_GRID (day_view), day_view->mc_hscrollbar, 1, 2, 1, 1);
2466 	g_object_set (G_OBJECT (day_view->mc_hscrollbar),
2467 		"hexpand", FALSE,
2468 		"vexpand", FALSE,
2469 		"halign", GTK_ALIGN_FILL,
2470 		"valign", GTK_ALIGN_START,
2471 		NULL);
2472 	gtk_widget_show (day_view->mc_hscrollbar);
2473 
2474 	scrollable = GTK_SCROLLABLE (day_view->top_canvas);
2475 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
2476 	day_view->tc_vscrollbar = gtk_scrollbar_new (
2477 		GTK_ORIENTATION_VERTICAL, adjustment);
2478 	gtk_grid_attach (GTK_GRID (day_view), day_view->tc_vscrollbar, 2, 0, 1, 1);
2479 	g_object_set (G_OBJECT (day_view->tc_vscrollbar),
2480 		"hexpand", FALSE,
2481 		"vexpand", FALSE,
2482 		"halign", GTK_ALIGN_START,
2483 		"valign", GTK_ALIGN_FILL,
2484 		NULL);
2485 	/* gtk_widget_show (day_view->tc_vscrollbar); */
2486 
2487 	scrollable = GTK_SCROLLABLE (day_view->main_canvas);
2488 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
2489 	day_view->vscrollbar = gtk_scrollbar_new (
2490 		GTK_ORIENTATION_VERTICAL, adjustment);
2491 	gtk_grid_attach (GTK_GRID (day_view), day_view->vscrollbar, 2, 1, 1, 1);
2492 	g_object_set (G_OBJECT (day_view->vscrollbar),
2493 		"hexpand", FALSE,
2494 		"vexpand", TRUE,
2495 		"halign", GTK_ALIGN_START,
2496 		"valign", GTK_ALIGN_FILL,
2497 		NULL);
2498 	gtk_widget_show (day_view->vscrollbar);
2499 
2500 	/* Create the cursors. */
2501 	day_view->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR);
2502 	day_view->move_cursor = gdk_cursor_new (GDK_FLEUR);
2503 	day_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
2504 	day_view->resize_height_cursor = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
2505 	day_view->last_cursor_set_in_top_canvas = NULL;
2506 	day_view->last_cursor_set_in_main_canvas = NULL;
2507 
2508 	/* Set up the drop sites. */
2509 	gtk_drag_dest_set (
2510 		day_view->top_canvas, GTK_DEST_DEFAULT_ALL,
2511 		target_table, G_N_ELEMENTS (target_table),
2512 		GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
2513 
2514 	e_drag_dest_add_calendar_targets (day_view->top_canvas);
2515 
2516 	gtk_drag_dest_set (
2517 		day_view->main_canvas, GTK_DEST_DEFAULT_ALL,
2518 		target_table, G_N_ELEMENTS (target_table),
2519 		GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
2520 
2521 	e_drag_dest_add_calendar_targets (day_view->main_canvas);
2522 
2523 	day_view->requires_update = FALSE;
2524 }
2525 
2526 static void
e_day_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)2527 e_day_view_precalc_visible_time_range (ECalendarView *cal_view,
2528 				       time_t in_start_time,
2529 				       time_t in_end_time,
2530 				       time_t *out_start_time,
2531 				       time_t *out_end_time)
2532 {
2533 	EDayView *day_view;
2534 	gint days_shown;
2535 	time_t lower;
2536 	ICalTimezone *zone;
2537 
2538 	g_return_if_fail (E_IS_DAY_VIEW (cal_view));
2539 	g_return_if_fail (out_start_time != NULL);
2540 	g_return_if_fail (out_end_time != NULL);
2541 
2542 	day_view = E_DAY_VIEW (cal_view);
2543 	days_shown = e_day_view_get_days_shown (day_view);
2544 	zone = e_calendar_view_get_timezone (cal_view);
2545 
2546 	/* Calculate the first day that should be shown, based on start_time
2547 	 * and the days_shown setting. If we are showing 1 day it is just the
2548 	 * start of the day given by start_time, otherwise it is the previous
2549 	 * work-week start day. */
2550 	if (!e_day_view_get_work_week_view (day_view)) {
2551 		lower = time_day_begin_with_zone (in_start_time, zone);
2552 	} else {
2553 		lower = e_day_view_find_work_week_start (day_view, in_start_time);
2554 	}
2555 
2556 	/* See if we need to change the days shown. */
2557 	if (lower == day_view->lower) {
2558 		*out_start_time = day_view->lower;
2559 		*out_end_time = day_view->upper;
2560 	} else {
2561 		gint day;
2562 
2563 		*out_start_time = lower;
2564 		*out_end_time = lower;
2565 
2566 		for (day = 1; day <= days_shown; day++) {
2567 			*out_end_time = time_add_day_with_zone (*out_end_time, 1, zone);
2568 		}
2569 	}
2570 }
2571 
2572 static void
time_range_changed_cb(ECalModel * model,gint64 i64_start_time,gint64 i64_end_time,gpointer user_data)2573 time_range_changed_cb (ECalModel *model,
2574                        gint64 i64_start_time,
2575                        gint64 i64_end_time,
2576                        gpointer user_data)
2577 {
2578 	EDayView *day_view = E_DAY_VIEW (user_data);
2579 	EDayViewTimeItem *eti;
2580 	gint days_shown;
2581 	time_t lower, start_time = (time_t) i64_start_time, end_time = (time_t) i64_end_time;
2582 
2583 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
2584 
2585 	days_shown = e_day_view_get_days_shown (day_view);
2586 
2587 	/* Calculate the first day that should be shown, based on start_time
2588 	 * and the days_shown setting. If we are showing 1 day it is just the
2589 	 * start of the day given by start_time, otherwise it is the previous
2590 	 * work-week start day. */
2591 	if (!e_day_view_get_work_week_view (day_view)) {
2592 		lower = time_day_begin_with_zone (start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
2593 	} else {
2594 		lower = e_day_view_find_work_week_start (day_view, start_time);
2595 	}
2596 
2597 	/* See if we need to change the days shown. */
2598 	if (lower != day_view->lower)
2599 		e_day_view_recalc_day_starts (day_view, lower);
2600 
2601 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2602 		e_day_view_free_events (day_view);
2603 		day_view->requires_update = TRUE;
2604 		return;
2605 	}
2606 
2607 	/* If we don't show the new selection, don't preserve it */
2608 	if (day_view->selection_start_day == -1 || days_shown <= day_view->selection_start_day)
2609 		day_view_set_selected_time_range (E_CALENDAR_VIEW (day_view), start_time, end_time);
2610 
2611 	if (day_view->selection_start_row != -1)
2612 		e_day_view_ensure_rows_visible (day_view, day_view->selection_start_row, day_view->selection_start_row);
2613 
2614 	/* update the time canvas to show proper date in it */
2615 	eti = E_DAY_VIEW_TIME_ITEM (day_view->time_canvas_item);
2616 	if (eti && e_day_view_time_item_get_second_zone (eti))
2617 		gtk_widget_queue_draw (day_view->time_canvas);
2618 }
2619 
2620 static void
process_component(EDayView * day_view,ECalModelComponent * comp_data)2621 process_component (EDayView *day_view,
2622                    ECalModelComponent *comp_data)
2623 {
2624 	ECalModel *model;
2625 	ECalComponent *comp;
2626 	ESourceRegistry *registry;
2627 	AddEventData add_event_data;
2628 
2629 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
2630 	registry = e_cal_model_get_registry (model);
2631 
2632 	/* If our time hasn't been set yet, just return. */
2633 	if (day_view->lower == 0 && day_view->upper == 0)
2634 		return;
2635 
2636 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (comp_data->icalcomp));
2637 	if (!comp) {
2638 		g_message (G_STRLOC ": Could not set ICalComponent on ECalComponent");
2639 		return;
2640 	}
2641 
2642 	/* Add the object */
2643 	add_event_data.day_view = day_view;
2644 	add_event_data.comp_data = comp_data;
2645 	e_day_view_add_event (
2646 		registry, comp_data->client, comp, comp_data->instance_start,
2647 		comp_data->instance_end, &add_event_data);
2648 
2649 	g_object_unref (comp);
2650 }
2651 
2652 static void
update_row(EDayView * day_view,gint row,gboolean do_cancel_editing)2653 update_row (EDayView *day_view,
2654 	    gint row,
2655 	    gboolean do_cancel_editing)
2656 {
2657 	ECalModelComponent *comp_data;
2658 	ECalModel *model;
2659 	gint day, event_num;
2660 	const gchar *uid;
2661 	gchar *rid;
2662 
2663 	if (do_cancel_editing)
2664 		cancel_editing (day_view);
2665 	else
2666 		e_day_view_stop_editing_event (day_view);
2667 
2668 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
2669 	comp_data = e_cal_model_get_component_at (model, row);
2670 	g_return_if_fail (comp_data != NULL);
2671 
2672 	uid = i_cal_component_get_uid (comp_data->icalcomp);
2673 	rid = e_cal_util_component_get_recurid_as_string (comp_data->icalcomp);
2674 
2675 	if (e_day_view_find_event_from_uid (day_view, comp_data->client, uid, rid, &day, &event_num))
2676 		e_day_view_remove_event_cb (day_view, day, event_num, NULL);
2677 
2678 	g_free (rid);
2679 
2680 	process_component (day_view, comp_data);
2681 
2682 	gtk_widget_queue_draw (day_view->top_canvas);
2683 	gtk_widget_queue_draw (day_view->main_canvas);
2684 	e_day_view_queue_layout (day_view);
2685 }
2686 
2687 static void
model_row_changed_cb(ETableModel * etm,gint row,gpointer user_data)2688 model_row_changed_cb (ETableModel *etm,
2689                       gint row,
2690                       gpointer user_data)
2691 {
2692 	EDayView *day_view = E_DAY_VIEW (user_data);
2693 
2694 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2695 		e_day_view_free_events (day_view);
2696 		day_view->requires_update = TRUE;
2697 		return;
2698 	}
2699 
2700 	update_row (day_view, row, TRUE);
2701 }
2702 
2703 static void
model_cell_changed_cb(ETableModel * etm,gint col,gint row,gpointer user_data)2704 model_cell_changed_cb (ETableModel *etm,
2705                        gint col,
2706                        gint row,
2707                        gpointer user_data)
2708 {
2709 	EDayView *day_view = E_DAY_VIEW (user_data);
2710 
2711 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2712 		e_day_view_free_events (day_view);
2713 		day_view->requires_update = TRUE;
2714 		return;
2715 	}
2716 
2717 	update_row (day_view, row, FALSE);
2718 }
2719 
2720 static void
model_rows_inserted_cb(ETableModel * etm,gint row,gint count,gpointer user_data)2721 model_rows_inserted_cb (ETableModel *etm,
2722                         gint row,
2723                         gint count,
2724                         gpointer user_data)
2725 {
2726 	EDayView *day_view = E_DAY_VIEW (user_data);
2727 	ECalModel *model;
2728 	gint i;
2729 
2730 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2731 		e_day_view_free_events (day_view);
2732 		day_view->requires_update = TRUE;
2733 		return;
2734 	}
2735 
2736 	e_day_view_stop_editing_event (day_view);
2737 
2738 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
2739 	for (i = 0; i < count; i++) {
2740 		ECalModelComponent *comp_data;
2741 
2742 		comp_data = e_cal_model_get_component_at (model, row + i);
2743 		if (comp_data == NULL) {
2744 			g_warning ("comp_data is NULL\n");
2745 			continue;
2746 		}
2747 		process_component (day_view, comp_data);
2748 	}
2749 
2750 	gtk_widget_queue_draw (day_view->top_canvas);
2751 	gtk_widget_queue_draw (day_view->main_canvas);
2752 	e_day_view_queue_layout (day_view);
2753 
2754 }
2755 
2756 static void
model_comps_deleted_cb(ETableModel * etm,gpointer data,gpointer user_data)2757 model_comps_deleted_cb (ETableModel *etm,
2758                         gpointer data,
2759                         gpointer user_data)
2760 {
2761 	EDayView *day_view = E_DAY_VIEW (user_data);
2762 	GSList *l, *list = data;
2763 
2764 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2765 		e_day_view_free_events (day_view);
2766 		day_view->requires_update = TRUE;
2767 		return;
2768 	}
2769 
2770 	e_day_view_stop_editing_event (day_view);
2771 
2772 	for (l = list; l != NULL; l = g_slist_next (l)) {
2773 		ECalModelComponent *comp_data = l->data;
2774 		gint day, event_num;
2775 		const gchar *uid;
2776 		gchar *rid;
2777 
2778 		uid = i_cal_component_get_uid (comp_data->icalcomp);
2779 		rid = e_cal_util_component_get_recurid_as_string (comp_data->icalcomp);
2780 
2781 		if (e_day_view_find_event_from_uid (day_view, comp_data->client, uid, rid, &day, &event_num))
2782 			e_day_view_remove_event_cb (day_view, day, event_num, NULL);
2783 
2784 		g_free (rid);
2785 	}
2786 
2787 	gtk_widget_queue_draw (day_view->top_canvas);
2788 	gtk_widget_queue_draw (day_view->main_canvas);
2789 	e_day_view_queue_layout (day_view);
2790 }
2791 
2792 static void
timezone_changed_cb(ECalModel * cal_model,ICalTimezone * old_zone,ICalTimezone * new_zone,gpointer user_data)2793 timezone_changed_cb (ECalModel *cal_model,
2794 		     ICalTimezone *old_zone,
2795 		     ICalTimezone *new_zone,
2796 		     gpointer user_data)
2797 {
2798 	ICalTime *tt;
2799 	time_t lower;
2800 	EDayView *day_view = (EDayView *) user_data;
2801 	ECalendarView *cal_view = (ECalendarView *) day_view;
2802 
2803 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
2804 
2805 	e_day_view_update_timezone_name_labels (day_view);
2806 
2807 	if (!cal_view->in_focus) {
2808 		e_day_view_free_events (day_view);
2809 		day_view->requires_update = TRUE;
2810 		return;
2811 	}
2812 
2813 	/* If our time hasn't been set yet, just return. */
2814 	if (day_view->lower == 0 && day_view->upper == 0)
2815 		return;
2816 
2817 	/* Recalculate the new start of the first day. We just use exactly
2818 	 * the same time, but with the new timezone. */
2819 	tt = i_cal_time_new_from_timet_with_zone (
2820 		day_view->lower, FALSE,
2821 		old_zone);
2822 
2823 	lower = i_cal_time_as_timet_with_zone (tt, new_zone);
2824 
2825 	g_clear_object (&tt);
2826 
2827 	e_day_view_recalc_day_starts (day_view, lower);
2828 	e_day_view_update_query (day_view);
2829 }
2830 
2831 static void
init_model(EDayView * day_view,ECalModel * model)2832 init_model (EDayView *day_view,
2833             ECalModel *model)
2834 {
2835 	gulong handler_id;
2836 
2837 	handler_id = g_signal_connect (
2838 		model, "time_range_changed",
2839 		G_CALLBACK (time_range_changed_cb), day_view);
2840 	day_view->priv->time_range_changed_handler_id = handler_id;
2841 
2842 	handler_id = g_signal_connect (
2843 		model, "model_row_changed",
2844 		G_CALLBACK (model_row_changed_cb), day_view);
2845 	day_view->priv->model_row_changed_handler_id = handler_id;
2846 
2847 	handler_id = g_signal_connect (
2848 		model, "model_cell_changed",
2849 		G_CALLBACK (model_cell_changed_cb), day_view);
2850 	day_view->priv->model_cell_changed_handler_id = handler_id;
2851 
2852 	handler_id = g_signal_connect (
2853 		model, "model_rows_inserted",
2854 		G_CALLBACK (model_rows_inserted_cb), day_view);
2855 	day_view->priv->model_rows_inserted_handler_id = handler_id;
2856 
2857 	handler_id = g_signal_connect (
2858 		model, "comps_deleted",
2859 		G_CALLBACK (model_comps_deleted_cb), day_view);
2860 	day_view->priv->comps_deleted_handler_id = handler_id;
2861 
2862 	handler_id = g_signal_connect (
2863 		model, "timezone_changed",
2864 		G_CALLBACK (timezone_changed_cb), day_view);
2865 	day_view->priv->timezone_changed_handler_id = handler_id;
2866 }
2867 
2868 /* Turn off the background of the canvas windows. This reduces flicker
2869  * considerably when scrolling. (Why isn't it in GnomeCanvas?). */
2870 static void
e_day_view_on_canvas_realized(GtkWidget * widget,EDayView * day_view)2871 e_day_view_on_canvas_realized (GtkWidget *widget,
2872                                EDayView *day_view)
2873 {
2874 	GdkWindow *window;
2875 
2876 	window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
2877 	gdk_window_set_background_pattern (window, NULL);
2878 }
2879 
2880 /**
2881  * e_day_view_new:
2882  * @Returns: a new #EDayView.
2883  *
2884  * Creates a new #EDayView.
2885  **/
2886 ECalendarView *
e_day_view_new(ECalModel * model)2887 e_day_view_new (ECalModel *model)
2888 {
2889 	ECalendarView *day_view;
2890 
2891 	g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
2892 
2893 	day_view = g_object_new (E_TYPE_DAY_VIEW, "model", model, NULL);
2894 	init_model (E_DAY_VIEW (day_view), model);
2895 
2896 	return day_view;
2897 }
2898 
2899 static void
e_day_view_set_colors(EDayView * day_view)2900 e_day_view_set_colors (EDayView *day_view)
2901 {
2902 	GtkWidget *widget = GTK_WIDGET (day_view);
2903 	GdkRGBA base_bg, bg_bg, selected_bg, unfocused_selected_bg, dark_bg, light_bg;
2904 
2905 	e_utils_get_theme_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &base_bg);
2906 	e_utils_get_theme_color (widget, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR, &bg_bg);
2907 	e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &selected_bg);
2908 	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);
2909 
2910 	e_utils_shade_color (&bg_bg, &dark_bg, E_UTILS_DARKNESS_MULT);
2911 	e_utils_shade_color (&bg_bg, &light_bg, E_UTILS_LIGHTNESS_MULT);
2912 
2913 	e_rgba_to_color (&base_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING]);
2914 	e_rgba_to_color (&bg_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING]);
2915 	e_rgba_to_color (&selected_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_SELECTED]);
2916 	e_rgba_to_color (&unfocused_selected_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_SELECTED_UNFOCUSSED]);
2917 	e_rgba_to_color (&dark_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_GRID]);
2918 	e_rgba_to_color (&dark_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS]);
2919 	e_rgba_to_color (&selected_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS_SELECTED]);
2920 	e_rgba_to_color (&light_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS_GRID]);
2921 	e_rgba_to_color (&selected_bg, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR]);
2922 	e_rgba_to_color (&base_bg, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND]);
2923 	e_rgba_to_color (&dark_bg, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER]);
2924 	e_rgba_to_color (&base_bg, &day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BACKGROUND]);
2925 	e_rgba_to_color (&dark_bg, &day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BORDER]);
2926 
2927 	day_view->colors[E_DAY_VIEW_COLOR_BG_MULTIDAY_TODAY] = get_today_background (day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING]);
2928 
2929 	bg_bg.red = 0.5;
2930 	bg_bg.green = 1.0;
2931 	bg_bg.blue = 1.0;
2932 
2933 	e_rgba_to_color (&bg_bg, &day_view->colors[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE]);
2934 }
2935 
2936 static void
e_day_view_update_top_scroll(EDayView * day_view,gboolean scroll_to_top)2937 e_day_view_update_top_scroll (EDayView *day_view,
2938                               gboolean scroll_to_top)
2939 {
2940 	GtkAllocation allocation;
2941 	gint top_rows, top_canvas_height;
2942 	gdouble old_x2, old_y2, new_x2, new_y2;
2943 
2944 	/* Set the height of the top canvas based on the row height and the
2945 	 * number of rows needed (min 1 + 1 for the dates + 1 space for DnD).*/
2946 	top_rows = MAX (1, day_view->rows_in_top_display);
2947 	top_canvas_height = (top_rows + 1) * day_view->top_row_height;
2948 	if (top_rows <= E_DAY_VIEW_MAX_ROWS_AT_TOP) {
2949 		gtk_widget_set_size_request (day_view->top_canvas, -1, top_canvas_height);
2950 		gtk_widget_hide (day_view->tc_vscrollbar);
2951 	} else {
2952 		gtk_widget_set_size_request (day_view->top_canvas, -1, (E_DAY_VIEW_MAX_ROWS_AT_TOP + 1) * day_view->top_row_height);
2953 		gtk_widget_show (day_view->tc_vscrollbar);
2954 	}
2955 
2956 	/* Set the scroll region of the top canvas */
2957 	gnome_canvas_get_scroll_region (
2958 		GNOME_CANVAS (day_view->top_canvas),
2959 		NULL, NULL, &old_x2, &old_y2);
2960 	gtk_widget_get_allocation (day_view->top_canvas, &allocation);
2961 	new_x2 = allocation.width - 1;
2962 	new_y2 = (MAX (1, day_view->rows_in_top_display) + 1) * day_view->top_row_height - 1;
2963 	if (old_x2 != new_x2 || old_y2 != new_y2) {
2964 		gnome_canvas_set_scroll_region (
2965 			GNOME_CANVAS (day_view->top_canvas),
2966 			0, 0, new_x2, new_y2);
2967 
2968 		if (scroll_to_top)
2969 			gnome_canvas_scroll_to (GNOME_CANVAS (day_view->top_canvas), 0, 0);
2970 	}
2971 	new_y2 = day_view->top_row_height - 1 - 2;
2972 	gnome_canvas_get_scroll_region (GNOME_CANVAS (day_view->top_dates_canvas), NULL, NULL, &old_x2, &old_y2);
2973 
2974 	if (old_x2 != new_x2 || old_y2 != new_y2) {
2975 		gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->top_dates_canvas), 0, 0, new_x2, new_y2);
2976 		gnome_canvas_scroll_to (GNOME_CANVAS (day_view->top_dates_canvas), 0, 0);
2977 	}
2978 }
2979 
2980 static void
e_day_view_recalc_cell_sizes(EDayView * day_view)2981 e_day_view_recalc_cell_sizes (EDayView *day_view)
2982 {
2983 	/* An array of dates, one for each month in the year 2000. They must
2984 	 * all be Sundays. */
2985 	static const gint days[12] = { 23, 20, 19, 23, 21, 18,
2986 				      23, 20, 17, 22, 19, 24 };
2987 	gfloat width, offset;
2988 	gint day, max_width;
2989 	struct tm date_tm;
2990 	gchar buffer[128];
2991 	GtkAllocation allocation;
2992 	PangoContext *pango_context;
2993 	PangoLayout *layout;
2994 	gint pango_width;
2995 	gint days_shown;
2996 
2997 	days_shown = e_day_view_get_days_shown (day_view);
2998 
2999 	gtk_widget_get_allocation (day_view->main_canvas, &allocation);
3000 
3001 	/* Set up Pango prerequisites */
3002 	pango_context = gtk_widget_get_pango_context (GTK_WIDGET (day_view));
3003 	layout = pango_layout_new (pango_context);
3004 
3005 	/* Calculate the column sizes, using floating point so that pixels
3006 	 * get divided evenly. Note that we use one more element than the
3007 	 * number of columns, to make it easy to get the column widths. */
3008 	width = allocation.width;
3009 	if (days_shown == 1)
3010 		width = MAX (width, day_view->max_cols * (E_DAY_VIEW_MIN_DAY_COL_WIDTH + E_DAY_VIEW_GAP_WIDTH) - E_DAY_VIEW_MIN_DAY_COL_WIDTH - 1);
3011 	width /= days_shown;
3012 	offset = 0;
3013 	for (day = 0; day <= days_shown; day++) {
3014 		day_view->day_offsets[day] = floor (offset + 0.5);
3015 		offset += width;
3016 	}
3017 
3018 	/* Calculate the days widths based on the offsets. */
3019 	for (day = 0; day < days_shown; day++) {
3020 		day_view->day_widths[day] = day_view->day_offsets[day + 1] - day_view->day_offsets[day];
3021 	}
3022 
3023 	/* Determine which date format to use, based on the column widths.
3024 	 * We want to check the widths using the longest full or abbreviated
3025 	 * month name and the longest full or abbreviated weekday name, as
3026 	 * appropriate. */
3027 	max_width = day_view->day_widths[0];
3028 
3029 	memset (&date_tm, 0, sizeof (date_tm));
3030 	date_tm.tm_year = 100;
3031 
3032 	/* Try "Thursday 21 January". */
3033 	date_tm.tm_mon = day_view->longest_month_name;
3034 	date_tm.tm_mday = days[date_tm.tm_mon]
3035 		+ day_view->longest_weekday_name;
3036 	date_tm.tm_wday = day_view->longest_weekday_name;
3037 	date_tm.tm_isdst = -1;
3038 	/* strftime format %A = full weekday name, %d = day of month,
3039 	 * %B = full month name. Don't use any other specifiers. */
3040 	e_utf8_strftime (buffer, sizeof (buffer), _("%A %d %B"), &date_tm);
3041 	pango_layout_set_text (layout, buffer, -1);
3042 	pango_layout_get_pixel_size (layout, &pango_width, NULL);
3043 
3044 	if (pango_width < max_width) {
3045 		day_view->date_format = E_DAY_VIEW_DATE_FULL;
3046 		goto exit;
3047 	}
3048 
3049 	/* Try "Thu 21 Jan". */
3050 	date_tm.tm_mon = day_view->longest_abbreviated_month_name;
3051 	date_tm.tm_mday = days[date_tm.tm_mon]
3052 		+ day_view->longest_abbreviated_weekday_name;
3053 	date_tm.tm_wday = day_view->longest_abbreviated_weekday_name;
3054 	date_tm.tm_isdst = -1;
3055 	/* strftime format %a = abbreviated weekday name, %d = day of month,
3056 	 * %b = abbreviated month name. Don't use any other specifiers. */
3057 	e_utf8_strftime (buffer, sizeof (buffer), _("%a %d %b"), &date_tm);
3058 	pango_layout_set_text (layout, buffer, -1);
3059 	pango_layout_get_pixel_size (layout, &pango_width, NULL);
3060 
3061 	if (pango_width < max_width) {
3062 		day_view->date_format = E_DAY_VIEW_DATE_ABBREVIATED;
3063 		goto exit;
3064 	}
3065 
3066 	/* Try "23 Jan". */
3067 	date_tm.tm_mon = day_view->longest_abbreviated_month_name;
3068 	date_tm.tm_mday = 23;
3069 	date_tm.tm_wday = 0;
3070 	date_tm.tm_isdst = -1;
3071 	/* strftime format %d = day of month, %b = abbreviated month name.
3072 	 * Don't use any other specifiers. */
3073 	e_utf8_strftime (buffer, sizeof (buffer), _("%d %b"), &date_tm);
3074 	pango_layout_set_text (layout, buffer, -1);
3075 	pango_layout_get_pixel_size (layout, &pango_width, NULL);
3076 
3077 	if (pango_width < max_width)
3078 		day_view->date_format = E_DAY_VIEW_DATE_NO_WEEKDAY;
3079 	else
3080 		day_view->date_format = E_DAY_VIEW_DATE_SHORT;
3081 
3082 exit:
3083 	g_object_unref (layout);
3084 }
3085 
3086 /* This calls a given function for each event instance (in both views).
3087  * If the callback returns FALSE the iteration is stopped.
3088  * Note that it is safe for the callback to remove the event (since we
3089  * step backwards through the arrays). */
3090 static void
e_day_view_foreach_event(EDayView * day_view,EDayViewForeachEventCallback callback,gpointer data)3091 e_day_view_foreach_event (EDayView *day_view,
3092                           EDayViewForeachEventCallback callback,
3093                           gpointer data)
3094 {
3095 	gint day, event_num;
3096 	gint days_shown;
3097 
3098 	days_shown = e_day_view_get_days_shown (day_view);
3099 
3100 	for (day = 0; day < days_shown; day++) {
3101 		for (event_num = day_view->events[day]->len - 1;
3102 		     event_num >= 0;
3103 		     event_num--) {
3104 			if (!(*callback) (day_view, day, event_num, data))
3105 				return;
3106 		}
3107 	}
3108 
3109 	for (event_num = day_view->long_events->len - 1;
3110 	     event_num >= 0;
3111 	     event_num--) {
3112 		if (!(*callback) (day_view, E_DAY_VIEW_LONG_EVENT, event_num,
3113 				  data))
3114 			return;
3115 	}
3116 }
3117 
3118 /* This calls a given function for each event instance that matches the given
3119  * uid. If the callback returns FALSE the iteration is stopped.
3120  * Note that it is safe for the callback to remove the event (since we
3121  * step backwards through the arrays). */
3122 static void
e_day_view_foreach_event_with_uid(EDayView * day_view,const gchar * uid,EDayViewForeachEventCallback callback,gpointer data)3123 e_day_view_foreach_event_with_uid (EDayView *day_view,
3124                                    const gchar *uid,
3125                                    EDayViewForeachEventCallback callback,
3126                                    gpointer data)
3127 {
3128 	EDayViewEvent *event;
3129 	gint day, event_num;
3130 	gint days_shown;
3131 	const gchar *u;
3132 
3133 	if (!uid)
3134 		return;
3135 
3136 	days_shown = e_day_view_get_days_shown (day_view);
3137 
3138 	for (day = 0; day < days_shown; day++) {
3139 		for (event_num = day_view->events[day]->len - 1;
3140 		     event_num >= 0;
3141 		     event_num--) {
3142 			event = &g_array_index (day_view->events[day],
3143 						EDayViewEvent, event_num);
3144 
3145 			if (!is_comp_data_valid (event))
3146 				continue;
3147 
3148 			u = i_cal_component_get_uid (event->comp_data->icalcomp);
3149 			if (u && !strcmp (uid, u)) {
3150 				if (!(*callback) (day_view, day, event_num, data))
3151 					return;
3152 			}
3153 		}
3154 	}
3155 
3156 	for (event_num = day_view->long_events->len - 1;
3157 	     event_num >= 0;
3158 	     event_num--) {
3159 		event = &g_array_index (day_view->long_events,
3160 					EDayViewEvent, event_num);
3161 
3162 		if (!is_comp_data_valid (event))
3163 			continue;
3164 
3165 		u = i_cal_component_get_uid (event->comp_data->icalcomp);
3166 		if (u && !strcmp (uid, u)) {
3167 			if (!(*callback) (day_view, E_DAY_VIEW_LONG_EVENT, event_num, data))
3168 				return;
3169 		}
3170 	}
3171 }
3172 
3173 static gboolean
e_day_view_remove_event_cb(EDayView * day_view,gint day,gint event_num,gpointer data)3174 e_day_view_remove_event_cb (EDayView *day_view,
3175                             gint day,
3176                             gint event_num,
3177                             gpointer data)
3178 {
3179 	EDayViewEvent *event;
3180 
3181 	if (day == E_DAY_VIEW_LONG_EVENT) {
3182 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
3183 			return TRUE;
3184 
3185 		event = &g_array_index (day_view->long_events,
3186 					EDayViewEvent, event_num);
3187 	} else {
3188 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
3189 			return TRUE;
3190 
3191 		event = &g_array_index (day_view->events[day],
3192 					EDayViewEvent, event_num);
3193 	}
3194 
3195 	/* If we were editing this event, set editing_event_day to -1 so
3196 	 * on_editing_stopped doesn't try to update the event. */
3197 	if (day_view->editing_event_num == event_num && day_view->editing_event_day == day) {
3198 		cancel_editing (day_view);
3199 		day_view->editing_event_num = -1;
3200 		day_view->editing_event_day = -1;
3201 		g_object_notify (G_OBJECT (day_view), "is-editing");
3202 	} else if (day_view->editing_event_num > event_num && day_view->editing_event_day == day) {
3203 		day_view->editing_event_num--;
3204 	}
3205 
3206 	if (day_view->popup_event_num == event_num && day_view->popup_event_day == day) {
3207 		e_day_view_set_popup_event (day_view, -1, -1);
3208 	} else if (day_view->popup_event_num > event_num && day_view->popup_event_day == day) {
3209 		day_view->popup_event_num--;
3210 	}
3211 
3212 	if (event->timeout > 0) {
3213 		g_source_remove (event->timeout);
3214 		event->timeout = -1;
3215 	}
3216 
3217 	if (day_view->resize_bars_event_num >= event_num && day_view->resize_bars_event_day == day) {
3218 		if (day_view->resize_bars_event_num == event_num) {
3219 			day_view->resize_bars_event_num = -1;
3220 			day_view->resize_bars_event_day = -1;
3221 		} else {
3222 			day_view->resize_bars_event_num--;
3223 		}
3224 	}
3225 
3226 	if (day_view->resize_event_num >= event_num && day_view->resize_event_day == day) {
3227 		if (day_view->resize_event_num == event_num) {
3228 			e_day_view_abort_resize (day_view);
3229 			day_view->resize_event_num = -1;
3230 			day_view->resize_event_day = -1;
3231 		} else {
3232 			day_view->resize_event_num--;
3233 		}
3234 	}
3235 
3236 	if (day_view->pressed_event_num >= event_num && day_view->pressed_event_day == day) {
3237 		if (day_view->pressed_event_num == event_num) {
3238 			day_view->pressed_event_num = -1;
3239 			day_view->pressed_event_day = -1;
3240 		} else {
3241 			day_view->pressed_event_num--;
3242 		}
3243 	}
3244 
3245 	if (day_view->drag_event_num >= event_num && day_view->drag_event_day == day) {
3246 		if (day_view->drag_event_num == event_num) {
3247 			day_view->drag_event_num = -1;
3248 			day_view->drag_event_day = -1;
3249 			if (day_view->priv->drag_context) {
3250 				gtk_drag_cancel (day_view->priv->drag_context);
3251 			}
3252 		} else {
3253 			day_view->drag_event_num--;
3254 		}
3255 	}
3256 
3257 	if (event->canvas_item)
3258 		g_object_run_dispose (G_OBJECT (event->canvas_item));
3259 
3260 	if (is_comp_data_valid (event))
3261 		g_object_unref (event->comp_data);
3262 	event->comp_data = NULL;
3263 
3264 	if (day == E_DAY_VIEW_LONG_EVENT) {
3265 		g_array_remove_index (day_view->long_events, event_num);
3266 		day_view->long_events_need_layout = TRUE;
3267 		gtk_widget_grab_focus (GTK_WIDGET (day_view->top_canvas));
3268 	} else {
3269 		g_array_remove_index (day_view->events[day], event_num);
3270 		day_view->need_layout[day] = TRUE;
3271 		gtk_widget_grab_focus (GTK_WIDGET (day_view->main_canvas));
3272 	}
3273 
3274 	return TRUE;
3275 }
3276 
3277 /* Checks if the users participation status is NEEDS-ACTION and shows the summary as bold text */
3278 static void
set_style_from_attendee(EDayViewEvent * event,ESourceRegistry * registry)3279 set_style_from_attendee (EDayViewEvent *event,
3280 			 ESourceRegistry *registry)
3281 {
3282 	ECalComponent *comp;
3283 	GSList *attendees = NULL, *l;
3284 	gchar *address;
3285 	ICalParameterPartstat partstat = I_CAL_PARTSTAT_NONE;
3286 
3287 	if (!is_comp_data_valid (event))
3288 		return;
3289 
3290 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
3291 	if (!comp)
3292 		return;
3293 
3294 	address = itip_get_comp_attendee (registry, comp, event->comp_data->client);
3295 	attendees = e_cal_component_get_attendees (comp);
3296 	for (l = attendees; l && address; l = l->next) {
3297 		ECalComponentAttendee *attendee = l->data;
3298 		const gchar *value, *sentby;
3299 
3300 		value = e_cal_component_attendee_get_value (attendee);
3301 		if (value)
3302 			value = itip_strip_mailto (value);
3303 
3304 		sentby = e_cal_component_attendee_get_sentby (attendee);
3305 		if (sentby)
3306 			value = itip_strip_mailto (sentby);
3307 
3308 		if ((value && g_ascii_strcasecmp (value, address) == 0) ||
3309 		    (sentby && g_ascii_strcasecmp (sentby, address) == 0)) {
3310 			partstat = e_cal_component_attendee_get_partstat (attendee);
3311 			break;
3312 		}
3313 	}
3314 
3315 	if (i_cal_component_get_status (event->comp_data->icalcomp) == I_CAL_STATUS_CANCELLED)
3316 		gnome_canvas_item_set (event->canvas_item, "strikeout", TRUE, NULL);
3317 
3318 	/* The attendee has not yet accepted the meeting, display the summary as bolded.
3319 	 * If the attendee is not present, it might have come through a mailing list.
3320 	 * In that case, we never show the meeting as bold even if it is unaccepted. */
3321 	if (partstat == I_CAL_PARTSTAT_NEEDSACTION)
3322 		gnome_canvas_item_set (event->canvas_item, "bold", TRUE, NULL);
3323 	else if (partstat == I_CAL_PARTSTAT_DECLINED)
3324 		gnome_canvas_item_set (event->canvas_item, "strikeout", TRUE, NULL);
3325 	else if (partstat == I_CAL_PARTSTAT_TENTATIVE)
3326 		gnome_canvas_item_set (event->canvas_item, "italic", TRUE, NULL);
3327 	else if (partstat == I_CAL_PARTSTAT_DELEGATED)
3328 		gnome_canvas_item_set (event->canvas_item, "italic", TRUE, "strikeout", TRUE, NULL);
3329 
3330 	g_slist_free_full (attendees, e_cal_component_attendee_free);
3331 	g_free (address);
3332 	g_object_unref (comp);
3333 }
3334 
3335 /* This updates the text shown for an event. If the event start or end do not
3336  * lie on a row boundary, the time is displayed before the summary. */
3337 static void
e_day_view_update_event_label(EDayView * day_view,gint day,gint event_num)3338 e_day_view_update_event_label (EDayView *day_view,
3339                                gint day,
3340                                gint event_num)
3341 {
3342 	EDayViewEvent *event;
3343 	ECalendarView *cal_view;
3344 	ESourceRegistry *registry;
3345 	ECalModel *model;
3346 	gboolean free_text = FALSE, editing_event = FALSE, short_event = FALSE;
3347 	const gchar *summary;
3348 	gchar *text;
3349 	gint time_divisions;
3350 	gint interval;
3351 
3352 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
3353 		return;
3354 
3355 	event = &g_array_index (day_view->events[day], EDayViewEvent, event_num);
3356 
3357 	/* If the event isn't visible just return. */
3358 	if (!event->canvas_item || !is_comp_data_valid (event))
3359 		return;
3360 
3361 	summary = i_cal_component_get_summary (event->comp_data->icalcomp);
3362 	text = summary ? (gchar *) summary : (gchar *) "";
3363 
3364 	if (day_view->editing_event_day == day
3365 	    && day_view->editing_event_num == event_num)
3366 		editing_event = TRUE;
3367 
3368 	interval = event->end_minute - event->start_minute;
3369 
3370 	cal_view = E_CALENDAR_VIEW (day_view);
3371 	model = e_calendar_view_get_model (cal_view);
3372 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
3373 
3374 	registry = e_cal_model_get_registry (model);
3375 
3376 	if ((interval / time_divisions) >= 2)
3377 		short_event = FALSE;
3378 	else if ((interval % time_divisions) == 0) {
3379 		if (((event->end_minute % time_divisions) == 0) ||
3380 		    ((event->start_minute % time_divisions) == 0)) {
3381 			short_event = TRUE;
3382 		}
3383 	} else
3384 		short_event = FALSE;
3385 
3386 	if (!editing_event) {
3387 		if (!short_event) {
3388 			const gchar *description, *location;
3389 			gint days_shown;
3390 
3391 			days_shown = e_day_view_get_days_shown (day_view);
3392 			description = i_cal_component_get_description (event->comp_data->icalcomp);
3393 			location = i_cal_component_get_location (event->comp_data->icalcomp);
3394 
3395 			if (description && *description) {
3396 				if (location && *location)
3397 					text = g_strdup_printf (" \n%s%c(%s)\n\n%s", text, days_shown == 1 ? ' ' : '\n', location, description);
3398 				else
3399 					text = g_strdup_printf (" \n%s\n\n%s", text, description);
3400 			} else if (location && *location)
3401 				text = g_strdup_printf (" \n%s%c(%s)", text, days_shown == 1 ? ' ' : '\n', location);
3402 			else
3403 				text = g_strdup_printf (" \n%s", text);
3404 
3405 			free_text = TRUE;
3406 		}
3407 	}
3408 
3409 	gnome_canvas_item_set (
3410 		event->canvas_item,
3411 		"text", text,
3412 		NULL);
3413 
3414 	if (e_cal_util_component_has_attendee (event->comp_data->icalcomp))
3415 		set_style_from_attendee (event, registry);
3416 	else if (i_cal_component_get_status (event->comp_data->icalcomp) == I_CAL_STATUS_CANCELLED)
3417 		gnome_canvas_item_set (event->canvas_item, "strikeout", TRUE, NULL);
3418 
3419 	if (free_text)
3420 		g_free (text);
3421 }
3422 
3423 static void
e_day_view_update_long_event_label(EDayView * day_view,gint event_num)3424 e_day_view_update_long_event_label (EDayView *day_view,
3425                                     gint event_num)
3426 {
3427 	EDayViewEvent *event;
3428 	ECalendarView *cal_view;
3429 	ECalModel *model;
3430 	ESourceRegistry *registry;
3431 	gchar *summary;
3432 
3433 	cal_view = E_CALENDAR_VIEW (day_view);
3434 	model = e_calendar_view_get_model (cal_view);
3435 
3436 	registry = e_cal_model_get_registry (model);
3437 
3438 	if (!is_array_index_in_bounds (day_view->long_events, event_num))
3439 		return;
3440 
3441 	event = &g_array_index (day_view->long_events, EDayViewEvent,
3442 				event_num);
3443 
3444 	/* If the event isn't visible just return. */
3445 	if (!event->canvas_item || !is_comp_data_valid (event))
3446 		return;
3447 
3448 	summary = e_calendar_view_dup_component_summary (event->comp_data->icalcomp);
3449 
3450 	gnome_canvas_item_set (
3451 		event->canvas_item,
3452 		"text", summary ? summary : "",
3453 		NULL);
3454 
3455 	g_free (summary);
3456 
3457 	if (e_cal_util_component_has_attendee (event->comp_data->icalcomp))
3458 		set_style_from_attendee (event, registry);
3459 	else if (i_cal_component_get_status (event->comp_data->icalcomp) == I_CAL_STATUS_CANCELLED)
3460 		gnome_canvas_item_set (event->canvas_item, "strikeout", TRUE, NULL);
3461 }
3462 
3463 /* Finds the day and index of the event with the given canvas item.
3464  * If it is a long event, -1 is returned as the day.
3465  * Returns TRUE if the event was found. */
3466 gboolean
e_day_view_find_event_from_item(EDayView * day_view,GnomeCanvasItem * item,gint * day_return,gint * event_num_return)3467 e_day_view_find_event_from_item (EDayView *day_view,
3468                                  GnomeCanvasItem *item,
3469                                  gint *day_return,
3470                                  gint *event_num_return)
3471 {
3472 	EDayViewEvent *event;
3473 	gint day, event_num;
3474 	gint days_shown;
3475 
3476 	days_shown = e_day_view_get_days_shown (day_view);
3477 
3478 	for (day = 0; day < days_shown; day++) {
3479 		for (event_num = 0; event_num < day_view->events[day]->len;
3480 		     event_num++) {
3481 			event = &g_array_index (day_view->events[day],
3482 						EDayViewEvent, event_num);
3483 			if (event->canvas_item == item) {
3484 				*day_return = day;
3485 				*event_num_return = event_num;
3486 				return TRUE;
3487 			}
3488 		}
3489 	}
3490 
3491 	for (event_num = 0; event_num < day_view->long_events->len;
3492 	     event_num++) {
3493 		event = &g_array_index (day_view->long_events,
3494 					EDayViewEvent, event_num);
3495 		if (event->canvas_item == item) {
3496 			*day_return = E_DAY_VIEW_LONG_EVENT;
3497 			*event_num_return = event_num;
3498 			return TRUE;
3499 		}
3500 	}
3501 
3502 	return FALSE;
3503 }
3504 
3505 /* Finds the day and index of the event with the given uid.
3506  * If it is a long event, E_DAY_VIEW_LONG_EVENT is returned as the day.
3507  * Returns TRUE if an event with the uid was found.
3508  * Note that for recurring events there may be several EDayViewEvents, one
3509  * for each instance, all with the same iCalObject and uid. So only use this
3510  * function if you know the event doesn't recur or you are just checking to
3511  * see if any events with the uid exist. */
3512 static gboolean
e_day_view_find_event_from_uid(EDayView * day_view,ECalClient * client,const gchar * uid,const gchar * rid,gint * day_return,gint * event_num_return)3513 e_day_view_find_event_from_uid (EDayView *day_view,
3514                                 ECalClient *client,
3515                                 const gchar *uid,
3516                                 const gchar *rid,
3517                                 gint *day_return,
3518                                 gint *event_num_return)
3519 {
3520 	EDayViewEvent *event;
3521 	gint day, event_num;
3522 	gint days_shown;
3523 	const gchar *u;
3524 	gchar *r = NULL;
3525 
3526 	if (!uid)
3527 		return FALSE;
3528 
3529 	days_shown = e_day_view_get_days_shown (day_view);
3530 
3531 	for (day = 0; day < days_shown; day++) {
3532 		for (event_num = 0; event_num < day_view->events[day]->len;
3533 		     event_num++) {
3534 			event = &g_array_index (day_view->events[day],
3535 						EDayViewEvent, event_num);
3536 
3537 			if (!is_comp_data_valid (event))
3538 				continue;
3539 
3540 			if (event->comp_data->client != client)
3541 				continue;
3542 
3543 			u = i_cal_component_get_uid (event->comp_data->icalcomp);
3544 			if (u && !strcmp (uid, u)) {
3545 				if (rid && *rid) {
3546 					r = e_cal_util_component_get_recurid_as_string (event->comp_data->icalcomp);
3547 
3548 					if (!r || !*r || strcmp (rid, r) != 0) {
3549 						g_free (r);
3550 						continue;
3551 					}
3552 
3553 					g_free (r);
3554 				}
3555 
3556 				*day_return = day;
3557 				*event_num_return = event_num;
3558 				return TRUE;
3559 			}
3560 		}
3561 	}
3562 
3563 	for (event_num = 0; event_num < day_view->long_events->len;
3564 	     event_num++) {
3565 		event = &g_array_index (day_view->long_events,
3566 					EDayViewEvent, event_num);
3567 
3568 		if (!is_comp_data_valid (event))
3569 			continue;
3570 
3571 		if (event->comp_data->client != client)
3572 			continue;
3573 
3574 		u = i_cal_component_get_uid (event->comp_data->icalcomp);
3575 		if (u && !strcmp (uid, u)) {
3576 			*day_return = E_DAY_VIEW_LONG_EVENT;
3577 			*event_num_return = event_num;
3578 			return TRUE;
3579 		}
3580 	}
3581 
3582 	return FALSE;
3583 }
3584 
3585 static void
e_day_view_set_selected_time_range_in_top_visible(EDayView * day_view,time_t start_time,time_t end_time)3586 e_day_view_set_selected_time_range_in_top_visible (EDayView *day_view,
3587                                                    time_t start_time,
3588                                                    time_t end_time)
3589 {
3590 	gint start_row, start_col, end_row, end_col;
3591 	gboolean need_redraw = FALSE, start_in_grid, end_in_grid;
3592 
3593 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3594 
3595 	/* Set the selection. */
3596 	start_in_grid = e_day_view_convert_time_to_grid_position (
3597 		day_view,
3598 		start_time,
3599 		&start_col,
3600 		&start_row);
3601 	end_in_grid = e_day_view_convert_time_to_grid_position (
3602 		day_view,
3603 		end_time - 60,
3604 		&end_col,
3605 		&end_row);
3606 
3607 	if (!start_in_grid)
3608 		start_col = 0;
3609 	if (!end_in_grid)
3610 		end_col = e_day_view_get_days_shown (day_view) - 1;
3611 
3612 	if (start_row != day_view->selection_start_row
3613 	    || start_col != day_view->selection_start_day) {
3614 		need_redraw = TRUE;
3615 		day_view->selection_in_top_canvas = TRUE;
3616 		day_view->selection_start_row = -1;
3617 		day_view->selection_start_day = start_col;
3618 	}
3619 
3620 	if (end_row != day_view->selection_end_row
3621 	    || end_col != day_view->selection_end_day) {
3622 		need_redraw = TRUE;
3623 		day_view->selection_in_top_canvas = TRUE;
3624 		day_view->selection_end_row = -1;
3625 		day_view->selection_end_day = end_col;
3626 	}
3627 
3628 	if (need_redraw) {
3629 		gtk_widget_queue_draw (day_view->top_canvas);
3630 		gtk_widget_queue_draw (day_view->top_dates_canvas);
3631 		gtk_widget_queue_draw (day_view->main_canvas);
3632 	}
3633 }
3634 
3635 static void
e_day_view_set_selected_time_range_visible(EDayView * day_view,time_t start_time,time_t end_time)3636 e_day_view_set_selected_time_range_visible (EDayView *day_view,
3637                                             time_t start_time,
3638                                             time_t end_time)
3639 {
3640 	gint work_day_start_hour;
3641 	gint work_day_start_minute;
3642 	gint work_day_end_hour;
3643 	gint work_day_end_minute;
3644 	gint start_row, start_col, end_row, end_col;
3645 	gboolean need_redraw = FALSE, start_in_grid, end_in_grid;
3646 
3647 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3648 
3649 	/* Set the selection. */
3650 	start_in_grid = e_day_view_convert_time_to_grid_position (
3651 		day_view,
3652 		start_time,
3653 		&start_col,
3654 		&start_row);
3655 	end_in_grid = e_day_view_convert_time_to_grid_position (
3656 		day_view,
3657 		end_time - 60,
3658 		&end_col,
3659 		&end_row);
3660 
3661 	e_day_view_get_work_day_range_for_day (day_view, start_col,
3662 		&work_day_start_hour, &work_day_start_minute,
3663 		&work_day_end_hour, &work_day_end_minute);
3664 
3665 	/* If either of the times isn't in the grid, or the selection covers
3666 	 * an entire day, we set the selection to 1 row from the start of the
3667 	 * working day, in the day corresponding to the start time. */
3668 	if (!start_in_grid || !end_in_grid
3669 	    || (start_row == 0 && end_row == day_view->rows - 1)) {
3670 		end_col = start_col;
3671 
3672 		start_row = e_day_view_convert_time_to_row (
3673 			day_view, work_day_start_hour, work_day_start_minute);
3674 		start_row = CLAMP (start_row, 0, day_view->rows - 1);
3675 		end_row = start_row;
3676 	}
3677 
3678 	if (start_row != day_view->selection_start_row
3679 	    || start_col != day_view->selection_start_day) {
3680 		need_redraw = TRUE;
3681 		day_view->selection_in_top_canvas = FALSE;
3682 		day_view->selection_start_row = start_row;
3683 		day_view->selection_start_day = start_col;
3684 	}
3685 
3686 	if (end_row != day_view->selection_end_row
3687 	    || end_col != day_view->selection_end_day) {
3688 		need_redraw = TRUE;
3689 		day_view->selection_in_top_canvas = FALSE;
3690 		day_view->selection_end_row = end_row;
3691 		day_view->selection_end_day = end_col;
3692 	}
3693 
3694 	if (need_redraw) {
3695 		gtk_widget_queue_draw (day_view->top_canvas);
3696 		gtk_widget_queue_draw (day_view->top_dates_canvas);
3697 		gtk_widget_queue_draw (day_view->main_canvas);
3698 	}
3699 }
3700 
3701 /* Finds the start of the working week which includes the given time. */
3702 static time_t
e_day_view_find_work_week_start(EDayView * day_view,time_t start_time)3703 e_day_view_find_work_week_start (EDayView *day_view,
3704                                  time_t start_time)
3705 {
3706 	GDate date;
3707 	ECalModel *model;
3708 	guint offset;
3709 	GDateWeekday weekday;
3710 	GDateWeekday first_work_day;
3711 	ICalTime *tt = NULL;
3712 	ICalTimezone *zone;
3713 	time_t res;
3714 
3715 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
3716 	zone = e_cal_model_get_timezone (model);
3717 
3718 	time_to_gdate_with_zone (&date, start_time, zone);
3719 
3720 	/* The start of the work-week is the first working day after the
3721 	 * week start day. */
3722 
3723 	/* Get the weekday corresponding to start_time. */
3724 	weekday = g_date_get_weekday (&date);
3725 
3726 	/* Calculate the first working day of the week. */
3727 	first_work_day = e_cal_model_get_work_day_first (model);
3728 	if (first_work_day == G_DATE_BAD_WEEKDAY)
3729 		first_work_day = e_cal_model_get_week_start_day (model);
3730 
3731 	/* Calculate how many days we need to go back to the first workday. */
3732 	if (weekday < first_work_day)
3733 		offset = (weekday + 7) - first_work_day;
3734 	else
3735 		offset = weekday - first_work_day;
3736 
3737 	if (offset > 0)
3738 		g_date_subtract_days (&date, offset);
3739 
3740 	tt = i_cal_time_new_null_time ();
3741 
3742 	i_cal_time_set_date (tt,
3743 		g_date_get_year (&date),
3744 		g_date_get_month (&date),
3745 		g_date_get_day (&date));
3746 
3747 	res = i_cal_time_as_timet_with_zone (tt, zone);
3748 
3749 	g_clear_object (&tt);
3750 
3751 	return res;
3752 }
3753 
3754 static void
e_day_view_recalc_day_starts(EDayView * day_view,time_t start_time)3755 e_day_view_recalc_day_starts (EDayView *day_view,
3756                               time_t start_time)
3757 {
3758 	gint day;
3759 	gchar *str;
3760 	gint days_shown;
3761 	ICalTime *tt;
3762 	GDate dt;
3763 
3764 	days_shown = e_day_view_get_days_shown (day_view);
3765 	if (days_shown <= 0)
3766 		return;
3767 
3768 	day_view->day_starts[0] = start_time;
3769 	for (day = 1; day <= days_shown; day++) {
3770 		day_view->day_starts[day] = time_add_day_with_zone (day_view->day_starts[day - 1], 1, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
3771 	}
3772 
3773 	day_view->lower = start_time;
3774 	day_view->upper = day_view->day_starts[days_shown];
3775 
3776 	tt = i_cal_time_new_from_timet_with_zone (day_view->day_starts[0], FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
3777 	g_date_clear (&dt, 1);
3778 	g_date_set_dmy (&dt, i_cal_time_get_day (tt), i_cal_time_get_month (tt), i_cal_time_get_year (tt));
3779 	/* To Translators: the %d stands for a week number, it's value between 1 and 52/53 */
3780 	str = g_strdup_printf (_("Week %d"), g_date_get_iso8601_week_of_year (&dt));
3781 	gtk_label_set_text (GTK_LABEL (day_view->week_number_label), str);
3782 	g_free (str);
3783 
3784 	e_day_view_recalc_work_week (day_view);
3785 
3786 	g_clear_object (&tt);
3787 }
3788 
3789 /* Whether we are displaying a work-week, in which case the display always
3790  * starts on the first day of the working week. */
3791 gboolean
e_day_view_get_work_week_view(EDayView * day_view)3792 e_day_view_get_work_week_view (EDayView *day_view)
3793 {
3794 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE);
3795 
3796 	return day_view->priv->work_week_view;
3797 }
3798 
3799 void
e_day_view_set_work_week_view(EDayView * day_view,gboolean work_week_view)3800 e_day_view_set_work_week_view (EDayView *day_view,
3801                                gboolean work_week_view)
3802 {
3803 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3804 
3805 	if (work_week_view == day_view->priv->work_week_view)
3806 		return;
3807 
3808 	day_view->priv->work_week_view = work_week_view;
3809 
3810 	e_day_view_recalc_work_week (day_view);
3811 }
3812 
3813 gint
e_day_view_get_days_shown(EDayView * day_view)3814 e_day_view_get_days_shown (EDayView *day_view)
3815 {
3816 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), -1);
3817 
3818 	return day_view->priv->days_shown;
3819 }
3820 
3821 void
e_day_view_set_days_shown(EDayView * day_view,gint days_shown)3822 e_day_view_set_days_shown (EDayView *day_view,
3823                            gint days_shown)
3824 {
3825 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3826 	g_return_if_fail (days_shown >= 1);
3827 	g_return_if_fail (days_shown <= E_DAY_VIEW_MAX_DAYS);
3828 
3829 	if (days_shown == day_view->priv->days_shown)
3830 		return;
3831 
3832 	day_view->priv->days_shown = days_shown;
3833 
3834 	/* If the date isn't set, just return. */
3835 	if (day_view->lower == 0 && day_view->upper == 0)
3836 		return;
3837 
3838 	e_day_view_recalc_day_starts (day_view, day_view->lower);
3839 	e_day_view_recalc_cell_sizes (day_view);
3840 
3841 	e_day_view_update_query (day_view);
3842 }
3843 
3844 static void
e_day_view_recalc_work_week_days_shown(EDayView * day_view)3845 e_day_view_recalc_work_week_days_shown (EDayView *day_view)
3846 {
3847 	ECalModel *model;
3848 	GDateWeekday first_work_day;
3849 	GDateWeekday last_work_day;
3850 	gint days_shown;
3851 
3852 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
3853 
3854 	/* Find the first working day in the week. */
3855 	first_work_day = e_cal_model_get_work_day_first (model);
3856 
3857 	if (first_work_day != G_DATE_BAD_WEEKDAY) {
3858 		last_work_day = e_cal_model_get_work_day_last (model);
3859 
3860 		/* Now calculate the days we need to show to include all the
3861 		 * working days in the week. Add 1 to make it inclusive. */
3862 		days_shown = e_weekday_get_days_between (
3863 			first_work_day, last_work_day) + 1;
3864 	} else {
3865 		/* If no working days are set, just use 7. */
3866 		days_shown = 7;
3867 	}
3868 
3869 	e_day_view_set_days_shown (day_view, days_shown);
3870 }
3871 
3872 /* Force a redraw of the Marcus Bains Lines */
3873 void
e_day_view_marcus_bains_update(EDayView * day_view)3874 e_day_view_marcus_bains_update (EDayView *day_view)
3875 {
3876 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3877 	gtk_widget_queue_draw (day_view->main_canvas);
3878 	gtk_widget_queue_draw (day_view->time_canvas);
3879 }
3880 
3881 gboolean
e_day_view_marcus_bains_get_show_line(EDayView * day_view)3882 e_day_view_marcus_bains_get_show_line (EDayView *day_view)
3883 {
3884 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE);
3885 
3886 	return day_view->priv->marcus_bains_show_line;
3887 }
3888 
3889 void
e_day_view_marcus_bains_set_show_line(EDayView * day_view,gboolean show_line)3890 e_day_view_marcus_bains_set_show_line (EDayView *day_view,
3891                                        gboolean show_line)
3892 {
3893 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3894 
3895 	if (show_line == day_view->priv->marcus_bains_show_line)
3896 		return;
3897 
3898 	day_view->priv->marcus_bains_show_line = show_line;
3899 
3900 	e_day_view_marcus_bains_update (day_view);
3901 
3902 	g_object_notify (G_OBJECT (day_view), "marcus-bains-show-line");
3903 }
3904 
3905 gboolean
e_day_view_get_draw_flat_events(EDayView * day_view)3906 e_day_view_get_draw_flat_events (EDayView *day_view)
3907 {
3908 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE);
3909 
3910 	return day_view->priv->draw_flat_events;
3911 }
3912 
3913 void
e_day_view_set_draw_flat_events(EDayView * day_view,gboolean draw_flat_events)3914 e_day_view_set_draw_flat_events (EDayView *day_view,
3915 				 gboolean draw_flat_events)
3916 {
3917 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3918 
3919 	if ((day_view->priv->draw_flat_events ? 1 : 0) == (draw_flat_events ? 1 : 0))
3920 		return;
3921 
3922 	day_view->priv->draw_flat_events = draw_flat_events;
3923 
3924 	gtk_widget_queue_draw (day_view->top_canvas);
3925 	gtk_widget_queue_draw (day_view->top_dates_canvas);
3926 	gtk_widget_queue_draw (day_view->main_canvas);
3927 
3928 	g_object_notify (G_OBJECT (day_view), "draw-flat-events");
3929 }
3930 
3931 const gchar *
e_day_view_marcus_bains_get_day_view_color(EDayView * day_view)3932 e_day_view_marcus_bains_get_day_view_color (EDayView *day_view)
3933 {
3934 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), NULL);
3935 
3936 	return day_view->priv->marcus_bains_day_view_color;
3937 }
3938 
3939 void
e_day_view_marcus_bains_set_day_view_color(EDayView * day_view,const gchar * day_view_color)3940 e_day_view_marcus_bains_set_day_view_color (EDayView *day_view,
3941                                             const gchar *day_view_color)
3942 {
3943 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3944 
3945 	g_free (day_view->priv->marcus_bains_day_view_color);
3946 	day_view->priv->marcus_bains_day_view_color = g_strdup (day_view_color);
3947 
3948 	e_day_view_marcus_bains_update (day_view);
3949 
3950 	g_object_notify (G_OBJECT (day_view), "marcus-bains-day-view-color");
3951 }
3952 
3953 const gchar *
e_day_view_marcus_bains_get_time_bar_color(EDayView * day_view)3954 e_day_view_marcus_bains_get_time_bar_color (EDayView *day_view)
3955 {
3956 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), NULL);
3957 
3958 	return day_view->priv->marcus_bains_time_bar_color;
3959 }
3960 
3961 void
e_day_view_marcus_bains_set_time_bar_color(EDayView * day_view,const gchar * time_bar_color)3962 e_day_view_marcus_bains_set_time_bar_color (EDayView *day_view,
3963                                             const gchar *time_bar_color)
3964 {
3965 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3966 
3967 	g_free (day_view->priv->marcus_bains_time_bar_color);
3968 	day_view->priv->marcus_bains_time_bar_color = g_strdup (time_bar_color);
3969 
3970 	e_day_view_marcus_bains_update (day_view);
3971 
3972 	g_object_notify (G_OBJECT (day_view), "marcus-bains-time-bar-color");
3973 }
3974 
3975 /* Whether we display event end times in the main canvas. */
3976 gboolean
e_day_view_get_show_event_end_times(EDayView * day_view)3977 e_day_view_get_show_event_end_times (EDayView *day_view)
3978 {
3979 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), TRUE);
3980 
3981 	return day_view->show_event_end_times;
3982 }
3983 
3984 void
e_day_view_set_show_event_end_times(EDayView * day_view,gboolean show)3985 e_day_view_set_show_event_end_times (EDayView *day_view,
3986                                      gboolean show)
3987 {
3988 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
3989 
3990 	if (day_view->show_event_end_times != show) {
3991 		day_view->show_event_end_times = show;
3992 		e_day_view_foreach_event (day_view,
3993 					  e_day_view_set_show_times_cb, NULL);
3994 	}
3995 }
3996 
3997 /* This is a callback used to update all day event labels. */
3998 static gboolean
e_day_view_set_show_times_cb(EDayView * day_view,gint day,gint event_num,gpointer data)3999 e_day_view_set_show_times_cb (EDayView *day_view,
4000                               gint day,
4001                               gint event_num,
4002                               gpointer data)
4003 {
4004 	if (day != E_DAY_VIEW_LONG_EVENT) {
4005 		e_day_view_update_event_label (day_view, day, event_num);
4006 	}
4007 
4008 	return TRUE;
4009 }
4010 
4011 static void
e_day_view_recalc_work_week(EDayView * day_view)4012 e_day_view_recalc_work_week (EDayView *day_view)
4013 {
4014 	time_t lower;
4015 
4016 	/* If we aren't showing the work week, just return. */
4017 	if (!e_day_view_get_work_week_view (day_view))
4018 		return;
4019 
4020 	e_day_view_recalc_work_week_days_shown (day_view);
4021 
4022 	/* If the date isn't set, just return. */
4023 	if (day_view->lower == 0 && day_view->upper == 0)
4024 		return;
4025 
4026 	lower = e_day_view_find_work_week_start (day_view, day_view->lower);
4027 	if (lower != day_view->lower) {
4028 		/* Reset the selection, as it may disappear. */
4029 		day_view->selection_start_day = -1;
4030 
4031 		e_day_view_recalc_day_starts (day_view, lower);
4032 		e_day_view_update_query (day_view);
4033 
4034 		/* This updates the date navigator. */
4035 		e_day_view_update_calendar_selection_time (day_view);
4036 	}
4037 }
4038 
4039 static gboolean
e_day_view_update_scroll_regions(EDayView * day_view)4040 e_day_view_update_scroll_regions (EDayView *day_view)
4041 {
4042 	GtkAllocation main_canvas_allocation;
4043 	GtkAllocation time_canvas_allocation;
4044 	gdouble old_x2, old_y2, new_x2, new_y2;
4045 	gboolean need_reshape = FALSE;
4046 
4047 	gtk_widget_get_allocation (
4048 		day_view->main_canvas, &main_canvas_allocation);
4049 	gtk_widget_get_allocation (
4050 		day_view->time_canvas, &time_canvas_allocation);
4051 
4052 	/* Set the scroll region of the time canvas to its allocated width,
4053 	 * but with the height the same as the main canvas. */
4054 	gnome_canvas_get_scroll_region (
4055 		GNOME_CANVAS (day_view->time_canvas),
4056 		NULL, NULL, &old_x2, &old_y2);
4057 	new_x2 = time_canvas_allocation.width - 1;
4058 	new_y2 = MAX (
4059 		day_view->rows * day_view->row_height,
4060 		main_canvas_allocation.height - 1);
4061 	if (old_x2 != new_x2 || old_y2 != new_y2)
4062 		gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->time_canvas),
4063 						0, 0, new_x2, new_y2);
4064 
4065 	/* Set the scroll region of the main canvas to its allocated width,
4066 	 * but with the height depending on the number of rows needed. */
4067 	gnome_canvas_get_scroll_region (
4068 		GNOME_CANVAS (day_view->main_canvas),
4069 		NULL, NULL, &old_x2, &old_y2);
4070 	new_x2 = main_canvas_allocation.width - 1;
4071 
4072 	if (e_day_view_get_days_shown (day_view) == 1)
4073 		new_x2 = MAX (new_x2, day_view->max_cols * (E_DAY_VIEW_MIN_DAY_COL_WIDTH + E_DAY_VIEW_GAP_WIDTH) - E_DAY_VIEW_MIN_DAY_COL_WIDTH - 1);
4074 
4075 	if (old_x2 != new_x2 || old_y2 != new_y2) {
4076 		need_reshape = TRUE;
4077 		gnome_canvas_set_scroll_region (
4078 			GNOME_CANVAS (day_view->main_canvas),
4079 			0, 0, new_x2, new_y2);
4080 	}
4081 
4082 	if (new_x2 <= main_canvas_allocation.width - 1)
4083 		gtk_widget_hide (day_view->mc_hscrollbar);
4084 	else
4085 		gtk_widget_show (day_view->mc_hscrollbar);
4086 
4087 	return need_reshape;
4088 }
4089 
4090 /* This recalculates the number of rows to display, based on the time range
4091  * shown and the minutes per row. */
4092 static void
e_day_view_recalc_num_rows(EDayView * day_view)4093 e_day_view_recalc_num_rows (EDayView *day_view)
4094 {
4095 	ECalendarView *cal_view;
4096 	gint time_divisions;
4097 	gint hours, minutes, total_minutes;
4098 
4099 	cal_view = E_CALENDAR_VIEW (day_view);
4100 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
4101 
4102 	hours = day_view->last_hour_shown - day_view->first_hour_shown;
4103 	/* This could be negative but it works out OK. */
4104 	minutes = day_view->last_minute_shown - day_view->first_minute_shown;
4105 	total_minutes = hours * 60 + minutes;
4106 	day_view->rows = total_minutes / time_divisions;
4107 }
4108 
4109 /* Converts an hour and minute to a row in the canvas. Note that if we aren't
4110  * showing all 24 hours of the day, the returned row may be negative or
4111  * greater than day_view->rows. */
4112 gint
e_day_view_convert_time_to_row(EDayView * day_view,gint hour,gint minute)4113 e_day_view_convert_time_to_row (EDayView *day_view,
4114                                 gint hour,
4115                                 gint minute)
4116 {
4117 	ECalendarView *cal_view;
4118 	gint time_divisions;
4119 	gint total_minutes, start_minute, offset;
4120 
4121 	cal_view = E_CALENDAR_VIEW (day_view);
4122 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
4123 
4124 	total_minutes = hour * 60 + minute;
4125 	start_minute = day_view->first_hour_shown * 60
4126 		+ day_view->first_minute_shown;
4127 	offset = total_minutes - start_minute;
4128 	if (offset < 0)
4129 		return -1;
4130 	else
4131 		return offset / time_divisions;
4132 }
4133 
4134 /* Converts an hour and minute to a y coordinate in the canvas. */
4135 gint
e_day_view_convert_time_to_position(EDayView * day_view,gint hour,gint minute)4136 e_day_view_convert_time_to_position (EDayView *day_view,
4137                                      gint hour,
4138                                      gint minute)
4139 {
4140 	ECalendarView *cal_view;
4141 	gint time_divisions;
4142 	gint total_minutes, start_minute, offset;
4143 
4144 	cal_view = E_CALENDAR_VIEW (day_view);
4145 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
4146 
4147 	total_minutes = hour * 60 + minute;
4148 	start_minute = day_view->first_hour_shown * 60
4149 		+ day_view->first_minute_shown;
4150 	offset = total_minutes - start_minute;
4151 
4152 	return offset * day_view->row_height / time_divisions;
4153 }
4154 
4155 static gboolean
e_day_view_on_top_canvas_button_press(GtkWidget * widget,GdkEvent * button_event,EDayView * day_view)4156 e_day_view_on_top_canvas_button_press (GtkWidget *widget,
4157                                        GdkEvent *button_event,
4158                                        EDayView *day_view)
4159 {
4160 	gint event_x, event_y, day, event_num;
4161 	ECalendarViewPosition pos;
4162 	GtkLayout *layout;
4163 	GdkWindow *window;
4164 	GdkDevice *event_device;
4165 	guint event_button = 0;
4166 	guint32 event_time;
4167 
4168 	layout = GTK_LAYOUT (widget);
4169 	window = gtk_layout_get_bin_window (layout);
4170 
4171 	gdk_event_get_button (button_event, &event_button);
4172 	event_device = gdk_event_get_device (button_event);
4173 	event_time = gdk_event_get_time (button_event);
4174 
4175 	if (day_view->resize_event_num != -1)
4176 		day_view->resize_event_num = -1;
4177 
4178 	if (day_view->drag_event_num != -1)
4179 		day_view->drag_event_num = -1;
4180 
4181 	/* Convert the coords to the main canvas window, or return if the
4182 	 * window is not found. */
4183 	if (!e_day_view_convert_event_coords (
4184 		day_view, button_event, window, &event_x, &event_y))
4185 		return FALSE;
4186 
4187 	pos = e_day_view_convert_position_in_top_canvas (
4188 		day_view,
4189 		event_x, event_y,
4190 		&day, &event_num);
4191 
4192 	if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
4193 		return FALSE;
4194 
4195 	if (pos != E_CALENDAR_VIEW_POS_NONE)
4196 		return e_day_view_on_long_event_button_press (
4197 			day_view,
4198 			event_num,
4199 			button_event,
4200 			pos,
4201 			event_x,
4202 			event_y);
4203 
4204 	e_day_view_stop_editing_event (day_view);
4205 
4206 	if (event_button == 1) {
4207 		GdkGrabStatus grab_status;
4208 
4209 		if (button_event->type == GDK_2BUTTON_PRESS) {
4210 			time_t dtstart, dtend;
4211 
4212 			day_view_get_selected_time_range ((ECalendarView *) day_view, &dtstart, &dtend);
4213 			if (dtstart < day_view->before_click_dtend && dtend > day_view->before_click_dtstart) {
4214 				dtstart = day_view->before_click_dtstart;
4215 				dtend = day_view->before_click_dtend;
4216 				day_view_set_selected_time_range ((ECalendarView *) day_view, dtstart, dtend);
4217 			}
4218 
4219 			e_cal_ops_new_component_editor_from_model (
4220 				e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), NULL,
4221 				dtstart, dtend, calendar_config_get_prefer_meeting (), TRUE);
4222 			return TRUE;
4223 		}
4224 
4225 		if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4226 			gtk_widget_grab_focus (GTK_WIDGET (day_view));
4227 
4228 		grab_status = gdk_device_grab (
4229 			event_device,
4230 			window,
4231 			GDK_OWNERSHIP_NONE,
4232 			FALSE,
4233 			GDK_POINTER_MOTION_MASK |
4234 			GDK_BUTTON_RELEASE_MASK,
4235 			NULL,
4236 			event_time);
4237 
4238 		if (grab_status == GDK_GRAB_SUCCESS) {
4239 			g_warn_if_fail (day_view->grabbed_pointer == NULL);
4240 			day_view->grabbed_pointer = g_object_ref (event_device);
4241 
4242 			if (event_time - day_view->bc_event_time > 250)
4243 				day_view_get_selected_time_range (
4244 					E_CALENDAR_VIEW (day_view),
4245 					&day_view->before_click_dtstart,
4246 					&day_view->before_click_dtend);
4247 			day_view->bc_event_time = event_time;
4248 			e_day_view_start_selection (day_view, day, -1);
4249 		}
4250 	} else if (event_button == 3) {
4251 		if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4252 			gtk_widget_grab_focus (GTK_WIDGET (day_view));
4253 
4254 		if (day < day_view->selection_start_day || day > day_view->selection_end_day) {
4255 			e_day_view_start_selection (day_view, day, -1);
4256 			e_day_view_finish_selection (day_view);
4257 		}
4258 
4259 		e_day_view_on_event_right_click (day_view, button_event, -1, -1);
4260 	}
4261 
4262 	return TRUE;
4263 }
4264 
4265 static gboolean
e_day_view_convert_event_coords(EDayView * day_view,GdkEvent * event,GdkWindow * window,gint * x_return,gint * y_return)4266 e_day_view_convert_event_coords (EDayView *day_view,
4267                                  GdkEvent *event,
4268                                  GdkWindow *window,
4269                                  gint *x_return,
4270                                  gint *y_return)
4271 {
4272 	gint event_x, event_y, win_x, win_y;
4273 	GdkWindow *event_window;;
4274 
4275 	/* Get the event window, x & y from the appropriate event struct. */
4276 	switch (event->type) {
4277 	case GDK_BUTTON_PRESS:
4278 	case GDK_2BUTTON_PRESS:
4279 	case GDK_3BUTTON_PRESS:
4280 	case GDK_BUTTON_RELEASE:
4281 		event_x = event->button.x;
4282 		event_y = event->button.y;
4283 		event_window = event->button.window;
4284 		break;
4285 	case GDK_MOTION_NOTIFY:
4286 		event_x = event->motion.x;
4287 		event_y = event->motion.y;
4288 		event_window = event->motion.window;
4289 		break;
4290 	case GDK_ENTER_NOTIFY:
4291 	case GDK_LEAVE_NOTIFY:
4292 		event_x = event->crossing.x;
4293 		event_y = event->crossing.y;
4294 		event_window = event->crossing.window;
4295 		break;
4296 	default:
4297 		/* Shouldn't get here. */
4298 		g_return_val_if_reached (FALSE);
4299 	}
4300 
4301 	while (event_window && event_window != window
4302 	       && event_window != gdk_get_default_root_window ()) {
4303 		gdk_window_get_position (event_window, &win_x, &win_y);
4304 		event_x += win_x;
4305 		event_y += win_y;
4306 		event_window = gdk_window_get_parent (event_window);
4307 	}
4308 
4309 	*x_return = event_x;
4310 	*y_return = event_y;
4311 
4312 	return (event_window == window) ? TRUE : FALSE;
4313 }
4314 
4315 static gboolean
e_day_view_on_main_canvas_button_press(GtkWidget * widget,GdkEvent * button_event,EDayView * day_view)4316 e_day_view_on_main_canvas_button_press (GtkWidget *widget,
4317                                         GdkEvent *button_event,
4318                                         EDayView *day_view)
4319 {
4320 	gint event_x, event_y, row, day, event_num;
4321 	ECalendarViewPosition pos;
4322 	GtkLayout *layout;
4323 	GdkWindow *window;
4324 	GdkDevice *event_device;
4325 	guint event_button = 0;
4326 	guint32 event_time;
4327 
4328 	layout = GTK_LAYOUT (widget);
4329 	window = gtk_layout_get_bin_window (layout);
4330 
4331 	gdk_event_get_button (button_event, &event_button);
4332 	event_device = gdk_event_get_device (button_event);
4333 	event_time = gdk_event_get_time (button_event);
4334 
4335 	if (day_view->resize_event_num != -1)
4336 		day_view->resize_event_num = -1;
4337 
4338 	if (day_view->drag_event_num != -1)
4339 		day_view->drag_event_num = -1;
4340 
4341 	/* Convert the coords to the main canvas window, or return if the
4342 	 * window is not found. */
4343 	if (!e_day_view_convert_event_coords (
4344 		day_view, button_event, window, &event_x, &event_y))
4345 		return FALSE;
4346 
4347 	/* Find out where the mouse is. */
4348 	pos = e_day_view_convert_position_in_main_canvas (
4349 		day_view,
4350 		event_x, event_y,
4351 		&day, &row,
4352 		&event_num);
4353 
4354 	if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
4355 		return FALSE;
4356 
4357 	if (pos != E_CALENDAR_VIEW_POS_NONE)
4358 		return e_day_view_on_event_button_press (
4359 			day_view,
4360 			day,
4361 			event_num,
4362 			button_event,
4363 			pos,
4364 			event_x,
4365 			event_y);
4366 
4367 	e_day_view_stop_editing_event (day_view);
4368 
4369 	/* Start the selection drag. */
4370 	if (event_button == 1) {
4371 		GdkGrabStatus grab_status;
4372 
4373 		if (button_event->type == GDK_2BUTTON_PRESS) {
4374 			time_t dtstart, dtend;
4375 
4376 			day_view_get_selected_time_range ((ECalendarView *) day_view, &dtstart, &dtend);
4377 			if (dtstart < day_view->before_click_dtend && dtend > day_view->before_click_dtstart) {
4378 				dtstart = day_view->before_click_dtstart;
4379 				dtend = day_view->before_click_dtend;
4380 				day_view_set_selected_time_range ((ECalendarView *) day_view, dtstart, dtend);
4381 			}
4382 			e_cal_ops_new_component_editor_from_model (
4383 				e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), NULL,
4384 				dtstart, dtend, calendar_config_get_prefer_meeting (), FALSE);
4385 			return TRUE;
4386 		}
4387 
4388 		if (!gtk_widget_has_focus (GTK_WIDGET (day_view)) && !gtk_widget_has_focus (GTK_WIDGET (day_view->main_canvas)))
4389 			gtk_widget_grab_focus (GTK_WIDGET (day_view));
4390 
4391 		grab_status = gdk_device_grab (
4392 			event_device,
4393 			window,
4394 			GDK_OWNERSHIP_NONE,
4395 			FALSE,
4396 			GDK_POINTER_MOTION_MASK |
4397 			GDK_BUTTON_RELEASE_MASK,
4398 			NULL,
4399 			event_time);
4400 
4401 		if (grab_status == GDK_GRAB_SUCCESS) {
4402 			g_warn_if_fail (day_view->grabbed_pointer == NULL);
4403 			day_view->grabbed_pointer = g_object_ref (event_device);
4404 
4405 			if (event_time - day_view->bc_event_time > 250)
4406 				day_view_get_selected_time_range (
4407 					E_CALENDAR_VIEW (day_view),
4408 					&day_view->before_click_dtstart,
4409 					&day_view->before_click_dtend);
4410 			day_view->bc_event_time = event_time;
4411 			e_day_view_start_selection (day_view, day, row);
4412 			g_signal_emit_by_name (day_view, "selected_time_changed");
4413 		}
4414 	} else if (event_button == 3) {
4415 		if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4416 			gtk_widget_grab_focus (GTK_WIDGET (day_view));
4417 
4418 		if ((day < day_view->selection_start_day || day > day_view->selection_end_day)
4419 		    || (day == day_view->selection_start_day && row < day_view->selection_start_row)
4420 		    || (day == day_view->selection_end_day && row > day_view->selection_end_row)) {
4421 			e_day_view_start_selection (day_view, day, row);
4422 			e_day_view_finish_selection (day_view);
4423 		}
4424 
4425 		e_day_view_on_event_right_click (day_view, button_event, -1, -1);
4426 	}
4427 
4428 	return TRUE;
4429 }
4430 
4431 static gboolean
e_day_view_on_main_canvas_scroll(GtkWidget * widget,GdkEventScroll * scroll,EDayView * day_view)4432 e_day_view_on_main_canvas_scroll (GtkWidget *widget,
4433                                   GdkEventScroll *scroll,
4434                                   EDayView *day_view)
4435 {
4436 	switch (scroll->direction) {
4437 	case GDK_SCROLL_UP:
4438 		e_day_view_scroll (day_view, E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4439 		return TRUE;
4440 	case GDK_SCROLL_DOWN:
4441 		e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4442 		return TRUE;
4443 	case GDK_SCROLL_SMOOTH:
4444 		if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) {
4445 			e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE * scroll->delta_y);
4446 			return TRUE;
4447 		}
4448 		break;
4449 	default:
4450 		break;
4451 	}
4452 
4453 	return FALSE;
4454 }
4455 
4456 static gboolean
e_day_view_on_top_canvas_scroll(GtkWidget * widget,GdkEventScroll * scroll,EDayView * day_view)4457 e_day_view_on_top_canvas_scroll (GtkWidget *widget,
4458                                   GdkEventScroll *scroll,
4459                                   EDayView *day_view)
4460 {
4461 	switch (scroll->direction) {
4462 	case GDK_SCROLL_UP:
4463 		e_day_view_top_scroll (day_view, E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4464 		return TRUE;
4465 	case GDK_SCROLL_DOWN:
4466 		e_day_view_top_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4467 		return TRUE;
4468 	case GDK_SCROLL_SMOOTH:
4469 		if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) {
4470 			e_day_view_top_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE * scroll->delta_y);
4471 			return TRUE;
4472 		}
4473 		break;
4474 	default:
4475 		break;
4476 	}
4477 
4478 	return FALSE;
4479 }
4480 
4481 static gboolean
e_day_view_on_time_canvas_scroll(GtkWidget * widget,GdkEventScroll * scroll,EDayView * day_view)4482 e_day_view_on_time_canvas_scroll (GtkWidget *widget,
4483                                   GdkEventScroll *scroll,
4484                                   EDayView *day_view)
4485 {
4486 	GtkWidget *tool_window = g_object_get_data ((GObject *) day_view, "tooltip-window");
4487 
4488 	if (tool_window) {
4489 		gtk_widget_destroy (tool_window);
4490 		g_object_set_data (G_OBJECT (day_view), "tooltip-window", NULL);
4491 	}
4492 
4493 	switch (scroll->direction) {
4494 	case GDK_SCROLL_UP:
4495 		e_day_view_scroll (day_view, E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4496 		return TRUE;
4497 	case GDK_SCROLL_DOWN:
4498 		e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4499 		return TRUE;
4500 	case GDK_SCROLL_SMOOTH:
4501 		if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) {
4502 			e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE * scroll->delta_y);
4503 			return TRUE;
4504 		}
4505 		break;
4506 	default:
4507 		break;
4508 	}
4509 
4510 	return FALSE;
4511 }
4512 
4513 static gboolean
e_day_view_on_long_event_button_press(EDayView * day_view,gint event_num,GdkEvent * button_event,ECalendarViewPosition pos,gint event_x,gint event_y)4514 e_day_view_on_long_event_button_press (EDayView *day_view,
4515                                        gint event_num,
4516                                        GdkEvent *button_event,
4517                                        ECalendarViewPosition pos,
4518                                        gint event_x,
4519                                        gint event_y)
4520 {
4521 	guint event_button = 0;
4522 
4523 	gdk_event_get_button (button_event, &event_button);
4524 
4525 	if (event_button == 1) {
4526 		if (button_event->type == GDK_BUTTON_PRESS) {
4527 			e_day_view_on_long_event_click (
4528 				day_view, event_num,
4529 				button_event, pos,
4530 				event_x, event_y);
4531 			return TRUE;
4532 		} else if (button_event->type == GDK_2BUTTON_PRESS) {
4533 			e_day_view_on_event_double_click (
4534 				day_view, -1,
4535 				event_num);
4536 			g_signal_stop_emission_by_name (day_view->top_canvas, "button_press_event");
4537 			return TRUE;
4538 		}
4539 	} else if (event_button == 3) {
4540 		EDayViewEvent *e;
4541 
4542 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
4543 			return TRUE;
4544 
4545 		e = &g_array_index (day_view->long_events, EDayViewEvent, event_num);
4546 
4547 		e_day_view_set_selected_time_range_in_top_visible (day_view, e->start, e->end);
4548 
4549 		e_day_view_on_event_right_click (
4550 			day_view, button_event,
4551 			E_DAY_VIEW_LONG_EVENT,
4552 			event_num);
4553 
4554 		return TRUE;
4555 	}
4556 	return FALSE;
4557 }
4558 
4559 static gboolean
e_day_view_on_event_button_press(EDayView * day_view,gint day,gint event_num,GdkEvent * button_event,ECalendarViewPosition pos,gint event_x,gint event_y)4560 e_day_view_on_event_button_press (EDayView *day_view,
4561                                   gint day,
4562                                   gint event_num,
4563                                   GdkEvent *button_event,
4564                                   ECalendarViewPosition pos,
4565                                   gint event_x,
4566                                   gint event_y)
4567 {
4568 	guint event_button = 0;
4569 
4570 	gdk_event_get_button (button_event, &event_button);
4571 
4572 	if (event_button == 1) {
4573 		if (button_event->type == GDK_BUTTON_PRESS) {
4574 			e_day_view_on_event_click (
4575 				day_view, day, event_num,
4576 				button_event, pos,
4577 				event_x, event_y);
4578 			return TRUE;
4579 		} else if (button_event->type == GDK_2BUTTON_PRESS) {
4580 			e_day_view_on_event_double_click (
4581 				day_view, day,
4582 				event_num);
4583 
4584 			g_signal_stop_emission_by_name (day_view->main_canvas, "button_press_event");
4585 			return TRUE;
4586 		}
4587 	} else if (event_button == 3) {
4588 		EDayViewEvent *e;
4589 
4590 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
4591 			return TRUE;
4592 
4593 		e = &g_array_index (day_view->events[day], EDayViewEvent, event_num);
4594 
4595 		e_day_view_set_selected_time_range_visible (day_view, e->start, e->end);
4596 
4597 		e_day_view_on_event_right_click (
4598 			day_view, button_event, day, event_num);
4599 
4600 		return TRUE;
4601 	}
4602 	return FALSE;
4603 }
4604 
4605 static void
e_day_view_on_long_event_click(EDayView * day_view,gint event_num,GdkEvent * button_event,ECalendarViewPosition pos,gint event_x,gint event_y)4606 e_day_view_on_long_event_click (EDayView *day_view,
4607                                 gint event_num,
4608                                 GdkEvent *button_event,
4609                                 ECalendarViewPosition pos,
4610                                 gint event_x,
4611                                 gint event_y)
4612 {
4613 	EDayViewEvent *event;
4614 	GtkLayout *layout;
4615 	GdkWindow *window;
4616 	gint start_day, end_day, day;
4617 	gint item_x, item_y, item_w, item_h;
4618 
4619 	if (!is_array_index_in_bounds (day_view->long_events, event_num))
4620 		return;
4621 
4622 	event = &g_array_index (day_view->long_events, EDayViewEvent,
4623 				event_num);
4624 
4625 	if (!is_comp_data_valid (event))
4626 		return;
4627 
4628 	/* Ignore clicks on the EText while editing. */
4629 	if (pos == E_CALENDAR_VIEW_POS_EVENT
4630 	    && E_TEXT (event->canvas_item)->editing) {
4631 		GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (
4632 			event->canvas_item, button_event);
4633 		return;
4634 	}
4635 
4636 	e_day_view_set_popup_event (day_view, E_DAY_VIEW_LONG_EVENT, event_num);
4637 
4638 	if ((e_cal_util_component_is_instance (event->comp_data->icalcomp) ||
4639 	     !e_cal_util_component_has_recurrences (event->comp_data->icalcomp))
4640 	    && (pos == E_CALENDAR_VIEW_POS_LEFT_EDGE
4641 		|| pos == E_CALENDAR_VIEW_POS_RIGHT_EDGE)) {
4642 		GdkGrabStatus grab_status;
4643 		GdkDevice *event_device;
4644 		guint32 event_time;
4645 
4646 		if (!e_day_view_find_long_event_days (
4647 			event,
4648 			e_day_view_get_days_shown (day_view),
4649 			day_view->day_starts,
4650 			&start_day, &end_day))
4651 			return;
4652 
4653 		/* Grab the keyboard focus, so the event being edited is saved
4654 		 * and we can use the Escape key to abort the resize. */
4655 		if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4656 			gtk_widget_grab_focus (GTK_WIDGET (day_view));
4657 
4658 		layout = GTK_LAYOUT (day_view->top_canvas);
4659 		window = gtk_layout_get_bin_window (layout);
4660 
4661 		event_device = gdk_event_get_device (button_event);
4662 		event_time = gdk_event_get_time (button_event);
4663 
4664 		grab_status = gdk_device_grab (
4665 			event_device,
4666 			window,
4667 			GDK_OWNERSHIP_NONE,
4668 			FALSE,
4669 			GDK_POINTER_MOTION_MASK |
4670 			GDK_BUTTON_RELEASE_MASK,
4671 			NULL,
4672 			event_time);
4673 
4674 		if (grab_status == GDK_GRAB_SUCCESS) {
4675 			g_warn_if_fail (day_view->grabbed_pointer == NULL);
4676 			day_view->grabbed_pointer = g_object_ref (event_device);
4677 
4678 			day_view->resize_event_day = E_DAY_VIEW_LONG_EVENT;
4679 			day_view->resize_event_num = event_num;
4680 			day_view->resize_drag_pos = pos;
4681 			day_view->resize_start_row = start_day;
4682 			day_view->resize_end_row = end_day;
4683 
4684 			/* Raise the event's item, above the rect as well. */
4685 			gnome_canvas_item_raise_to_top (event->canvas_item);
4686 		}
4687 	} else if (e_day_view_get_long_event_position (day_view, event_num,
4688 						       &start_day, &end_day,
4689 						       &item_x, &item_y,
4690 						       &item_w, &item_h)) {
4691 		/* Remember the item clicked and the mouse position,
4692 		 * so we can start a drag if the mouse moves. */
4693 		day_view->pressed_event_day = E_DAY_VIEW_LONG_EVENT;
4694 		day_view->pressed_event_num = event_num;
4695 
4696 		day_view->drag_event_x = event_x;
4697 		day_view->drag_event_y = event_y;
4698 
4699 		pos = e_day_view_convert_position_in_top_canvas (
4700 			day_view,
4701 			event_x, event_y,
4702 			&day, NULL);
4703 
4704 		if (pos != E_CALENDAR_VIEW_POS_NONE && pos != E_CALENDAR_VIEW_POS_OUTSIDE)
4705 			day_view->drag_event_offset = day - start_day;
4706 	}
4707 }
4708 
4709 static void
e_day_view_on_event_click(EDayView * day_view,gint day,gint event_num,GdkEvent * button_event,ECalendarViewPosition pos,gint event_x,gint event_y)4710 e_day_view_on_event_click (EDayView *day_view,
4711                            gint day,
4712                            gint event_num,
4713                            GdkEvent *button_event,
4714                            ECalendarViewPosition pos,
4715                            gint event_x,
4716                            gint event_y)
4717 {
4718 	EDayViewEvent *event;
4719 	ECalendarView *cal_view;
4720 	GtkLayout *layout;
4721 	GdkWindow *window;
4722 	gint time_divisions;
4723 	gint tmp_day, row, start_row;
4724 
4725 	cal_view = E_CALENDAR_VIEW (day_view);
4726 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
4727 
4728 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
4729 		return;
4730 
4731 	event = &g_array_index (day_view->events[day], EDayViewEvent,
4732 				event_num);
4733 
4734 	if (!is_comp_data_valid (event))
4735 		return;
4736 
4737 	/* Ignore clicks on the EText while editing. */
4738 	if (pos == E_CALENDAR_VIEW_POS_EVENT
4739 	    && E_TEXT (event->canvas_item)->editing) {
4740 		GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (
4741 			event->canvas_item, button_event);
4742 		return;
4743 	}
4744 
4745 	e_day_view_set_popup_event (day_view, day, event_num);
4746 
4747 	if ((e_cal_util_component_is_instance (event->comp_data->icalcomp) ||
4748 	     !e_cal_util_component_has_recurrences (event->comp_data->icalcomp))
4749 	    && (pos == E_CALENDAR_VIEW_POS_TOP_EDGE
4750 		|| pos == E_CALENDAR_VIEW_POS_BOTTOM_EDGE)) {
4751 		GdkGrabStatus grab_status;
4752 		GdkDevice *event_device;
4753 		guint32 event_time;
4754 
4755 		if (event && (!event->is_editable || e_client_is_readonly (E_CLIENT (event->comp_data->client)))) {
4756 			return;
4757 		}
4758 
4759 		/* Grab the keyboard focus, so the event being edited is saved
4760 		 * and we can use the Escape key to abort the resize. */
4761 		if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4762 			gtk_widget_grab_focus (GTK_WIDGET (day_view));
4763 
4764 		layout = GTK_LAYOUT (day_view->main_canvas);
4765 		window = gtk_layout_get_bin_window (layout);
4766 
4767 		event_device = gdk_event_get_device (button_event);
4768 		event_time = gdk_event_get_time (button_event);
4769 
4770 		grab_status = gdk_device_grab (
4771 			event_device,
4772 			window,
4773 			GDK_OWNERSHIP_NONE,
4774 			FALSE,
4775 			GDK_POINTER_MOTION_MASK |
4776 			GDK_BUTTON_RELEASE_MASK,
4777 			NULL,
4778 			event_time);
4779 
4780 		if (grab_status == GDK_GRAB_SUCCESS) {
4781 			g_warn_if_fail (day_view->grabbed_pointer == NULL);
4782 			day_view->grabbed_pointer = g_object_ref (event_device);
4783 
4784 			day_view->resize_event_day = day;
4785 			day_view->resize_event_num = event_num;
4786 			day_view->resize_drag_pos = pos;
4787 			day_view->resize_start_row = event->start_minute / time_divisions;
4788 			day_view->resize_end_row = (event->end_minute - 1) / time_divisions;
4789 			if (day_view->resize_end_row < day_view->resize_start_row)
4790 				day_view->resize_end_row = day_view->resize_start_row;
4791 
4792 			day_view->resize_bars_event_day = day;
4793 			day_view->resize_bars_event_num = event_num;
4794 
4795 			e_day_view_reshape_main_canvas_resize_bars (day_view);
4796 
4797 			/* Raise the event's item, above the rect as well. */
4798 			gnome_canvas_item_raise_to_top (event->canvas_item);
4799 		}
4800 
4801 	} else {
4802 		/* Remember the item clicked and the mouse position,
4803 		 * so we can start a drag if the mouse moves. */
4804 		day_view->pressed_event_day = day;
4805 		day_view->pressed_event_num = event_num;
4806 
4807 		day_view->drag_event_x = event_x;
4808 		day_view->drag_event_y = event_y;
4809 
4810 		pos = e_day_view_convert_position_in_main_canvas (
4811 			day_view,
4812 			event_x, event_y,
4813 			&tmp_day, &row,
4814 			NULL);
4815 
4816 		if (pos != E_CALENDAR_VIEW_POS_NONE && pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
4817 			start_row = event->start_minute / time_divisions;
4818 			day_view->drag_event_offset = row - start_row;
4819 		}
4820 	}
4821 }
4822 
4823 static void
e_day_view_on_event_double_click(EDayView * day_view,gint day,gint event_num)4824 e_day_view_on_event_double_click (EDayView *day_view,
4825                                   gint day,
4826                                   gint event_num)
4827 {
4828 	EDayViewEvent *event;
4829 
4830 	if (day == -1) {
4831 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
4832 			return;
4833 
4834 		event = &g_array_index (day_view->long_events, EDayViewEvent,
4835 				event_num);
4836 	} else {
4837 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
4838 			return;
4839 
4840 		event = &g_array_index (day_view->events[day], EDayViewEvent,
4841 				event_num);
4842 	}
4843 
4844 	if (!is_comp_data_valid (event))
4845 		return;
4846 
4847 	e_calendar_view_edit_appointment ((ECalendarView *) day_view, event->comp_data->client, event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT);
4848 }
4849 
4850 static void
e_day_view_show_popup_menu(EDayView * day_view,GdkEvent * button_event,gint day,gint event_num)4851 e_day_view_show_popup_menu (EDayView *day_view,
4852                             GdkEvent *button_event,
4853                             gint day,
4854                             gint event_num)
4855 {
4856 	EDayViewEvent *pevent = NULL;
4857 
4858 	if (event_num >= 0)
4859 		pevent = tooltip_get_view_event (day_view, day, event_num);
4860 
4861 	if (pevent && pevent->canvas_item)
4862 		tooltip_destroy (day_view, pevent->canvas_item);
4863 
4864 	e_day_view_set_popup_event (day_view, day, event_num);
4865 
4866 	e_calendar_view_popup_event (E_CALENDAR_VIEW (day_view), button_event);
4867 }
4868 
4869 /* Restarts a query for the day view */
4870 static void
e_day_view_update_query(EDayView * day_view)4871 e_day_view_update_query (EDayView *day_view)
4872 {
4873 	gint rows, r;
4874 
4875 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
4876 		e_day_view_free_events (day_view);
4877 		day_view->requires_update = TRUE;
4878 		return;
4879 	}
4880 
4881 	day_view->requires_update = FALSE;
4882 
4883 	e_day_view_stop_editing_event (day_view);
4884 
4885 	gtk_widget_queue_draw (day_view->top_canvas);
4886 	gtk_widget_queue_draw (day_view->top_dates_canvas);
4887 	gtk_widget_queue_draw (day_view->main_canvas);
4888 	e_day_view_free_events (day_view);
4889 	e_day_view_queue_layout (day_view);
4890 
4891 	rows = e_table_model_row_count (E_TABLE_MODEL (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view))));
4892 	for (r = 0; r < rows; r++) {
4893 		ECalModelComponent *comp_data;
4894 
4895 		comp_data = e_cal_model_get_component_at (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), r);
4896 		g_return_if_fail (comp_data != NULL);
4897 		process_component (day_view, comp_data);
4898 	}
4899 }
4900 
4901 static void
e_day_view_on_event_right_click(EDayView * day_view,GdkEvent * button_event,gint day,gint event_num)4902 e_day_view_on_event_right_click (EDayView *day_view,
4903                                  GdkEvent *button_event,
4904                                  gint day,
4905                                  gint event_num)
4906 {
4907 	e_day_view_show_popup_menu (day_view, button_event, day, event_num);
4908 }
4909 
4910 static gboolean
e_day_view_on_top_canvas_button_release(GtkWidget * widget,GdkEvent * button_event,EDayView * day_view)4911 e_day_view_on_top_canvas_button_release (GtkWidget *widget,
4912                                          GdkEvent *button_event,
4913                                          EDayView *day_view)
4914 {
4915 	GdkDevice *event_device;
4916 	guint32 event_time;
4917 
4918 	event_device = gdk_event_get_device (button_event);
4919 	event_time = gdk_event_get_time (button_event);
4920 
4921 	if (day_view->grabbed_pointer == event_device) {
4922 		gdk_device_ungrab (day_view->grabbed_pointer, event_time);
4923 		g_object_unref (day_view->grabbed_pointer);
4924 		day_view->grabbed_pointer = NULL;
4925 	}
4926 
4927 	if (day_view->selection_is_being_dragged) {
4928 		e_day_view_finish_selection (day_view);
4929 	} else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
4930 		e_day_view_finish_long_event_resize (day_view);
4931 	} else if (day_view->pressed_event_day != -1 &&
4932 		   e_calendar_view_get_allow_direct_summary_edit (E_CALENDAR_VIEW (day_view))) {
4933 		e_day_view_start_editing_event (
4934 			day_view,
4935 			day_view->pressed_event_day,
4936 			day_view->pressed_event_num,
4937 			NULL);
4938 	}
4939 
4940 	day_view->pressed_event_day = -1;
4941 
4942 	return FALSE;
4943 }
4944 
4945 static gboolean
e_day_view_on_main_canvas_button_release(GtkWidget * widget,GdkEvent * button_event,EDayView * day_view)4946 e_day_view_on_main_canvas_button_release (GtkWidget *widget,
4947                                           GdkEvent *button_event,
4948                                           EDayView *day_view)
4949 {
4950 	GdkDevice *event_device;
4951 	guint32 event_time;
4952 
4953 	event_device = gdk_event_get_device (button_event);
4954 	event_time = gdk_event_get_time (button_event);
4955 
4956 	if (day_view->grabbed_pointer == event_device) {
4957 		gdk_device_ungrab (day_view->grabbed_pointer, event_time);
4958 		g_object_unref (day_view->grabbed_pointer);
4959 		day_view->grabbed_pointer = NULL;
4960 	}
4961 
4962 	if (day_view->selection_is_being_dragged) {
4963 		e_day_view_finish_selection (day_view);
4964 		e_day_view_stop_auto_scroll (day_view);
4965 	} else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
4966 		e_day_view_finish_resize (day_view);
4967 		e_day_view_stop_auto_scroll (day_view);
4968 	} else if (day_view->pressed_event_day != -1 &&
4969 		   e_calendar_view_get_allow_direct_summary_edit (E_CALENDAR_VIEW (day_view))) {
4970 		e_day_view_start_editing_event (
4971 			day_view,
4972 			day_view->pressed_event_day,
4973 			day_view->pressed_event_num,
4974 			NULL);
4975 	}
4976 
4977 	day_view->pressed_event_day = -1;
4978 
4979 	return FALSE;
4980 }
4981 
4982 void
e_day_view_update_calendar_selection_time(EDayView * day_view)4983 e_day_view_update_calendar_selection_time (EDayView *day_view)
4984 {
4985 	time_t start, end;
4986 
4987 	day_view_get_selected_time_range ((ECalendarView *) day_view, &start, &end);
4988 }
4989 
4990 static gboolean
e_day_view_on_top_canvas_motion(GtkWidget * widget,GdkEventMotion * mevent,EDayView * day_view)4991 e_day_view_on_top_canvas_motion (GtkWidget *widget,
4992                                  GdkEventMotion *mevent,
4993                                  EDayView *day_view)
4994 {
4995 	EDayViewEvent *event = NULL;
4996 	ECalendarViewPosition pos;
4997 	gint event_x, event_y, canvas_x, canvas_y;
4998 	gint day, event_num;
4999 	GdkCursor *cursor;
5000 	GdkWindow *window;
5001 
5002 	window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
5003 
5004 	/* Convert the coords to the main canvas window, or return if the
5005 	 * window is not found. */
5006 	if (!e_day_view_convert_event_coords (
5007 		day_view, (GdkEvent *) mevent, window, &event_x, &event_y))
5008 		return FALSE;
5009 
5010 	canvas_x = event_x;
5011 	canvas_y = event_y;
5012 
5013 	pos = e_day_view_convert_position_in_top_canvas (
5014 		day_view,
5015 		canvas_x, canvas_y,
5016 		&day, &event_num);
5017 	if (event_num != -1) {
5018 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
5019 			return FALSE;
5020 
5021 		event = &g_array_index (day_view->long_events, EDayViewEvent,
5022 					event_num);
5023 	}
5024 
5025 	if (day_view->selection_is_being_dragged) {
5026 		e_day_view_update_selection (day_view, day, -1);
5027 		return TRUE;
5028 	} else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
5029 		if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
5030 			e_day_view_update_long_event_resize (day_view, day);
5031 			return TRUE;
5032 		}
5033 	} else if (day_view->pressed_event_day == E_DAY_VIEW_LONG_EVENT) {
5034 		GtkTargetList *target_list;
5035 
5036 		if (!is_array_index_in_bounds (day_view->long_events, day_view->pressed_event_num))
5037 			return FALSE;
5038 
5039 		event = &g_array_index (day_view->long_events, EDayViewEvent,
5040 					day_view->pressed_event_num);
5041 
5042 		if (!is_comp_data_valid (event))
5043 			return FALSE;
5044 
5045 		if (!e_cal_util_component_has_recurrences (event->comp_data->icalcomp)
5046 		    && gtk_drag_check_threshold (widget, day_view->drag_event_x, day_view->drag_event_y, canvas_x, canvas_y)) {
5047 			day_view->drag_event_day = day_view->pressed_event_day;
5048 			day_view->drag_event_num = day_view->pressed_event_num;
5049 			day_view->pressed_event_day = -1;
5050 
5051 			/* Hide the horizontal bars. */
5052 			if (day_view->resize_bars_event_day != -1) {
5053 				day_view->resize_bars_event_day = -1;
5054 				day_view->resize_bars_event_num = -1;
5055 			}
5056 
5057 			target_list = gtk_target_list_new (
5058 				target_table, G_N_ELEMENTS (target_table));
5059 			e_target_list_add_calendar_targets (target_list, 0);
5060 			g_clear_object (&day_view->priv->drag_context);
5061 			day_view->priv->drag_context = gtk_drag_begin (
5062 				widget, target_list,
5063 				GDK_ACTION_COPY | GDK_ACTION_MOVE,
5064 				1, (GdkEvent *) mevent);
5065 			gtk_target_list_unref (target_list);
5066 
5067 			if (day_view->priv->drag_context)
5068 				g_object_ref (day_view->priv->drag_context);
5069 		}
5070 	} else {
5071 		cursor = day_view->normal_cursor;
5072 
5073 		/* Recurring events can't be resized. */
5074 		if (event && is_comp_data_valid (event) && !e_cal_util_component_has_recurrences (event->comp_data->icalcomp)) {
5075 			switch (pos) {
5076 			case E_CALENDAR_VIEW_POS_LEFT_EDGE:
5077 			case E_CALENDAR_VIEW_POS_RIGHT_EDGE:
5078 				cursor = day_view->resize_width_cursor;
5079 				break;
5080 			default:
5081 				break;
5082 			}
5083 		}
5084 
5085 		/* Only set the cursor if it is different to last one set. */
5086 		if (day_view->last_cursor_set_in_top_canvas != cursor) {
5087 			GdkWindow *window;
5088 
5089 			day_view->last_cursor_set_in_top_canvas = cursor;
5090 
5091 			window = gtk_widget_get_window (widget);
5092 			gdk_window_set_cursor (window, cursor);
5093 		}
5094 
5095 		if (event && E_IS_TEXT (event->canvas_item) && E_TEXT (event->canvas_item)->editing) {
5096 			GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (event->canvas_item, (GdkEvent *) mevent);
5097 		}
5098 	}
5099 
5100 	return FALSE;
5101 }
5102 
5103 static gboolean
e_day_view_on_main_canvas_motion(GtkWidget * widget,GdkEventMotion * mevent,EDayView * day_view)5104 e_day_view_on_main_canvas_motion (GtkWidget *widget,
5105                                   GdkEventMotion *mevent,
5106                                   EDayView *day_view)
5107 {
5108 	EDayViewEvent *event = NULL;
5109 	ECalendarViewPosition pos;
5110 	gint event_x, event_y, canvas_x, canvas_y;
5111 	gint row, day, event_num;
5112 	GdkWindow *window;
5113 	GdkCursor *cursor;
5114 
5115 	window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
5116 
5117 	/* Convert the coords to the main canvas window, or return if the
5118 	 * window is not found. */
5119 	if (!e_day_view_convert_event_coords (
5120 		day_view, (GdkEvent *) mevent, window, &event_x, &event_y))
5121 		return FALSE;
5122 
5123 	canvas_x = event_x;
5124 	canvas_y = event_y;
5125 
5126 	pos = e_day_view_convert_position_in_main_canvas (
5127 		day_view,
5128 		canvas_x, canvas_y,
5129 		&day, &row,
5130 		&event_num);
5131 	if (event_num != -1) {
5132 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
5133 			return FALSE;
5134 
5135 		event = &g_array_index (day_view->events[day], EDayViewEvent,
5136 					event_num);
5137 	}
5138 
5139 	if (day_view->selection_is_being_dragged) {
5140 		if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
5141 			e_day_view_update_selection (day_view, day, row);
5142 			e_day_view_check_auto_scroll (
5143 				day_view,
5144 				event_x, event_y);
5145 			return TRUE;
5146 		}
5147 	} else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
5148 		if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
5149 			e_day_view_update_resize (day_view, row);
5150 			e_day_view_check_auto_scroll (
5151 				day_view,
5152 				event_x, event_y);
5153 			return TRUE;
5154 		}
5155 	} else if (day_view->pressed_event_day != -1
5156 		   && day_view->pressed_event_day != E_DAY_VIEW_LONG_EVENT) {
5157 		GtkTargetList *target_list;
5158 
5159 		if (gtk_drag_check_threshold (widget, day_view->drag_event_x, day_view->drag_event_y, canvas_x, canvas_y)) {
5160 			day_view->drag_event_day = day_view->pressed_event_day;
5161 			day_view->drag_event_num = day_view->pressed_event_num;
5162 			day_view->pressed_event_day = -1;
5163 
5164 			/* Hide the horizontal bars. */
5165 			if (day_view->resize_bars_event_day != -1) {
5166 				day_view->resize_bars_event_day = -1;
5167 				day_view->resize_bars_event_num = -1;
5168 			}
5169 
5170 			target_list = gtk_target_list_new (
5171 				target_table, G_N_ELEMENTS (target_table));
5172 			e_target_list_add_calendar_targets (target_list, 0);
5173 			g_clear_object (&day_view->priv->drag_context);
5174 			day_view->priv->drag_context = gtk_drag_begin (
5175 				widget, target_list,
5176 				GDK_ACTION_COPY | GDK_ACTION_MOVE,
5177 				1, (GdkEvent *) mevent);
5178 			gtk_target_list_unref (target_list);
5179 
5180 			if (day_view->priv->drag_context)
5181 				g_object_ref (day_view->priv->drag_context);
5182 		}
5183 	} else {
5184 		cursor = day_view->normal_cursor;
5185 
5186 		/* Check if the event is editable and client is not readonly while changing the cursor */
5187 		if (event && event->is_editable && is_comp_data_valid (event) && !e_client_is_readonly (E_CLIENT (event->comp_data->client))) {
5188 
5189 			switch (pos) {
5190 			case E_CALENDAR_VIEW_POS_LEFT_EDGE:
5191 				cursor = day_view->move_cursor;
5192 				break;
5193 			case E_CALENDAR_VIEW_POS_TOP_EDGE:
5194 			case E_CALENDAR_VIEW_POS_BOTTOM_EDGE:
5195 				cursor = day_view->resize_height_cursor;
5196 				break;
5197 			default:
5198 				break;
5199 			}
5200 		}
5201 
5202 		/* Only set the cursor if it is different to last one set. */
5203 		if (day_view->last_cursor_set_in_main_canvas != cursor) {
5204 			GdkWindow *window;
5205 
5206 			day_view->last_cursor_set_in_main_canvas = cursor;
5207 
5208 			window = gtk_widget_get_window (widget);
5209 			gdk_window_set_cursor (window, cursor);
5210 		}
5211 
5212 		if (event && E_IS_TEXT (event->canvas_item) && E_TEXT (event->canvas_item)->editing) {
5213 			GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (event->canvas_item, (GdkEvent *) mevent);
5214 		}
5215 	}
5216 
5217 	return FALSE;
5218 }
5219 
5220 /* This sets the selection to a single cell. If day is -1 then the current
5221  * start day is reused. If row is -1 then the selection is in the top canvas.
5222 */
5223 void
e_day_view_start_selection(EDayView * day_view,gint day,gint row)5224 e_day_view_start_selection (EDayView *day_view,
5225                             gint day,
5226                             gint row)
5227 {
5228 	if (day == -1) {
5229 		day = day_view->selection_start_day;
5230 		if (day == -1)
5231 			day = 0;
5232 	}
5233 
5234 	day_view->selection_start_day = day;
5235 	day_view->selection_end_day = day;
5236 
5237 	day_view->selection_start_row = row;
5238 	day_view->selection_end_row = row;
5239 
5240 	day_view->selection_is_being_dragged = TRUE;
5241 	day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
5242 	day_view->selection_in_top_canvas = (row == -1) ? TRUE : FALSE;
5243 
5244 	/* FIXME: Optimise? */
5245 	gtk_widget_queue_draw (day_view->top_canvas);
5246 	gtk_widget_queue_draw (day_view->main_canvas);
5247 }
5248 
5249 /* Updates the selection during a drag. If day is -1 the selection day is
5250  * unchanged. */
5251 void
e_day_view_update_selection(EDayView * day_view,gint day,gint row)5252 e_day_view_update_selection (EDayView *day_view,
5253                              gint day,
5254                              gint row)
5255 {
5256 	gboolean need_redraw = FALSE;
5257 
5258 	day_view->selection_in_top_canvas = (row == -1) ? TRUE : FALSE;
5259 
5260 	if (day == -1)
5261 		day = (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
5262 			? day_view->selection_start_day
5263 			: day_view->selection_end_day;
5264 
5265 	if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) {
5266 		if (row != day_view->selection_start_row
5267 		    || day != day_view->selection_start_day) {
5268 			need_redraw = TRUE;
5269 			day_view->selection_start_row = row;
5270 			day_view->selection_start_day = day;
5271 		}
5272 	} else {
5273 		if (row != day_view->selection_end_row
5274 		    || day != day_view->selection_end_day) {
5275 			need_redraw = TRUE;
5276 			day_view->selection_end_row = row;
5277 			day_view->selection_end_day = day;
5278 		}
5279 	}
5280 
5281 	e_day_view_normalize_selection (day_view);
5282 
5283 	/* FIXME: Optimise? */
5284 	if (need_redraw) {
5285 		gtk_widget_queue_draw (day_view->top_canvas);
5286 		gtk_widget_queue_draw (day_view->main_canvas);
5287 	}
5288 }
5289 
5290 static void
e_day_view_normalize_selection(EDayView * day_view)5291 e_day_view_normalize_selection (EDayView *day_view)
5292 {
5293 	gint tmp_row, tmp_day;
5294 
5295 	/* Switch the drag position if necessary. */
5296 	if (day_view->selection_start_day > day_view->selection_end_day
5297 	    || (day_view->selection_start_day == day_view->selection_end_day
5298 		&& day_view->selection_start_row > day_view->selection_end_row)) {
5299 		tmp_row = day_view->selection_start_row;
5300 		tmp_day = day_view->selection_start_day;
5301 		day_view->selection_start_day = day_view->selection_end_day;
5302 		day_view->selection_start_row = day_view->selection_end_row;
5303 		day_view->selection_end_day = tmp_day;
5304 		day_view->selection_end_row = tmp_row;
5305 		if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
5306 			day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
5307 		else
5308 			day_view->selection_drag_pos = E_DAY_VIEW_DRAG_START;
5309 	}
5310 }
5311 
5312 void
e_day_view_finish_selection(EDayView * day_view)5313 e_day_view_finish_selection (EDayView *day_view)
5314 {
5315 	day_view->selection_is_being_dragged = FALSE;
5316 	e_day_view_update_calendar_selection_time (day_view);
5317 }
5318 
5319 static void
e_day_view_update_long_event_resize(EDayView * day_view,gint day)5320 e_day_view_update_long_event_resize (EDayView *day_view,
5321                                      gint day)
5322 {
5323 	gint event_num;
5324 	gboolean need_reshape = FALSE;
5325 
5326 	event_num = day_view->resize_event_num;
5327 
5328 	if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE) {
5329 		day = MIN (day, day_view->resize_end_row);
5330 		if (day != day_view->resize_start_row) {
5331 			need_reshape = TRUE;
5332 			day_view->resize_start_row = day;
5333 
5334 		}
5335 	} else {
5336 		day = MAX (day, day_view->resize_start_row);
5337 		if (day != day_view->resize_end_row) {
5338 			need_reshape = TRUE;
5339 			day_view->resize_end_row = day;
5340 		}
5341 	}
5342 
5343 	/* FIXME: Optimise? */
5344 	if (need_reshape) {
5345 		e_day_view_reshape_long_event (day_view, event_num);
5346 		gtk_widget_queue_draw (day_view->top_canvas);
5347 	}
5348 }
5349 
5350 static void
e_day_view_update_resize(EDayView * day_view,gint row)5351 e_day_view_update_resize (EDayView *day_view,
5352                           gint row)
5353 {
5354 	/* Same thing again? */
5355 	EDayViewEvent *event;
5356 	gint day, event_num;
5357 	gboolean need_reshape = FALSE;
5358 
5359 	if (day_view->resize_event_num == -1)
5360 		return;
5361 
5362 	day = day_view->resize_event_day;
5363 	event_num = day_view->resize_event_num;
5364 
5365 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
5366 		return;
5367 
5368 	event = &g_array_index (day_view->events[day], EDayViewEvent,
5369 				event_num);
5370 
5371 	if (event && (!event->is_editable || !is_comp_data_valid (event) || e_client_is_readonly (E_CLIENT (event->comp_data->client)))) {
5372 		return;
5373 	}
5374 
5375 	if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) {
5376 		row = MIN (row, day_view->resize_end_row);
5377 		if (row != day_view->resize_start_row) {
5378 			need_reshape = TRUE;
5379 			day_view->resize_start_row = row;
5380 
5381 		}
5382 	} else {
5383 		row = MAX (row, day_view->resize_start_row);
5384 		if (row != day_view->resize_end_row) {
5385 			need_reshape = TRUE;
5386 			day_view->resize_end_row = row;
5387 		}
5388 	}
5389 
5390 	/* FIXME: Optimise? */
5391 	if (need_reshape) {
5392 		e_day_view_reshape_day_event (day_view, day, event_num);
5393 		e_day_view_reshape_main_canvas_resize_bars (day_view);
5394 		gtk_widget_queue_draw (day_view->main_canvas);
5395 	}
5396 }
5397 
5398 /* This converts the resize start or end row back to a time and updates the
5399  * event. */
5400 static void
e_day_view_finish_long_event_resize(EDayView * day_view)5401 e_day_view_finish_long_event_resize (EDayView *day_view)
5402 {
5403 	EDayViewEvent *event;
5404 	gint event_num;
5405 	ECalComponent *comp;
5406 	ECalComponentDateTime *date = NULL;
5407 	time_t dt;
5408 	ECalModel *model;
5409 	ECalClient *client;
5410 	ESourceRegistry *registry;
5411 	ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
5412 	ICalTimezone *zone;
5413 	gint is_date;
5414 
5415 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
5416 	registry = e_cal_model_get_registry (model);
5417 
5418 	event_num = day_view->resize_event_num;
5419 
5420 	if (!is_array_index_in_bounds (day_view->long_events, event_num))
5421 		return;
5422 
5423 	event = &g_array_index (day_view->long_events, EDayViewEvent,
5424 				event_num);
5425 
5426 	if (!is_comp_data_valid (event))
5427 		return;
5428 
5429 	client = event->comp_data->client;
5430 
5431 	/* We use a temporary copy of the comp since we don't want to
5432 	 * change the original comp here. Otherwise we would not detect that
5433 	 * the event's time had changed in the "update_event" callback. */
5434 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
5435 	if (!comp)
5436 		return;
5437 
5438 	if (e_cal_component_has_attendees (comp) &&
5439 	    !itip_organizer_is_user (registry, comp, client)) {
5440 		g_object_unref (comp);
5441 		e_day_view_abort_resize (day_view);
5442 		return;
5443 	}
5444 
5445 	zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view));
5446 
5447 	if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE) {
5448 		ECalComponentDateTime *ecdt;
5449 
5450 		ecdt = e_cal_component_get_dtstart (comp);
5451 		is_date = ecdt && e_cal_component_datetime_get_value (ecdt) &&
5452 			  i_cal_time_is_date (e_cal_component_datetime_get_value (ecdt));
5453 
5454 		dt = day_view->day_starts[day_view->resize_start_row];
5455 		date = e_cal_component_datetime_new_take (
5456 			i_cal_time_new_from_timet_with_zone (dt, is_date, zone),
5457 			(zone && !is_date) ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
5458 		cal_comp_set_dtstart_with_oldzone (client, comp, date);
5459 
5460 		e_cal_component_datetime_free (ecdt);
5461 
5462 		/* do not reuse it later */
5463 		e_cal_component_datetime_set_tzid (date, NULL);
5464 	} else {
5465 		ECalComponentDateTime *ecdt;
5466 
5467 		ecdt = e_cal_component_get_dtend (comp);
5468 		is_date = ecdt && e_cal_component_datetime_get_value (ecdt) &&
5469 			  i_cal_time_is_date (e_cal_component_datetime_get_value (ecdt));
5470 
5471 		dt = day_view->day_starts[day_view->resize_end_row + 1];
5472 		date = e_cal_component_datetime_new_take (
5473 			i_cal_time_new_from_timet_with_zone (dt, is_date, zone),
5474 			(zone && !is_date) ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
5475 		cal_comp_set_dtend_with_oldzone (client, comp, date);
5476 
5477 		e_cal_component_datetime_free (ecdt);
5478 
5479 		/* do not reuse it later */
5480 		e_cal_component_datetime_set_tzid (date, NULL);
5481 	}
5482 
5483 	e_cal_component_commit_sequence (comp);
5484 	if (e_cal_component_has_recurrences (comp)) {
5485 		if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
5486 			gtk_widget_queue_draw (day_view->top_canvas);
5487 			goto out;
5488 		}
5489 
5490 		if (mod == E_CAL_OBJ_MOD_THIS) {
5491 			/* set the correct DTSTART/DTEND on the individual recurrence */
5492 			if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) {
5493 				e_cal_component_datetime_take_value (date,
5494 					i_cal_time_new_from_timet_with_zone (event->comp_data->instance_end, FALSE, zone));
5495 				cal_comp_set_dtend_with_oldzone (client, comp, date);
5496 			} else {
5497 				e_cal_component_datetime_take_value (date,
5498 					i_cal_time_new_from_timet_with_zone (event->comp_data->instance_start, FALSE, zone));
5499 				cal_comp_set_dtstart_with_oldzone (client, comp, date);
5500 			}
5501 
5502 			e_cal_component_set_rdates (comp, NULL);
5503 			e_cal_component_set_rrules (comp, NULL);
5504 			e_cal_component_set_exdates (comp, NULL);
5505 			e_cal_component_set_exrules (comp, NULL);
5506 		}
5507 	} else if (e_cal_component_is_instance (comp)) {
5508 		mod = E_CAL_OBJ_MOD_THIS;
5509 	}
5510 
5511 	e_cal_component_commit_sequence (comp);
5512 
5513 	e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp),
5514 		mod, E_CAL_OPS_SEND_FLAG_ASK | E_CAL_OPS_SEND_FLAG_IS_NEW_COMPONENT);
5515 
5516  out:
5517 	day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
5518 
5519 	e_cal_component_datetime_free (date);
5520 	g_object_unref (comp);
5521 }
5522 
5523 /* This converts the resize start or end row back to a time and updates the
5524  * event. */
5525 static void
e_day_view_finish_resize(EDayView * day_view)5526 e_day_view_finish_resize (EDayView *day_view)
5527 {
5528 	EDayViewEvent *event;
5529 	gint day, event_num;
5530 	ECalComponent *comp;
5531 	ECalComponentDateTime *date = NULL;
5532 	ICalTimezone *zone;
5533 	time_t dt;
5534 	ECalModel *model;
5535 	ECalClient *client;
5536 	ESourceRegistry *registry;
5537 	ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
5538 	GtkWindow *toplevel;
5539 	GtkResponseType send = GTK_RESPONSE_NO;
5540 	gboolean only_new_attendees = FALSE;
5541 	gboolean strip_alarms = TRUE;
5542 
5543 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
5544 	registry = e_cal_model_get_registry (model);
5545 
5546 	if (day_view->resize_event_num == -1)
5547 		return;
5548 
5549 	day = day_view->resize_event_day;
5550 	event_num = day_view->resize_event_num;
5551 
5552 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
5553 		return;
5554 
5555 	event = &g_array_index (day_view->events[day], EDayViewEvent,
5556 				event_num);
5557 
5558 	if (!is_comp_data_valid (event))
5559 		return;
5560 
5561 	client = event->comp_data->client;
5562 
5563 	/* We use a temporary shallow copy of the ico since we don't want to
5564 	 * change the original ico here. Otherwise we would not detect that
5565 	 * the event's time had changed in the "update_event" callback. */
5566 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
5567 	if (!comp)
5568 		return;
5569 
5570 	if (e_cal_component_has_attendees (comp) &&
5571 	    !itip_organizer_is_user (registry, comp, client)) {
5572 		g_object_unref (comp);
5573 		e_day_view_abort_resize (day_view);
5574 		return;
5575 	}
5576 
5577 	toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view)));
5578 
5579 	if (itip_has_any_attendees (comp) &&
5580 	    (itip_organizer_is_user (registry, comp, client) ||
5581 	     itip_sentby_is_user (registry, comp, client)))
5582 		send = e_cal_dialogs_send_dragged_or_resized_component (
5583 				toplevel, client, comp, &strip_alarms, &only_new_attendees);
5584 
5585 	if (send == GTK_RESPONSE_CANCEL) {
5586 		e_day_view_abort_resize (day_view);
5587 		goto out;
5588 	}
5589 
5590 	zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view));
5591 
5592 	if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) {
5593 		dt = e_day_view_convert_grid_position_to_time (day_view, day, day_view->resize_start_row);
5594 		date = e_cal_component_datetime_new_take (
5595 			i_cal_time_new_from_timet_with_zone (dt, FALSE, zone),
5596 			zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
5597 		cal_comp_set_dtstart_with_oldzone (client, comp, date);
5598 	} else {
5599 		dt = e_day_view_convert_grid_position_to_time (day_view, day, day_view->resize_end_row + 1);
5600 		date = e_cal_component_datetime_new_take (
5601 			i_cal_time_new_from_timet_with_zone (dt, FALSE, zone),
5602 			zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
5603 		cal_comp_set_dtend_with_oldzone (client, comp, date);
5604 	}
5605 
5606 	e_cal_component_commit_sequence (comp);
5607 
5608 	g_clear_pointer (&day_view->last_edited_comp_string, g_free);
5609 
5610 	day_view->last_edited_comp_string = e_cal_component_get_as_string (comp);
5611 
5612 	/* Hide the horizontal bars. */
5613 	day_view->resize_bars_event_day = -1;
5614 	day_view->resize_bars_event_num = -1;
5615 
5616 	day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
5617 
5618 	if (e_cal_component_has_recurrences (comp)) {
5619 		if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
5620 			gtk_widget_queue_draw (day_view->main_canvas);
5621 			goto out;
5622 		}
5623 
5624 		if (mod == E_CAL_OBJ_MOD_THIS) {
5625 			/* set the correct DTSTART/DTEND on the individual recurrence */
5626 			if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) {
5627 				e_cal_component_datetime_take_value (date,
5628 					i_cal_time_new_from_timet_with_zone (event->comp_data->instance_end, FALSE, zone));
5629 				cal_comp_set_dtend_with_oldzone (client, comp, date);
5630 			} else {
5631 				e_cal_component_datetime_take_value (date,
5632 					i_cal_time_new_from_timet_with_zone (event->comp_data->instance_start, FALSE, zone));
5633 				cal_comp_set_dtstart_with_oldzone (client, comp, date);
5634 			}
5635 
5636 			e_cal_component_set_rdates (comp, NULL);
5637 			e_cal_component_set_rrules (comp, NULL);
5638 			e_cal_component_set_exdates (comp, NULL);
5639 			e_cal_component_set_exrules (comp, NULL);
5640 		}
5641 	} else if (e_cal_component_is_instance (comp)) {
5642 		mod = E_CAL_OBJ_MOD_THIS;
5643 	}
5644 
5645 	e_cal_component_commit_sequence (comp);
5646 
5647 	e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp), mod,
5648 		(send == GTK_RESPONSE_YES ? E_CAL_OPS_SEND_FLAG_SEND : E_CAL_OPS_SEND_FLAG_DONT_SEND) |
5649 		(strip_alarms ? E_CAL_OPS_SEND_FLAG_STRIP_ALARMS : 0) |
5650 		(only_new_attendees ? E_CAL_OPS_SEND_FLAG_ONLY_NEW_ATTENDEES : 0));
5651 
5652  out:
5653 	e_cal_component_datetime_free (date);
5654 	g_object_unref (comp);
5655 }
5656 
5657 static void
e_day_view_abort_resize(EDayView * day_view)5658 e_day_view_abort_resize (EDayView *day_view)
5659 {
5660 	GdkWindow *window;
5661 	gint day, event_num;
5662 
5663 	if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_NONE)
5664 		return;
5665 
5666 	day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
5667 
5668 	day = day_view->resize_event_day;
5669 	event_num = day_view->resize_event_num;
5670 
5671 	if (day == E_DAY_VIEW_LONG_EVENT) {
5672 		e_day_view_reshape_long_event (day_view, event_num);
5673 		gtk_widget_queue_draw (day_view->top_canvas);
5674 
5675 		day_view->last_cursor_set_in_top_canvas = day_view->normal_cursor;
5676 		window = gtk_widget_get_window (day_view->top_canvas);
5677 		gdk_window_set_cursor (window, day_view->normal_cursor);
5678 	} else {
5679 		e_day_view_reshape_day_event (day_view, day, event_num);
5680 		e_day_view_reshape_main_canvas_resize_bars (day_view);
5681 		gtk_widget_queue_draw (day_view->main_canvas);
5682 
5683 		day_view->last_cursor_set_in_main_canvas = day_view->normal_cursor;
5684 		window = gtk_widget_get_window (day_view->main_canvas);
5685 		gdk_window_set_cursor (window, day_view->normal_cursor);
5686 	}
5687 }
5688 
5689 static void
e_day_view_free_events(EDayView * day_view)5690 e_day_view_free_events (EDayView *day_view)
5691 {
5692 	gint day;
5693 	gboolean did_editing = day_view->editing_event_day != -1;
5694 
5695 	/* Reset all our indices. */
5696 	day_view->editing_event_day = -1;
5697 	day_view->popup_event_day = -1;
5698 	day_view->resize_bars_event_day = -1;
5699 	day_view->resize_event_day = -1;
5700 	day_view->pressed_event_day = -1;
5701 	day_view->drag_event_day = -1;
5702 	day_view->editing_event_num = -1;
5703 	day_view->popup_event_num = -1;
5704 
5705 	g_clear_object (&day_view->priv->drag_context);
5706 
5707 	e_day_view_free_event_array (day_view, day_view->long_events);
5708 
5709 	for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
5710 		e_day_view_free_event_array (day_view, day_view->events[day]);
5711 
5712 	if (did_editing)
5713 		g_object_notify (G_OBJECT (day_view), "is-editing");
5714 }
5715 
5716 static void
e_day_view_free_event_array(EDayView * day_view,GArray * array)5717 e_day_view_free_event_array (EDayView *day_view,
5718                              GArray *array)
5719 {
5720 	EDayViewEvent *event;
5721 	gint event_num;
5722 
5723 	for (event_num = 0; event_num < array->len; event_num++) {
5724 		event = &g_array_index (array, EDayViewEvent, event_num);
5725 		if (event->canvas_item)
5726 			g_object_run_dispose (G_OBJECT (event->canvas_item));
5727 
5728 		if (is_comp_data_valid (event))
5729 			g_object_unref (event->comp_data);
5730 
5731 		if (event->timeout > 0) {
5732 			g_source_remove (event->timeout);
5733 			event->timeout = -1;
5734 		}
5735 	}
5736 
5737 	g_array_set_size (array, 0);
5738 }
5739 
5740 /* This adds one event to the view, adding it to the appropriate array. */
5741 static void
e_day_view_add_event(ESourceRegistry * registry,ECalClient * client,ECalComponent * comp,time_t start,time_t end,gpointer data)5742 e_day_view_add_event (ESourceRegistry *registry,
5743 		      ECalClient *client,
5744                       ECalComponent *comp,
5745                       time_t start,
5746                       time_t end,
5747                       gpointer data)
5748 
5749 {
5750 	EDayViewEvent event;
5751 	gint day, offset;
5752 	gint days_shown;
5753 	ICalTime *start_tt, *end_tt;
5754 	ICalTimezone *zone;
5755 	AddEventData *add_event_data;
5756 
5757 	add_event_data = data;
5758 
5759 	/*if (end < start || start >= add_event_data->day_view->upper || end < add_event_data->day_view->lower) {
5760 		g_print ("%s: day_view: %p\n", G_STRFUNC, add_event_data->day_view);
5761 		g_print ("\tDay view lower: %s", ctime (&add_event_data->day_view->lower));
5762 		g_print ("\tDay view upper: %s", ctime (&add_event_data->day_view->upper));
5763 		g_print ("\tEvent start: %s", ctime (&start));
5764 		g_print ("\tEvent end  : %s\n", ctime (&end));
5765 	}*/
5766 
5767 	/* Check that the event times are valid. */
5768 	g_return_if_fail (start <= end);
5769 	g_return_if_fail (start < add_event_data->day_view->upper);
5770 
5771 	if (end != start || end < add_event_data->day_view->lower)
5772 		g_return_if_fail (end > add_event_data->day_view->lower);
5773 
5774 	zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->day_view));
5775 	start_tt = i_cal_time_new_from_timet_with_zone (start, FALSE, zone);
5776 	end_tt = i_cal_time_new_from_timet_with_zone (end, FALSE, zone);
5777 
5778 	if (add_event_data->comp_data) {
5779 		event.comp_data = g_object_ref (add_event_data->comp_data);
5780 	} else {
5781 		event.comp_data = g_object_new (E_TYPE_CAL_MODEL_COMPONENT, NULL);
5782 		event.comp_data->is_new_component = TRUE;
5783 		event.comp_data->client = g_object_ref (client);
5784 		e_cal_component_abort_sequence (comp);
5785 		event.comp_data->icalcomp = i_cal_component_clone (e_cal_component_get_icalcomponent (comp));
5786 	}
5787 
5788 	event.start = start;
5789 	event.tooltip = NULL;
5790 	event.color = NULL;
5791 	event.timeout = -1;
5792 	event.end = end;
5793 	event.canvas_item = NULL;
5794 	event.comp_data->instance_start = start;
5795 	event.comp_data->instance_end = end;
5796 
5797 	/* Calculate the start & end minute, relative to the top of the
5798 	 * display. */
5799 	offset = add_event_data->day_view->first_hour_shown * 60
5800 		+ add_event_data->day_view->first_minute_shown;
5801 	event.start_minute = i_cal_time_get_hour (start_tt) * 60 + i_cal_time_get_minute (start_tt) - offset;
5802 	event.end_minute = i_cal_time_get_hour (end_tt) * 60 + i_cal_time_get_minute (end_tt) - offset;
5803 
5804 	g_clear_object (&start_tt);
5805 	g_clear_object (&end_tt);
5806 
5807 	event.start_row_or_col = 0;
5808 	event.num_columns = 0;
5809 
5810 	event.different_timezone = FALSE;
5811 	if (!cal_comp_util_compare_event_timezones (comp, event.comp_data->client, zone))
5812 		event.different_timezone = TRUE;
5813 
5814 	if (!e_cal_component_has_attendees (comp) ||
5815 	    itip_organizer_is_user (registry, comp, event.comp_data->client) ||
5816 	    itip_sentby_is_user (registry, comp, event.comp_data->client))
5817 		event.is_editable = TRUE;
5818 	else
5819 		event.is_editable = FALSE;
5820 
5821 	days_shown = e_day_view_get_days_shown (add_event_data->day_view);
5822 
5823 	/* Find out which array to add the event to. */
5824 	for (day = 0; day < days_shown; day++) {
5825 		if (start >= add_event_data->day_view->day_starts[day]
5826 		    && end <= add_event_data->day_view->day_starts[day + 1]) {
5827 
5828 			if (start == end && start == add_event_data->day_view->day_starts[day + 1])
5829 				continue;
5830 
5831 			/* Special case for when the appointment ends at
5832 			 * midnight, i.e. the start of the next day. */
5833 			if (end == add_event_data->day_view->day_starts[day + 1] && start != end) {
5834 
5835 				/* If the event last the entire day, then we
5836 				 * skip it here so it gets added to the top
5837 				 * canvas. */
5838 				if (start == add_event_data->day_view->day_starts[day])
5839 					break;
5840 
5841 				event.end_minute = 24 * 60;
5842 			}
5843 
5844 			g_array_append_val (add_event_data->day_view->events[day], event);
5845 			add_event_data->day_view->events_sorted[day] = FALSE;
5846 			add_event_data->day_view->need_layout[day] = TRUE;
5847 			return;
5848 		}
5849 	}
5850 
5851 	/* The event wasn't within one day so it must be a long event,
5852 	 * i.e. shown in the top canvas. */
5853 	g_array_append_val (add_event_data->day_view->long_events, event);
5854 	add_event_data->day_view->long_events_sorted = FALSE;
5855 	add_event_data->day_view->long_events_need_layout = TRUE;
5856 	return;
5857 }
5858 
5859 /* This lays out the short (less than 1 day) events in the columns.
5860  * Any long events are simply skipped. */
5861 void
e_day_view_check_layout(EDayView * day_view)5862 e_day_view_check_layout (EDayView *day_view)
5863 {
5864 	ECalendarView *cal_view;
5865 	gint time_divisions;
5866 	gint day, rows_in_top_display;
5867 	gint days_shown;
5868 	gint max_cols = -1;
5869 
5870 	days_shown = e_day_view_get_days_shown (day_view);
5871 
5872 	cal_view = E_CALENDAR_VIEW (day_view);
5873 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
5874 
5875 	/* Don't bother if we aren't visible. */
5876 	if (!E_CALENDAR_VIEW (day_view)->in_focus) {
5877 		e_day_view_free_events (day_view);
5878 		day_view->requires_update = TRUE;
5879 		return;
5880 	}
5881 
5882 	/* Make sure the events are sorted (by start and size). */
5883 	e_day_view_ensure_events_sorted (day_view);
5884 
5885 	for (day = 0; day < days_shown; day++) {
5886 		if (day_view->need_layout[day]) {
5887 			gint cols;
5888 
5889 			cols = e_day_view_layout_day_events (
5890 				day_view->events[day],
5891 				day_view->rows,
5892 				time_divisions,
5893 				day_view->cols_per_row[day],
5894 				days_shown == 1 ? -1 :
5895 				E_DAY_VIEW_MULTI_DAY_MAX_COLUMNS);
5896 
5897 			max_cols = MAX (cols, max_cols);
5898 		}
5899 
5900 		if (day_view->need_layout[day]
5901 		    || day_view->need_reshape[day]) {
5902 			e_day_view_reshape_day_events (day_view, day);
5903 
5904 			if (day_view->resize_bars_event_day == day)
5905 				e_day_view_reshape_main_canvas_resize_bars (day_view);
5906 		}
5907 
5908 		day_view->need_layout[day] = FALSE;
5909 		day_view->need_reshape[day] = FALSE;
5910 	}
5911 
5912 	if (day_view->long_events_need_layout) {
5913 		e_day_view_layout_long_events (
5914 			day_view->long_events,
5915 			days_shown,
5916 			day_view->day_starts,
5917 			&rows_in_top_display);
5918 	}
5919 
5920 	if (day_view->long_events_need_layout
5921 	    || day_view->long_events_need_reshape)
5922 		e_day_view_reshape_long_events (day_view);
5923 
5924 	if (day_view->long_events_need_layout
5925 			&& day_view->rows_in_top_display != rows_in_top_display) {
5926 		day_view->rows_in_top_display = rows_in_top_display;
5927 		e_day_view_update_top_scroll (day_view, FALSE);
5928 	}
5929 
5930 	day_view->long_events_need_layout = FALSE;
5931 	day_view->long_events_need_reshape = FALSE;
5932 
5933 	if (max_cols != -1 && max_cols != day_view->max_cols) {
5934 		day_view->max_cols = max_cols;
5935 		e_day_view_recalc_main_canvas_size (day_view);
5936 	}
5937 }
5938 
5939 static void
e_day_view_on_text_item_notify_text_width(GObject * etext,GParamSpec * param,gpointer user_data)5940 e_day_view_on_text_item_notify_text_width (GObject *etext,
5941 					   GParamSpec *param,
5942 					   gpointer user_data)
5943 {
5944 	EDayView *day_view = user_data;
5945 	gint event_num, day;
5946 
5947 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
5948 
5949 	event_num = GPOINTER_TO_INT (g_object_get_data (etext, "event-num"));
5950 	day = GPOINTER_TO_INT (g_object_get_data (etext, "event-day"));
5951 
5952 	if (day == E_DAY_VIEW_LONG_EVENT)
5953 		e_day_view_reshape_long_event (day_view, event_num);
5954 	else
5955 		e_day_view_reshape_day_event (day_view, day, event_num);
5956 }
5957 
5958 static void
e_day_view_reshape_long_events(EDayView * day_view)5959 e_day_view_reshape_long_events (EDayView *day_view)
5960 {
5961 	EDayViewEvent *event;
5962 	gint event_num;
5963 
5964 	for (event_num = 0; event_num < day_view->long_events->len;
5965 	     event_num++) {
5966 		event = &g_array_index (day_view->long_events, EDayViewEvent,
5967 					event_num);
5968 
5969 		if (event->num_columns == 0) {
5970 			if (event->canvas_item) {
5971 				g_object_run_dispose (G_OBJECT (event->canvas_item));
5972 				event->canvas_item = NULL;
5973 			}
5974 		} else {
5975 			e_day_view_reshape_long_event (day_view, event_num);
5976 		}
5977 	}
5978 }
5979 
5980 static void
e_day_view_reshape_long_event(EDayView * day_view,gint event_num)5981 e_day_view_reshape_long_event (EDayView *day_view,
5982                                gint event_num)
5983 {
5984 	EDayViewEvent *event;
5985 	gint start_day, end_day, item_x, item_y, item_w, item_h;
5986 	gint text_x, text_w, num_icons, icons_width, width, time_width;
5987 	ECalComponent *comp;
5988 	gint min_text_x, max_text_w, text_width, line_len;
5989 	gchar *text, *end_of_line;
5990 	gboolean show_icons = TRUE, use_max_width = FALSE;
5991 	PangoContext *pango_context;
5992 	PangoLayout *layout;
5993 
5994 	if (!is_array_index_in_bounds (day_view->long_events, event_num))
5995 		return;
5996 
5997 	event = &g_array_index (day_view->long_events, EDayViewEvent,
5998 				event_num);
5999 
6000 	if (!e_day_view_get_long_event_position (day_view, event_num,
6001 						 &start_day, &end_day,
6002 						 &item_x, &item_y,
6003 						 &item_w, &item_h)) {
6004 		if (event->canvas_item) {
6005 			g_object_run_dispose (G_OBJECT (event->canvas_item));
6006 			event->canvas_item = NULL;
6007 		}
6008 		return;
6009 	}
6010 
6011 	if (!is_comp_data_valid (event))
6012 		return;
6013 
6014 	/* Take off the border and padding. */
6015 	item_x += E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD;
6016 	item_w -= (E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD) * 2;
6017 	item_y += E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD;
6018 	item_h -= (E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD) * 2;
6019 
6020 	/* We don't show the icons while resizing, since we'd have to
6021 	 * draw them on top of the resize rect. Nor when editing. */
6022 	num_icons = 0;
6023 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
6024 	if (!comp)
6025 		return;
6026 
6027 	/* Set up Pango prerequisites */
6028 	pango_context = gtk_widget_get_pango_context (GTK_WIDGET (day_view));
6029 	layout = pango_layout_new (pango_context);
6030 
6031 	if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE
6032 	    && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT
6033 	    && day_view->resize_event_num == event_num)
6034 		show_icons = FALSE;
6035 
6036 	if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT
6037 	    && day_view->editing_event_num == event_num) {
6038 		show_icons = FALSE;
6039 		use_max_width = TRUE;
6040 	}
6041 
6042 	if (show_icons) {
6043 		if (e_cal_component_has_alarms (comp))
6044 			num_icons++;
6045 		if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp))
6046 			num_icons++;
6047 		if (event->different_timezone)
6048 			num_icons++;
6049 
6050 		if (e_cal_component_has_attendees (comp))
6051 			num_icons++;
6052 		if (e_cal_component_has_attachments (comp))
6053 			num_icons++;
6054 		num_icons += cal_comp_util_get_n_icons (comp, NULL);
6055 	}
6056 
6057 	if (!event->canvas_item) {
6058 		GdkColor color;
6059 
6060 		color = e_day_view_get_text_color (day_view, event);
6061 
6062 		event->canvas_item =
6063 			gnome_canvas_item_new (
6064 				GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_canvas)->root),
6065 				e_text_get_type (),
6066 				"clip", TRUE,
6067 				"max_lines", 1,
6068 				"editable", TRUE,
6069 				"use_ellipsis", TRUE,
6070 				"fill_color_gdk", &color,
6071 				"im_context", E_CANVAS (day_view->top_canvas)->im_context,
6072 				NULL);
6073 		g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num));
6074 		g_object_set_data (G_OBJECT (event->canvas_item), "event-day", GINT_TO_POINTER (E_DAY_VIEW_LONG_EVENT));
6075 		g_signal_connect (
6076 			event->canvas_item, "event",
6077 			G_CALLBACK (e_day_view_on_text_item_event), day_view);
6078 		g_signal_connect (
6079 			event->canvas_item, "notify::text-width",
6080 			G_CALLBACK (e_day_view_on_text_item_notify_text_width), day_view);
6081 		g_signal_emit_by_name (day_view, "event_added", event);
6082 
6083 		e_day_view_update_long_event_label (day_view, event_num);
6084 	} else if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->canvas_item), "event-num")) != event_num) {
6085 		g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num));
6086 	}
6087 
6088 	/* Calculate its position. We first calculate the ideal position which
6089 	 * is centered with the icons. We then make sure we haven't gone off
6090 	 * the left edge of the available space. Finally we make sure we don't
6091 	 * go off the right edge. */
6092 	icons_width = (E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD)
6093 		* num_icons + E_DAY_VIEW_LONG_EVENT_ICON_R_PAD;
6094 	time_width = e_day_view_get_time_string_width (day_view);
6095 
6096 	if (use_max_width) {
6097 		text_x = item_x;
6098 		text_w = item_w;
6099 	} else {
6100 		gdouble item_text_width = 0;
6101 
6102 		/* Get the requested size of the label. */
6103 		g_object_get (event->canvas_item, "text-width", &item_text_width, NULL);
6104 
6105 		text_width = (gint) item_text_width;
6106 
6107 		if (text_width <= 0) {
6108 			g_object_get (event->canvas_item, "text", &text, NULL);
6109 			text_width = 0;
6110 			if (text) {
6111 				end_of_line = strchr (text, '\n');
6112 				if (end_of_line)
6113 					line_len = end_of_line - text;
6114 				else
6115 					line_len = strlen (text);
6116 				pango_layout_set_text (layout, text, line_len);
6117 				pango_layout_get_pixel_size (layout, &text_width, NULL);
6118 				g_free (text);
6119 			}
6120 		}
6121 
6122 		width = text_width + icons_width;
6123 		text_x = item_x + (item_w - width) / 2;
6124 
6125 		min_text_x = item_x;
6126 		if (event->start > day_view->day_starts[start_day])
6127 			min_text_x += time_width + E_DAY_VIEW_LONG_EVENT_TIME_X_PAD;
6128 
6129 		text_x = MAX (text_x, min_text_x);
6130 
6131 		max_text_w = item_x + item_w - text_x;
6132 		if (event->end < day_view->day_starts[end_day + 1])
6133 			max_text_w -= time_width + E_DAY_VIEW_LONG_EVENT_TIME_X_PAD;
6134 
6135 		text_w = MIN (width, max_text_w);
6136 
6137 		/* Now take out the space for the icons. */
6138 		text_x += icons_width;
6139 		text_w -= icons_width;
6140 	}
6141 
6142 	text_w = MAX (text_w, 0);
6143 	gnome_canvas_item_set (
6144 		event->canvas_item,
6145 		"x_offset", (gdouble) MAX (0, text_x - item_x),
6146 		"clip_width", (gdouble) MAX (0, item_w - (E_DAY_VIEW_LONG_EVENT_TIME_X_PAD * 2)),
6147 		"clip_height", (gdouble) item_h,
6148 		NULL);
6149 	e_canvas_item_move_absolute (
6150 		event->canvas_item,
6151 		item_x, item_y);
6152 
6153 	g_object_unref (layout);
6154 	g_object_unref (comp);
6155 }
6156 
6157 /* This creates or updates the sizes of the canvas items for one day of the
6158  * main canvas. */
6159 static void
e_day_view_reshape_day_events(EDayView * day_view,gint day)6160 e_day_view_reshape_day_events (EDayView *day_view,
6161                                gint day)
6162 {
6163 	gint event_num;
6164 
6165 	for (event_num = 0; event_num < day_view->events[day]->len;
6166 	     event_num++) {
6167 		EDayViewEvent *event;
6168 		gchar *current_comp_string;
6169 
6170 		e_day_view_reshape_day_event (day_view, day, event_num);
6171 		event = &g_array_index (day_view->events[day], EDayViewEvent, event_num);
6172 
6173 		if (!is_comp_data_valid (event))
6174 			continue;
6175 
6176 		current_comp_string = i_cal_component_as_ical_string (event->comp_data->icalcomp);
6177 		if (day_view->last_edited_comp_string == NULL) {
6178 			g_free (current_comp_string);
6179 			continue;
6180 		}
6181 
6182 		if (strncmp (current_comp_string, day_view->last_edited_comp_string, 50) == 0) {
6183 			if (e_calendar_view_get_allow_direct_summary_edit (E_CALENDAR_VIEW (day_view)))
6184 				e_canvas_item_grab_focus (event->canvas_item, TRUE);
6185 
6186 			g_free (day_view->last_edited_comp_string);
6187 			day_view-> last_edited_comp_string = NULL;
6188 		}
6189 		g_free (current_comp_string);
6190 	}
6191 }
6192 
6193 static void
e_day_view_reshape_day_event(EDayView * day_view,gint day,gint event_num)6194 e_day_view_reshape_day_event (EDayView *day_view,
6195                               gint day,
6196                               gint event_num)
6197 {
6198 	EDayViewEvent *event;
6199 	gint item_x, item_y, item_w, item_h;
6200 	gint num_icons, icons_offset;
6201 
6202 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
6203 		return;
6204 
6205 	event = &g_array_index (day_view->events[day], EDayViewEvent,
6206 				event_num);
6207 
6208 	if (!e_day_view_get_event_position (day_view, day, event_num,
6209 					    &item_x, &item_y,
6210 					    &item_w, &item_h)) {
6211 		if (event->canvas_item) {
6212 			g_object_run_dispose (G_OBJECT (event->canvas_item));
6213 			event->canvas_item = NULL;
6214 		}
6215 	} else {
6216 		/* Skip the border and padding. */
6217 		item_x += E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD;
6218 		item_w -= E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD * 2;
6219 		item_y += E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD;
6220 		item_h -= (E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD) * 2;
6221 
6222 		/* We don't show the icons while resizing, since we'd have to
6223 		 * draw them on top of the resize rect. */
6224 		icons_offset = 0;
6225 		num_icons = 0;
6226 		if (is_comp_data_valid (event) && (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_NONE
6227 		    || day_view->resize_event_day != day
6228 		    || day_view->resize_event_num != event_num)) {
6229 			ECalComponent *comp;
6230 
6231 			comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
6232 			if (comp) {
6233 				if (e_cal_component_has_alarms (comp))
6234 					num_icons++;
6235 				if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp))
6236 					num_icons++;
6237 				if (e_cal_component_has_attachments (comp))
6238 					num_icons++;
6239 				if (event->different_timezone)
6240 					num_icons++;
6241 				if (e_cal_component_has_attendees (comp))
6242 					num_icons++;
6243 
6244 				num_icons += cal_comp_util_get_n_icons (comp, NULL);
6245 				g_object_unref (comp);
6246 			}
6247 		}
6248 
6249 		if (num_icons > 0) {
6250 			if (item_h >= (E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD) * num_icons)
6251 				icons_offset = E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD * 2;
6252 			else if (item_h <= (E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD) * 2 || num_icons == 1)
6253 				icons_offset = (E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD) * num_icons + E_DAY_VIEW_ICON_X_PAD;
6254 			else
6255 				icons_offset = E_DAY_VIEW_ICON_X_PAD;
6256 		}
6257 
6258 		if (!event->canvas_item) {
6259 			GdkColor color;
6260 
6261 			color = e_day_view_get_text_color (day_view, event);
6262 
6263 			event->canvas_item = gnome_canvas_item_new (
6264 				GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->main_canvas)->root),
6265 				e_text_get_type (),
6266 				"line_wrap", TRUE,
6267 				"editable", TRUE,
6268 				"clip", TRUE,
6269 				"use_ellipsis", TRUE,
6270 				"fill_color_gdk", &color,
6271 				"im_context", E_CANVAS (day_view->main_canvas)->im_context,
6272 				NULL);
6273 			g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num));
6274 			g_object_set_data (G_OBJECT (event->canvas_item), "event-day", GINT_TO_POINTER (day));
6275 			g_signal_connect (
6276 				event->canvas_item, "event",
6277 				G_CALLBACK (e_day_view_on_text_item_event), day_view);
6278 			g_signal_emit_by_name (day_view, "event_added", event);
6279 
6280 			e_day_view_update_event_label (day_view, day, event_num);
6281 		} else if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->canvas_item), "event-num")) != event_num) {
6282 			g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num));
6283 		}
6284 
6285 		item_w = MAX (item_w, 0);
6286 		gnome_canvas_item_set (
6287 			event->canvas_item,
6288 			"clip_width", (gdouble) item_w,
6289 			"clip_height", (gdouble) item_h,
6290 			"x_offset", (gdouble) icons_offset,
6291 			NULL);
6292 		e_canvas_item_move_absolute (
6293 			event->canvas_item,
6294 			item_x, item_y);
6295 	}
6296 }
6297 
6298 /* This creates or resizes the horizontal bars used to resize events in the
6299  * main canvas. */
6300 static void
e_day_view_reshape_main_canvas_resize_bars(EDayView * day_view)6301 e_day_view_reshape_main_canvas_resize_bars (EDayView *day_view)
6302 {
6303 	gint day, event_num;
6304 	gint item_x, item_y, item_w, item_h;
6305 	gdouble x, y, w, h;
6306 
6307 	day = day_view->resize_bars_event_day;
6308 	event_num = day_view->resize_bars_event_num;
6309 
6310 	/* If we're not editing an event, or the event is not shown,
6311 	 * hide the resize bars. */
6312 	if (day != -1 && day == day_view->drag_event_day
6313 	    && event_num == day_view->drag_event_num) {
6314 		g_object_get (
6315 			day_view->drag_rect_item,
6316 			"x1", &x,
6317 			"y1", &y,
6318 			"x2", &w,
6319 			"y2", &h,
6320 			NULL);
6321 		w -= x;
6322 		x++;
6323 		h -= y;
6324 	} else if (day != -1
6325 		   && e_day_view_get_event_position (day_view, day, event_num,
6326 						     &item_x, &item_y,
6327 						     &item_w, &item_h)) {
6328 		x = item_x + E_DAY_VIEW_BAR_WIDTH;
6329 		y = item_y;
6330 		w = item_w - E_DAY_VIEW_BAR_WIDTH;
6331 		h = item_h;
6332 
6333 		gtk_widget_queue_draw (day_view->main_canvas);
6334 	} else {
6335 		return;
6336 	}
6337 }
6338 
6339 static void
e_day_view_ensure_events_sorted(EDayView * day_view)6340 e_day_view_ensure_events_sorted (EDayView *day_view)
6341 {
6342 	gint day;
6343 	gint days_shown;
6344 
6345 	days_shown = e_day_view_get_days_shown (day_view);
6346 
6347 	/* Sort the long events. */
6348 	if (!day_view->long_events_sorted) {
6349 		qsort (
6350 			day_view->long_events->data,
6351 			day_view->long_events->len,
6352 			sizeof (EDayViewEvent),
6353 			e_day_view_event_sort_func);
6354 		day_view->long_events_sorted = TRUE;
6355 	}
6356 
6357 	/* Sort the events for each day. */
6358 	for (day = 0; day < days_shown; day++) {
6359 		if (!day_view->events_sorted[day]) {
6360 			qsort (
6361 				day_view->events[day]->data,
6362 				day_view->events[day]->len,
6363 				sizeof (EDayViewEvent),
6364 				e_day_view_event_sort_func);
6365 			day_view->events_sorted[day] = TRUE;
6366 		}
6367 	}
6368 }
6369 
6370 gint
e_day_view_event_sort_func(gconstpointer arg1,gconstpointer arg2)6371 e_day_view_event_sort_func (gconstpointer arg1,
6372                             gconstpointer arg2)
6373 {
6374 	EDayViewEvent *event1, *event2;
6375 
6376 	event1 = (EDayViewEvent *) arg1;
6377 	event2 = (EDayViewEvent *) arg2;
6378 
6379 	if (event1->start < event2->start)
6380 		return -1;
6381 	if (event1->start > event2->start)
6382 		return 1;
6383 
6384 	if (event1->end > event2->end)
6385 		return -1;
6386 	if (event1->end < event2->end)
6387 		return 1;
6388 
6389 	return 0;
6390 }
6391 
6392 static gboolean
e_day_view_do_key_press(GtkWidget * widget,GdkEventKey * event)6393 e_day_view_do_key_press (GtkWidget *widget,
6394                          GdkEventKey *event)
6395 {
6396 	EDayView *day_view;
6397 	guint keyval;
6398 	gboolean stop_emission;
6399 
6400 	g_return_val_if_fail (widget != NULL, FALSE);
6401 	g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
6402 	g_return_val_if_fail (event != NULL, FALSE);
6403 
6404 	day_view = E_DAY_VIEW (widget);
6405 	keyval = event->keyval;
6406 
6407 	/* The Escape key aborts a resize operation. */
6408 	if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
6409 		if (keyval == GDK_KEY_Escape) {
6410 			if (day_view->grabbed_pointer != NULL) {
6411 				gdk_device_ungrab (
6412 					day_view->grabbed_pointer,
6413 					event->time);
6414 				g_object_unref (day_view->grabbed_pointer);
6415 				day_view->grabbed_pointer = NULL;
6416 			}
6417 			e_day_view_abort_resize (day_view);
6418 		}
6419 		return FALSE;
6420 	}
6421 
6422 	/* Alt + Arrow Keys to move a selected event through time lines */
6423 	if (((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK)
6424 		&&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6425 		&&((event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK)) {
6426 		if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up)
6427 			return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_UP);
6428 		else if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
6429 			return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_DOWN);
6430 		else if (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left)
6431 			return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_LEFT);
6432 		else if (keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right)
6433 			return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_RIGHT);
6434 	}
6435 
6436 	/*Go to the start/end of a work day*/
6437 	if ((keyval == GDK_KEY_Home)
6438 			&&((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK)
6439 			&&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6440 			&&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) {
6441 		e_day_view_goto_start_of_work_day (day_view);
6442 		return TRUE;
6443 	}
6444 	if ((keyval == GDK_KEY_End)
6445 	    &&((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK)
6446 	    &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6447 	    &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) {
6448 		e_day_view_goto_end_of_work_day (day_view);
6449 		return TRUE;
6450 	}
6451 
6452 	/* In DayView, Shift+Home/End, Change the duration to the time that begins/ends the current work day */
6453 	if ((keyval == GDK_KEY_Home)
6454 	    &&((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6455 	    &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6456 	    &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) {
6457 		e_day_view_change_duration_to_start_of_work_day (day_view);
6458 		return TRUE;
6459 	}
6460 	if ((keyval == GDK_KEY_End)
6461 	    &&((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6462 	    &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6463 	    &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) {
6464 		e_day_view_change_duration_to_end_of_work_day (day_view);
6465 		return TRUE;
6466 	}
6467 
6468 	/* Handle the cursor keys for moving & extending the selection. */
6469 	stop_emission = TRUE;
6470 	if (event->state & GDK_SHIFT_MASK) {
6471 		switch (keyval) {
6472 		case GDK_KEY_Up:
6473 			e_day_view_cursor_key_up_shifted (day_view, event);
6474 			break;
6475 		case GDK_KEY_Down:
6476 			e_day_view_cursor_key_down_shifted (day_view, event);
6477 			break;
6478 		case GDK_KEY_Left:
6479 			e_day_view_cursor_key_left_shifted (day_view, event);
6480 			break;
6481 		case GDK_KEY_Right:
6482 			e_day_view_cursor_key_right_shifted (day_view, event);
6483 			break;
6484 		default:
6485 			stop_emission = FALSE;
6486 			break;
6487 		}
6488 	} else if (!(event->state & GDK_MOD1_MASK)) {
6489 		switch (keyval) {
6490 		case GDK_KEY_Up:
6491 			e_day_view_cursor_key_up (day_view, event);
6492 			break;
6493 		case GDK_KEY_Down:
6494 			e_day_view_cursor_key_down (day_view, event);
6495 			break;
6496 		case GDK_KEY_Left:
6497 			e_day_view_cursor_key_left (day_view, event);
6498 			break;
6499 		case GDK_KEY_Right:
6500 			e_day_view_cursor_key_right (day_view, event);
6501 			break;
6502 		case GDK_KEY_Page_Up:
6503 			e_day_view_scroll (day_view, E_DAY_VIEW_PAGE_STEP);
6504 			break;
6505 		case GDK_KEY_Page_Down:
6506 			e_day_view_scroll (day_view, -E_DAY_VIEW_PAGE_STEP);
6507 			break;
6508 		default:
6509 			stop_emission = FALSE;
6510 			break;
6511 		}
6512 	}
6513 	else
6514 		stop_emission = FALSE;
6515 	if (stop_emission)
6516 		return TRUE;
6517 
6518 	if (day_view->selection_start_day == -1)
6519 		return FALSE;
6520 
6521 	/* We only want to start an edit with a return key or a simple
6522 	 * character. */
6523 	if ((keyval != GDK_KEY_Return && keyval != GDK_KEY_KP_Enter) &&
6524 	    (((keyval >= 0x20) && (keyval <= 0xFF)
6525 	      && (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
6526 	     || (event->length == 0)
6527 	     || (keyval == GDK_KEY_Tab)
6528 	     || (keyval == GDK_KEY_Escape)
6529 	     || (keyval == GDK_KEY_Delete)
6530 	     || (keyval == GDK_KEY_KP_Delete))) {
6531 		return FALSE;
6532 	}
6533 
6534 	e_day_view_add_new_event_in_selected_range (day_view, event, FALSE);
6535 
6536 	return TRUE;
6537 }
6538 
6539 /* Select the time that begins a work day*/
6540 static void
e_day_view_goto_start_of_work_day(EDayView * day_view)6541 e_day_view_goto_start_of_work_day (EDayView *day_view)
6542 {
6543 	gint work_day_start_hour;
6544 	gint work_day_start_minute;
6545 	gint work_day_end_hour;
6546 	gint work_day_end_minute;
6547 
6548 	if (day_view->selection_in_top_canvas)
6549 		return;
6550 
6551 	e_day_view_get_work_day_range_for_day (day_view, day_view->selection_start_day,
6552 		&work_day_start_hour, &work_day_start_minute,
6553 		&work_day_end_hour, &work_day_end_minute);
6554 
6555 	day_view->selection_start_row =
6556 		e_day_view_convert_time_to_row (
6557 		day_view, work_day_start_hour, work_day_start_minute);
6558 	day_view->selection_end_row = day_view->selection_start_row;
6559 	day_view->selection_end_day = day_view->selection_start_day;
6560 
6561 	e_day_view_ensure_rows_visible (
6562 		day_view,
6563 		day_view->selection_start_row,
6564 		day_view->selection_end_row);
6565 
6566 	e_day_view_update_calendar_selection_time (day_view);
6567 
6568 	gtk_widget_queue_draw (day_view->top_canvas);
6569 	gtk_widget_queue_draw (day_view->top_dates_canvas);
6570 	gtk_widget_queue_draw (day_view->main_canvas);
6571 }
6572 
6573 /* Select the time that ends a work day*/
6574 static void
e_day_view_goto_end_of_work_day(EDayView * day_view)6575 e_day_view_goto_end_of_work_day (EDayView *day_view)
6576 {
6577 	gint work_day_start_hour;
6578 	gint work_day_start_minute;
6579 	gint work_day_end_hour;
6580 	gint work_day_end_minute;
6581 
6582 	if (day_view->selection_in_top_canvas)
6583 		return;
6584 
6585 	e_day_view_get_work_day_range_for_day (day_view, day_view->selection_end_day,
6586 		&work_day_start_hour, &work_day_start_minute,
6587 		&work_day_end_hour, &work_day_end_minute);
6588 
6589 	day_view->selection_start_row =
6590 		e_day_view_convert_time_to_row (
6591 		day_view, work_day_end_hour - 1, work_day_end_minute + 30);
6592 	day_view->selection_end_row = day_view->selection_start_row;
6593 	day_view->selection_start_day = day_view->selection_end_day;
6594 
6595 	e_day_view_ensure_rows_visible (
6596 		day_view,
6597 		day_view->selection_start_row,
6598 		day_view->selection_end_row);
6599 
6600 	e_day_view_update_calendar_selection_time (day_view);
6601 
6602 	gtk_widget_queue_draw (day_view->top_canvas);
6603 	gtk_widget_queue_draw (day_view->top_dates_canvas);
6604 	gtk_widget_queue_draw (day_view->main_canvas);
6605 }
6606 
6607 /* Change the duration to the time that begins the current work day */
6608 static void
e_day_view_change_duration_to_start_of_work_day(EDayView * day_view)6609 e_day_view_change_duration_to_start_of_work_day (EDayView *day_view)
6610 {
6611 	gint work_start_row;
6612 	gint work_day_start_hour;
6613 	gint work_day_start_minute;
6614 	gint work_day_end_hour;
6615 	gint work_day_end_minute;
6616 
6617 	g_return_if_fail (day_view != NULL);
6618 
6619 	if (day_view->selection_in_top_canvas)
6620 		return;
6621 
6622 	e_day_view_get_work_day_range_for_day (day_view, day_view->selection_start_day,
6623 		&work_day_start_hour, &work_day_start_minute,
6624 		&work_day_end_hour, &work_day_end_minute);
6625 
6626 	work_start_row = e_day_view_convert_time_to_row (day_view, work_day_start_hour, work_day_start_minute);
6627 
6628 	if (day_view->selection_start_row < work_start_row)
6629 		day_view->selection_end_row = work_start_row - 1;
6630 	else
6631 		day_view->selection_start_row = work_start_row;
6632 
6633 	e_day_view_ensure_rows_visible (
6634 		day_view,
6635 		day_view->selection_start_row,
6636 		day_view->selection_end_row);
6637 
6638 	e_day_view_update_calendar_selection_time (day_view);
6639 
6640 	gtk_widget_queue_draw (day_view->top_canvas);
6641 	gtk_widget_queue_draw (day_view->top_dates_canvas);
6642 	gtk_widget_queue_draw (day_view->main_canvas);
6643 }
6644 
6645 /* Change the duration to the time that ends the current work day */
6646 static void
e_day_view_change_duration_to_end_of_work_day(EDayView * day_view)6647 e_day_view_change_duration_to_end_of_work_day (EDayView *day_view)
6648 {
6649 	gint selection_start_row, work_end_row;
6650 	gint work_day_start_hour;
6651 	gint work_day_start_minute;
6652 	gint work_day_end_hour;
6653 	gint work_day_end_minute;
6654 
6655 	g_return_if_fail (day_view != NULL);
6656 
6657 	if (day_view->selection_in_top_canvas)
6658 		return;
6659 
6660 	e_day_view_get_work_day_range_for_day (day_view, day_view->selection_start_day,
6661 		&work_day_start_hour, &work_day_start_minute,
6662 		&work_day_end_hour, &work_day_end_minute);
6663 
6664 	work_end_row = e_day_view_convert_time_to_row (
6665 		day_view, work_day_end_hour - 1, work_day_end_minute + 30);
6666 	selection_start_row = day_view->selection_start_row;
6667 
6668 	if (selection_start_row <= work_end_row) {
6669 		day_view->selection_end_row = work_end_row;
6670 	} else {
6671 		day_view->selection_start_row = work_end_row + 1;
6672 		day_view->selection_end_row = selection_start_row;
6673 	}
6674 
6675 	e_day_view_ensure_rows_visible (
6676 		day_view,
6677 		day_view->selection_start_row,
6678 		day_view->selection_end_row);
6679 
6680 	e_day_view_update_calendar_selection_time (day_view);
6681 
6682 	gtk_widget_queue_draw (day_view->top_canvas);
6683 	gtk_widget_queue_draw (day_view->top_dates_canvas);
6684 	gtk_widget_queue_draw (day_view->main_canvas);
6685 }
6686 
6687 static void
e_day_view_cursor_key_up_shifted(EDayView * day_view,GdkEventKey * event)6688 e_day_view_cursor_key_up_shifted (EDayView *day_view,
6689                                   GdkEventKey *event)
6690 {
6691 	gint *row;
6692 
6693 	if (day_view->selection_in_top_canvas)
6694 		return;
6695 
6696 	if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
6697 		row = &day_view->selection_start_row;
6698 	else
6699 		row = &day_view->selection_end_row;
6700 
6701 	if (*row == 0)
6702 		return;
6703 
6704 	*row = *row - 1;
6705 
6706 	e_day_view_ensure_rows_visible (day_view, *row, *row);
6707 
6708 	e_day_view_normalize_selection (day_view);
6709 
6710 	e_day_view_update_calendar_selection_time (day_view);
6711 
6712 	/* FIXME: Optimise? */
6713 	gtk_widget_queue_draw (day_view->top_canvas);
6714 	gtk_widget_queue_draw (day_view->main_canvas);
6715 }
6716 
6717 /**
6718  * e_day_view_get_extreme_event
6719  * @day_view: the day view widget operates on
6720  * @start_day, @end_day: range of search, both inclusive
6721  * @first: %TURE indicate to return the data for the first event in the range,
6722  *         %FALSE to return data for the last event in the range.
6723  * @day_out: out value, day of the event found. -1 for no event found.
6724  * @event_num_out: out value, event number of the event found.
6725  *                  -1 for no event found.
6726  *
6727  * Get day and event_num value for the first or last event found in the day range.
6728  *
6729  * Return value: %TRUE, if a event found.
6730  **/
6731 static gboolean
e_day_view_get_extreme_event(EDayView * day_view,gint start_day,gint end_day,gboolean first,gint * day_out,gint * event_num_out)6732 e_day_view_get_extreme_event (EDayView *day_view,
6733                               gint start_day,
6734                               gint end_day,
6735                               gboolean first,
6736                               gint *day_out,
6737                               gint *event_num_out)
6738 {
6739 	gint loop_day;
6740 
6741 	g_return_val_if_fail (day_view != NULL, FALSE);
6742 	g_return_val_if_fail (start_day >= 0, FALSE);
6743 	g_return_val_if_fail (end_day <= E_DAY_VIEW_LONG_EVENT, FALSE);
6744 	g_return_val_if_fail (day_out && event_num_out, FALSE);
6745 
6746 	if (start_day > end_day)
6747 		return FALSE;
6748 	if (first) {
6749 		for (loop_day = start_day; loop_day <= end_day; ++loop_day)
6750 			if (day_view->events[loop_day]->len > 0) {
6751 				*day_out = loop_day;
6752 				*event_num_out = 0;
6753 				return TRUE;
6754 			}
6755 	}
6756 	else {
6757 		for (loop_day = end_day; loop_day >= start_day; --loop_day)
6758 			if (day_view->events[loop_day]->len > 0) {
6759 				*day_out = loop_day;
6760 				*event_num_out =
6761 					day_view->events[loop_day]->len - 1;
6762 				return TRUE;
6763 			}
6764 	}
6765 	*day_out = -1;
6766 	*event_num_out = -1;
6767 	return FALSE;
6768 }
6769 
6770 /**
6771  * e_day_view_get_extreme_long_event
6772  * @day_view: the day view widget operates on
6773  * @first: %TURE indicate to return the data for the first event in the range,
6774  *         %FALSE to return data for the last event in the range.
6775  * @event_num_out: out value, event number of the event found.
6776  *                  -1 for no event found.
6777  *
6778  * Similar to e_day_view_get_extreme_event, but run for long events.
6779  *
6780  * Return value: %TRUE, if a event found.
6781  **/
6782 static gboolean
e_day_view_get_extreme_long_event(EDayView * day_view,gboolean first,gint * day_out,gint * event_num_out)6783 e_day_view_get_extreme_long_event (EDayView *day_view,
6784                                    gboolean first,
6785                                    gint *day_out,
6786                                    gint *event_num_out)
6787 {
6788 	g_return_val_if_fail (day_view != NULL, FALSE);
6789 	g_return_val_if_fail (day_out && event_num_out, FALSE);
6790 
6791 	if (first && (day_view->long_events->len > 0)) {
6792 		*day_out = E_DAY_VIEW_LONG_EVENT;
6793 		*event_num_out = 0;
6794 		return TRUE;
6795 	}
6796 	if ((!first) && (day_view->long_events->len > 0)) {
6797 		*day_out = E_DAY_VIEW_LONG_EVENT;
6798 		*event_num_out = day_view->long_events->len - 1;
6799 		return TRUE;
6800 	}
6801 	*day_out = -1;
6802 	*event_num_out = -1;
6803 	return FALSE;
6804 }
6805 
6806 /**
6807  * e_day_view_get_next_tab_event
6808  * @day_view: the day view widget operates on
6809  * @direction: GTK_DIR_TAB_BACKWARD or GTK_DIR_TAB_FORWARD
6810  * @day_out: out value, day of the event found. -1 for no event found.
6811  * @event_num_out: out value, event number of the event found.
6812  *                  -1 for no event found.
6813  *
6814  * Decide on which event the focus should go next.
6815  * if ((day_out == -1) && (event_num_out == -1)) is true, focus should go
6816  * to day_view widget itself.
6817  *
6818  * Return value: %TRUE, if a event found.
6819  **/
6820 static gboolean
e_day_view_get_next_tab_event(EDayView * day_view,GtkDirectionType direction,gint * day_out,gint * event_num_out)6821 e_day_view_get_next_tab_event (EDayView *day_view,
6822                                GtkDirectionType direction,
6823                                gint *day_out,
6824                                gint *event_num_out)
6825 {
6826 	gint new_day;
6827 	gint new_event_num;
6828 	gint days_shown;
6829 
6830 	g_return_val_if_fail (day_view != NULL, FALSE);
6831 	g_return_val_if_fail (day_out != NULL, FALSE);
6832 	g_return_val_if_fail (event_num_out != NULL, FALSE);
6833 
6834 	days_shown = e_day_view_get_days_shown (day_view);
6835 	*day_out = -1;
6836 	*event_num_out = -1;
6837 
6838 	g_return_val_if_fail (days_shown > 0, FALSE);
6839 
6840 	switch (direction) {
6841 	case GTK_DIR_TAB_BACKWARD:
6842 		new_event_num = day_view->editing_event_num - 1;
6843 		break;
6844 	case GTK_DIR_TAB_FORWARD:
6845 		new_event_num = day_view->editing_event_num + 1;
6846 		break;
6847 	default:
6848 		return FALSE;
6849 	}
6850 
6851 	new_day = day_view->editing_event_day;
6852 
6853 	/* not current editing event, set to first long event if there is one
6854 	 */
6855 	if (new_day == -1) {
6856 		if (direction == GTK_DIR_TAB_FORWARD) {
6857 			if (e_day_view_get_extreme_long_event (day_view, TRUE,
6858 							       day_out,
6859 							       event_num_out))
6860 				return TRUE;
6861 
6862 			/* no long event, set to first event if there is
6863 			 */
6864 			e_day_view_get_extreme_event (
6865 				day_view, 0,
6866 				days_shown - 1, TRUE,
6867 				day_out, event_num_out);
6868 			/* go to event if found, or day view widget
6869 			 */
6870 			return TRUE;
6871 		}
6872 		else {
6873 			if (e_day_view_get_extreme_event (day_view, 0,
6874 							  days_shown - 1, FALSE,
6875 							  day_out, event_num_out))
6876 				return TRUE;
6877 
6878 			/* no event, set to last long event if there is
6879 			 */
6880 			e_day_view_get_extreme_long_event (
6881 				day_view, FALSE,
6882 				day_out,
6883 				event_num_out);
6884 
6885 			/* go to long event if found, or day view widget
6886 			 */
6887 			return TRUE;
6888 		}
6889 	}
6890 	/* go backward from the first long event */
6891 	else if ((new_day == E_DAY_VIEW_LONG_EVENT) && (new_event_num < 0)) {
6892 		/* let focus go to day view widget in this case
6893 		 */
6894 		return TRUE;
6895 	}
6896 	/* go forward from the last long event */
6897 	else if ((new_day == E_DAY_VIEW_LONG_EVENT) &&
6898 		 (new_event_num >= day_view->long_events->len)) {
6899 		e_day_view_get_extreme_event (
6900 			day_view, 0,
6901 			days_shown - 1, TRUE,
6902 			day_out, event_num_out);
6903 		/* go to the next main item event if found or day view widget
6904 		 */
6905 		return TRUE;
6906 	}
6907 
6908 	/* go backward from the first event in current editting day */
6909 	else if ((new_day < E_DAY_VIEW_LONG_EVENT) && (new_event_num < 0)) {
6910 		/* try to find a event from the previous day in days shown
6911 		 */
6912 		if (e_day_view_get_extreme_event (day_view, 0,
6913 						  new_day - 1, FALSE,
6914 						  day_out, event_num_out))
6915 			return TRUE;
6916 		/* try to find a long event
6917 		 */
6918 		e_day_view_get_extreme_long_event (
6919 			day_view, FALSE,
6920 			day_out, event_num_out);
6921 		/* go to a long event if found, or day view widget
6922 		 */
6923 		return TRUE;
6924 	}
6925 	/* go forward from the last event in current editting day */
6926 	else if ((new_day < E_DAY_VIEW_LONG_EVENT) &&
6927 		 (new_event_num >= day_view->events[new_day]->len)) {
6928 		/* try to find a event from the next day in days shown
6929 		 */
6930 		e_day_view_get_extreme_event (
6931 			day_view, (new_day + 1),
6932 			days_shown - 1, TRUE,
6933 			day_out, event_num_out);
6934 		/* go to a event found, or day view widget
6935 		 */
6936 		return TRUE;
6937 	}
6938 	/* in the normal case
6939 	 */
6940 	*day_out = new_day;
6941 	*event_num_out = new_event_num;
6942 	return TRUE;
6943 }
6944 
6945 static void
e_day_view_cursor_key_down_shifted(EDayView * day_view,GdkEventKey * event)6946 e_day_view_cursor_key_down_shifted (EDayView *day_view,
6947                                     GdkEventKey *event)
6948 {
6949 	gint *row;
6950 
6951 	if (day_view->selection_in_top_canvas)
6952 		return;
6953 
6954 	if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
6955 		row = &day_view->selection_start_row;
6956 	else
6957 		row = &day_view->selection_end_row;
6958 
6959 	if (*row >= day_view->rows - 1)
6960 		return;
6961 
6962 	*row = *row + 1;
6963 
6964 	e_day_view_ensure_rows_visible (day_view, *row, *row);
6965 
6966 	e_day_view_normalize_selection (day_view);
6967 
6968 	e_day_view_update_calendar_selection_time (day_view);
6969 
6970 	/* FIXME: Optimise? */
6971 	gtk_widget_queue_draw (day_view->top_canvas);
6972 	gtk_widget_queue_draw (day_view->main_canvas);
6973 }
6974 
6975 static void
e_day_view_cursor_key_left_shifted(EDayView * day_view,GdkEventKey * event)6976 e_day_view_cursor_key_left_shifted (EDayView *day_view,
6977                                     GdkEventKey *event)
6978 {
6979 	gint *day;
6980 
6981 	if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
6982 		day = &day_view->selection_start_day;
6983 	else
6984 		day = &day_view->selection_end_day;
6985 
6986 	if (*day == 0)
6987 		return;
6988 
6989 	*day = *day - 1;
6990 
6991 	e_day_view_normalize_selection (day_view);
6992 
6993 	e_day_view_update_calendar_selection_time (day_view);
6994 
6995 	/* FIXME: Optimise? */
6996 	gtk_widget_queue_draw (day_view->top_canvas);
6997 	gtk_widget_queue_draw (day_view->main_canvas);
6998 }
6999 
7000 static void
e_day_view_cursor_key_right_shifted(EDayView * day_view,GdkEventKey * event)7001 e_day_view_cursor_key_right_shifted (EDayView *day_view,
7002                                      GdkEventKey *event)
7003 {
7004 	gint *day;
7005 
7006 	if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
7007 		day = &day_view->selection_start_day;
7008 	else
7009 		day = &day_view->selection_end_day;
7010 
7011 	if (*day >= e_day_view_get_days_shown (day_view) - 1)
7012 		return;
7013 
7014 	*day = *day + 1;
7015 
7016 	e_day_view_normalize_selection (day_view);
7017 
7018 	e_day_view_update_calendar_selection_time (day_view);
7019 
7020 	/* FIXME: Optimise? */
7021 	gtk_widget_queue_draw (day_view->top_canvas);
7022 	gtk_widget_queue_draw (day_view->main_canvas);
7023 }
7024 
7025 static void
e_day_view_cursor_key_up(EDayView * day_view,GdkEventKey * event)7026 e_day_view_cursor_key_up (EDayView *day_view,
7027                           GdkEventKey *event)
7028 {
7029 	if (day_view->selection_start_day == -1) {
7030 		day_view->selection_start_day = 0;
7031 		day_view->selection_start_row = 0;
7032 	}
7033 	day_view->selection_end_day = day_view->selection_start_day;
7034 
7035 	if (day_view->selection_in_top_canvas) {
7036 		return;
7037 	} else if (day_view->selection_start_row == 0) {
7038 		day_view->selection_in_top_canvas = TRUE;
7039 		day_view->selection_start_row = -1;
7040 	} else {
7041 		day_view->selection_start_row--;
7042 	}
7043 	day_view->selection_end_row = day_view->selection_start_row;
7044 
7045 	if (!day_view->selection_in_top_canvas)
7046 		e_day_view_ensure_rows_visible (
7047 			day_view,
7048 			day_view->selection_start_row,
7049 			day_view->selection_end_row);
7050 
7051 	g_signal_emit_by_name (day_view, "selected_time_changed");
7052 	e_day_view_update_calendar_selection_time (day_view);
7053 
7054 	/* FIXME: Optimise? */
7055 	gtk_widget_queue_draw (day_view->top_canvas);
7056 	gtk_widget_queue_draw (day_view->main_canvas);
7057 }
7058 
7059 static void
e_day_view_cursor_key_down(EDayView * day_view,GdkEventKey * event)7060 e_day_view_cursor_key_down (EDayView *day_view,
7061                             GdkEventKey *event)
7062 {
7063 	if (day_view->selection_start_day == -1) {
7064 		day_view->selection_start_day = 0;
7065 		day_view->selection_start_row = 0;
7066 	}
7067 	day_view->selection_end_day = day_view->selection_start_day;
7068 
7069 	if (day_view->selection_in_top_canvas) {
7070 		day_view->selection_in_top_canvas = FALSE;
7071 		day_view->selection_start_row = 0;
7072 	} else if (day_view->selection_start_row >= day_view->rows - 1) {
7073 		return;
7074 	} else {
7075 		day_view->selection_start_row++;
7076 	}
7077 	day_view->selection_end_row = day_view->selection_start_row;
7078 
7079 	if (!day_view->selection_in_top_canvas)
7080 		e_day_view_ensure_rows_visible (
7081 			day_view,
7082 						day_view->selection_start_row,
7083 						day_view->selection_end_row);
7084 
7085 	g_signal_emit_by_name (day_view, "selected_time_changed");
7086 	e_day_view_update_calendar_selection_time (day_view);
7087 
7088 	/* FIXME: Optimise? */
7089 	gtk_widget_queue_draw (day_view->top_canvas);
7090 	gtk_widget_queue_draw (day_view->main_canvas);
7091 }
7092 
7093 static void
e_day_view_cursor_key_left(EDayView * day_view,GdkEventKey * event)7094 e_day_view_cursor_key_left (EDayView *day_view,
7095                             GdkEventKey *event)
7096 {
7097 	if (day_view->selection_start_day == 0) {
7098 		e_calendar_view_move_view_range (E_CALENDAR_VIEW (day_view), E_CALENDAR_VIEW_MOVE_PREVIOUS, 0);
7099 	} else {
7100 		day_view->selection_start_day--;
7101 		day_view->selection_end_day--;
7102 
7103 		e_day_view_update_calendar_selection_time (day_view);
7104 
7105 		/* FIXME: Optimise? */
7106 		gtk_widget_queue_draw (day_view->top_canvas);
7107 		gtk_widget_queue_draw (day_view->main_canvas);
7108 	}
7109 	g_signal_emit_by_name (day_view, "selected_time_changed");
7110 }
7111 
7112 static void
e_day_view_cursor_key_right(EDayView * day_view,GdkEventKey * event)7113 e_day_view_cursor_key_right (EDayView *day_view,
7114                              GdkEventKey *event)
7115 {
7116 	gint days_shown;
7117 
7118 	days_shown = e_day_view_get_days_shown (day_view);
7119 
7120 	if (day_view->selection_end_day == days_shown - 1) {
7121 		e_calendar_view_move_view_range (E_CALENDAR_VIEW (day_view), E_CALENDAR_VIEW_MOVE_NEXT, 0);
7122 	} else {
7123 		day_view->selection_start_day++;
7124 		day_view->selection_end_day++;
7125 
7126 		e_day_view_update_calendar_selection_time (day_view);
7127 
7128 		/* FIXME: Optimise? */
7129 		gtk_widget_queue_draw (day_view->top_canvas);
7130 		gtk_widget_queue_draw (day_view->main_canvas);
7131 	}
7132 	g_signal_emit_by_name (day_view, "selected_time_changed");
7133 }
7134 
7135 /* Scrolls the main canvas up or down. The pages_to_scroll argument
7136  * is multiplied with the adjustment's page size and added to the adjustment's
7137  * value, while ensuring we stay within the bounds. A positive value will
7138  * scroll the canvas down and a negative value will scroll it up. */
7139 static void
e_day_view_scroll(EDayView * day_view,gfloat pages_to_scroll)7140 e_day_view_scroll (EDayView *day_view,
7141                    gfloat pages_to_scroll)
7142 {
7143 	GtkAdjustment *adjustment;
7144 	GtkScrollable *scrollable;
7145 	gdouble new_value;
7146 	gdouble page_size;
7147 	gdouble lower;
7148 	gdouble upper;
7149 	gdouble value;
7150 
7151 	scrollable = GTK_SCROLLABLE (day_view->main_canvas);
7152 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
7153 
7154 	page_size = gtk_adjustment_get_page_size (adjustment);
7155 	lower = gtk_adjustment_get_lower (adjustment);
7156 	upper = gtk_adjustment_get_upper (adjustment);
7157 	value = gtk_adjustment_get_value (adjustment);
7158 
7159 	new_value = value - page_size * pages_to_scroll;
7160 	new_value = CLAMP (new_value, lower, upper - page_size);
7161 	gtk_adjustment_set_value (adjustment, new_value);
7162 }
7163 
7164 static void
e_day_view_top_scroll(EDayView * day_view,gfloat pages_to_scroll)7165 e_day_view_top_scroll (EDayView *day_view,
7166                        gfloat pages_to_scroll)
7167 {
7168 	GtkAdjustment *adjustment;
7169 	GtkScrollable *scrollable;
7170 	gdouble new_value;
7171 	gdouble page_size;
7172 	gdouble lower;
7173 	gdouble upper;
7174 	gdouble value;
7175 
7176 	scrollable = GTK_SCROLLABLE (day_view->top_canvas);
7177 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
7178 
7179 	page_size = gtk_adjustment_get_page_size (adjustment);
7180 	lower = gtk_adjustment_get_lower (adjustment);
7181 	upper = gtk_adjustment_get_upper (adjustment);
7182 	value = gtk_adjustment_get_value (adjustment);
7183 
7184 	new_value = value - page_size * pages_to_scroll;
7185 	new_value = CLAMP (new_value, lower, upper - page_size);
7186 	gtk_adjustment_set_value (adjustment, new_value);
7187 }
7188 
7189 void
e_day_view_ensure_rows_visible(EDayView * day_view,gint start_row,gint end_row)7190 e_day_view_ensure_rows_visible (EDayView *day_view,
7191                                 gint start_row,
7192                                 gint end_row)
7193 {
7194 	GtkAdjustment *adjustment;
7195 	GtkScrollable *scrollable;
7196 	gdouble max_value;
7197 	gdouble min_value;
7198 	gdouble page_size;
7199 	gdouble value;
7200 
7201 	scrollable = GTK_SCROLLABLE (day_view->main_canvas);
7202 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
7203 
7204 	value = gtk_adjustment_get_value (adjustment);
7205 	page_size = gtk_adjustment_get_page_size (adjustment);
7206 
7207 	min_value = (end_row + 1) * day_view->row_height - page_size;
7208 	if (value < min_value)
7209 		value = min_value;
7210 
7211 	max_value = start_row * day_view->row_height;
7212 	if (value > max_value)
7213 		value = max_value;
7214 
7215 	gtk_adjustment_set_value (adjustment, value);
7216 }
7217 
7218 static void
e_day_view_start_editing_event(EDayView * day_view,gint day,gint event_num,GdkEventKey * key_event)7219 e_day_view_start_editing_event (EDayView *day_view,
7220                                 gint day,
7221                                 gint event_num,
7222                                 GdkEventKey *key_event)
7223 {
7224 	EDayViewEvent *event;
7225 	ETextEventProcessor *event_processor = NULL;
7226 	ETextEventProcessorCommand command;
7227 
7228 	/* If we are already editing the event, just return. */
7229 	if (day == day_view->editing_event_day
7230 	    && event_num == day_view->editing_event_num)
7231 		return;
7232 
7233 	if (day == E_DAY_VIEW_LONG_EVENT) {
7234 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
7235 			return;
7236 
7237 		event = &g_array_index (day_view->long_events, EDayViewEvent,
7238 					event_num);
7239 	} else {
7240 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
7241 			return;
7242 
7243 		event = &g_array_index (day_view->events[day], EDayViewEvent,
7244 					event_num);
7245 	}
7246 
7247 	if (!is_comp_data_valid (event))
7248 		return;
7249 
7250 	if (e_client_is_readonly (E_CLIENT (event->comp_data->client)) ||
7251 	    (!key_event && !e_calendar_view_get_allow_direct_summary_edit (E_CALENDAR_VIEW (day_view))))
7252 		return;
7253 
7254 	/* If the event is not shown, don't try to edit it. */
7255 	if (!event->canvas_item)
7256 		return;
7257 
7258 	/* We must grab the focus before setting the initial text, since
7259 	 * grabbing the focus will result in a call to
7260 	 * e_day_view_on_editing_started (), which will reset the text to get
7261 	 * rid of the start and end times. */
7262 	e_canvas_item_grab_focus (event->canvas_item, TRUE);
7263 
7264 	if (key_event) {
7265 		if (gtk_im_context_filter_keypress (((EText *)(event->canvas_item))->im_context, key_event)) {
7266 			((EText *)(event->canvas_item))->need_im_reset = TRUE;
7267 		} else if (key_event->keyval != GDK_KEY_Return && key_event->keyval != GDK_KEY_KP_Enter) {
7268 			gchar *initial_text;
7269 
7270 			initial_text = e_utf8_from_gtk_event_key (GTK_WIDGET (day_view), key_event->keyval, key_event->string);
7271 			gnome_canvas_item_set (
7272 				event->canvas_item,
7273 				"text", initial_text,
7274 				NULL);
7275 			g_free (initial_text);
7276 		}
7277 	}
7278 
7279 	/* Try to move the cursor to the end of the text. */
7280 	g_object_get (
7281 		event->canvas_item,
7282 		"event_processor", &event_processor,
7283 		NULL);
7284 	if (event_processor) {
7285 		command.action = E_TEP_MOVE;
7286 		command.position = E_TEP_END_OF_BUFFER;
7287 		g_signal_emit_by_name (
7288 			event_processor,
7289 			"command", &command);
7290 	}
7291 }
7292 
7293 /* This stops the current edit. If accept is TRUE the event summary is updated,
7294  * else the edit is cancelled. */
7295 static void
e_day_view_stop_editing_event(EDayView * day_view)7296 e_day_view_stop_editing_event (EDayView *day_view)
7297 {
7298 	GtkWidget *toplevel;
7299 
7300 	/* Check we are editing an event. */
7301 	if (day_view->editing_event_day == -1)
7302 		return;
7303 
7304 	/* Set focus to the toplevel so the item loses focus. */
7305 	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (day_view));
7306 	if (toplevel && GTK_IS_WINDOW (toplevel))
7307 		gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
7308 }
7309 
7310 /* Cancels the current edition by resetting the appointment's text to its original value */
7311 static void
cancel_editing(EDayView * day_view)7312 cancel_editing (EDayView *day_view)
7313 {
7314 	gint day, event_num;
7315 	EDayViewEvent *event;
7316 	const gchar *summary;
7317 
7318 	day = day_view->editing_event_day;
7319 	event_num = day_view->editing_event_num;
7320 
7321 	if (day == -1)
7322 		return;
7323 
7324 	if (day == E_DAY_VIEW_LONG_EVENT) {
7325 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
7326 			return;
7327 
7328 		event = &g_array_index (day_view->long_events, EDayViewEvent, event_num);
7329 	} else {
7330 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
7331 			return;
7332 
7333 		event = &g_array_index (day_view->events[day], EDayViewEvent, event_num);
7334 	}
7335 
7336 	if (!is_comp_data_valid (event))
7337 		return;
7338 
7339 	/* Reset the text to what was in the component */
7340 
7341 	summary = i_cal_component_get_summary (event->comp_data->icalcomp);
7342 	g_object_set (
7343 		event->canvas_item,
7344 		"text", summary ? summary : "",
7345 		NULL);
7346 
7347 	/* Stop editing */
7348 	e_day_view_stop_editing_event (day_view);
7349 }
7350 
7351 static EDayViewEvent *
tooltip_get_view_event(EDayView * day_view,gint day,gint event_num)7352 tooltip_get_view_event (EDayView *day_view,
7353                         gint day,
7354                         gint event_num)
7355 {
7356 	EDayViewEvent *pevent;
7357 
7358 	if (day == E_DAY_VIEW_LONG_EVENT) {
7359 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
7360 			return  NULL;
7361 
7362 		pevent = &g_array_index (day_view->long_events, EDayViewEvent,
7363 					event_num);
7364 	} else {
7365 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
7366 			return NULL;
7367 
7368 		pevent = &g_array_index (day_view->events[day], EDayViewEvent,
7369 					event_num);
7370 	}
7371 
7372 	return pevent;
7373 }
7374 
7375 static void
tooltip_destroy(EDayView * day_view,GnomeCanvasItem * item)7376 tooltip_destroy (EDayView *day_view,
7377                  GnomeCanvasItem *item)
7378 {
7379 	GtkWidget *tooltip = g_object_get_data (G_OBJECT (day_view), "tooltip-window");
7380 
7381 	if (tooltip) {
7382 		gtk_widget_destroy (tooltip);
7383 		g_object_set_data (G_OBJECT (day_view), "tooltip-window", NULL);
7384 	}
7385 
7386 	if (item) {
7387 		EDayViewEvent *pevent;
7388 		gint event_num, day;
7389 
7390 		e_day_view_check_layout (day_view);
7391 
7392 		event_num = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-num"));
7393 		day = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-day"));
7394 		pevent = tooltip_get_view_event (day_view, day, event_num);
7395 		if (pevent) {
7396 			pevent->tooltip = NULL;
7397 			if (pevent->timeout != -1) {
7398 				g_source_remove (pevent->timeout);
7399 				pevent->timeout = -1;
7400 			}
7401 		}
7402 	}
7403 }
7404 
7405 static gboolean
e_day_view_on_text_item_event(GnomeCanvasItem * item,GdkEvent * event,EDayView * day_view)7406 e_day_view_on_text_item_event (GnomeCanvasItem *item,
7407                                GdkEvent *event,
7408                                EDayView *day_view)
7409 {
7410 	switch (event->type) {
7411 	case GDK_KEY_PRESS:
7412 		tooltip_destroy (day_view, item);
7413 		if (!E_TEXT (item)->preedit_len && event && (
7414 		     event->key.keyval == GDK_KEY_Return ||
7415 		     event->key.keyval == GDK_KEY_KP_Enter)) {
7416 			day_view->resize_event_num = -1;
7417 
7418 			/* We set the keyboard focus to the EDayView, so the
7419 			 * EText item loses it and stops the edit. */
7420 			gtk_widget_grab_focus (GTK_WIDGET (day_view));
7421 
7422 			/* Stop the signal last or we will also stop any
7423 			 * other events getting to the EText item. */
7424 			g_signal_stop_emission_by_name (item, "event");
7425 			return TRUE;
7426 		} else if (event->key.keyval == GDK_KEY_Escape) {
7427 			cancel_editing (day_view);
7428 			g_signal_stop_emission_by_name (item, "event");
7429 			/* focus should go to day view when stop editing */
7430 			gtk_widget_grab_focus (GTK_WIDGET (day_view));
7431 			return TRUE;
7432 	       } else if ((event->key.keyval == GDK_KEY_Up)
7433 			  && (event->key.state & GDK_SHIFT_MASK)
7434 			  && (event->key.state & GDK_CONTROL_MASK)
7435 			  && !(event->key.state & GDK_MOD1_MASK)) {
7436 		       e_day_view_change_event_end_time_up (day_view);
7437 		       return TRUE;
7438 	       } else if ((event->key.keyval == GDK_KEY_Down)
7439 			  && (event->key.state & GDK_SHIFT_MASK)
7440 			  && (event->key.state & GDK_CONTROL_MASK)
7441 			  && !(event->key.state & GDK_MOD1_MASK)) {
7442 		       e_day_view_change_event_end_time_down (day_view);
7443 		       return TRUE;
7444 		}
7445 		break;
7446 	case GDK_2BUTTON_PRESS:
7447 		break;
7448 
7449 	case GDK_BUTTON_RELEASE:
7450 		if (day_view->resize_event_num != -1)
7451 			day_view->resize_event_num = -1;
7452 
7453 		if (day_view->drag_event_num != -1)
7454 			day_view->drag_event_num = -1;
7455 		tooltip_destroy (day_view, item);
7456 		/* Only let the EText handle the event while editing. */
7457 		if (!E_TEXT (item)->editing)
7458 			g_signal_stop_emission_by_name (item, "event");
7459 		break;
7460 
7461 	case GDK_BUTTON_PRESS:
7462 		tooltip_destroy (day_view, item);
7463 		/* Only let the EText handle the event while editing. */
7464 		if (!E_TEXT (item)->editing)
7465 			g_signal_stop_emission_by_name (item, "event");
7466 		break;
7467 	case GDK_FOCUS_CHANGE:
7468 		if (event->focus_change.in)
7469 			e_day_view_on_editing_started (day_view, item);
7470 		else
7471 			e_day_view_on_editing_stopped (day_view, item);
7472 
7473 		return FALSE;
7474 	case GDK_ENTER_NOTIFY:
7475 		{
7476 			EDayViewEvent *pevent;
7477 			ECalendarViewEventData *data;
7478 			gint event_x, event_y, row, day, event_num;
7479 			ECalendarViewPosition pos;
7480 			gboolean main_canvas = TRUE;
7481 			GdkWindow *window;
7482 			GtkLayout *layout;
7483 
7484 			if (day_view->editing_event_num != -1)
7485 				break;
7486 
7487 			if (day_view->resize_event_num != -1)
7488 				break;
7489 
7490 			if (day_view->drag_event_num != -1)
7491 				break;
7492 
7493 			/* Convert the coords to the main canvas window, or return if the
7494 			 * window is not found. */
7495 			layout = GTK_LAYOUT (day_view->main_canvas);
7496 			window = gtk_layout_get_bin_window (layout);
7497 			if (!e_day_view_convert_event_coords (
7498 				day_view, (GdkEvent *) event,
7499 				window, &event_x, &event_y)) {
7500 
7501 				main_canvas = FALSE;
7502 
7503 				layout = GTK_LAYOUT (day_view->top_canvas);
7504 				window = gtk_layout_get_bin_window (layout);
7505 				if (!e_day_view_convert_event_coords (
7506 					day_view, (GdkEvent *) event,
7507 					window, &event_x, &event_y))  {
7508 					return FALSE;
7509 				}
7510 			}
7511 			/* Find out where the mouse is. */
7512 			if (main_canvas) {
7513 				pos = e_day_view_convert_position_in_main_canvas (
7514 					day_view,
7515 					event_x, event_y,
7516 					&day, &row,
7517 					&event_num);
7518 			} else {
7519 				gint tmp;
7520 
7521 				pos = e_day_view_convert_position_in_top_canvas (
7522 					day_view,
7523 					event_x, event_y,
7524 					&tmp, &event_num);
7525 				day = E_DAY_VIEW_LONG_EVENT;
7526 			}
7527 
7528 			if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
7529 				break;
7530 
7531 			/* even when returns position inside, or other, then the day and/or event_num
7532 			 * can be unknown, thus check for this here, otherwise it will crash later */
7533 			if (day == -1 || event_num == -1)
7534 				break;
7535 
7536 			pevent = tooltip_get_view_event (day_view, day, event_num);
7537 			if (!pevent)
7538 				break;
7539 
7540 			g_object_set_data (G_OBJECT (item), "event-num", GINT_TO_POINTER (event_num));
7541 			g_object_set_data (G_OBJECT (item), "event-day", GINT_TO_POINTER (day));
7542 
7543 			data = g_malloc (sizeof (ECalendarViewEventData));
7544 			pevent->x = ((GdkEventCrossing *) event)->x_root;
7545 			pevent->y = ((GdkEventCrossing *) event)->y_root;
7546 			pevent->tooltip = NULL;
7547 
7548 			data->cal_view = (ECalendarView *) day_view;
7549 			data->day = day;
7550 			data->event_num = event_num;
7551 			data->get_view_event = (ECalendarViewEvent * (*)(ECalendarView *, int, gint)) tooltip_get_view_event;
7552 			pevent->timeout = e_named_timeout_add_full (
7553 				G_PRIORITY_DEFAULT, 500,
7554 				(GSourceFunc) e_calendar_view_get_tooltips,
7555 				data, (GDestroyNotify) g_free);
7556 
7557 		return TRUE;
7558 		}
7559 	case GDK_LEAVE_NOTIFY:
7560 		tooltip_destroy (day_view, item);
7561 		return TRUE;
7562 	case GDK_MOTION_NOTIFY:
7563 		{
7564 			EDayViewEvent *pevent;
7565 			gint event_num, day;
7566 
7567 			e_day_view_check_layout (day_view);
7568 
7569 			event_num = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-num"));
7570 			day = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-day"));
7571 
7572 			pevent = tooltip_get_view_event (day_view, day, event_num);
7573 			if (!pevent)
7574 				break;
7575 
7576 			pevent->x = ((GdkEventMotion *) event)->x_root;
7577 			pevent->y = ((GdkEventMotion *) event)->y_root;
7578 			pevent->tooltip = (GtkWidget *) g_object_get_data (G_OBJECT (day_view), "tooltip-window");
7579 
7580 			if (pevent->tooltip) {
7581 				e_calendar_view_move_tip (pevent->tooltip, pevent->x, pevent->y);
7582 			}
7583 
7584 			return TRUE;
7585 		}
7586 	default:
7587 		break;
7588 	}
7589 
7590 	return FALSE;
7591 }
7592 
7593 static gboolean
e_day_view_event_move(ECalendarView * cal_view,ECalViewMoveDirection direction)7594 e_day_view_event_move (ECalendarView *cal_view,
7595                        ECalViewMoveDirection direction)
7596 {
7597 	EDayViewEvent *event;
7598 	EDayView *day_view;
7599 	gint time_divisions;
7600 	gint day, event_num, resize_start_row, resize_end_row;
7601 	time_t start_dt, end_dt;
7602 	ICalTime *start_time, *end_time;
7603 
7604 	day_view = E_DAY_VIEW (cal_view);
7605 	day = day_view->editing_event_day;
7606 	event_num = day_view->editing_event_num;
7607 
7608 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
7609 
7610 	if ((day == -1) || (day == E_DAY_VIEW_LONG_EVENT))
7611 		return FALSE;
7612 
7613 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
7614 		return FALSE;
7615 
7616 	event = &g_array_index (day_view->events[day], EDayViewEvent,
7617 				event_num);
7618 	day_view->resize_event_day = day;
7619 	day_view->resize_event_num = event_num;
7620 	day_view->resize_bars_event_day = day;
7621 	day_view->resize_bars_event_num = event_num;
7622 	resize_start_row = event->start_minute / time_divisions;
7623 	resize_end_row = (event->end_minute - 1) / time_divisions;
7624 	if (resize_end_row < resize_start_row)
7625 		resize_end_row = resize_start_row;
7626 
7627 	switch (direction) {
7628 	case E_CAL_VIEW_MOVE_UP:
7629 		if (resize_start_row <= 0)
7630 			return FALSE;
7631 		resize_start_row--;
7632 		resize_end_row--;
7633 		start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row);
7634 		end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1);
7635 		break;
7636 	case E_CAL_VIEW_MOVE_DOWN:
7637 		if (resize_end_row >= day_view->rows - 1)
7638 			return FALSE;
7639 		resize_start_row++;
7640 		resize_end_row++;
7641 		start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row);
7642 		end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1);
7643 		break;
7644 	case E_CAL_VIEW_MOVE_LEFT:
7645 		if (day <= 0)
7646 			return TRUE;
7647 		start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row);
7648 		end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1);
7649 		start_time = i_cal_time_new_from_timet_with_zone (start_dt, 0, NULL);
7650 		end_time = i_cal_time_new_from_timet_with_zone (end_dt, 0, NULL);
7651 		i_cal_time_adjust (start_time, -1, 0, 0, 0);
7652 		i_cal_time_adjust (end_time, -1, 0, 0, 0);
7653 		start_dt = i_cal_time_as_timet (start_time);
7654 		end_dt = i_cal_time_as_timet (end_time);
7655 		g_clear_object (&start_time);
7656 		g_clear_object (&end_time);
7657 		break;
7658 	case E_CAL_VIEW_MOVE_RIGHT:
7659 		if (day + 1 >= e_day_view_get_days_shown (day_view))
7660 			return TRUE;
7661 		start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row);
7662 		end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1);
7663 		start_time = i_cal_time_new_from_timet_with_zone (start_dt, 0, NULL);
7664 		end_time = i_cal_time_new_from_timet_with_zone (end_dt, 0, NULL);
7665 		i_cal_time_adjust (start_time ,1,0,0,0);
7666 		i_cal_time_adjust (end_time ,1,0,0,0);
7667 		start_dt = i_cal_time_as_timet (start_time);
7668 		end_dt = i_cal_time_as_timet (end_time);
7669 		g_clear_object (&start_time);
7670 		g_clear_object (&end_time);
7671 		break;
7672 	default:
7673 		return FALSE;
7674 	}
7675 
7676 	e_day_view_change_event_time (day_view, start_dt, end_dt);
7677 	e_day_view_ensure_rows_visible (day_view, resize_start_row, resize_end_row);
7678 
7679 	return TRUE;
7680 }
7681 
7682 static void
e_day_view_change_event_time(EDayView * day_view,time_t start_dt,time_t end_dt)7683 e_day_view_change_event_time (EDayView *day_view,
7684                               time_t start_dt,
7685                               time_t end_dt)
7686 {
7687 	EDayViewEvent *event;
7688 	gint day, event_num;
7689 	ECalComponent *comp;
7690 	ECalComponentDateTime *date;
7691 	ICalTimezone *zone;
7692 	ECalModel *model;
7693 	ECalClient *client;
7694 	ESourceRegistry *registry;
7695 	ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
7696 
7697 	day = day_view->editing_event_day;
7698 	event_num = day_view->editing_event_num;
7699 
7700 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
7701 	registry = e_cal_model_get_registry (model);
7702 
7703 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
7704 		return;
7705 
7706 	event = &g_array_index (day_view->events[day], EDayViewEvent,
7707 				event_num);
7708 
7709 	if (!is_comp_data_valid (event))
7710 		return;
7711 
7712 	client = event->comp_data->client;
7713 
7714 	/* We use a temporary shallow copy of the ico since we don't want to
7715 	 * change the original ico here. Otherwise we would not detect that
7716 	 * the event's time had changed in the "update_event" callback. */
7717 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
7718 
7719 	if (e_cal_component_has_attendees (comp) &&
7720 	    !itip_organizer_is_user (registry, comp, client)) {
7721 		g_object_unref (comp);
7722 		return;
7723 	}
7724 
7725 	zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view));
7726 
7727 	date = e_cal_component_datetime_new_take (
7728 		i_cal_time_new_from_timet_with_zone (start_dt, FALSE, zone),
7729 		zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
7730 	cal_comp_set_dtstart_with_oldzone (client, comp, date);
7731 
7732 	e_cal_component_datetime_take_value (date,
7733 		i_cal_time_new_from_timet_with_zone (end_dt, FALSE, zone));
7734 	cal_comp_set_dtend_with_oldzone (client, comp, date);
7735 
7736 	e_cal_component_datetime_free (date);
7737 
7738 	e_cal_component_commit_sequence (comp);
7739 
7740 	g_clear_pointer (&day_view->last_edited_comp_string, g_free);
7741 
7742 	day_view->last_edited_comp_string = e_cal_component_get_as_string (comp);
7743 
7744 	day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
7745 
7746 	if (e_cal_component_has_recurrences (comp)) {
7747 		if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
7748 			gtk_widget_queue_draw (day_view->top_canvas);
7749 			goto out;
7750 		}
7751 
7752 		if (mod == E_CAL_OBJ_MOD_THIS) {
7753 			e_cal_component_set_rdates (comp, NULL);
7754 			e_cal_component_set_rrules (comp, NULL);
7755 			e_cal_component_set_exdates (comp, NULL);
7756 			e_cal_component_set_exrules (comp, NULL);
7757 		}
7758 	} else if (e_cal_component_is_instance (comp))
7759 		mod = E_CAL_OBJ_MOD_THIS;
7760 
7761 	e_cal_component_commit_sequence (comp);
7762 
7763 	e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp),
7764 		mod, E_CAL_OPS_SEND_FLAG_ASK | E_CAL_OPS_SEND_FLAG_IS_NEW_COMPONENT);
7765 
7766 out:
7767 	g_object_unref (comp);
7768 }
7769 
7770 static void
e_day_view_change_event_end_time_up(EDayView * day_view)7771 e_day_view_change_event_end_time_up (EDayView *day_view)
7772 {
7773 	EDayViewEvent *event;
7774 	ECalendarView *cal_view;
7775 	gint time_divisions;
7776 	gint day, event_num, resize_start_row, resize_end_row;
7777 
7778 	day = day_view->editing_event_day;
7779 	event_num = day_view->editing_event_num;
7780 	if ((day == -1) || (day == E_DAY_VIEW_LONG_EVENT))
7781 		return;
7782 
7783 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
7784 		return;
7785 
7786 	cal_view = E_CALENDAR_VIEW (day_view);
7787 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
7788 
7789 	event = &g_array_index (day_view->events[day], EDayViewEvent,
7790 				event_num);
7791 	day_view->resize_event_day = day;
7792 	day_view->resize_event_num = event_num;
7793 	day_view->resize_bars_event_day = day;
7794 	day_view->resize_bars_event_num = event_num;
7795 	resize_start_row = event->start_minute / time_divisions;
7796 	resize_end_row = (event->end_minute - 1) / time_divisions;
7797 	if (resize_end_row < resize_start_row)
7798 		resize_end_row = resize_start_row;
7799 	if (resize_end_row == resize_start_row)
7800 		return;
7801 	day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_BOTTOM_EDGE;
7802 	resize_end_row--;
7803 	day_view->resize_start_row = resize_start_row;
7804 	day_view->resize_end_row = resize_end_row;
7805 	e_day_view_finish_resize (day_view);
7806 	e_day_view_ensure_rows_visible (day_view, resize_start_row, resize_end_row);
7807 }
7808 
7809 static void
e_day_view_change_event_end_time_down(EDayView * day_view)7810 e_day_view_change_event_end_time_down (EDayView *day_view)
7811 {
7812 	EDayViewEvent *event;
7813 	ECalendarView *cal_view;
7814 	gint time_divisions;
7815 	gint day, event_num, resize_start_row, resize_end_row;
7816 
7817 	cal_view = E_CALENDAR_VIEW (day_view);
7818 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
7819 
7820 	day = day_view->editing_event_day;
7821 	event_num = day_view->editing_event_num;
7822 	if ((day == -1) || (day == E_DAY_VIEW_LONG_EVENT))
7823 		return;
7824 
7825 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
7826 		return;
7827 
7828 	event = &g_array_index (day_view->events[day], EDayViewEvent,
7829 				event_num);
7830 	day_view->resize_event_day = day;
7831 	day_view->resize_event_num = event_num;
7832 	day_view->resize_bars_event_day = day;
7833 	day_view->resize_bars_event_num = event_num;
7834 	resize_start_row = event->start_minute / time_divisions;
7835 	resize_end_row = (event->end_minute - 1) / time_divisions;
7836 	if (resize_end_row < resize_start_row)
7837 		resize_end_row = resize_start_row;
7838 	if (resize_end_row == day_view->rows -1)
7839 		return;
7840 	day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_BOTTOM_EDGE;
7841 	resize_end_row++;
7842 	day_view->resize_start_row = resize_start_row;
7843 	day_view->resize_end_row = resize_end_row;
7844 	e_day_view_finish_resize (day_view);
7845 	e_day_view_ensure_rows_visible (day_view, resize_start_row, resize_end_row);
7846 }
7847 
7848 static void
e_day_view_on_editing_started(EDayView * day_view,GnomeCanvasItem * item)7849 e_day_view_on_editing_started (EDayView *day_view,
7850                                GnomeCanvasItem *item)
7851 {
7852 	GtkAllocation allocation;
7853 	gint day, event_num;
7854 
7855 	if (!e_day_view_find_event_from_item (day_view, item,
7856 					      &day, &event_num))
7857 		return;
7858 
7859 	/* FIXME: This is a temporary workaround for a bug which seems to stop
7860 	 * us getting focus_out signals. It is not a complete fix since if we
7861 	 * don't get focus_out signals we don't save the appointment text so
7862 	 * this may be lost. */
7863 	if (day_view->editing_event_day == day
7864 	    && day_view->editing_event_num == event_num)
7865 		return;
7866 
7867 	day_view->editing_event_day = day;
7868 	day_view->editing_event_num = event_num;
7869 
7870 	gtk_widget_get_allocation (day_view->top_canvas, &allocation);
7871 
7872 	if (day == E_DAY_VIEW_LONG_EVENT) {
7873 		gint item_x, item_y, item_w, item_h, scroll_y;
7874 		gint start_day, end_day;
7875 
7876 		e_day_view_reshape_long_event (day_view, event_num);
7877 
7878 		if (e_day_view_get_long_event_position (day_view, event_num,
7879 						       &start_day, &end_day,
7880 						       &item_x, &item_y,
7881 						       &item_w, &item_h)) {
7882 			GtkAdjustment *adjustment;
7883 			GtkScrollable *scrollable;
7884 
7885 			scrollable = GTK_SCROLLABLE (day_view->top_canvas);
7886 			adjustment = gtk_scrollable_get_vadjustment (scrollable);
7887 
7888 			/* and ensure it's visible too */
7889 			/*item_y = (event_num * (day_view->top_row_height + 1)) - 1;*/
7890 			scroll_y = gtk_adjustment_get_value (adjustment);
7891 			if (item_y + day_view->top_row_height > allocation.height + scroll_y || item_y < scroll_y)
7892 				gnome_canvas_scroll_to (GNOME_CANVAS (day_view->top_canvas), 0, item_y);
7893 		}
7894 	} else {
7895 		day_view->resize_bars_event_day = day;
7896 		day_view->resize_bars_event_num = event_num;
7897 		e_day_view_update_event_label (day_view, day, event_num);
7898 		e_day_view_reshape_main_canvas_resize_bars (day_view);
7899 	}
7900 
7901 	g_signal_emit_by_name (day_view, "selection_changed");
7902 
7903 	g_object_notify (G_OBJECT (day_view), "is-editing");
7904 }
7905 
7906 static void
e_day_view_on_editing_stopped(EDayView * day_view,GnomeCanvasItem * item)7907 e_day_view_on_editing_stopped (EDayView *day_view,
7908                                GnomeCanvasItem *item)
7909 {
7910 	gint day, event_num;
7911 	EDayViewEvent *event;
7912 	gchar *text = NULL;
7913 	ECalComponentText *summary = NULL;
7914 	ECalComponent *comp;
7915 	ECalClient *client;
7916 	gboolean on_server;
7917 
7918 	/* Note: the item we are passed here isn't reliable, so we just stop
7919 	 * the edit of whatever item was being edited. We also receive this
7920 	 * event twice for some reason. */
7921 	day = day_view->editing_event_day;
7922 	event_num = day_view->editing_event_num;
7923 
7924 	/* If no item is being edited, just return. */
7925 	if (day == -1)
7926 		return;
7927 
7928 	if (day == E_DAY_VIEW_LONG_EVENT) {
7929 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
7930 			return;
7931 
7932 		event = &g_array_index (day_view->long_events, EDayViewEvent,
7933 					event_num);
7934 	} else {
7935 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
7936 			return;
7937 
7938 		event = &g_array_index (day_view->events[day], EDayViewEvent,
7939 					event_num);
7940 
7941 	}
7942 
7943 	if (!is_comp_data_valid (event))
7944 		return;
7945 
7946 	/* Reset the edit fields. */
7947 	day_view->editing_event_day = -1;
7948 	day_view->editing_event_num = -1;
7949 
7950 	day_view->resize_bars_event_day = -1;
7951 	day_view->resize_bars_event_num = -1;
7952 
7953 	g_object_set (event->canvas_item, "handle_popup", FALSE, NULL);
7954 	g_object_get (
7955 		event->canvas_item,
7956 		"text", &text,
7957 		NULL);
7958 	g_return_if_fail (text != NULL);
7959 
7960 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
7961 	if (!comp) {
7962 		g_free (text);
7963 		return;
7964 	}
7965 
7966 	client = event->comp_data->client;
7967 	on_server = !event->comp_data->is_new_component;
7968 
7969 	if (string_is_empty (text) && !on_server) {
7970 		const gchar *uid;
7971 
7972 		uid = e_cal_component_get_uid (comp);
7973 
7974 		e_day_view_foreach_event_with_uid (day_view, uid,
7975 						   e_day_view_remove_event_cb, NULL);
7976 		e_day_view_check_layout (day_view);
7977 		gtk_widget_queue_draw (day_view->top_canvas);
7978 		gtk_widget_queue_draw (day_view->main_canvas);
7979 		goto out;
7980 	}
7981 
7982 	/* Only update the summary if necessary. */
7983 	summary = e_cal_component_get_summary (comp);
7984 	if (summary && !g_strcmp0 (text, e_cal_component_text_get_value (summary))) {
7985 		if (day == E_DAY_VIEW_LONG_EVENT)
7986 			e_day_view_reshape_long_event (day_view, event_num);
7987 		else
7988 			e_day_view_update_event_label (
7989 				day_view, day,
7990 				event_num);
7991 	} else if ((summary && e_cal_component_text_get_value (summary)) || !string_is_empty (text)) {
7992 		ICalComponent *icomp = e_cal_component_get_icalcomponent (comp);
7993 
7994 		if (summary)
7995 			e_cal_component_text_free (summary);
7996 		summary = e_cal_component_text_new (text, NULL);
7997 		e_cal_component_set_summary (comp, summary);
7998 		e_cal_component_commit_sequence (comp);
7999 
8000 		if (!on_server) {
8001 			e_cal_ops_create_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), client, icomp,
8002 				e_calendar_view_component_created_cb, g_object_ref (day_view), g_object_unref);
8003 
8004 			/* we remove the object since we either got the update from the server or failed */
8005 			e_day_view_remove_event_cb (day_view, day, event_num, NULL);
8006 		} else {
8007 			ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
8008 
8009 			if (e_cal_component_has_recurrences (comp)) {
8010 				if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
8011 					goto out;
8012 				}
8013 
8014 				if (mod == E_CAL_OBJ_MOD_THIS) {
8015 					ECalComponentDateTime *olddt, *dt;
8016 
8017 					olddt = e_cal_component_get_dtstart (comp);
8018 
8019 					if (olddt && e_cal_component_datetime_get_value (olddt) &&
8020 					    i_cal_time_get_timezone (e_cal_component_datetime_get_value (olddt))) {
8021 						ICalTime *itt;
8022 
8023 						itt = e_cal_component_datetime_get_value (olddt);
8024 
8025 						dt = e_cal_component_datetime_new_take (
8026 							i_cal_time_new_from_timet_with_zone (event->comp_data->instance_start,
8027 								i_cal_time_is_date (itt), i_cal_time_get_timezone (itt)),
8028 							g_strdup (e_cal_component_datetime_get_tzid (olddt)));
8029 					} else {
8030 						ICalTime *itt;
8031 						ICalTimezone *zone;
8032 
8033 						zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view));
8034 						itt = olddt ? e_cal_component_datetime_get_value (olddt) : NULL;
8035 
8036 						dt = e_cal_component_datetime_new_take (
8037 							i_cal_time_new_from_timet_with_zone (event->comp_data->instance_start,
8038 								itt ? i_cal_time_is_date (itt) : FALSE, zone),
8039 							zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
8040 					}
8041 					e_cal_component_set_dtstart (comp, dt);
8042 
8043 					e_cal_component_datetime_free (olddt);
8044 					e_cal_component_datetime_free (dt);
8045 
8046 					olddt = e_cal_component_get_dtend (comp);
8047 
8048 					if (olddt && e_cal_component_datetime_get_value (olddt) &&
8049 					    i_cal_time_get_timezone (e_cal_component_datetime_get_value (olddt))) {
8050 						ICalTime *itt;
8051 
8052 						itt = e_cal_component_datetime_get_value (olddt);
8053 
8054 						dt = e_cal_component_datetime_new_take (
8055 							i_cal_time_new_from_timet_with_zone (event->comp_data->instance_end,
8056 								i_cal_time_is_date (itt), i_cal_time_get_timezone (itt)),
8057 							g_strdup (e_cal_component_datetime_get_tzid (olddt)));
8058 					} else {
8059 						ICalTime *itt;
8060 						ICalTimezone *zone;
8061 
8062 						zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view));
8063 						itt = olddt ? e_cal_component_datetime_get_value (olddt) : NULL;
8064 
8065 						dt = e_cal_component_datetime_new_take (
8066 							i_cal_time_new_from_timet_with_zone (event->comp_data->instance_end,
8067 								itt ? i_cal_time_is_date (itt) : FALSE, zone),
8068 							zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
8069 					}
8070 					e_cal_component_set_dtend (comp, dt);
8071 
8072 					e_cal_component_datetime_free (olddt);
8073 					e_cal_component_datetime_free (dt);
8074 
8075 					e_cal_component_set_rdates (comp, NULL);
8076 					e_cal_component_set_rrules (comp, NULL);
8077 					e_cal_component_set_exdates (comp, NULL);
8078 					e_cal_component_set_exrules (comp, NULL);
8079 
8080 					e_cal_component_commit_sequence (comp);
8081 				}
8082 			} else if (e_cal_component_is_instance (comp))
8083 				mod = E_CAL_OBJ_MOD_THIS;
8084 
8085 			e_cal_ops_modify_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)),
8086 				client, e_cal_component_get_icalcomponent (comp), mod, E_CAL_OPS_SEND_FLAG_ASK);
8087 		}
8088 
8089 	}
8090 	gtk_widget_queue_draw (day_view->main_canvas);
8091 
8092  out:
8093 
8094 	e_cal_component_text_free (summary);
8095 	g_object_unref (comp);
8096 	g_free (text);
8097 
8098 	g_signal_emit_by_name (day_view, "selection_changed");
8099 
8100 	g_object_notify (G_OBJECT (day_view), "is-editing");
8101 }
8102 
8103 /* FIXME: It is possible that we may produce an invalid time due to daylight
8104  * saving times (i.e. when clocks go forward there is a range of time which
8105  * is not valid). I don't know the best way to handle daylight saving time. */
8106 static time_t
e_day_view_convert_grid_position_to_time(EDayView * day_view,gint col,gint row)8107 e_day_view_convert_grid_position_to_time (EDayView *day_view,
8108                                           gint col,
8109                                           gint row)
8110 {
8111 	ECalendarView *cal_view;
8112 	gint time_divisions;
8113 	ICalTime *tt;
8114 	time_t val;
8115 	gint minutes;
8116 
8117 	cal_view = E_CALENDAR_VIEW (day_view);
8118 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
8119 
8120 	/* Calulate the number of minutes since the start of the day. */
8121 	minutes = day_view->first_hour_shown * 60
8122 		+ day_view->first_minute_shown
8123 		+ row * time_divisions;
8124 
8125 	/* A special case for midnight, where we can use the start of the
8126 	 * next day. */
8127 	if (minutes == 60 * 24)
8128 		return day_view->day_starts[col + 1];
8129 
8130 	/* Create an ICalTime and convert to a time_t. */
8131 	tt = i_cal_time_new_from_timet_with_zone (
8132 		day_view->day_starts[col],
8133 		FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
8134 	i_cal_time_set_hour (tt, minutes / 60);
8135 	i_cal_time_set_minute (tt, minutes % 60);
8136 	i_cal_time_set_second (tt, 0);
8137 
8138 	val = i_cal_time_as_timet_with_zone (tt, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
8139 
8140 	g_clear_object (&tt);
8141 
8142 	return val;
8143 }
8144 
8145 static gboolean
e_day_view_convert_time_to_grid_position(EDayView * day_view,time_t time,gint * col,gint * row)8146 e_day_view_convert_time_to_grid_position (EDayView *day_view,
8147                                           time_t time,
8148                                           gint *col,
8149                                           gint *row)
8150 {
8151 	ECalendarView *cal_view;
8152 	ICalTime *tt;
8153 	gint time_divisions;
8154 	gint day, minutes;
8155 	gint days_shown;
8156 
8157 	*col = *row = 0;
8158 
8159 	cal_view = E_CALENDAR_VIEW (day_view);
8160 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
8161 
8162 	if (time < day_view->lower || time >= day_view->upper)
8163 		return FALSE;
8164 
8165 	days_shown = e_day_view_get_days_shown (day_view);
8166 
8167 	/* We can find the column easily using the day_starts array. */
8168 	for (day = 1; day <= days_shown; day++) {
8169 		if (time < day_view->day_starts[day]) {
8170 			*col = day - 1;
8171 			break;
8172 		}
8173 	}
8174 
8175 	/* To find the row we need to convert the time to an ICalTime,
8176 	 * calculate the offset in minutes from the top of the display and
8177 	 * divide it by the mins per row setting. */
8178 	tt = i_cal_time_new_from_timet_with_zone (time, FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
8179 
8180 	minutes = i_cal_time_get_hour (tt) * 60 + i_cal_time_get_minute (tt);
8181 	minutes -= day_view->first_hour_shown * 60 + day_view->first_minute_shown;
8182 
8183 	g_clear_object (&tt);
8184 
8185 	*row = minutes / time_divisions;
8186 
8187 	if (*row < 0 || *row >= day_view->rows)
8188 		return FALSE;
8189 
8190 	return TRUE;
8191 }
8192 
8193 /* This starts or stops auto-scrolling when dragging a selection or resizing
8194  * an event. */
8195 void
e_day_view_check_auto_scroll(EDayView * day_view,gint event_x,gint event_y)8196 e_day_view_check_auto_scroll (EDayView *day_view,
8197                               gint event_x,
8198                               gint event_y)
8199 {
8200 	GtkAllocation allocation;
8201 	gint scroll_x, scroll_y;
8202 
8203 	gnome_canvas_get_scroll_offsets (
8204 		GNOME_CANVAS (day_view->main_canvas),
8205 		&scroll_x, &scroll_y);
8206 
8207 	event_x -= scroll_x;
8208 	event_y -= scroll_y;
8209 
8210 	day_view->last_mouse_x = event_x;
8211 	day_view->last_mouse_y = event_y;
8212 
8213 	gtk_widget_get_allocation (day_view->main_canvas, &allocation);
8214 
8215 	if (event_y < E_DAY_VIEW_AUTO_SCROLL_OFFSET)
8216 		e_day_view_start_auto_scroll (day_view, TRUE);
8217 	else if (event_y >= allocation.height - E_DAY_VIEW_AUTO_SCROLL_OFFSET)
8218 		e_day_view_start_auto_scroll (day_view, FALSE);
8219 	else
8220 		e_day_view_stop_auto_scroll (day_view);
8221 }
8222 
8223 static void
e_day_view_start_auto_scroll(EDayView * day_view,gboolean scroll_up)8224 e_day_view_start_auto_scroll (EDayView *day_view,
8225                               gboolean scroll_up)
8226 {
8227 	if (day_view->auto_scroll_timeout_id == 0) {
8228 		day_view->auto_scroll_timeout_id = e_named_timeout_add (
8229 			E_DAY_VIEW_AUTO_SCROLL_TIMEOUT,
8230 			e_day_view_auto_scroll_handler, day_view);
8231 		day_view->auto_scroll_delay = E_DAY_VIEW_AUTO_SCROLL_DELAY;
8232 	}
8233 	day_view->auto_scroll_up = scroll_up;
8234 }
8235 
8236 void
e_day_view_stop_auto_scroll(EDayView * day_view)8237 e_day_view_stop_auto_scroll (EDayView *day_view)
8238 {
8239 	if (day_view->auto_scroll_timeout_id != 0) {
8240 		g_source_remove (day_view->auto_scroll_timeout_id);
8241 		day_view->auto_scroll_timeout_id = 0;
8242 	}
8243 }
8244 
8245 static gboolean
e_day_view_auto_scroll_handler(gpointer data)8246 e_day_view_auto_scroll_handler (gpointer data)
8247 {
8248 	EDayView *day_view;
8249 	ECalendarViewPosition pos;
8250 	gint scroll_x, scroll_y, new_scroll_y, canvas_x, canvas_y, row, day;
8251 	GtkAdjustment *adjustment;
8252 	GtkScrollable *scrollable;
8253 	gdouble step_increment;
8254 	gdouble page_size;
8255 	gdouble upper;
8256 
8257 	g_return_val_if_fail (E_IS_DAY_VIEW (data), FALSE);
8258 
8259 	day_view = E_DAY_VIEW (data);
8260 
8261 	if (day_view->auto_scroll_delay > 0) {
8262 		day_view->auto_scroll_delay--;
8263 		return TRUE;
8264 	}
8265 
8266 	gnome_canvas_get_scroll_offsets (
8267 		GNOME_CANVAS (day_view->main_canvas),
8268 		&scroll_x, &scroll_y);
8269 
8270 	scrollable = GTK_SCROLLABLE (day_view->main_canvas);
8271 	adjustment = gtk_scrollable_get_vadjustment (scrollable);
8272 
8273 	step_increment = gtk_adjustment_get_step_increment (adjustment);
8274 	page_size = gtk_adjustment_get_page_size (adjustment);
8275 	upper = gtk_adjustment_get_upper (adjustment);
8276 
8277 	if (day_view->auto_scroll_up)
8278 		new_scroll_y = MAX (scroll_y - step_increment, 0);
8279 	else
8280 		new_scroll_y = MIN (
8281 			scroll_y + step_increment,
8282 			upper - page_size);
8283 
8284 	if (new_scroll_y != scroll_y) {
8285 		/* NOTE: This reduces flicker, but only works if we don't use
8286 		 * canvas items which have X windows. */
8287 		gnome_canvas_scroll_to (
8288 			GNOME_CANVAS (day_view->main_canvas),
8289 			scroll_x, new_scroll_y);
8290 	}
8291 
8292 	canvas_x = day_view->last_mouse_x + scroll_x;
8293 	canvas_y = day_view->last_mouse_y + new_scroll_y;
8294 
8295 	/* The last_mouse_x position is set to -1 when we are selecting using
8296 	 * the time column. In this case we set canvas_x to 0 and we ignore
8297 	 * the resulting day. */
8298 	if (day_view->last_mouse_x == -1)
8299 		canvas_x = 0;
8300 
8301 	/* Update the selection/resize/drag if necessary. */
8302 	pos = e_day_view_convert_position_in_main_canvas (
8303 		day_view,
8304 		canvas_x, canvas_y,
8305 		&day, &row, NULL);
8306 
8307 	if (day_view->last_mouse_x == -1)
8308 		day = -1;
8309 
8310 	if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
8311 		if (day_view->selection_is_being_dragged) {
8312 			e_day_view_update_selection (day_view, day, row);
8313 		} else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
8314 			e_day_view_update_resize (day_view, row);
8315 		} else if (day_view->drag_item->flags & GNOME_CANVAS_ITEM_VISIBLE) {
8316 			e_day_view_update_main_canvas_drag (day_view, row, day);
8317 		}
8318 	}
8319 
8320 	return TRUE;
8321 }
8322 
8323 gboolean
e_day_view_get_event_rows(EDayView * day_view,gint day,gint event_num,gint * start_row_out,gint * end_row_out)8324 e_day_view_get_event_rows (EDayView *day_view,
8325                            gint day,
8326                            gint event_num,
8327                            gint *start_row_out,
8328                            gint *end_row_out)
8329 {
8330 	ECalendarView *cal_view;
8331 	EDayViewEvent *event;
8332 	gint time_divisions;
8333 	gint start_row, end_row;
8334 
8335 	g_return_val_if_fail (day >= 0, FALSE);
8336 	g_return_val_if_fail (day < E_DAY_VIEW_LONG_EVENT, FALSE);
8337 	g_return_val_if_fail (event_num >= 0, FALSE);
8338 
8339 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
8340 		return FALSE;
8341 
8342 	cal_view = E_CALENDAR_VIEW (day_view);
8343 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
8344 
8345 	event = &g_array_index (day_view->events[day], EDayViewEvent,
8346 				event_num);
8347 	start_row = event->start_minute / time_divisions;
8348 	end_row = (event->end_minute - 1) / time_divisions;
8349 	if (end_row < start_row)
8350 		end_row = start_row;
8351 
8352 	*start_row_out = start_row;
8353 	*end_row_out = end_row;
8354 	return TRUE;
8355 }
8356 
8357 gboolean
e_day_view_get_event_position(EDayView * day_view,gint day,gint event_num,gint * item_x,gint * item_y,gint * item_w,gint * item_h)8358 e_day_view_get_event_position (EDayView *day_view,
8359                                gint day,
8360                                gint event_num,
8361                                gint *item_x,
8362                                gint *item_y,
8363                                gint *item_w,
8364                                gint *item_h)
8365 {
8366 	EDayViewEvent *event;
8367 	gint start_row, end_row, cols_in_row, start_col, num_columns;
8368 
8369 	if (!is_array_index_in_bounds (day_view->events[day], event_num))
8370 		return FALSE;
8371 
8372 	event = &g_array_index (day_view->events[day], EDayViewEvent,
8373 				event_num);
8374 
8375 	/* If the event is flagged as not displayed, return FALSE. */
8376 	if (event->num_columns == 0)
8377 		return FALSE;
8378 
8379 	e_day_view_get_event_rows (day_view, day, event_num, &start_row, &end_row);
8380 
8381 	cols_in_row = day_view->cols_per_row[day][start_row];
8382 	start_col = event->start_row_or_col;
8383 	num_columns = event->num_columns;
8384 
8385 	if (cols_in_row == 0)
8386 		return FALSE;
8387 
8388 	/* If the event is being resize, use the resize position. */
8389 	if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE
8390 	    && day_view->resize_event_day == day
8391 	    && day_view->resize_event_num == event_num) {
8392 		if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE)
8393 			start_row = day_view->resize_start_row;
8394 		else if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_BOTTOM_EDGE)
8395 			end_row = day_view->resize_end_row;
8396 	}
8397 
8398 	*item_x = day_view->day_offsets[day]
8399 		+ day_view->day_widths[day] * start_col / cols_in_row;
8400 	*item_w = day_view->day_widths[day] * num_columns / cols_in_row
8401 		- E_DAY_VIEW_GAP_WIDTH;
8402 	*item_w = MAX (*item_w, 0);
8403 	*item_y = start_row * day_view->row_height;
8404 
8405 	/* This makes the event end on the grid line of the next row,
8406 	 * which maybe looks nicer if you have 2 events on consecutive rows. */
8407 	*item_h = (end_row - start_row + 1) * day_view->row_height + 1;
8408 
8409 	return TRUE;
8410 }
8411 
8412 gboolean
e_day_view_get_long_event_position(EDayView * day_view,gint event_num,gint * start_day,gint * end_day,gint * item_x,gint * item_y,gint * item_w,gint * item_h)8413 e_day_view_get_long_event_position (EDayView *day_view,
8414                                     gint event_num,
8415                                     gint *start_day,
8416                                     gint *end_day,
8417                                     gint *item_x,
8418                                     gint *item_y,
8419                                     gint *item_w,
8420                                     gint *item_h)
8421 {
8422 	EDayViewEvent *event;
8423 	gint days_shown;
8424 
8425 	days_shown = e_day_view_get_days_shown (day_view);
8426 
8427 	if (!is_array_index_in_bounds (day_view->long_events, event_num))
8428 		return FALSE;
8429 
8430 	event = &g_array_index (day_view->long_events, EDayViewEvent,
8431 				event_num);
8432 
8433 	/* If the event is flagged as not displayed, return FALSE. */
8434 	if (event->num_columns == 0)
8435 		return FALSE;
8436 
8437 	if (!e_day_view_find_long_event_days (event,
8438 					      days_shown,
8439 					      day_view->day_starts,
8440 					      start_day, end_day))
8441 		return FALSE;
8442 
8443 	/* If the event is being resize, use the resize position. */
8444 	if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE
8445 	    && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT
8446 	    && day_view->resize_event_num == event_num) {
8447 		if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE)
8448 			*start_day = day_view->resize_start_row;
8449 		else if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_RIGHT_EDGE)
8450 			*end_day = day_view->resize_end_row;
8451 	}
8452 
8453 	*item_x = day_view->day_offsets[*start_day] + E_DAY_VIEW_BAR_WIDTH;
8454 	if (days_shown == 1) {
8455 		GtkAllocation allocation;
8456 
8457 		gtk_widget_get_allocation (day_view->top_canvas, &allocation);
8458 		*item_w = allocation.width;
8459 	} else
8460 		*item_w = day_view->day_offsets[*end_day + 1];
8461 	*item_w = MAX (*item_w - *item_x - E_DAY_VIEW_GAP_WIDTH, 0);
8462 	*item_y = (event->start_row_or_col) * day_view->top_row_height;
8463 	*item_h = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP;
8464 	return TRUE;
8465 }
8466 
8467 /* Converts a position within the entire top canvas to a day & event and
8468  * a place within the event if appropriate. If event_num_return is NULL, it
8469  * simply returns the grid position without trying to find the event. */
8470 static ECalendarViewPosition
e_day_view_convert_position_in_top_canvas(EDayView * day_view,gint x,gint y,gint * day_return,gint * event_num_return)8471 e_day_view_convert_position_in_top_canvas (EDayView *day_view,
8472                                            gint x,
8473                                            gint y,
8474                                            gint *day_return,
8475                                            gint *event_num_return)
8476 {
8477 	EDayViewEvent *event;
8478 	gint day, row, col;
8479 	gint event_num, start_day, end_day, item_x, item_y, item_w, item_h;
8480 	gint days_shown;
8481 
8482 	days_shown = e_day_view_get_days_shown (day_view);
8483 
8484 	*day_return = -1;
8485 	if (event_num_return)
8486 		*event_num_return = -1;
8487 
8488 	if (x < 0 || y < 0)
8489 		return E_CALENDAR_VIEW_POS_OUTSIDE;
8490 
8491 	row = y / day_view->top_row_height;
8492 
8493 	day = -1;
8494 	for (col = 1; col <= days_shown; col++) {
8495 		if (x < day_view->day_offsets[col]) {
8496 			day = col - 1;
8497 			break;
8498 		}
8499 	}
8500 	if (day == -1)
8501 		return E_CALENDAR_VIEW_POS_OUTSIDE;
8502 
8503 	*day_return = day;
8504 
8505 	/* If only the grid position is wanted, return. */
8506 	if (event_num_return == NULL)
8507 		return E_CALENDAR_VIEW_POS_NONE;
8508 
8509 	for (event_num = 0; event_num < day_view->long_events->len;
8510 	     event_num++) {
8511 		event = &g_array_index (day_view->long_events, EDayViewEvent,
8512 					event_num);
8513 
8514 		if (event->start_row_or_col != row)
8515 			continue;
8516 
8517 		if (!e_day_view_get_long_event_position (day_view, event_num,
8518 							 &start_day, &end_day,
8519 							 &item_x, &item_y,
8520 							 &item_w, &item_h))
8521 			continue;
8522 
8523 		if (x < item_x)
8524 			continue;
8525 
8526 		if (x >= item_x + item_w)
8527 			continue;
8528 
8529 		*event_num_return = event_num;
8530 
8531 		if (x < item_x + E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH
8532 		    + E_DAY_VIEW_LONG_EVENT_X_PAD)
8533 			return E_CALENDAR_VIEW_POS_LEFT_EDGE;
8534 
8535 		if (x >= item_x + item_w - E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH
8536 		    - E_DAY_VIEW_LONG_EVENT_X_PAD)
8537 			return E_CALENDAR_VIEW_POS_RIGHT_EDGE;
8538 
8539 		return E_CALENDAR_VIEW_POS_EVENT;
8540 	}
8541 
8542 	return E_CALENDAR_VIEW_POS_NONE;
8543 }
8544 
8545 /* Converts a position within the entire main canvas to a day, row, event and
8546  * a place within the event if appropriate. If event_num_return is NULL, it
8547  * simply returns the grid position without trying to find the event. */
8548 static ECalendarViewPosition
e_day_view_convert_position_in_main_canvas(EDayView * day_view,gint x,gint y,gint * day_return,gint * row_return,gint * event_num_return)8549 e_day_view_convert_position_in_main_canvas (EDayView *day_view,
8550                                             gint x,
8551                                             gint y,
8552                                             gint *day_return,
8553                                             gint *row_return,
8554                                             gint *event_num_return)
8555 {
8556 	gint day, row, col, event_num;
8557 	gint item_x, item_y, item_w, item_h;
8558 	gint days_shown;
8559 
8560 	days_shown = e_day_view_get_days_shown (day_view);
8561 
8562 	*day_return = -1;
8563 	*row_return = -1;
8564 	if (event_num_return)
8565 		*event_num_return = -1;
8566 
8567 	/* Check the position is inside the canvas, and determine the day
8568 	 * and row. */
8569 	if (x < 0 || y < 0)
8570 		return E_CALENDAR_VIEW_POS_OUTSIDE;
8571 
8572 	row = y / day_view->row_height;
8573 	if (row >= day_view->rows)
8574 		return E_CALENDAR_VIEW_POS_OUTSIDE;
8575 
8576 	day = -1;
8577 	for (col = 1; col <= days_shown; col++) {
8578 		if (x < day_view->day_offsets[col]) {
8579 			day = col - 1;
8580 			break;
8581 		}
8582 	}
8583 	if (day == -1)
8584 		return E_CALENDAR_VIEW_POS_OUTSIDE;
8585 
8586 	*day_return = day;
8587 	*row_return = row;
8588 
8589 	/* If only the grid position is wanted, return. */
8590 	if (event_num_return == NULL)
8591 		return E_CALENDAR_VIEW_POS_NONE;
8592 
8593 	/* Check the selected item first, since the horizontal resizing bars
8594 	 * may be above other events. */
8595 	if (day_view->resize_bars_event_day == day) {
8596 		if (e_day_view_get_event_position (day_view, day,
8597 						   day_view->resize_bars_event_num,
8598 						   &item_x, &item_y,
8599 						   &item_w, &item_h)) {
8600 			if (x >= item_x && x < item_x + item_w) {
8601 				*event_num_return = day_view->resize_bars_event_num;
8602 				if (y >= item_y - E_DAY_VIEW_BAR_HEIGHT
8603 				    && y < item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT)
8604 					return E_CALENDAR_VIEW_POS_TOP_EDGE;
8605 				if (y >= item_y + item_h - E_DAY_VIEW_EVENT_BORDER_HEIGHT
8606 				    && y < item_y + item_h + E_DAY_VIEW_BAR_HEIGHT)
8607 					return E_CALENDAR_VIEW_POS_BOTTOM_EDGE;
8608 			}
8609 		}
8610 	}
8611 
8612 	/* Try to find the event at the found position. */
8613 	*event_num_return = -1;
8614 	for (event_num = 0; event_num < day_view->events[day]->len;
8615 	     event_num++) {
8616 		if (!e_day_view_get_event_position (day_view, day, event_num,
8617 						    &item_x, &item_y,
8618 						    &item_w, &item_h))
8619 			continue;
8620 
8621 		if (x < item_x || x >= item_x + item_w
8622 		    || y < item_y || y >= item_y + item_h)
8623 			continue;
8624 
8625 		*event_num_return = event_num;
8626 
8627 		if (x < item_x + E_DAY_VIEW_BAR_WIDTH)
8628 			return E_CALENDAR_VIEW_POS_LEFT_EDGE;
8629 
8630 		if (y < item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT
8631 		    + E_DAY_VIEW_EVENT_Y_PAD)
8632 			return E_CALENDAR_VIEW_POS_TOP_EDGE;
8633 
8634 		if (y >= item_y + item_h - E_DAY_VIEW_EVENT_BORDER_HEIGHT
8635 		    - E_DAY_VIEW_EVENT_Y_PAD)
8636 			return E_CALENDAR_VIEW_POS_BOTTOM_EDGE;
8637 
8638 		return E_CALENDAR_VIEW_POS_EVENT;
8639 	}
8640 
8641 	return E_CALENDAR_VIEW_POS_NONE;
8642 }
8643 
8644 static gboolean
e_day_view_on_top_canvas_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,EDayView * day_view)8645 e_day_view_on_top_canvas_drag_motion (GtkWidget *widget,
8646                                       GdkDragContext *context,
8647                                       gint x,
8648                                       gint y,
8649                                       guint time,
8650                                       EDayView *day_view)
8651 {
8652 	gint scroll_x, scroll_y;
8653 
8654 	gnome_canvas_get_scroll_offsets (
8655 		GNOME_CANVAS (widget),
8656 		&scroll_x, &scroll_y);
8657 	day_view->drag_event_x = x + scroll_x;
8658 	day_view->drag_event_y = y + scroll_y;
8659 
8660 	e_day_view_reshape_top_canvas_drag_item (day_view);
8661 
8662 	return TRUE;
8663 }
8664 
8665 static void
e_day_view_reshape_top_canvas_drag_item(EDayView * day_view)8666 e_day_view_reshape_top_canvas_drag_item (EDayView *day_view)
8667 {
8668 	ECalendarViewPosition pos;
8669 	gint x, y, day;
8670 
8671 	/* Calculate the day & start row of the event being dragged, using
8672 	 * the current mouse position. */
8673 	x = day_view->drag_event_x;
8674 	y = day_view->drag_event_y;
8675 	pos = e_day_view_convert_position_in_top_canvas (
8676 		day_view, x, y,
8677 		&day, NULL);
8678 	/* This shouldn't really happen in a drag. */
8679 	if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
8680 		return;
8681 
8682 	if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT)
8683 		day -= day_view->drag_event_offset;
8684 	day = MAX (day, 0);
8685 
8686 	e_day_view_update_top_canvas_drag (day_view, day);
8687 }
8688 
8689 static void
e_day_view_update_top_canvas_drag(EDayView * day_view,gint day)8690 e_day_view_update_top_canvas_drag (EDayView *day_view,
8691                                    gint day)
8692 {
8693 	EDayViewEvent *event = NULL;
8694 	gint row, num_days, start_day, end_day;
8695 	gdouble item_x, item_y, item_w, item_h;
8696 	gint days_shown;
8697 	gchar *text;
8698 
8699 	days_shown = e_day_view_get_days_shown (day_view);
8700 
8701 	/* Calculate the event's position. If the event is in the same
8702 	 * position we started in, we use the same columns. */
8703 	row = day_view->rows_in_top_display + 1;
8704 	num_days = 1;
8705 
8706 	if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
8707 		if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num))
8708 			return;
8709 
8710 		event = &g_array_index (day_view->long_events, EDayViewEvent,
8711 					day_view->drag_event_num);
8712 		row = event->start_row_or_col + 1;
8713 
8714 		if (!e_day_view_find_long_event_days (event,
8715 						      days_shown,
8716 						      day_view->day_starts,
8717 						      &start_day, &end_day))
8718 			return;
8719 
8720 		num_days = end_day - start_day + 1;
8721 
8722 		/* Make sure we don't go off the screen. */
8723 		day = MIN (day, days_shown - num_days);
8724 
8725 	} else if (day_view->drag_event_day != -1) {
8726 		if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num))
8727 			return;
8728 
8729 		event = &g_array_index (day_view->events[day_view->drag_event_day],
8730 					EDayViewEvent,
8731 					day_view->drag_event_num);
8732 	}
8733 
8734 	/* If the position hasn't changed, just return. */
8735 	if (day_view->drag_last_day == day
8736 	    && (day_view->drag_long_event_item->flags & GNOME_CANVAS_ITEM_VISIBLE))
8737 		return;
8738 
8739 	day_view->drag_last_day = day;
8740 
8741 	item_x = day_view->day_offsets[day] + E_DAY_VIEW_BAR_WIDTH;
8742 	item_w = day_view->day_offsets[day + num_days] - item_x
8743 		- E_DAY_VIEW_GAP_WIDTH;
8744 	item_y = row * day_view->top_row_height;
8745 	item_h = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP;
8746 
8747 	/* Set the positions of the event & associated items. */
8748 	gnome_canvas_item_set (
8749 		day_view->drag_long_event_rect_item,
8750 		"x1", item_x,
8751 		"y1", item_y,
8752 		"x2", item_x + item_w - 1,
8753 		"y2", item_y + item_h - 1,
8754 		NULL);
8755 
8756 	gnome_canvas_item_set (
8757 		day_view->drag_long_event_item,
8758 		"clip_width", item_w - (E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD) * 2,
8759 		"clip_height", item_h - (E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD) * 2,
8760 		NULL);
8761 	e_canvas_item_move_absolute (
8762 		day_view->drag_long_event_item,
8763 		item_x + E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD,
8764 		item_y + E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD);
8765 
8766 	if (!(day_view->drag_long_event_rect_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8767 		gnome_canvas_item_raise_to_top (day_view->drag_long_event_rect_item);
8768 		gnome_canvas_item_show (day_view->drag_long_event_rect_item);
8769 	}
8770 
8771 	/* Set the text, if necessary. We don't want to set the text every
8772 	 * time it moves, so we check if it is currently invisible and only
8773 	 * set the text then. */
8774 	if (!(day_view->drag_long_event_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8775 		const gchar *summary;
8776 
8777 		if (event && is_comp_data_valid (event)) {
8778 			summary = i_cal_component_get_summary (event->comp_data->icalcomp);
8779 			text = g_strdup (summary);
8780 		} else {
8781 			text = NULL;
8782 		}
8783 
8784 		gnome_canvas_item_set (
8785 			day_view->drag_long_event_item,
8786 			"text", text ? text : "",
8787 			NULL);
8788 		gnome_canvas_item_raise_to_top (day_view->drag_long_event_item);
8789 		gnome_canvas_item_show (day_view->drag_long_event_item);
8790 
8791 		g_free (text);
8792 	}
8793 }
8794 
8795 static gboolean
e_day_view_on_main_canvas_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,EDayView * day_view)8796 e_day_view_on_main_canvas_drag_motion (GtkWidget *widget,
8797                                        GdkDragContext *context,
8798                                        gint x,
8799                                        gint y,
8800                                        guint time,
8801                                        EDayView *day_view)
8802 {
8803 	gint scroll_x, scroll_y;
8804 
8805 	gnome_canvas_get_scroll_offsets (
8806 		GNOME_CANVAS (widget),
8807 		&scroll_x, &scroll_y);
8808 
8809 	day_view->drag_event_x = x + scroll_x;
8810 	day_view->drag_event_y = y + scroll_y;
8811 
8812 	e_day_view_reshape_main_canvas_drag_item (day_view);
8813 	e_day_view_reshape_main_canvas_resize_bars (day_view);
8814 
8815 	e_day_view_check_auto_scroll (day_view, day_view->drag_event_x, day_view->drag_event_y);
8816 
8817 	return TRUE;
8818 }
8819 
8820 static void
e_day_view_reshape_main_canvas_drag_item(EDayView * day_view)8821 e_day_view_reshape_main_canvas_drag_item (EDayView *day_view)
8822 {
8823 	ECalendarViewPosition pos;
8824 	gint x, y, day, row;
8825 
8826 	/* Calculate the day & start row of the event being dragged, using
8827 	 * the current mouse position. */
8828 	x = day_view->drag_event_x;
8829 	y = day_view->drag_event_y;
8830 	pos = e_day_view_convert_position_in_main_canvas (
8831 		day_view, x, y,
8832 		&day, &row, NULL);
8833 	/* This shouldn't really happen in a drag. */
8834 	if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
8835 		return;
8836 
8837 	if (day_view->drag_event_day != -1
8838 	    && day_view->drag_event_day != E_DAY_VIEW_LONG_EVENT)
8839 		row -= day_view->drag_event_offset;
8840 	row = MAX (row, 0);
8841 
8842 	e_day_view_update_main_canvas_drag (day_view, row, day);
8843 }
8844 
8845 static void
e_day_view_update_main_canvas_drag(EDayView * day_view,gint row,gint day)8846 e_day_view_update_main_canvas_drag (EDayView *day_view,
8847                                     gint row,
8848                                     gint day)
8849 {
8850 	EDayViewEvent *event = NULL;
8851 	ECalendarView *cal_view;
8852 	gint time_divisions;
8853 	gint cols_in_row, start_col, num_columns, num_rows, start_row, end_row;
8854 	gdouble item_x, item_y, item_w, item_h;
8855 	gchar *text;
8856 
8857 	cal_view = E_CALENDAR_VIEW (day_view);
8858 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
8859 
8860 	/* If the position hasn't changed, just return. */
8861 	if (day_view->drag_last_day == day
8862 	    && day_view->drag_last_row == row
8863 	    && (day_view->drag_item->flags & GNOME_CANVAS_ITEM_VISIBLE))
8864 		return;
8865 
8866 	day_view->drag_last_day = day;
8867 	day_view->drag_last_row = row;
8868 
8869 	/* Calculate the event's position. If the event is in the same
8870 	 * position we started in, we use the same columns. */
8871 	cols_in_row = 1;
8872 	start_row = 0;
8873 	start_col = 0;
8874 	num_columns = 1;
8875 	num_rows = 1;
8876 
8877 	if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
8878 		if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num))
8879 			return;
8880 
8881 		event = &g_array_index (day_view->long_events, EDayViewEvent,
8882 					day_view->drag_event_num);
8883 	} else if (day_view->drag_event_day != -1) {
8884 		if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num))
8885 			return;
8886 
8887 		event = &g_array_index (day_view->events[day_view->drag_event_day],
8888 					EDayViewEvent,
8889 					day_view->drag_event_num);
8890 		start_row = event->start_minute / time_divisions;
8891 		end_row = (event->end_minute - 1) / time_divisions;
8892 		if (end_row < start_row)
8893 			end_row = start_row;
8894 
8895 		num_rows = end_row - start_row + 1;
8896 
8897 		if (day_view->drag_event_day == day && start_row == row) {
8898 			cols_in_row = day_view->cols_per_row[day][row];
8899 			start_col = event->start_row_or_col;
8900 			num_columns = event->num_columns;
8901 		}
8902 	}
8903 
8904 	item_x = day_view->day_offsets[day]
8905 		+ day_view->day_widths[day] * start_col / cols_in_row;
8906 	item_w = day_view->day_widths[day] * num_columns / cols_in_row
8907 		- E_DAY_VIEW_GAP_WIDTH;
8908 	item_y = row * day_view->row_height;
8909 	item_h = num_rows * day_view->row_height;
8910 
8911 	/* Set the positions of the event & associated items. */
8912 	gnome_canvas_item_set (
8913 		day_view->drag_rect_item,
8914 		"x1", item_x + E_DAY_VIEW_BAR_WIDTH - 1,
8915 		"y1", item_y,
8916 		"x2", item_x + item_w - 1,
8917 		"y2", item_y + item_h - 1,
8918 		NULL);
8919 
8920 	gnome_canvas_item_set (
8921 		day_view->drag_bar_item,
8922 		"x1", item_x,
8923 		"y1", item_y,
8924 		"x2", item_x + E_DAY_VIEW_BAR_WIDTH - 1,
8925 		"y2", item_y + item_h - 1,
8926 		NULL);
8927 
8928 	gnome_canvas_item_set (
8929 		day_view->drag_item,
8930 		"clip_width", item_w - E_DAY_VIEW_BAR_WIDTH - E_DAY_VIEW_EVENT_X_PAD * 2,
8931 		"clip_height", item_h - (E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD) * 2,
8932 		NULL);
8933 	e_canvas_item_move_absolute (
8934 		day_view->drag_item,
8935 		item_x + E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD,
8936 		item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD);
8937 
8938 	if (!(day_view->drag_bar_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8939 		gnome_canvas_item_raise_to_top (day_view->drag_bar_item);
8940 		gnome_canvas_item_show (day_view->drag_bar_item);
8941 	}
8942 
8943 	if (!(day_view->drag_rect_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8944 		gnome_canvas_item_raise_to_top (day_view->drag_rect_item);
8945 		gnome_canvas_item_show (day_view->drag_rect_item);
8946 	}
8947 
8948 	/* Set the text, if necessary. We don't want to set the text every
8949 	 * time it moves, so we check if it is currently invisible and only
8950 	 * set the text then. */
8951 	if (!(day_view->drag_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8952 		const gchar *summary;
8953 
8954 		if (event && is_comp_data_valid (event)) {
8955 			summary = i_cal_component_get_summary (event->comp_data->icalcomp);
8956 			text = g_strdup (summary);
8957 		} else {
8958 			text = NULL;
8959 		}
8960 
8961 		gnome_canvas_item_set (
8962 			day_view->drag_item,
8963 			"text", text ? text : "",
8964 			NULL);
8965 		gnome_canvas_item_raise_to_top (day_view->drag_item);
8966 		gnome_canvas_item_show (day_view->drag_item);
8967 
8968 		g_free (text);
8969 	}
8970 }
8971 
8972 static void
e_day_view_on_top_canvas_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time,EDayView * day_view)8973 e_day_view_on_top_canvas_drag_leave (GtkWidget *widget,
8974                                      GdkDragContext *context,
8975                                      guint time,
8976                                      EDayView *day_view)
8977 {
8978 	day_view->drag_last_day = -1;
8979 
8980 	gnome_canvas_item_hide (day_view->drag_long_event_rect_item);
8981 	gnome_canvas_item_hide (day_view->drag_long_event_item);
8982 }
8983 
8984 static void
e_day_view_on_main_canvas_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time,EDayView * day_view)8985 e_day_view_on_main_canvas_drag_leave (GtkWidget *widget,
8986                                       GdkDragContext *context,
8987                                       guint time,
8988                                       EDayView *day_view)
8989 {
8990 	day_view->drag_last_day = -1;
8991 
8992 	e_day_view_stop_auto_scroll (day_view);
8993 
8994 	gnome_canvas_item_hide (day_view->drag_rect_item);
8995 	gnome_canvas_item_hide (day_view->drag_bar_item);
8996 	gnome_canvas_item_hide (day_view->drag_item);
8997 
8998 	/* Hide the resize bars if they are being used in the drag. */
8999 	if (day_view->drag_event_day == day_view->resize_bars_event_day
9000 	    && day_view->drag_event_num == day_view->resize_bars_event_num) {
9001 	}
9002 }
9003 
9004 static void
e_day_view_on_drag_begin(GtkWidget * widget,GdkDragContext * context,EDayView * day_view)9005 e_day_view_on_drag_begin (GtkWidget *widget,
9006                           GdkDragContext *context,
9007                           EDayView *day_view)
9008 {
9009 	EDayViewEvent *event;
9010 	gint day, event_num;
9011 
9012 	day = day_view->drag_event_day;
9013 	event_num = day_view->drag_event_num;
9014 
9015 	/* These should both be set. */
9016 	if (day == -1) {
9017 		g_warn_if_reached ();
9018 		return;
9019 	}
9020 
9021 	g_return_if_fail (event_num != -1);
9022 
9023 	if (day == E_DAY_VIEW_LONG_EVENT) {
9024 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
9025 			return;
9026 
9027 		event = &g_array_index (day_view->long_events, EDayViewEvent,
9028 					event_num);
9029 	} else {
9030 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
9031 			return;
9032 
9033 		event = &g_array_index (day_view->events[day], EDayViewEvent,
9034 					event_num);
9035 	}
9036 
9037 	/* Hide the text item, since it will be shown in the special drag
9038 	 * items. */
9039 	gnome_canvas_item_hide (event->canvas_item);
9040 }
9041 
9042 static void
e_day_view_on_drag_end(GtkWidget * widget,GdkDragContext * context,EDayView * day_view)9043 e_day_view_on_drag_end (GtkWidget *widget,
9044                         GdkDragContext *context,
9045                         EDayView *day_view)
9046 {
9047 	EDayViewEvent *event;
9048 	gint day, event_num;
9049 
9050 	day = day_view->drag_event_day;
9051 	event_num = day_view->drag_event_num;
9052 
9053 	/* If the calendar has already been updated in drag_data_received()
9054 	 * we just return. */
9055 	if (day == -1 || event_num == -1)
9056 		return;
9057 
9058 	if (day == E_DAY_VIEW_LONG_EVENT) {
9059 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
9060 			return;
9061 
9062 		event = &g_array_index (day_view->long_events, EDayViewEvent,
9063 					event_num);
9064 		gtk_widget_queue_draw (day_view->top_canvas);
9065 	} else {
9066 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
9067 			return;
9068 
9069 		event = &g_array_index (day_view->events[day], EDayViewEvent,
9070 					event_num);
9071 		gtk_widget_queue_draw (day_view->main_canvas);
9072 	}
9073 
9074 	/* Show the text item again. */
9075 	gnome_canvas_item_show (event->canvas_item);
9076 
9077 	day_view->drag_event_day = -1;
9078 	day_view->drag_event_num = -1;
9079 	g_clear_object (&day_view->priv->drag_context);
9080 }
9081 
9082 static void
e_day_view_on_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time,EDayView * day_view)9083 e_day_view_on_drag_data_get (GtkWidget *widget,
9084                              GdkDragContext *context,
9085                              GtkSelectionData *selection_data,
9086                              guint info,
9087                              guint time,
9088                              EDayView *day_view)
9089 {
9090 	EDayViewEvent *event;
9091 	ICalComponent *vcal;
9092 	gint day, event_num;
9093 	gchar *comp_str;
9094 
9095 	day = day_view->drag_event_day;
9096 	event_num = day_view->drag_event_num;
9097 
9098 	/* These should both be set. */
9099 	if (day == -1) {
9100 		g_warn_if_reached ();
9101 		return;
9102 	}
9103 
9104 	g_return_if_fail (event_num != -1);
9105 
9106 	if (day == E_DAY_VIEW_LONG_EVENT) {
9107 		if (!is_array_index_in_bounds (day_view->long_events, event_num))
9108 			return;
9109 
9110 		event = &g_array_index (day_view->long_events,
9111 					EDayViewEvent, event_num);
9112 	} else {
9113 		if (!is_array_index_in_bounds (day_view->events[day], event_num))
9114 			return;
9115 
9116 		event = &g_array_index (day_view->events[day],
9117 					EDayViewEvent, event_num);
9118 	}
9119 
9120 	if (!is_comp_data_valid (event))
9121 		return;
9122 
9123 	vcal = e_cal_util_new_top_level ();
9124 	e_cal_util_add_timezones_from_component (
9125 		vcal, event->comp_data->icalcomp);
9126 	i_cal_component_take_component (
9127 		vcal, i_cal_component_clone (event->comp_data->icalcomp));
9128 
9129 	comp_str = i_cal_component_as_ical_string (vcal);
9130 	if (comp_str) {
9131 		ESource *source;
9132 		const gchar *source_uid;
9133 		GdkAtom target;
9134 		gchar *tmp;
9135 
9136 		source = e_client_get_source (E_CLIENT (event->comp_data->client));
9137 		source_uid = e_source_get_uid (source);
9138 
9139 		tmp = g_strconcat (source_uid, "\n", comp_str, NULL);
9140 		target = gtk_selection_data_get_target (selection_data);
9141 		gtk_selection_data_set (
9142 			selection_data, target, 8,
9143 			(guchar *) tmp, strlen (tmp));
9144 
9145 		g_free (tmp);
9146 	}
9147 
9148 	g_clear_object (&vcal);
9149 	g_free (comp_str);
9150 }
9151 
9152 static void
e_day_view_on_top_canvas_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time,EDayView * day_view)9153 e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget,
9154                                              GdkDragContext *context,
9155                                              gint x,
9156                                              gint y,
9157                                              GtkSelectionData *selection_data,
9158                                              guint info,
9159                                              guint time,
9160                                              EDayView *day_view)
9161 {
9162 	EDayViewEvent *event = NULL;
9163 	ECalendarViewPosition pos;
9164 	gint day, start_day, end_day, num_days;
9165 	gint start_offset, end_offset;
9166 	ECalComponent *comp;
9167 	ESourceRegistry *registry;
9168 	time_t dt;
9169 	gboolean all_day_event;
9170 	ECalModel *model;
9171 	ECalendarView *cal_view;
9172 	gboolean drag_from_same_window;
9173 	const guchar *data;
9174 	gint format, length;
9175 	gint days_shown;
9176 	GtkResponseType send = GTK_RESPONSE_NO;
9177 	gboolean only_new_attendees = FALSE;
9178 	gboolean strip_alarms = TRUE;
9179 
9180 	data = gtk_selection_data_get_data (selection_data);
9181 	format = gtk_selection_data_get_format (selection_data);
9182 	length = gtk_selection_data_get_length (selection_data);
9183 
9184 	days_shown = e_day_view_get_days_shown (day_view);
9185 
9186 	if (day_view->drag_event_day != -1)
9187 		drag_from_same_window = TRUE;
9188 	else
9189 		drag_from_same_window = FALSE;
9190 
9191 	cal_view = E_CALENDAR_VIEW (day_view);
9192 	model = e_calendar_view_get_model (cal_view);
9193 
9194 	registry = e_cal_model_get_registry (model);
9195 
9196 	/* Note that we only support DnD within the EDayView at present. */
9197 	if (length >= 0 && format == 8 && day_view->drag_event_day != -1) {
9198 		/* We are dragging in the same window */
9199 
9200 		pos = e_day_view_convert_position_in_top_canvas (
9201 			day_view,
9202 			x, y, &day,
9203 			NULL);
9204 		if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
9205 			ECalComponentDateTime *date;
9206 			ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
9207 			ECalClient *client;
9208 			GtkWindow *toplevel;
9209 			ICalTime *itt;
9210 			ICalTimezone *zone;
9211 
9212 			num_days = 1;
9213 			start_offset = 0;
9214 			end_offset = 0;
9215 
9216 			if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
9217 				if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num))
9218 					return;
9219 
9220 				event = &g_array_index (day_view->long_events, EDayViewEvent,
9221 							day_view->drag_event_num);
9222 
9223 				if (!is_comp_data_valid (event))
9224 					return;
9225 
9226 				day -= day_view->drag_event_offset;
9227 				day = MAX (day, 0);
9228 
9229 				e_day_view_find_long_event_days (
9230 					event,
9231 					days_shown,
9232 					day_view->day_starts,
9233 					&start_day,
9234 					&end_day);
9235 				num_days = end_day - start_day + 1;
9236 				/* Make sure we don't go off the screen. */
9237 				day = MIN (day, days_shown - num_days);
9238 
9239 				start_offset = event->start_minute;
9240 				end_offset = event->end_minute;
9241 			} else {
9242 				if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num))
9243 					return;
9244 
9245 				event = &g_array_index (day_view->events[day_view->drag_event_day],
9246 							EDayViewEvent,
9247 							day_view->drag_event_num);
9248 
9249 				if (!is_comp_data_valid (event))
9250 					return;
9251 			}
9252 
9253 			client = event->comp_data->client;
9254 
9255 			/* We clone the event since we don't want to change
9256 			 * the original comp here.
9257 			 * Otherwise we would not detect that the event's time
9258 			 * had changed in the "update_event" callback. */
9259 
9260 			comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
9261 			if (!comp)
9262 				return;
9263 
9264 			if (e_cal_component_has_attendees (comp) &&
9265 			    !itip_organizer_is_user (registry, comp, client)) {
9266 				g_object_unref (comp);
9267 				return;
9268 			}
9269 
9270 			toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view)));
9271 
9272 			if (itip_has_any_attendees (comp) &&
9273 			    (itip_organizer_is_user (registry, comp, client) ||
9274 			     itip_sentby_is_user (registry, comp, client)))
9275 				send = e_cal_dialogs_send_dragged_or_resized_component (
9276 						toplevel, client, comp, &strip_alarms, &only_new_attendees);
9277 
9278 			if (send == GTK_RESPONSE_CANCEL) {
9279 				e_day_view_abort_resize (day_view);
9280 				g_object_unref (comp);
9281 				return;
9282 			}
9283 
9284 			if (start_offset == 0 && end_offset == 0)
9285 				all_day_event = TRUE;
9286 			else
9287 				all_day_event = FALSE;
9288 
9289 			zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view));
9290 			dt = day_view->day_starts[day] + start_offset * 60;
9291 			itt = i_cal_time_new_from_timet_with_zone (dt, FALSE, zone);
9292 			if (all_day_event) {
9293 				i_cal_time_set_is_date (itt, TRUE);
9294 				date = e_cal_component_datetime_new_take (itt, NULL);
9295 			} else {
9296 				date = e_cal_component_datetime_new_take (itt,
9297 					zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
9298 			}
9299 			cal_comp_set_dtstart_with_oldzone (client, comp, date);
9300 			e_cal_component_datetime_free (date);
9301 
9302 			if (end_offset == 0)
9303 				dt = day_view->day_starts[day + num_days];
9304 			else
9305 				dt = day_view->day_starts[day + num_days - 1] + end_offset * 60;
9306 			itt = i_cal_time_new_from_timet_with_zone (dt, FALSE, zone);
9307 			if (all_day_event) {
9308 				i_cal_time_set_is_date (itt, TRUE);
9309 				date = e_cal_component_datetime_new_take (itt, NULL);
9310 			} else {
9311 				date = e_cal_component_datetime_new_take (itt,
9312 					zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
9313 			}
9314 			cal_comp_set_dtend_with_oldzone (client, comp, date);
9315 			e_cal_component_datetime_free (date);
9316 
9317 			gtk_drag_finish (context, TRUE, TRUE, time);
9318 
9319 			/* Reset this since it will be invalid. */
9320 			day_view->drag_event_day = -1;
9321 			g_clear_object (&day_view->priv->drag_context);
9322 
9323 			/* Show the text item again, just in case it hasn't
9324 			 * moved. If we don't do this it may not appear. */
9325 			if (event->canvas_item)
9326 				gnome_canvas_item_show (event->canvas_item);
9327 
9328 			e_cal_component_commit_sequence (comp);
9329 			if (e_cal_component_has_recurrences (comp)) {
9330 				if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
9331 					gtk_widget_queue_draw (day_view->top_canvas);
9332 					g_object_unref (comp);
9333 					return;
9334 				}
9335 
9336 				if (mod == E_CAL_OBJ_MOD_THIS) {
9337 					e_cal_component_set_rdates (comp, NULL);
9338 					e_cal_component_set_rrules (comp, NULL);
9339 					e_cal_component_set_exdates (comp, NULL);
9340 					e_cal_component_set_exrules (comp, NULL);
9341 				}
9342 			} else if (e_cal_component_is_instance (comp))
9343 				mod = E_CAL_OBJ_MOD_THIS;
9344 
9345 			e_cal_component_commit_sequence (comp);
9346 
9347 			e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp), mod,
9348 				(send == GTK_RESPONSE_YES ? E_CAL_OPS_SEND_FLAG_SEND : E_CAL_OPS_SEND_FLAG_DONT_SEND) |
9349 				(strip_alarms ? E_CAL_OPS_SEND_FLAG_STRIP_ALARMS : 0) |
9350 				(only_new_attendees ? E_CAL_OPS_SEND_FLAG_ONLY_NEW_ATTENDEES : 0));
9351 
9352 			g_object_unref (comp);
9353 
9354 			return;
9355 		}
9356 	}
9357 
9358 	if (length >= 0 && format == 8 && !drag_from_same_window) {
9359 		/* We are dragging between different window */
9360 		ICalComponent *icomp;
9361 		ICalComponentKind kind;
9362 
9363 		pos = e_day_view_convert_position_in_top_canvas (
9364 			day_view,
9365 			x, y, &day,
9366 			NULL);
9367 		if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
9368 			goto error;
9369 
9370 		icomp = i_cal_parser_parse_string ((const gchar *) data);
9371 		if (!icomp)
9372 			goto error;
9373 
9374 		/* check the type of the component */
9375 		kind = i_cal_component_isa (icomp);
9376 		g_clear_object (&icomp);
9377 
9378 		if (kind != I_CAL_VCALENDAR_COMPONENT && kind != I_CAL_VEVENT_COMPONENT)
9379 			goto error;
9380 
9381 		e_cal_ops_paste_components (model, (const gchar *) data);
9382 
9383 		gtk_drag_finish (context, TRUE, TRUE, time);
9384 		return;
9385 	}
9386 
9387 error:
9388 	gtk_drag_finish (context, FALSE, FALSE, time);
9389 }
9390 
9391 static void
e_day_view_on_main_canvas_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time,EDayView * day_view)9392 e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget,
9393                                               GdkDragContext *context,
9394                                               gint x,
9395                                               gint y,
9396                                               GtkSelectionData *selection_data,
9397                                               guint info,
9398                                               guint time,
9399                                               EDayView *day_view)
9400 {
9401 	ECalendarView *cal_view;
9402 	EDayViewEvent *event = NULL;
9403 	ECalendarViewPosition pos;
9404 	gint time_divisions;
9405 	gint day, row, start_row, end_row, num_rows, scroll_x, scroll_y;
9406 	gint start_offset, end_offset;
9407 	ECalModel *model;
9408 	ECalComponent *comp;
9409 	ESourceRegistry *registry;
9410 	time_t dt;
9411 	gboolean drag_from_same_window;
9412 	const guchar *data;
9413 	gint format, length;
9414 	GtkResponseType send = GTK_RESPONSE_NO;
9415 	gboolean only_new_attendees = FALSE;
9416 	gboolean strip_alarms = TRUE;
9417 
9418 	cal_view = E_CALENDAR_VIEW (day_view);
9419 	model = e_calendar_view_get_model (cal_view);
9420 	time_divisions = e_calendar_view_get_time_divisions (cal_view);
9421 
9422 	registry = e_cal_model_get_registry (model);
9423 
9424 	data = gtk_selection_data_get_data (selection_data);
9425 	format = gtk_selection_data_get_format (selection_data);
9426 	length = gtk_selection_data_get_length (selection_data);
9427 
9428 	if (day_view->drag_event_day != -1)
9429 		drag_from_same_window = TRUE;
9430 	else
9431 		drag_from_same_window = FALSE;
9432 
9433 	gnome_canvas_get_scroll_offsets (
9434 		GNOME_CANVAS (widget),
9435 		&scroll_x, &scroll_y);
9436 	x += scroll_x;
9437 	y += scroll_y;
9438 
9439 	/* Note that we only support DnD within the EDayView at present. */
9440 	if (length >= 0 && format == 8 && (day_view->drag_event_day != -1)) {
9441 		/* We are dragging in the same window */
9442 
9443 		pos = e_day_view_convert_position_in_main_canvas (
9444 			day_view,
9445 			x, y, &day,
9446 			&row, NULL);
9447 		if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
9448 			ECalComponentDateTime *date;
9449 			ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
9450 			ECalClient *client;
9451 			GtkWindow *toplevel;
9452 			ICalTimezone *zone;
9453 
9454 			num_rows = 1;
9455 			start_offset = 0;
9456 			end_offset = 0;
9457 
9458 			if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
9459 				if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num))
9460 					return;
9461 
9462 				event = &g_array_index (day_view->long_events, EDayViewEvent,
9463 							day_view->drag_event_num);
9464 
9465 				if (!is_comp_data_valid (event))
9466 					return;
9467 			} else {
9468 				if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num))
9469 					return;
9470 
9471 				event = &g_array_index (day_view->events[day_view->drag_event_day],
9472 							EDayViewEvent,
9473 							day_view->drag_event_num);
9474 
9475 				if (!is_comp_data_valid (event))
9476 					return;
9477 
9478 				row -= day_view->drag_event_offset;
9479 
9480 				/* Calculate time offset from start row. */
9481 				start_row = event->start_minute / time_divisions;
9482 				end_row = (event->end_minute - 1) / time_divisions;
9483 				if (end_row < start_row)
9484 					end_row = start_row;
9485 
9486 				num_rows = end_row - start_row + 1;
9487 
9488 				start_offset = event->start_minute % time_divisions;
9489 				end_offset = event->end_minute % time_divisions;
9490 				if (end_offset != 0)
9491 					end_offset = time_divisions - end_offset;
9492 			}
9493 
9494 			client = event->comp_data->client;
9495 
9496 			/* We use a temporary shallow copy of comp since we
9497 			 * don't want to change the original comp here.
9498 			 * Otherwise we would not detect that the event's time
9499 			 * had changed in the "update_event" callback. */
9500 			comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
9501 			if (!comp)
9502 				return;
9503 
9504 			if (e_cal_component_has_attendees (comp) &&
9505 			    !itip_organizer_is_user (registry, comp, client)) {
9506 				g_object_unref (comp);
9507 				return;
9508 			}
9509 
9510 			toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view)));
9511 
9512 			if (itip_has_any_attendees (comp) &&
9513 			    (itip_organizer_is_user (registry, comp, client) ||
9514 			     itip_sentby_is_user (registry, comp, client)))
9515 				send = e_cal_dialogs_send_dragged_or_resized_component (
9516 						toplevel, client, comp, &strip_alarms, &only_new_attendees);
9517 
9518 			if (send == GTK_RESPONSE_CANCEL) {
9519 				e_day_view_abort_resize (day_view);
9520 				g_object_unref (comp);
9521 				return;
9522 			}
9523 
9524 			zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view));
9525 
9526 			dt = e_day_view_convert_grid_position_to_time (day_view, day, row) + start_offset * 60;
9527 			date = e_cal_component_datetime_new_take (
9528 				i_cal_time_new_from_timet_with_zone (dt, FALSE, zone),
9529 				zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
9530 			cal_comp_set_dtstart_with_oldzone (client, comp, date);
9531 			e_cal_component_datetime_free (date);
9532 
9533 			dt = e_day_view_convert_grid_position_to_time (day_view, day, row + num_rows) - end_offset * 60;
9534 			date = e_cal_component_datetime_new_take (
9535 				i_cal_time_new_from_timet_with_zone (dt, FALSE, zone),
9536 				zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
9537 			cal_comp_set_dtend_with_oldzone (client, comp, date);
9538 			e_cal_component_datetime_free (date);
9539 
9540 			e_cal_component_abort_sequence (comp);
9541 
9542 			gtk_drag_finish (context, TRUE, TRUE, time);
9543 
9544 			/* Reset this since it will be invalid. */
9545 			day_view->drag_event_day = -1;
9546 			g_clear_object (&day_view->priv->drag_context);
9547 
9548 			/* Show the text item again, just in case it hasn't
9549 			 * moved. If we don't do this it may not appear. */
9550 			if (event->canvas_item)
9551 				gnome_canvas_item_show (event->canvas_item);
9552 
9553 			e_cal_component_commit_sequence (comp);
9554 			if (e_cal_component_has_recurrences (comp)) {
9555 				if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
9556 					gtk_widget_queue_draw (day_view->main_canvas);
9557 					g_object_unref (comp);
9558 					return;
9559 				}
9560 
9561 				if (mod == E_CAL_OBJ_MOD_THIS) {
9562 					e_cal_component_set_rdates (comp, NULL);
9563 					e_cal_component_set_rrules (comp, NULL);
9564 					e_cal_component_set_exdates (comp, NULL);
9565 					e_cal_component_set_exrules (comp, NULL);
9566 				}
9567 			} else if (e_cal_component_is_instance (comp))
9568 				mod = E_CAL_OBJ_MOD_THIS;
9569 
9570 			e_cal_component_commit_sequence (comp);
9571 
9572 			e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp), mod,
9573 				(send == GTK_RESPONSE_YES ? E_CAL_OPS_SEND_FLAG_SEND : E_CAL_OPS_SEND_FLAG_DONT_SEND) |
9574 				(strip_alarms ? E_CAL_OPS_SEND_FLAG_STRIP_ALARMS : 0) |
9575 				(only_new_attendees ? E_CAL_OPS_SEND_FLAG_ONLY_NEW_ATTENDEES : 0));
9576 
9577 			g_object_unref (comp);
9578 
9579 			return;
9580 		}
9581 	}
9582 
9583 	if (length >= 0 && format == 8 && !drag_from_same_window) {
9584 		/* We are dragging between different window */
9585 		ICalComponent *icomp;
9586 		ICalComponentKind kind;
9587 
9588 		pos = e_day_view_convert_position_in_main_canvas (
9589 			day_view,
9590 			x, y, &day,
9591 			&row, NULL);
9592 		if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
9593 			goto error;
9594 
9595 		icomp = i_cal_parser_parse_string ((const gchar *) data);
9596 		if (!icomp)
9597 			goto error;
9598 
9599 		/* check the type of the component */
9600 		kind = i_cal_component_isa (icomp);
9601 		g_object_unref (&icomp);
9602 
9603 		if (kind != I_CAL_VCALENDAR_COMPONENT && kind != I_CAL_VEVENT_COMPONENT)
9604 			goto error;
9605 
9606 		e_cal_ops_paste_components (model, (const gchar *) data);
9607 
9608 		gtk_drag_finish (context, TRUE, TRUE, time);
9609 		return;
9610 	}
9611 
9612 error:
9613 	gtk_drag_finish (context, FALSE, FALSE, time);
9614 }
9615 
9616 /* Converts an hour from 0-23 to the preferred time format, and returns the
9617  * suffix to add and the width of it in the normal font. */
9618 void
e_day_view_convert_time_to_display(EDayView * day_view,gint hour,gint * display_hour,const gchar ** suffix,gint * suffix_width)9619 e_day_view_convert_time_to_display (EDayView *day_view,
9620                                     gint hour,
9621                                     gint *display_hour,
9622                                     const gchar **suffix,
9623                                     gint *suffix_width)
9624 {
9625 	ECalModel *model;
9626 
9627 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
9628 
9629 	/* Calculate the actual hour number to display. For 12-hour
9630 	 * format we convert 0-23 to 12-11am/12-11pm. */
9631 	*display_hour = hour;
9632 	if (e_cal_model_get_use_24_hour_format (model)) {
9633 		*suffix = "";
9634 		*suffix_width = 0;
9635 	} else {
9636 		if (hour < 12) {
9637 			*suffix = day_view->am_string;
9638 			*suffix_width = day_view->am_string_width;
9639 		} else {
9640 			*display_hour -= 12;
9641 			*suffix = day_view->pm_string;
9642 			*suffix_width = day_view->pm_string_width;
9643 		}
9644 
9645 		/* 12-hour uses 12:00 rather than 0:00. */
9646 		if (*display_hour == 0)
9647 			*display_hour = 12;
9648 	}
9649 }
9650 
9651 gint
e_day_view_get_time_string_width(EDayView * day_view)9652 e_day_view_get_time_string_width (EDayView *day_view)
9653 {
9654 	ECalModel *model;
9655 	gint time_width;
9656 
9657 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
9658 	time_width = day_view->digit_width * 4 + day_view->colon_width;
9659 
9660 	if (!e_cal_model_get_use_24_hour_format (model))
9661 		time_width += MAX (day_view->am_string_width,
9662 				   day_view->pm_string_width);
9663 
9664 	return time_width;
9665 }
9666 
9667 /* Queues a layout, unless one is already queued. */
9668 static void
e_day_view_queue_layout(EDayView * day_view)9669 e_day_view_queue_layout (EDayView *day_view)
9670 {
9671 	if (day_view->layout_timeout_id == 0) {
9672 		day_view->layout_timeout_id = e_named_timeout_add (
9673 			E_DAY_VIEW_LAYOUT_TIMEOUT,
9674 			e_day_view_layout_timeout_cb, day_view);
9675 	}
9676 }
9677 
9678 /* Removes any queued layout. */
9679 static void
e_day_view_cancel_layout(EDayView * day_view)9680 e_day_view_cancel_layout (EDayView *day_view)
9681 {
9682 	if (day_view->layout_timeout_id != 0) {
9683 		g_source_remove (day_view->layout_timeout_id);
9684 		day_view->layout_timeout_id = 0;
9685 	}
9686 }
9687 
9688 static gboolean
e_day_view_layout_timeout_cb(gpointer data)9689 e_day_view_layout_timeout_cb (gpointer data)
9690 {
9691 	EDayView *day_view = E_DAY_VIEW (data);
9692 
9693 	gtk_widget_queue_draw (day_view->top_canvas);
9694 	gtk_widget_queue_draw (day_view->top_dates_canvas);
9695 	gtk_widget_queue_draw (day_view->main_canvas);
9696 	e_day_view_check_layout (day_view);
9697 
9698 	day_view->layout_timeout_id = 0;
9699 	return FALSE;
9700 }
9701 
9702 /* Returns the number of selected events (0 or 1 at present). */
9703 gint
e_day_view_get_num_events_selected(EDayView * day_view)9704 e_day_view_get_num_events_selected (EDayView *day_view)
9705 {
9706 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), 0);
9707 
9708 	return (day_view->editing_event_day != -1) ? 1 : 0;
9709 }
9710 
9711 gboolean
e_day_view_is_editing(EDayView * day_view)9712 e_day_view_is_editing (EDayView *day_view)
9713 {
9714 	g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE);
9715 
9716 	return day_view->editing_event_day != -1;
9717 }
9718 
9719 static void
day_view_update_timezone_name_label(GtkWidget * label,ICalTimezone * zone)9720 day_view_update_timezone_name_label (GtkWidget *label,
9721 				     ICalTimezone *zone)
9722 {
9723 	const gchar *location, *dash;
9724 	gchar *markup;
9725 
9726 	g_return_if_fail (GTK_IS_LABEL (label));
9727 
9728 	if (!zone) {
9729 		location = NULL;
9730 	} else {
9731 		location = i_cal_timezone_get_location (zone);
9732 		if (location && *location)
9733 			location = _(location);
9734 		if (!location || !*location)
9735 			location = i_cal_timezone_get_tzid (zone);
9736 	}
9737 
9738 	if (!location)
9739 		location = "";
9740 
9741 	gtk_widget_set_tooltip_text (label, location);
9742 
9743 	dash = strrchr (location, '/');
9744 	if (dash && *dash && dash[1])
9745 		location = dash + 1;
9746 
9747 	markup = g_markup_printf_escaped ("<small>%s</small>", location);
9748 	gtk_label_set_markup (GTK_LABEL (label), markup);
9749 	g_free (markup);
9750 }
9751 
9752 void
e_day_view_update_timezone_name_labels(EDayView * day_view)9753 e_day_view_update_timezone_name_labels (EDayView *day_view)
9754 {
9755 	ICalTimezone *zone;
9756 
9757 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
9758 
9759 	zone = e_cal_model_get_timezone (day_view->priv->model);
9760 	day_view_update_timezone_name_label (day_view->priv->timezone_name_1_label, zone);
9761 
9762 	zone = e_day_view_time_item_get_second_zone (E_DAY_VIEW_TIME_ITEM (day_view->time_canvas_item));
9763 	if (!zone) {
9764 		gtk_widget_hide (day_view->priv->timezone_name_2_label);
9765 	} else {
9766 		day_view_update_timezone_name_label (day_view->priv->timezone_name_2_label, zone);
9767 		gtk_widget_show (day_view->priv->timezone_name_2_label);
9768 	}
9769 }
9770