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