Bug 793125 - Crash due to popup menus left attached too long
[evolution.git] / src / calendar / gui / e-day-view.c
blob63c01610d72e0b27d15bc39211cfd15027173bd9
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.
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.
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.
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/>.
17 * Authors:
18 * Damon Chaplin <damon@ximian.com>
19 * Rodrigo Moya <rodrigo@ximian.com>
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
24 #include "evolution-config.h"
26 #include "e-day-view.h"
28 #include <math.h>
29 #include <time.h>
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
34 #include "libgnomecanvas/libgnomecanvas.h"
36 #include "e-cal-dialogs.h"
37 #include "e-util/e-util.h"
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"
53 #define E_DAY_VIEW_GET_PRIVATE(obj) \
54 (G_TYPE_INSTANCE_GET_PRIVATE \
55 ((obj), E_TYPE_DAY_VIEW, EDayViewPrivate))
57 /* The minimum amount of space wanted on each side of the date string. */
58 #define E_DAY_VIEW_DATE_X_PAD 4
60 #define E_DAY_VIEW_LARGE_FONT_PTSIZE 18
61 #define E_DAY_VIEW_SMALL_FONT_PTSIZE 10
63 /* The offset from the top/bottom of the canvas before auto-scrolling starts.*/
64 #define E_DAY_VIEW_AUTO_SCROLL_OFFSET 16
66 /* The time between each auto-scroll, in milliseconds. */
67 #define E_DAY_VIEW_AUTO_SCROLL_TIMEOUT 50
69 /* The number of timeouts we skip before we start scrolling. */
70 #define E_DAY_VIEW_AUTO_SCROLL_DELAY 5
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
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
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
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
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;
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;
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;
148 /* "time_canvas" signal handlers */
149 gulong time_canvas_scroll_event_handler_id;
151 /* Whether we are showing the work-week view. */
152 gboolean work_week_view;
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;
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];
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;
169 GtkWidget *timezone_name_1_label; /* not referenced */
170 GtkWidget *timezone_name_2_label; /* not referenced */
172 GdkDragContext *drag_context;
174 gboolean draw_flat_events;
177 typedef struct {
178 EDayView *day_view;
179 ECalModelComponent *comp_data;
180 } AddEventData;
182 /* Drag and Drop stuff. */
183 static GtkTargetEntry target_table[] = {
184 { (gchar *) "application/x-e-calendar-event", 0, 0 }
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);
228 static void e_day_view_top_scroll (EDayView *day_view,
229 gfloat pages_to_scroll);
231 static void e_day_view_update_top_scroll (EDayView *day_view, gboolean scroll_to_top);
233 static void e_day_view_on_canvas_realized (GtkWidget *widget,
234 EDayView *day_view);
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);
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);
253 static gboolean e_day_view_on_top_canvas_scroll (GtkWidget *widget,
254 GdkEventScroll *scroll,
255 EDayView *day_view);
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);
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);
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);
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);
340 typedef gboolean (* EDayViewForeachEventCallback) (EDayView *day_view,
341 gint day,
342 gint event_num,
343 gpointer data);
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);
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);
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);
378 static void e_day_view_ensure_events_sorted (EDayView *day_view);
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);
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);
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);
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);
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);
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);
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
501 G_DEFINE_TYPE (EDayView, e_day_view, E_TYPE_CALENDAR_VIEW)
503 static void
504 day_view_notify_time_divisions_cb (EDayView *day_view)
506 gint day;
508 e_day_view_recalc_num_rows (day_view);
510 /* If we aren't visible, we'll sort it out later. */
511 if (!E_CALENDAR_VIEW (day_view)->in_focus) {
512 e_day_view_free_events (day_view);
513 day_view->requires_update = TRUE;
514 return;
517 for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
518 day_view->need_layout[day] = TRUE;
520 /* We need to update all the day event labels since the start & end
521 * times may or may not be on row boundaries any more. */
522 e_day_view_foreach_event (day_view,
523 e_day_view_set_show_times_cb, NULL);
525 /* We must layout the events before updating the scroll region, since
526 * that will result in a redraw which would crash otherwise. */
527 e_day_view_check_layout (day_view);
528 gtk_widget_queue_draw (day_view->time_canvas);
529 gtk_widget_queue_draw (day_view->main_canvas);
531 e_day_view_update_scroll_regions (day_view);
534 static void
535 day_view_notify_week_start_day_cb (EDayView *day_view)
537 /* FIXME Write an EWorkWeekView subclass, like EMonthView. */
539 if (day_view->priv->work_week_view)
540 e_day_view_recalc_work_week (day_view);
543 static void
544 day_view_notify_work_day_cb (ECalModel *model,
545 GParamSpec *pspec,
546 EDayView *day_view)
548 /* FIXME Write an EWorkWeekView subclass, like EMonthView. */
550 if (day_view->priv->work_week_view)
551 e_day_view_recalc_work_week (day_view);
553 /* We have to do this, as the new working days may have no effect on
554 * the days shown, but we still want the background color to change. */
555 gtk_widget_queue_draw (day_view->main_canvas);
558 static void
559 e_day_view_get_work_day_range_for_day (EDayView *day_view,
560 gint day,
561 gint *start_hour,
562 gint *start_minute,
563 gint *end_hour,
564 gint *end_minute)
566 ECalModel *model;
568 g_return_if_fail (E_IS_DAY_VIEW (day_view));
569 g_return_if_fail (start_hour != NULL);
570 g_return_if_fail (start_minute != NULL);
571 g_return_if_fail (end_hour != NULL);
572 g_return_if_fail (end_minute != NULL);
574 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
576 if (day >= 0 && day < e_day_view_get_days_shown (day_view)) {
577 GDateWeekday weekday;
578 struct icaltimetype tt;
580 tt = icaltime_from_timet_with_zone (day_view->day_starts[day], FALSE,
581 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
583 switch (icaltime_day_of_week (tt)) {
584 case 1:
585 weekday = G_DATE_SUNDAY;
586 break;
587 case 2:
588 weekday = G_DATE_MONDAY;
589 break;
590 case 3:
591 weekday = G_DATE_TUESDAY;
592 break;
593 case 4:
594 weekday = G_DATE_WEDNESDAY;
595 break;
596 case 5:
597 weekday = G_DATE_THURSDAY;
598 break;
599 case 6:
600 weekday = G_DATE_FRIDAY;
601 break;
602 case 7:
603 weekday = G_DATE_SATURDAY;
604 break;
605 default:
606 weekday = G_DATE_BAD_WEEKDAY;
607 break;
610 e_cal_model_get_work_day_range_for (model, weekday,
611 start_hour, start_minute,
612 end_hour, end_minute);
613 } else {
614 *start_hour = e_cal_model_get_work_day_start_hour (model);
615 *start_minute = e_cal_model_get_work_day_start_minute (model);
616 *end_hour = e_cal_model_get_work_day_end_hour (model);
617 *end_minute = e_cal_model_get_work_day_end_minute (model);
621 static void
622 e_day_view_recalc_main_canvas_size (EDayView *day_view)
624 gint day, scroll_y;
625 gboolean need_reshape;
627 /* Set the scroll region of the top canvas */
628 e_day_view_update_top_scroll (day_view, TRUE);
630 need_reshape = e_day_view_update_scroll_regions (day_view);
632 e_day_view_recalc_cell_sizes (day_view);
634 /* Scroll to the start of the working day, if this is the initial
635 * allocation. */
636 if (day_view->scroll_to_work_day) {
637 gint work_day_start_hour;
638 gint work_day_start_minute;
639 gint work_day_end_hour;
640 gint work_day_end_minute;
642 e_day_view_get_work_day_range_for_day (day_view, 0,
643 &work_day_start_hour, &work_day_start_minute,
644 &work_day_end_hour, &work_day_end_minute);
646 scroll_y = e_day_view_convert_time_to_position (
647 day_view, work_day_start_hour, work_day_start_minute);
648 gnome_canvas_scroll_to (
649 GNOME_CANVAS (day_view->main_canvas), 0, scroll_y);
650 day_view->scroll_to_work_day = FALSE;
653 /* Flag that we need to reshape the events. Note that changes in height
654 * don't matter, since the rows are always the same height. */
655 if (need_reshape) {
656 day_view->long_events_need_reshape = TRUE;
657 for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
658 day_view->need_reshape[day] = TRUE;
660 e_day_view_check_layout (day_view);
664 static GdkColor
665 e_day_view_get_text_color (EDayView *day_view,
666 EDayViewEvent *event)
668 GdkColor color;
669 GdkRGBA rgba;
671 if (is_comp_data_valid (event) &&
672 e_cal_model_get_rgba_for_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), event->comp_data, &rgba)) {
673 } else {
674 gdouble cc = 65535.0;
676 rgba.red = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].red / cc;
677 rgba.green = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].green / cc;
678 rgba.blue = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].blue / cc;
679 rgba.alpha = 1.0;
682 if ((rgba.red > 0.7) || (rgba.green > 0.7) || (rgba.blue > 0.7)) {
683 color.red = 0.0;
684 color.green = 0.0;
685 color.blue = 0.0;
686 } else {
687 color.red = 65535.0f;
688 color.green = 65535.0f;
689 color.blue = 65535.0f;
692 color.pixel = 0;
694 return color;
697 /* Returns the selected time range. */
698 static gboolean
699 day_view_get_selected_time_range (ECalendarView *cal_view,
700 time_t *start_time,
701 time_t *end_time)
703 gint start_col, start_row, end_col, end_row;
704 time_t start, end;
705 EDayView *day_view = E_DAY_VIEW (cal_view);
707 start_col = day_view->selection_start_day;
708 start_row = day_view->selection_start_row;
709 end_col = day_view->selection_end_day;
710 end_row = day_view->selection_end_row;
712 if (start_col == -1) {
713 start_col = 0;
714 start_row = 0;
715 end_col = 0;
716 end_row = 0;
719 /* Check if the selection is only in the top canvas, in which case
720 * we can simply use the day_starts array. */
721 if (day_view->selection_in_top_canvas) {
722 start = day_view->day_starts[start_col];
723 end = day_view->day_starts[end_col + 1];
724 } else {
725 /* Convert the start col + row into a time. */
726 start = e_day_view_convert_grid_position_to_time (day_view, start_col, start_row);
727 end = e_day_view_convert_grid_position_to_time (day_view, end_col, end_row + 1);
730 if (start_time)
731 *start_time = start;
733 if (end_time)
734 *end_time = end;
736 return TRUE;
739 typedef struct {
740 EDayView *day_view;
741 GdkEventKey *key_event;
742 time_t dtstart, dtend;
743 gboolean in_top_canvas;
744 gboolean paste_clipboard;
745 } NewEventInRangeData;
747 static void
748 new_event_in_rage_data_free (gpointer ptr)
750 NewEventInRangeData *ned = ptr;
752 if (ned) {
753 g_clear_object (&ned->day_view);
754 g_free (ned->key_event);
755 g_free (ned);
759 static void
760 day_view_new_event_in_selected_range_cb (ECalModel *model,
761 ECalClient *client,
762 icalcomponent *default_component,
763 gpointer user_data)
765 NewEventInRangeData *ned = user_data;
766 ECalComponent *comp = NULL;
767 gint day, event_num;
768 ECalComponentDateTime start_dt, end_dt;
769 struct icaltimetype start_tt, end_tt;
770 const gchar *uid;
771 AddEventData add_event_data;
772 ESourceRegistry *registry;
773 icaltimezone *zone;
775 g_return_if_fail (ned != NULL);
776 g_return_if_fail (E_IS_CAL_MODEL (model));
777 g_return_if_fail (E_IS_CAL_CLIENT (client));
778 g_return_if_fail (default_component != NULL);
780 /* Check if the client is read only */
781 if (e_client_is_readonly (E_CLIENT (client)))
782 return;
784 registry = e_cal_model_get_registry (model);
785 zone = e_cal_model_get_timezone (model);
786 uid = icalcomponent_get_uid (default_component);
788 comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (default_component));
789 g_return_if_fail (comp != NULL);
791 start_tt = icaltime_from_timet_with_zone (ned->dtstart, FALSE, zone);
792 end_tt = icaltime_from_timet_with_zone (ned->dtend, FALSE, zone);
794 if (ned->in_top_canvas) {
795 start_dt.tzid = NULL;
796 start_tt.is_date = 1;
797 end_tt.is_date = 1;
799 /* Editor default in day/work-week view - top canvas */
800 e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
801 } else {
802 start_dt.tzid = icaltimezone_get_tzid (zone);
804 /* Editor default in day/work-week view - main canvas */
805 e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
808 start_dt.value = &start_tt;
809 end_dt.value = &end_tt;
810 end_dt.tzid = start_dt.tzid;
811 e_cal_component_set_dtstart (comp, &start_dt);
812 e_cal_component_set_dtend (comp, &end_dt);
814 /* We add the event locally and start editing it. We don't send it
815 * to the server until the user finishes editing it. */
816 add_event_data.day_view = ned->day_view;
817 add_event_data.comp_data = NULL;
818 e_day_view_add_event (registry, client, comp, ned->dtstart, ned->dtend, &add_event_data);
819 e_day_view_check_layout (ned->day_view);
820 gtk_widget_queue_draw (ned->day_view->top_canvas);
821 gtk_widget_queue_draw (ned->day_view->main_canvas);
823 if (!e_day_view_find_event_from_uid (ned->day_view, client, uid, NULL, &day, &event_num)) {
824 g_warning ("Couldn't find event to start editing.\n");
825 } else {
826 e_day_view_start_editing_event (ned->day_view, day, event_num, ned->key_event);
828 if (ned->paste_clipboard) {
829 EDayViewEvent *event;
831 g_clear_object (&comp);
833 if (ned->day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT) {
834 if (!is_array_index_in_bounds (ned->day_view->long_events, ned->day_view->editing_event_num))
835 goto out;
837 event = &g_array_index (ned->day_view->long_events,
838 EDayViewEvent,
839 ned->day_view->editing_event_num);
840 } else {
841 if (!is_array_index_in_bounds (ned->day_view->events[ned->day_view->editing_event_day], ned->day_view->editing_event_num))
842 goto out;
844 event = &g_array_index (ned->day_view->events[ned->day_view->editing_event_day],
845 EDayViewEvent,
846 ned->day_view->editing_event_num);
849 if (event->canvas_item &&
850 E_IS_TEXT (event->canvas_item) &&
851 E_TEXT (event->canvas_item)->editing) {
852 e_text_paste_clipboard (E_TEXT (event->canvas_item));
857 out:
858 g_clear_object (&comp);
861 static void
862 e_day_view_add_new_event_in_selected_range (EDayView *day_view,
863 GdkEventKey *key_event,
864 gboolean paste_clipboard)
866 NewEventInRangeData *ned;
867 ECalModel *model;
868 const gchar *source_uid;
870 ned = g_new0 (NewEventInRangeData, 1);
871 ned->day_view = g_object_ref (day_view);
872 if (key_event) {
873 ned->key_event = g_new0 (GdkEventKey, 1);
874 *ned->key_event = *key_event;
876 day_view_get_selected_time_range (E_CALENDAR_VIEW (day_view), &ned->dtstart, &ned->dtend);
877 ned->in_top_canvas = day_view->selection_in_top_canvas;
878 ned->paste_clipboard = paste_clipboard;
880 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
881 source_uid = e_cal_model_get_default_source_uid (model);
883 e_cal_ops_get_default_component (model, source_uid, ned->in_top_canvas,
884 day_view_new_event_in_selected_range_cb, ned, new_event_in_rage_data_free);
887 static void
888 day_view_set_property (GObject *object,
889 guint property_id,
890 const GValue *value,
891 GParamSpec *pspec)
893 switch (property_id) {
894 case PROP_DRAW_FLAT_EVENTS:
895 e_day_view_set_draw_flat_events (
896 E_DAY_VIEW (object),
897 g_value_get_boolean (value));
898 return;
900 case PROP_MARCUS_BAINS_SHOW_LINE:
901 e_day_view_marcus_bains_set_show_line (
902 E_DAY_VIEW (object),
903 g_value_get_boolean (value));
904 return;
906 case PROP_MARCUS_BAINS_DAY_VIEW_COLOR:
907 e_day_view_marcus_bains_set_day_view_color (
908 E_DAY_VIEW (object),
909 g_value_get_string (value));
910 return;
912 case PROP_MARCUS_BAINS_TIME_BAR_COLOR:
913 e_day_view_marcus_bains_set_time_bar_color (
914 E_DAY_VIEW (object),
915 g_value_get_string (value));
916 return;
919 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
922 static void
923 day_view_get_property (GObject *object,
924 guint property_id,
925 GValue *value,
926 GParamSpec *pspec)
928 switch (property_id) {
929 case PROP_DRAW_FLAT_EVENTS:
930 g_value_set_boolean (
931 value,
932 e_day_view_get_draw_flat_events (
933 E_DAY_VIEW (object)));
934 return;
936 case PROP_MARCUS_BAINS_SHOW_LINE:
937 g_value_set_boolean (
938 value,
939 e_day_view_marcus_bains_get_show_line (
940 E_DAY_VIEW (object)));
941 return;
943 case PROP_MARCUS_BAINS_DAY_VIEW_COLOR:
944 g_value_set_string (
945 value,
946 e_day_view_marcus_bains_get_day_view_color (
947 E_DAY_VIEW (object)));
948 return;
950 case PROP_MARCUS_BAINS_TIME_BAR_COLOR:
951 g_value_set_string (
952 value,
953 e_day_view_marcus_bains_get_time_bar_color (
954 E_DAY_VIEW (object)));
955 return;
957 case PROP_IS_EDITING:
958 g_value_set_boolean (value, e_day_view_is_editing (E_DAY_VIEW (object)));
959 return;
962 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
965 static void
966 day_view_dispose (GObject *object)
968 EDayView *day_view;
969 gint day;
971 day_view = E_DAY_VIEW (object);
973 e_day_view_cancel_layout (day_view);
975 e_day_view_stop_auto_scroll (day_view);
977 if (day_view->large_font_desc) {
978 pango_font_description_free (day_view->large_font_desc);
979 day_view->large_font_desc = NULL;
982 if (day_view->small_font_desc) {
983 pango_font_description_free (day_view->small_font_desc);
984 day_view->small_font_desc = NULL;
987 if (day_view->normal_cursor) {
988 g_object_unref (day_view->normal_cursor);
989 day_view->normal_cursor = NULL;
991 if (day_view->move_cursor) {
992 g_object_unref (day_view->move_cursor);
993 day_view->move_cursor = NULL;
995 if (day_view->resize_width_cursor) {
996 g_object_unref (day_view->resize_width_cursor);
997 day_view->resize_width_cursor = NULL;
999 if (day_view->resize_height_cursor) {
1000 g_object_unref (day_view->resize_height_cursor);
1001 day_view->resize_height_cursor = NULL;
1004 if (day_view->long_events) {
1005 e_day_view_free_events (day_view);
1006 g_array_free (day_view->long_events, TRUE);
1007 day_view->long_events = NULL;
1010 for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++) {
1011 if (day_view->events[day]) {
1012 g_array_free (day_view->events[day], TRUE);
1013 day_view->events[day] = NULL;
1017 if (day_view->grabbed_pointer != NULL) {
1018 gdk_device_ungrab (
1019 day_view->grabbed_pointer,
1020 GDK_CURRENT_TIME);
1021 g_object_unref (day_view->grabbed_pointer);
1022 day_view->grabbed_pointer = NULL;
1025 #define disconnect_model_handler(x) G_STMT_START { \
1026 if ((x) > 0) { \
1027 g_signal_handler_disconnect (day_view->priv->model, (x)); \
1028 (x) = 0; \
1030 } G_STMT_END
1032 disconnect_model_handler (day_view->priv->notify_work_day_monday_handler_id);
1033 disconnect_model_handler (day_view->priv->notify_work_day_tuesday_handler_id);
1034 disconnect_model_handler (day_view->priv->notify_work_day_wednesday_handler_id);
1035 disconnect_model_handler (day_view->priv->notify_work_day_thursday_handler_id);
1036 disconnect_model_handler (day_view->priv->notify_work_day_friday_handler_id);
1037 disconnect_model_handler (day_view->priv->notify_work_day_saturday_handler_id);
1038 disconnect_model_handler (day_view->priv->notify_work_day_sunday_handler_id);
1039 disconnect_model_handler (day_view->priv->notify_week_start_day_handler_id);
1040 disconnect_model_handler (day_view->priv->notify_work_day_start_hour_handler_id);
1041 disconnect_model_handler (day_view->priv->notify_work_day_start_minute_handler_id);
1042 disconnect_model_handler (day_view->priv->notify_work_day_end_hour_handler_id);
1043 disconnect_model_handler (day_view->priv->notify_work_day_end_minute_handler_id);
1044 disconnect_model_handler (day_view->priv->notify_work_day_start_mon_handler_id);
1045 disconnect_model_handler (day_view->priv->notify_work_day_end_mon_handler_id);
1046 disconnect_model_handler (day_view->priv->notify_work_day_start_tue_handler_id);
1047 disconnect_model_handler (day_view->priv->notify_work_day_end_tue_handler_id);
1048 disconnect_model_handler (day_view->priv->notify_work_day_start_wed_handler_id);
1049 disconnect_model_handler (day_view->priv->notify_work_day_end_wed_handler_id);
1050 disconnect_model_handler (day_view->priv->notify_work_day_start_thu_handler_id);
1051 disconnect_model_handler (day_view->priv->notify_work_day_end_thu_handler_id);
1052 disconnect_model_handler (day_view->priv->notify_work_day_start_fri_handler_id);
1053 disconnect_model_handler (day_view->priv->notify_work_day_end_fri_handler_id);
1054 disconnect_model_handler (day_view->priv->notify_work_day_start_sat_handler_id);
1055 disconnect_model_handler (day_view->priv->notify_work_day_end_sat_handler_id);
1056 disconnect_model_handler (day_view->priv->notify_work_day_start_sun_handler_id);
1057 disconnect_model_handler (day_view->priv->notify_work_day_end_sun_handler_id);
1058 disconnect_model_handler (day_view->priv->time_range_changed_handler_id);
1059 disconnect_model_handler (day_view->priv->model_row_changed_handler_id);
1060 disconnect_model_handler (day_view->priv->model_cell_changed_handler_id);
1061 disconnect_model_handler (day_view->priv->model_rows_inserted_handler_id);
1062 disconnect_model_handler (day_view->priv->comps_deleted_handler_id);
1063 disconnect_model_handler (day_view->priv->timezone_changed_handler_id);
1065 #undef disconnect_model_handler
1067 if (day_view->priv->top_canvas_button_press_event_handler_id > 0) {
1068 g_signal_handler_disconnect (
1069 day_view->top_canvas,
1070 day_view->priv->top_canvas_button_press_event_handler_id);
1071 day_view->priv->top_canvas_button_press_event_handler_id = 0;
1074 if (day_view->priv->top_canvas_button_release_event_handler_id > 0) {
1075 g_signal_handler_disconnect (
1076 day_view->top_canvas,
1077 day_view->priv->top_canvas_button_release_event_handler_id);
1078 day_view->priv->top_canvas_button_release_event_handler_id = 0;
1081 if (day_view->priv->top_canvas_scroll_event_handler_id > 0) {
1082 g_signal_handler_disconnect (
1083 day_view->top_canvas,
1084 day_view->priv->top_canvas_scroll_event_handler_id);
1085 day_view->priv->top_canvas_scroll_event_handler_id = 0;
1088 if (day_view->priv->top_canvas_motion_notify_event_handler_id > 0) {
1089 g_signal_handler_disconnect (
1090 day_view->top_canvas,
1091 day_view->priv->top_canvas_motion_notify_event_handler_id);
1092 day_view->priv->top_canvas_motion_notify_event_handler_id = 0;
1095 if (day_view->priv->top_canvas_drag_motion_handler_id > 0) {
1096 g_signal_handler_disconnect (
1097 day_view->top_canvas,
1098 day_view->priv->top_canvas_drag_motion_handler_id);
1099 day_view->priv->top_canvas_drag_motion_handler_id = 0;
1102 if (day_view->priv->top_canvas_drag_leave_handler_id > 0) {
1103 g_signal_handler_disconnect (
1104 day_view->top_canvas,
1105 day_view->priv->top_canvas_drag_leave_handler_id);
1106 day_view->priv->top_canvas_drag_leave_handler_id = 0;
1109 if (day_view->priv->top_canvas_drag_begin_handler_id > 0) {
1110 g_signal_handler_disconnect (
1111 day_view->top_canvas,
1112 day_view->priv->top_canvas_drag_begin_handler_id);
1113 day_view->priv->top_canvas_drag_begin_handler_id = 0;
1116 if (day_view->priv->top_canvas_drag_end_handler_id > 0) {
1117 g_signal_handler_disconnect (
1118 day_view->top_canvas,
1119 day_view->priv->top_canvas_drag_end_handler_id);
1120 day_view->priv->top_canvas_drag_end_handler_id = 0;
1123 if (day_view->priv->top_canvas_drag_data_get_handler_id > 0) {
1124 g_signal_handler_disconnect (
1125 day_view->top_canvas,
1126 day_view->priv->top_canvas_drag_data_get_handler_id);
1127 day_view->priv->top_canvas_drag_data_get_handler_id = 0;
1130 if (day_view->priv->top_canvas_drag_data_received_handler_id > 0) {
1131 g_signal_handler_disconnect (
1132 day_view->top_canvas,
1133 day_view->priv->top_canvas_drag_data_received_handler_id);
1134 day_view->priv->top_canvas_drag_data_received_handler_id = 0;
1137 if (day_view->priv->main_canvas_realize_handler_id > 0) {
1138 g_signal_handler_disconnect (
1139 day_view->main_canvas,
1140 day_view->priv->main_canvas_realize_handler_id);
1141 day_view->priv->main_canvas_realize_handler_id = 0;
1144 if (day_view->priv->main_canvas_button_press_event_handler_id > 0) {
1145 g_signal_handler_disconnect (
1146 day_view->main_canvas,
1147 day_view->priv->main_canvas_button_press_event_handler_id);
1148 day_view->priv->main_canvas_button_press_event_handler_id = 0;
1151 if (day_view->priv->main_canvas_button_release_event_handler_id > 0) {
1152 g_signal_handler_disconnect (
1153 day_view->main_canvas,
1154 day_view->priv->main_canvas_button_release_event_handler_id);
1155 day_view->priv->main_canvas_button_release_event_handler_id = 0;
1158 if (day_view->priv->main_canvas_scroll_event_handler_id > 0) {
1159 g_signal_handler_disconnect (
1160 day_view->main_canvas,
1161 day_view->priv->main_canvas_scroll_event_handler_id);
1162 day_view->priv->main_canvas_scroll_event_handler_id = 0;
1165 if (day_view->priv->main_canvas_motion_notify_event_handler_id > 0) {
1166 g_signal_handler_disconnect (
1167 day_view->main_canvas,
1168 day_view->priv->main_canvas_motion_notify_event_handler_id);
1169 day_view->priv->main_canvas_motion_notify_event_handler_id = 0;
1172 if (day_view->priv->main_canvas_drag_motion_handler_id > 0) {
1173 g_signal_handler_disconnect (
1174 day_view->main_canvas,
1175 day_view->priv->main_canvas_drag_motion_handler_id);
1176 day_view->priv->main_canvas_drag_motion_handler_id = 0;
1179 if (day_view->priv->main_canvas_drag_leave_handler_id > 0) {
1180 g_signal_handler_disconnect (
1181 day_view->main_canvas,
1182 day_view->priv->main_canvas_drag_leave_handler_id);
1183 day_view->priv->main_canvas_drag_leave_handler_id = 0;
1186 if (day_view->priv->main_canvas_drag_begin_handler_id > 0) {
1187 g_signal_handler_disconnect (
1188 day_view->main_canvas,
1189 day_view->priv->main_canvas_drag_begin_handler_id);
1190 day_view->priv->main_canvas_drag_begin_handler_id = 0;
1193 if (day_view->priv->main_canvas_drag_end_handler_id > 0) {
1194 g_signal_handler_disconnect (
1195 day_view->main_canvas,
1196 day_view->priv->main_canvas_drag_end_handler_id);
1197 day_view->priv->main_canvas_drag_end_handler_id = 0;
1200 if (day_view->priv->main_canvas_drag_data_get_handler_id > 0) {
1201 g_signal_handler_disconnect (
1202 day_view->main_canvas,
1203 day_view->priv->main_canvas_drag_data_get_handler_id);
1204 day_view->priv->main_canvas_drag_data_get_handler_id = 0;
1207 if (day_view->priv->main_canvas_drag_data_received_handler_id > 0) {
1208 g_signal_handler_disconnect (
1209 day_view->main_canvas,
1210 day_view->priv->main_canvas_drag_data_received_handler_id);
1211 day_view->priv->main_canvas_drag_data_received_handler_id = 0;
1214 if (day_view->priv->time_canvas_scroll_event_handler_id > 0) {
1215 g_signal_handler_disconnect (
1216 day_view->time_canvas,
1217 day_view->priv->time_canvas_scroll_event_handler_id);
1218 day_view->priv->time_canvas_scroll_event_handler_id = 0;
1221 g_clear_object (&day_view->top_canvas);
1222 g_clear_object (&day_view->main_canvas);
1223 g_clear_object (&day_view->time_canvas);
1224 g_clear_object (&day_view->priv->model);
1225 g_clear_object (&day_view->priv->drag_context);
1227 g_free (day_view->priv->marcus_bains_day_view_color);
1228 day_view->priv->marcus_bains_day_view_color = NULL;
1230 g_free (day_view->priv->marcus_bains_time_bar_color);
1231 day_view->priv->marcus_bains_time_bar_color = NULL;
1233 /* Chain up to parent's dispose() method. */
1234 G_OBJECT_CLASS (e_day_view_parent_class)->dispose (object);
1237 static void
1238 day_view_notify (GObject *object,
1239 GParamSpec *pspec)
1241 /* Don't chain up. None of our parent classes, not
1242 * even GObjectClass itself, implements this method. */
1244 if (g_str_equal (pspec->name, "time-divisions"))
1245 day_view_notify_time_divisions_cb (E_DAY_VIEW (object));
1248 static void
1249 day_view_constructed (GObject *object)
1251 EDayView *day_view;
1252 ECalModel *model;
1253 gulong handler_id;
1255 day_view = E_DAY_VIEW (object);
1257 /* Chain up to parent's constructed() method. */
1258 G_OBJECT_CLASS (e_day_view_parent_class)->constructed (object);
1260 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
1262 /* Keep our own model reference so we can
1263 * disconnect signal handlers in dispose(). */
1264 day_view->priv->model = g_object_ref (model);
1266 handler_id = e_signal_connect_notify (
1267 model, "notify::work-day-monday",
1268 G_CALLBACK (day_view_notify_work_day_cb), day_view);
1269 day_view->priv->notify_work_day_monday_handler_id = handler_id;
1271 handler_id = e_signal_connect_notify (
1272 model, "notify::work-day-tuesday",
1273 G_CALLBACK (day_view_notify_work_day_cb), day_view);
1274 day_view->priv->notify_work_day_tuesday_handler_id = handler_id;
1276 handler_id = e_signal_connect_notify (
1277 model, "notify::work-day-wednesday",
1278 G_CALLBACK (day_view_notify_work_day_cb), day_view);
1279 day_view->priv->notify_work_day_wednesday_handler_id = handler_id;
1281 handler_id = e_signal_connect_notify (
1282 model, "notify::work-day-thursday",
1283 G_CALLBACK (day_view_notify_work_day_cb), day_view);
1284 day_view->priv->notify_work_day_thursday_handler_id = handler_id;
1286 handler_id = e_signal_connect_notify (
1287 model, "notify::work-day-friday",
1288 G_CALLBACK (day_view_notify_work_day_cb), day_view);
1289 day_view->priv->notify_work_day_friday_handler_id = handler_id;
1291 handler_id = e_signal_connect_notify (
1292 model, "notify::work-day-saturday",
1293 G_CALLBACK (day_view_notify_work_day_cb), day_view);
1294 day_view->priv->notify_work_day_saturday_handler_id = handler_id;
1296 handler_id = e_signal_connect_notify (
1297 model, "notify::work-day-sunday",
1298 G_CALLBACK (day_view_notify_work_day_cb), day_view);
1299 day_view->priv->notify_work_day_sunday_handler_id = handler_id;
1301 handler_id = e_signal_connect_notify_swapped (
1302 model, "notify::week-start-day",
1303 G_CALLBACK (day_view_notify_week_start_day_cb), day_view);
1304 day_view->priv->notify_week_start_day_handler_id = handler_id;
1306 handler_id = e_signal_connect_notify_swapped (
1307 model, "notify::work-day-start-hour",
1308 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1309 day_view->priv->notify_work_day_start_hour_handler_id = handler_id;
1311 handler_id = e_signal_connect_notify_swapped (
1312 model, "notify::work-day-start-minute",
1313 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1314 day_view->priv->notify_work_day_start_minute_handler_id = handler_id;
1316 handler_id = e_signal_connect_notify_swapped (
1317 model, "notify::work-day-end-hour",
1318 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1319 day_view->priv->notify_work_day_end_hour_handler_id = handler_id;
1321 handler_id = e_signal_connect_notify_swapped (
1322 model, "notify::work-day-end-minute",
1323 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1324 day_view->priv->notify_work_day_end_minute_handler_id = handler_id;
1326 handler_id = e_signal_connect_notify_swapped (
1327 model, "notify::work-day-start-mon",
1328 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1329 day_view->priv->notify_work_day_start_mon_handler_id = handler_id;
1331 handler_id = e_signal_connect_notify_swapped (
1332 model, "notify::work-day-end-mon",
1333 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1334 day_view->priv->notify_work_day_end_mon_handler_id = handler_id;
1336 handler_id = e_signal_connect_notify_swapped (
1337 model, "notify::work-day-start-tue",
1338 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1339 day_view->priv->notify_work_day_start_tue_handler_id = handler_id;
1341 handler_id = e_signal_connect_notify_swapped (
1342 model, "notify::work-day-end-tue",
1343 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1344 day_view->priv->notify_work_day_end_tue_handler_id = handler_id;
1346 handler_id = e_signal_connect_notify_swapped (
1347 model, "notify::work-day-start-wed",
1348 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1349 day_view->priv->notify_work_day_start_wed_handler_id = handler_id;
1351 handler_id = e_signal_connect_notify_swapped (
1352 model, "notify::work-day-end-wed",
1353 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1354 day_view->priv->notify_work_day_end_wed_handler_id = handler_id;
1356 handler_id = e_signal_connect_notify_swapped (
1357 model, "notify::work-day-start-thu",
1358 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1359 day_view->priv->notify_work_day_start_thu_handler_id = handler_id;
1361 handler_id = e_signal_connect_notify_swapped (
1362 model, "notify::work-day-end-thu",
1363 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1364 day_view->priv->notify_work_day_end_thu_handler_id = handler_id;
1366 handler_id = e_signal_connect_notify_swapped (
1367 model, "notify::work-day-start-fri",
1368 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1369 day_view->priv->notify_work_day_start_fri_handler_id = handler_id;
1371 handler_id = e_signal_connect_notify_swapped (
1372 model, "notify::work-day-end-fri",
1373 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1374 day_view->priv->notify_work_day_end_fri_handler_id = handler_id;
1376 handler_id = e_signal_connect_notify_swapped (
1377 model, "notify::work-day-start-sat",
1378 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1379 day_view->priv->notify_work_day_start_sat_handler_id = handler_id;
1381 handler_id = e_signal_connect_notify_swapped (
1382 model, "notify::work-day-end-sat",
1383 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1384 day_view->priv->notify_work_day_end_sat_handler_id = handler_id;
1386 handler_id = e_signal_connect_notify_swapped (
1387 model, "notify::work-day-start-sun",
1388 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1389 day_view->priv->notify_work_day_start_sun_handler_id = handler_id;
1391 handler_id = e_signal_connect_notify_swapped (
1392 model, "notify::work-day-end-sun",
1393 G_CALLBACK (gtk_widget_queue_draw), day_view->main_canvas);
1394 day_view->priv->notify_work_day_end_sun_handler_id = handler_id;
1396 e_day_view_update_timezone_name_labels (day_view);
1399 static void
1400 day_view_update_style_settings (EDayView *day_view)
1402 gint hour;
1403 gint minute, max_minute_width, i;
1404 gint month, day, width;
1405 gint longest_month_width, longest_abbreviated_month_width;
1406 gint longest_weekday_width, longest_abbreviated_weekday_width;
1407 gchar buffer[128];
1408 const gchar *name;
1409 gint times_width;
1410 PangoFontDescription *font_desc;
1411 PangoContext *pango_context;
1412 PangoFontMetrics *font_metrics;
1413 PangoLayout *layout;
1414 gint week_day, event_num;
1415 GtkAdjustment *adjustment;
1416 EDayViewEvent *event;
1417 GdkColor color;
1419 g_return_if_fail (E_IS_DAY_VIEW (day_view));
1421 e_day_view_set_colors (day_view);
1423 for (week_day = 0; week_day < E_DAY_VIEW_MAX_DAYS; week_day++) {
1424 for (event_num = 0; event_num < day_view->events[week_day]->len; event_num++) {
1425 event = &g_array_index (day_view->events[week_day], EDayViewEvent, event_num);
1426 if (event->canvas_item) {
1427 color = e_day_view_get_text_color (day_view, event);
1428 gnome_canvas_item_set (
1429 event->canvas_item,
1430 "fill_color_gdk", &color,
1431 NULL);
1435 for (event_num = 0; event_num < day_view->long_events->len; event_num++) {
1436 event = &g_array_index (day_view->long_events, EDayViewEvent, event_num);
1437 if (event->canvas_item) {
1438 color = e_day_view_get_text_color (day_view, event);
1439 gnome_canvas_item_set (
1440 event->canvas_item,
1441 "fill_color_gdk", &color,
1442 NULL);
1446 /* Set up Pango prerequisites */
1447 pango_context = gtk_widget_get_pango_context (GTK_WIDGET (day_view));
1448 font_desc = pango_context_get_font_description (pango_context);
1449 font_metrics = pango_context_get_metrics (
1450 pango_context, font_desc,
1451 pango_context_get_language (pango_context));
1452 layout = pango_layout_new (pango_context);
1454 /* Create the large font. */
1455 if (day_view->large_font_desc != NULL)
1456 pango_font_description_free (day_view->large_font_desc);
1458 day_view->large_font_desc = pango_font_description_copy (font_desc);
1459 pango_font_description_set_size (
1460 day_view->large_font_desc,
1461 E_DAY_VIEW_LARGE_FONT_PTSIZE * PANGO_SCALE);
1463 /* Create the small fonts. */
1464 if (day_view->small_font_desc != NULL)
1465 pango_font_description_free (day_view->small_font_desc);
1467 day_view->small_font_desc = pango_font_description_copy (font_desc);
1468 pango_font_description_set_size (
1469 day_view->small_font_desc,
1470 E_DAY_VIEW_SMALL_FONT_PTSIZE * PANGO_SCALE);
1472 /* Recalculate the height of each row based on the font size. */
1473 day_view->row_height =
1474 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1475 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) +
1476 E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD * 2 + 2 /* FIXME */;
1477 day_view->row_height = MAX (
1478 day_view->row_height,
1479 E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD + 2);
1481 adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->main_canvas));
1482 gtk_adjustment_set_step_increment (adjustment, day_view->row_height);
1484 day_view->top_row_height =
1485 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1486 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) +
1487 E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT * 2 + E_DAY_VIEW_LONG_EVENT_Y_PAD * 2 +
1488 E_DAY_VIEW_TOP_CANVAS_Y_GAP;
1489 day_view->top_row_height =
1490 MAX (
1491 day_view->top_row_height,
1492 E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD + 2 +
1493 E_DAY_VIEW_TOP_CANVAS_Y_GAP);
1495 adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->top_canvas));
1496 gtk_adjustment_set_step_increment (adjustment, day_view->top_row_height);
1497 gtk_widget_set_size_request (day_view->top_dates_canvas, -1, day_view->top_row_height - 2);
1499 e_day_view_update_top_scroll (day_view, TRUE);
1501 /* Find the longest full & abbreviated month names. */
1502 longest_month_width = 0;
1503 longest_abbreviated_month_width = 0;
1504 for (month = 0; month < 12; month++) {
1505 name = e_get_month_name (month + 1, FALSE);
1506 pango_layout_set_text (layout, name, -1);
1507 pango_layout_get_pixel_size (layout, &width, NULL);
1509 if (width > longest_month_width) {
1510 longest_month_width = width;
1511 day_view->longest_month_name = month;
1514 name = e_get_month_name (month + 1, TRUE);
1515 pango_layout_set_text (layout, name, -1);
1516 pango_layout_get_pixel_size (layout, &width, NULL);
1518 if (width > longest_abbreviated_month_width) {
1519 longest_abbreviated_month_width = width;
1520 day_view->longest_abbreviated_month_name = month;
1524 /* Find the longest full & abbreviated weekday names. */
1525 longest_weekday_width = 0;
1526 longest_abbreviated_weekday_width = 0;
1527 for (day = 0; day < 7; day++) {
1528 name = e_get_weekday_name (day + 1, FALSE);
1529 pango_layout_set_text (layout, name, -1);
1530 pango_layout_get_pixel_size (layout, &width, NULL);
1532 if (width > longest_weekday_width) {
1533 longest_weekday_width = width;
1534 day_view->longest_weekday_name = day;
1537 name = e_get_weekday_name (day + 1, TRUE);
1538 pango_layout_set_text (layout, name, -1);
1539 pango_layout_get_pixel_size (layout, &width, NULL);
1541 if (width > longest_abbreviated_weekday_width) {
1542 longest_abbreviated_weekday_width = width;
1543 day_view->longest_abbreviated_weekday_name = day;
1547 /* Calculate the widths of all the time strings necessary. */
1548 day_view->max_small_hour_width = 0;
1549 for (hour = 0; hour < 24; hour++) {
1550 g_snprintf (buffer, sizeof (buffer), "%02i", hour);
1551 pango_layout_set_text (layout, buffer, -1);
1552 pango_layout_get_pixel_size (layout, &day_view->small_hour_widths[hour], NULL);
1554 day_view->max_small_hour_width = MAX (day_view->max_small_hour_width, day_view->small_hour_widths[hour]);
1557 max_minute_width = 0;
1558 for (minute = 0, i = 0; minute < 60; minute += 5, i++) {
1559 gint minute_width;
1561 g_snprintf (buffer, sizeof (buffer), "%02i", minute);
1562 pango_layout_set_text (layout, buffer, -1);
1563 pango_layout_get_pixel_size (layout, &minute_width, NULL);
1565 max_minute_width = MAX (max_minute_width, minute_width);
1567 day_view->max_minute_width = max_minute_width;
1569 pango_layout_set_text (layout, ":", 1);
1570 pango_layout_get_pixel_size (layout, &day_view->colon_width, NULL);
1571 pango_layout_set_text (layout, "0", 1);
1572 pango_layout_get_pixel_size (layout, &day_view->digit_width, NULL);
1574 pango_layout_set_text (layout, day_view->am_string, -1);
1575 pango_layout_get_pixel_size (layout, &day_view->am_string_width, NULL);
1576 pango_layout_set_text (layout, day_view->pm_string, -1);
1577 pango_layout_get_pixel_size (layout, &day_view->pm_string_width, NULL);
1579 /* Calculate the width of the time column. */
1580 times_width = e_day_view_time_item_get_column_width (E_DAY_VIEW_TIME_ITEM (day_view->time_canvas_item));
1581 gtk_widget_set_size_request (day_view->time_canvas, times_width, -1);
1583 g_object_unref (layout);
1584 pango_font_metrics_unref (font_metrics);
1587 static void
1588 day_view_realize (GtkWidget *widget)
1590 EDayView *day_view;
1592 if (GTK_WIDGET_CLASS (e_day_view_parent_class)->realize)
1593 (*GTK_WIDGET_CLASS (e_day_view_parent_class)->realize)(widget);
1595 day_view = E_DAY_VIEW (widget);
1597 day_view_update_style_settings (day_view);
1599 /* Create the pixmaps. */
1600 day_view->reminder_icon = e_icon_factory_get_icon ("stock_bell", GTK_ICON_SIZE_MENU);
1601 day_view->recurrence_icon = e_icon_factory_get_icon ("view-refresh", GTK_ICON_SIZE_MENU);
1602 day_view->timezone_icon = e_icon_factory_get_icon ("stock_timezone", GTK_ICON_SIZE_MENU);
1603 day_view->meeting_icon = e_icon_factory_get_icon ("stock_people", GTK_ICON_SIZE_MENU);
1604 day_view->attach_icon = e_icon_factory_get_icon ("mail-attachment", GTK_ICON_SIZE_MENU);
1606 /* Set the canvas item colors. */
1607 gnome_canvas_item_set (
1608 day_view->drag_long_event_rect_item,
1609 "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
1610 "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
1611 NULL);
1613 gnome_canvas_item_set (
1614 day_view->drag_rect_item,
1615 "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
1616 "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
1617 NULL);
1619 gnome_canvas_item_set (
1620 day_view->drag_bar_item,
1621 "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR],
1622 "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
1623 NULL);
1626 static void
1627 day_view_unrealize (GtkWidget *widget)
1629 EDayView *day_view;
1631 day_view = E_DAY_VIEW (widget);
1633 g_object_unref (day_view->reminder_icon);
1634 day_view->reminder_icon = NULL;
1635 g_object_unref (day_view->recurrence_icon);
1636 day_view->recurrence_icon = NULL;
1637 g_object_unref (day_view->timezone_icon);
1638 day_view->timezone_icon = NULL;
1639 g_object_unref (day_view->meeting_icon);
1640 day_view->meeting_icon = NULL;
1641 g_object_unref (day_view->attach_icon);
1642 day_view->attach_icon = NULL;
1644 if (GTK_WIDGET_CLASS (e_day_view_parent_class)->unrealize)
1645 (*GTK_WIDGET_CLASS (e_day_view_parent_class)->unrealize)(widget);
1648 static void
1649 day_view_size_allocate (GtkWidget *widget,
1650 GtkAllocation *allocation)
1652 (*GTK_WIDGET_CLASS (e_day_view_parent_class)->size_allocate) (widget, allocation);
1654 e_day_view_recalc_main_canvas_size (E_DAY_VIEW (widget));
1657 static void
1658 day_view_style_updated (GtkWidget *widget)
1660 if (GTK_WIDGET_CLASS (e_day_view_parent_class)->style_updated)
1661 (*GTK_WIDGET_CLASS (e_day_view_parent_class)->style_updated) (widget);
1663 day_view_update_style_settings (E_DAY_VIEW (widget));
1666 static gboolean
1667 day_view_focus (GtkWidget *widget,
1668 GtkDirectionType direction)
1670 EDayView *day_view;
1671 gint new_day;
1672 gint new_event_num;
1673 gint start_row, end_row;
1675 g_return_val_if_fail (widget != NULL, FALSE);
1676 g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
1677 day_view = E_DAY_VIEW (widget);
1679 if (!e_day_view_get_next_tab_event (day_view, direction,
1680 &new_day, &new_event_num))
1681 return FALSE;
1683 if ((new_day == -1) && (new_event_num == -1)) {
1684 /* focus should go to the day view widget itself
1686 gtk_widget_grab_focus (GTK_WIDGET (day_view));
1687 return TRUE;
1690 if (new_day != E_DAY_VIEW_LONG_EVENT && new_day != -1) {
1691 if (e_day_view_get_event_rows (day_view, new_day,
1692 new_event_num,
1693 &start_row, &end_row))
1694 /* ensure the event to be seen */
1695 e_day_view_ensure_rows_visible (
1696 day_view,
1697 start_row, end_row);
1698 } else if (new_day != -1) {
1699 e_day_view_start_editing_event (
1700 day_view, new_day,
1701 new_event_num, NULL);
1704 return TRUE;
1707 static gboolean
1708 day_view_key_press (GtkWidget *widget,
1709 GdkEventKey *event)
1711 gboolean handled = FALSE;
1712 handled = e_day_view_do_key_press (widget, event);
1714 /* if not handled, try key bindings */
1715 if (!handled)
1716 handled = GTK_WIDGET_CLASS (e_day_view_parent_class)->key_press_event (widget, event);
1717 return handled;
1720 static gint
1721 day_view_focus_in (GtkWidget *widget,
1722 GdkEventFocus *event)
1724 EDayView *day_view;
1726 g_return_val_if_fail (widget != NULL, FALSE);
1727 g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
1728 g_return_val_if_fail (event != NULL, FALSE);
1730 day_view = E_DAY_VIEW (widget);
1732 /* XXX Can't access flags directly anymore, but is it really needed?
1733 * If so, could we call gtk_widget_send_focus_change() instead? */
1734 #if 0
1735 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1736 #endif
1738 if (E_CALENDAR_VIEW (day_view)->in_focus && day_view->requires_update) {
1739 time_t my_start = 0, my_end = 0, model_start = 0, model_end = 0;
1741 day_view->requires_update = FALSE;
1743 e_cal_model_get_time_range (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), &model_start, &model_end);
1745 if (e_calendar_view_get_visible_time_range (E_CALENDAR_VIEW (day_view), &my_start, &my_end) &&
1746 model_start == my_start && model_end == my_end) {
1747 /* update only when the same time range is set in a view and in a model;
1748 * otherwise time range change invokes also query update */
1749 e_day_view_recalc_day_starts (day_view, day_view->lower);
1750 e_day_view_update_query (day_view);
1754 gtk_widget_queue_draw (day_view->top_canvas);
1755 gtk_widget_queue_draw (day_view->main_canvas);
1757 return FALSE;
1760 static gint
1761 day_view_focus_out (GtkWidget *widget,
1762 GdkEventFocus *event)
1764 EDayView *day_view;
1766 g_return_val_if_fail (widget != NULL, FALSE);
1767 g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
1768 g_return_val_if_fail (event != NULL, FALSE);
1770 day_view = E_DAY_VIEW (widget);
1772 /* XXX Can't access flags directly anymore, but is it really needed?
1773 * If so, could we call gtk_widget_send_focus_change() instead? */
1774 #if 0
1775 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1776 #endif
1778 gtk_widget_queue_draw (day_view->top_canvas);
1779 gtk_widget_queue_draw (day_view->main_canvas);
1781 return FALSE;
1784 static gboolean
1785 day_view_popup_menu (GtkWidget *widget)
1787 EDayView *day_view = E_DAY_VIEW (widget);
1788 e_day_view_show_popup_menu (
1789 day_view, NULL,
1790 day_view->editing_event_day,
1791 day_view->editing_event_num);
1792 return TRUE;
1795 /* Returns the currently-selected event, or NULL if none */
1796 static GList *
1797 day_view_get_selected_events (ECalendarView *cal_view)
1799 EDayViewEvent *event = NULL;
1800 GList *list = NULL;
1801 EDayView *day_view = (EDayView *) cal_view;
1803 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), NULL);
1805 if (day_view->editing_event_num != -1) {
1806 if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT) {
1807 if (!is_array_index_in_bounds (day_view->long_events, day_view->editing_event_num))
1808 return NULL;
1810 event = &g_array_index (day_view->long_events,
1811 EDayViewEvent,
1812 day_view->editing_event_num);
1813 } else {
1814 if (!is_array_index_in_bounds (day_view->events[day_view->editing_event_day], day_view->editing_event_num))
1815 return NULL;
1817 event = &g_array_index (day_view->events[day_view->editing_event_day],
1818 EDayViewEvent,
1819 day_view->editing_event_num);
1821 } else if (day_view->popup_event_num != -1) {
1822 if (day_view->popup_event_day == E_DAY_VIEW_LONG_EVENT) {
1823 if (!is_array_index_in_bounds (day_view->long_events, day_view->popup_event_num))
1824 return NULL;
1826 event = &g_array_index (day_view->long_events,
1827 EDayViewEvent,
1828 day_view->popup_event_num);
1829 } else {
1830 if (!is_array_index_in_bounds (day_view->events[day_view->popup_event_day], day_view->popup_event_num))
1831 return NULL;
1833 event = &g_array_index (day_view->events[day_view->popup_event_day],
1834 EDayViewEvent,
1835 day_view->popup_event_num);
1839 if (event)
1840 list = g_list_append (list, event);
1842 return list;
1845 /* This sets the selected time range. If the start_time & end_time are not equal
1846 * and are both visible in the view, then the selection is set to those times,
1847 * otherwise it is set to 1 hour from the start of the working day. */
1848 static void
1849 day_view_set_selected_time_range (ECalendarView *cal_view,
1850 time_t start_time,
1851 time_t end_time)
1853 EDayView *day_view;
1854 gint work_day_start_hour;
1855 gint work_day_start_minute;
1856 gint work_day_end_hour;
1857 gint work_day_end_minute;
1858 gint start_row, start_col, end_row, end_col;
1859 gboolean need_redraw = FALSE, start_in_grid, end_in_grid;
1861 day_view = E_DAY_VIEW (cal_view);
1863 if (start_time == end_time)
1864 end_time += e_calendar_view_get_time_divisions (cal_view) * 60;
1866 /* Set the selection. */
1867 start_in_grid = e_day_view_convert_time_to_grid_position (
1868 day_view,
1869 start_time,
1870 &start_col,
1871 &start_row);
1872 end_in_grid = e_day_view_convert_time_to_grid_position (
1873 day_view,
1874 end_time - 60,
1875 &end_col,
1876 &end_row);
1878 e_day_view_get_work_day_range_for_day (day_view, start_col,
1879 &work_day_start_hour, &work_day_start_minute,
1880 &work_day_end_hour, &work_day_end_minute);
1882 /* If either of the times isn't in the grid, or the selection covers
1883 * an entire day, we set the selection to 1 row from the start of the
1884 * working day, in the day corresponding to the start time. */
1885 if (!start_in_grid || !end_in_grid
1886 || (start_row == 0 && end_row == day_view->rows - 1)) {
1887 end_col = start_col;
1889 start_row = e_day_view_convert_time_to_row (
1890 day_view, work_day_start_hour, work_day_start_minute);
1891 start_row = CLAMP (start_row, 0, day_view->rows - 1);
1892 end_row = start_row;
1895 if (start_row != day_view->selection_start_row
1896 || start_col != day_view->selection_start_day) {
1897 need_redraw = TRUE;
1898 day_view->selection_in_top_canvas = FALSE;
1899 day_view->selection_start_row = start_row;
1900 day_view->selection_start_day = start_col;
1903 if (end_row != day_view->selection_end_row
1904 || end_col != day_view->selection_end_day) {
1905 need_redraw = TRUE;
1906 day_view->selection_in_top_canvas = FALSE;
1907 day_view->selection_end_row = end_row;
1908 day_view->selection_end_day = end_col;
1911 if (need_redraw) {
1912 gtk_widget_queue_draw (day_view->top_canvas);
1913 gtk_widget_queue_draw (day_view->top_dates_canvas);
1914 gtk_widget_queue_draw (day_view->main_canvas);
1916 e_day_view_ensure_rows_visible (day_view, day_view->selection_start_row, day_view->selection_end_row);
1920 /* Gets the visible time range. Returns FALSE if no time range has been set. */
1921 static gboolean
1922 day_view_get_visible_time_range (ECalendarView *cal_view,
1923 time_t *start_time,
1924 time_t *end_time)
1926 EDayView *day_view = E_DAY_VIEW (cal_view);
1927 gint days_shown;
1929 /* If the date isn't set, return FALSE. */
1930 if (day_view->lower == 0 && day_view->upper == 0)
1931 return FALSE;
1933 days_shown = e_day_view_get_days_shown (day_view);
1934 if (days_shown <= 0)
1935 return FALSE;
1937 *start_time = day_view->day_starts[0];
1938 *end_time = day_view->day_starts[days_shown];
1940 return TRUE;
1943 static void
1944 day_view_paste_text (ECalendarView *cal_view)
1946 EDayView *day_view;
1947 EDayViewEvent *event;
1949 g_return_if_fail (E_IS_DAY_VIEW (cal_view));
1951 day_view = E_DAY_VIEW (cal_view);
1953 if (day_view->editing_event_num == -1) {
1954 e_day_view_add_new_event_in_selected_range (day_view, NULL, TRUE);
1955 return;
1958 if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT) {
1959 if (!is_array_index_in_bounds (day_view->long_events, day_view->editing_event_num))
1960 return;
1962 event = &g_array_index (day_view->long_events,
1963 EDayViewEvent,
1964 day_view->editing_event_num);
1965 } else {
1966 if (!is_array_index_in_bounds (day_view->events[day_view->editing_event_day], day_view->editing_event_num))
1967 return;
1969 event = &g_array_index (day_view->events[day_view->editing_event_day],
1970 EDayViewEvent,
1971 day_view->editing_event_num);
1974 if (event->canvas_item &&
1975 E_IS_TEXT (event->canvas_item) &&
1976 E_TEXT (event->canvas_item)->editing) {
1977 e_text_paste_clipboard (E_TEXT (event->canvas_item));
1981 static void
1982 e_day_view_class_init (EDayViewClass *class)
1984 GObjectClass *object_class;
1985 GtkWidgetClass *widget_class;
1986 ECalendarViewClass *view_class;
1988 g_type_class_add_private (class, sizeof (EDayViewPrivate));
1990 object_class = G_OBJECT_CLASS (class);
1991 object_class->set_property = day_view_set_property;
1992 object_class->get_property = day_view_get_property;
1993 object_class->constructed = day_view_constructed;
1994 object_class->dispose = day_view_dispose;
1995 object_class->notify = day_view_notify;
1997 widget_class = GTK_WIDGET_CLASS (class);
1998 widget_class->realize = day_view_realize;
1999 widget_class->unrealize = day_view_unrealize;
2000 widget_class->size_allocate = day_view_size_allocate;
2001 widget_class->style_updated = day_view_style_updated;
2002 widget_class->focus = day_view_focus;
2003 widget_class->key_press_event = day_view_key_press;
2004 widget_class->focus_in_event = day_view_focus_in;
2005 widget_class->focus_out_event = day_view_focus_out;
2006 widget_class->popup_menu = day_view_popup_menu;
2008 view_class = E_CALENDAR_VIEW_CLASS (class);
2009 view_class->get_selected_events = day_view_get_selected_events;
2010 view_class->get_selected_time_range = day_view_get_selected_time_range;
2011 view_class->set_selected_time_range = day_view_set_selected_time_range;
2012 view_class->get_visible_time_range = day_view_get_visible_time_range;
2013 view_class->precalc_visible_time_range = e_day_view_precalc_visible_time_range;
2014 view_class->paste_text = day_view_paste_text;
2016 g_object_class_install_property (
2017 object_class,
2018 PROP_DRAW_FLAT_EVENTS,
2019 g_param_spec_boolean (
2020 "draw-flat-events",
2021 "Draw Flat Events",
2022 NULL,
2023 TRUE,
2024 G_PARAM_READWRITE |
2025 G_PARAM_CONSTRUCT |
2026 G_PARAM_STATIC_STRINGS));
2028 g_object_class_install_property (
2029 object_class,
2030 PROP_MARCUS_BAINS_SHOW_LINE,
2031 g_param_spec_boolean (
2032 "marcus-bains-show-line",
2033 "Marcus Bains Show Line",
2034 NULL,
2035 TRUE,
2036 G_PARAM_READWRITE |
2037 G_PARAM_CONSTRUCT |
2038 G_PARAM_STATIC_STRINGS));
2040 g_object_class_install_property (
2041 object_class,
2042 PROP_MARCUS_BAINS_DAY_VIEW_COLOR,
2043 g_param_spec_string (
2044 "marcus-bains-day-view-color",
2045 "Marcus Bains Day View Color",
2046 NULL,
2047 NULL,
2048 G_PARAM_READWRITE |
2049 G_PARAM_STATIC_STRINGS));
2051 g_object_class_install_property (
2052 object_class,
2053 PROP_MARCUS_BAINS_TIME_BAR_COLOR,
2054 g_param_spec_string (
2055 "marcus-bains-time-bar-color",
2056 "Marcus Bains Time Bar Color",
2057 NULL,
2058 NULL,
2059 G_PARAM_READWRITE |
2060 G_PARAM_STATIC_STRINGS));
2062 g_object_class_override_property (
2063 object_class,
2064 PROP_IS_EDITING,
2065 "is-editing");
2067 /* init the accessibility support for e_day_view */
2068 gtk_widget_class_set_accessible_type (widget_class, EA_TYPE_DAY_VIEW);
2071 static void
2072 e_day_view_init (EDayView *day_view)
2074 gint day;
2075 GnomeCanvasGroup *canvas_group;
2076 GtkAdjustment *adjustment;
2077 GtkScrollable *scrollable;
2078 GtkWidget *container;
2079 GtkWidget *widget;
2080 gulong handler_id;
2082 day_view->priv = E_DAY_VIEW_GET_PRIVATE (day_view);
2084 gtk_widget_set_can_focus (GTK_WIDGET (day_view), TRUE);
2086 day_view->long_events = g_array_new (
2087 FALSE, FALSE,
2088 sizeof (EDayViewEvent));
2089 day_view->long_events_sorted = TRUE;
2090 day_view->long_events_need_layout = FALSE;
2091 day_view->long_events_need_reshape = FALSE;
2093 day_view->layout_timeout_id = 0;
2095 for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++) {
2096 day_view->events[day] = g_array_new (
2097 FALSE, FALSE,
2098 sizeof (EDayViewEvent));
2099 day_view->events_sorted[day] = TRUE;
2100 day_view->need_layout[day] = FALSE;
2101 day_view->need_reshape[day] = FALSE;
2104 /* These indicate that the times haven't been set. */
2105 day_view->lower = 0;
2106 day_view->upper = 0;
2108 day_view->priv->days_shown = 1;
2110 day_view->date_format = E_DAY_VIEW_DATE_FULL;
2111 day_view->rows_in_top_display = 0;
2113 /* Note that these don't work yet. It would need a few fixes to the
2114 * way event->start_minute and event->end_minute are used, and there
2115 * may be problems with events that go outside the visible times. */
2116 day_view->first_hour_shown = 0;
2117 day_view->first_minute_shown = 0;
2118 day_view->last_hour_shown = 24;
2119 day_view->last_minute_shown = 0;
2121 e_day_view_recalc_num_rows (day_view);
2123 day_view->show_event_end_times = TRUE;
2124 day_view->scroll_to_work_day = TRUE;
2126 day_view->editing_event_day = -1;
2127 day_view->editing_event_num = -1;
2129 day_view->resize_event_num = -1;
2130 day_view->resize_bars_event_day = -1;
2131 day_view->resize_bars_event_num = -1;
2133 day_view->last_edited_comp_string = NULL;
2135 day_view->selection_start_row = -1;
2136 day_view->selection_start_day = -1;
2137 day_view->selection_end_row = -1;
2138 day_view->selection_end_day = -1;
2139 day_view->selection_is_being_dragged = FALSE;
2140 day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
2141 day_view->selection_in_top_canvas = FALSE;
2142 day_view->drag_last_day = -1;
2143 day_view->drag_event_day = -1;
2144 day_view->drag_event_num = -1;
2145 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
2146 day_view->priv->drag_context = NULL;
2148 day_view->pressed_event_day = -1;
2150 day_view->auto_scroll_timeout_id = 0;
2152 day_view->large_font_desc = NULL;
2153 day_view->small_font_desc = NULL;
2155 /* String to use in 12-hour time format for times in the morning. */
2156 day_view->am_string = _("am");
2158 /* String to use in 12-hour time format for times in the afternoon. */
2159 day_view->pm_string = _("pm");
2161 day_view->bc_event_time = 0;
2162 day_view->before_click_dtstart = 0;
2163 day_view->before_click_dtend = 0;
2165 gtk_widget_set_margin_top (GTK_WIDGET (day_view), 1);
2167 day_view->week_number_label = gtk_label_new ("");
2169 widget = gtk_label_new (NULL);
2170 gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
2171 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 1.0);
2172 day_view->priv->timezone_name_1_label = widget;
2174 widget = gtk_label_new (NULL);
2175 gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
2176 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 1.0);
2177 day_view->priv->timezone_name_2_label = widget;
2179 widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
2181 gtk_grid_attach (GTK_GRID (day_view), widget, 0, 0, 1, 1);
2182 g_object_set (G_OBJECT (widget),
2183 "hexpand", FALSE,
2184 "vexpand", FALSE,
2185 "halign", GTK_ALIGN_FILL,
2186 "valign", GTK_ALIGN_FILL,
2187 NULL);
2189 container = widget;
2191 gtk_box_pack_start (GTK_BOX (container), day_view->week_number_label, TRUE, TRUE, 2);
2193 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
2194 gtk_box_pack_end (GTK_BOX (container), widget, TRUE, TRUE, 2);
2196 gtk_widget_show_all (container);
2198 container = widget;
2200 gtk_box_set_homogeneous (GTK_BOX (container), TRUE);
2201 gtk_box_pack_start (GTK_BOX (container), day_view->priv->timezone_name_1_label, TRUE, TRUE, 2);
2202 gtk_box_pack_start (GTK_BOX (container), day_view->priv->timezone_name_2_label, TRUE, TRUE, 2);
2204 gtk_widget_show_all (container);
2207 * Top Canvas
2209 widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2210 gtk_grid_attach (GTK_GRID (day_view), widget, 1, 0, 1, 1);
2211 g_object_set (G_OBJECT (widget),
2212 "hexpand", TRUE,
2213 "vexpand", FALSE,
2214 "halign", GTK_ALIGN_FILL,
2215 "valign", GTK_ALIGN_FILL,
2216 NULL);
2217 gtk_widget_show (widget);
2219 container = widget;
2221 widget = e_canvas_new ();
2222 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
2223 day_view->top_dates_canvas = widget;
2224 gtk_widget_show (widget);
2226 /* Keep our own canvas reference so we can
2227 * disconnect signal handlers in dispose(). */
2228 widget = e_canvas_new ();
2229 gtk_box_pack_end (GTK_BOX (container), widget, TRUE, TRUE, 0);
2230 day_view->top_canvas = g_object_ref (widget);
2231 gtk_widget_show (widget);
2233 handler_id = g_signal_connect_after (
2234 day_view->top_canvas, "button_press_event",
2235 G_CALLBACK (e_day_view_on_top_canvas_button_press), day_view);
2236 day_view->priv->top_canvas_button_press_event_handler_id = handler_id;
2238 handler_id = g_signal_connect (
2239 day_view->top_canvas, "button_release_event",
2240 G_CALLBACK (e_day_view_on_top_canvas_button_release), day_view);
2241 day_view->priv->top_canvas_button_release_event_handler_id = handler_id;
2243 handler_id = g_signal_connect (
2244 day_view->top_canvas, "scroll_event",
2245 G_CALLBACK (e_day_view_on_top_canvas_scroll), day_view);
2246 day_view->priv->top_canvas_scroll_event_handler_id = handler_id;
2248 handler_id = g_signal_connect (
2249 day_view->top_canvas, "motion_notify_event",
2250 G_CALLBACK (e_day_view_on_top_canvas_motion), day_view);
2251 day_view->priv->top_canvas_motion_notify_event_handler_id = handler_id;
2253 handler_id = g_signal_connect (
2254 day_view->top_canvas, "drag_motion",
2255 G_CALLBACK (e_day_view_on_top_canvas_drag_motion), day_view);
2256 day_view->priv->top_canvas_drag_motion_handler_id = handler_id;
2258 handler_id = g_signal_connect (
2259 day_view->top_canvas, "drag_leave",
2260 G_CALLBACK (e_day_view_on_top_canvas_drag_leave), day_view);
2261 day_view->priv->top_canvas_drag_leave_handler_id = handler_id;
2263 handler_id = g_signal_connect (
2264 day_view->top_canvas, "drag_begin",
2265 G_CALLBACK (e_day_view_on_drag_begin), day_view);
2266 day_view->priv->top_canvas_drag_begin_handler_id = handler_id;
2268 handler_id = g_signal_connect (
2269 day_view->top_canvas, "drag_end",
2270 G_CALLBACK (e_day_view_on_drag_end), day_view);
2271 day_view->priv->top_canvas_drag_end_handler_id = handler_id;
2273 handler_id = g_signal_connect (
2274 day_view->top_canvas, "drag_data_get",
2275 G_CALLBACK (e_day_view_on_drag_data_get), day_view);
2276 day_view->priv->top_canvas_drag_data_get_handler_id = handler_id;
2278 handler_id = g_signal_connect (
2279 day_view->top_canvas, "drag_data_received",
2280 G_CALLBACK (e_day_view_on_top_canvas_drag_data_received), day_view);
2281 day_view->priv->top_canvas_drag_data_received_handler_id = handler_id;
2283 canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_dates_canvas)->root);
2285 day_view->top_dates_canvas_item =
2286 gnome_canvas_item_new (
2287 canvas_group,
2288 e_day_view_top_item_get_type (),
2289 "EDayViewTopItem::day_view", day_view,
2290 "EDayViewTopItem::show_dates", TRUE,
2291 NULL);
2292 gtk_widget_set_size_request (day_view->top_dates_canvas, -1, day_view->top_row_height);
2294 canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_canvas)->root);
2296 day_view->top_canvas_item =
2297 gnome_canvas_item_new (
2298 canvas_group,
2299 e_day_view_top_item_get_type (),
2300 "EDayViewTopItem::day_view", day_view,
2301 "EDayViewTopItem::show_dates", FALSE,
2302 NULL);
2304 day_view->drag_long_event_rect_item =
2305 gnome_canvas_item_new (
2306 canvas_group,
2307 gnome_canvas_rect_get_type (),
2308 "line_width", 1.0,
2309 NULL);
2310 gnome_canvas_item_hide (day_view->drag_long_event_rect_item);
2312 day_view->drag_long_event_item =
2313 gnome_canvas_item_new (
2314 canvas_group,
2315 e_text_get_type (),
2316 "line_wrap", TRUE,
2317 "clip", TRUE,
2318 "max_lines", 1,
2319 "editable", TRUE,
2320 "fill_color_rgba", GNOME_CANVAS_COLOR (0, 0, 0),
2321 NULL);
2322 gnome_canvas_item_hide (day_view->drag_long_event_item);
2325 * Main Canvas
2328 /* Keep our own canvas reference so we can
2329 * disconnect signal handlers in dispose(). */
2330 widget = e_canvas_new ();
2331 gtk_grid_attach (GTK_GRID (day_view), widget, 1, 1, 1, 1);
2332 g_object_set (G_OBJECT (widget),
2333 "hexpand", TRUE,
2334 "vexpand", TRUE,
2335 "halign", GTK_ALIGN_FILL,
2336 "valign", GTK_ALIGN_FILL,
2337 NULL);
2338 day_view->main_canvas = g_object_ref (widget);
2339 gtk_widget_show (widget);
2341 handler_id = g_signal_connect (
2342 day_view->main_canvas, "realize",
2343 G_CALLBACK (e_day_view_on_canvas_realized), day_view);
2344 day_view->priv->main_canvas_realize_handler_id = handler_id;
2346 handler_id = g_signal_connect (
2347 day_view->main_canvas, "button_press_event",
2348 G_CALLBACK (e_day_view_on_main_canvas_button_press), day_view);
2349 day_view->priv->main_canvas_button_press_event_handler_id = handler_id;
2351 handler_id = g_signal_connect (
2352 day_view->main_canvas, "button_release_event",
2353 G_CALLBACK (e_day_view_on_main_canvas_button_release),
2354 day_view);
2355 day_view->priv->main_canvas_button_release_event_handler_id = handler_id;
2357 handler_id = g_signal_connect (
2358 day_view->main_canvas, "scroll_event",
2359 G_CALLBACK (e_day_view_on_main_canvas_scroll), day_view);
2360 day_view->priv->main_canvas_scroll_event_handler_id = handler_id;
2362 handler_id = g_signal_connect (
2363 day_view->main_canvas, "motion_notify_event",
2364 G_CALLBACK (e_day_view_on_main_canvas_motion), day_view);
2365 day_view->priv->main_canvas_motion_notify_event_handler_id = handler_id;
2367 handler_id = g_signal_connect (
2368 day_view->main_canvas, "drag_motion",
2369 G_CALLBACK (e_day_view_on_main_canvas_drag_motion), day_view);
2370 day_view->priv->main_canvas_drag_motion_handler_id = handler_id;
2372 handler_id = g_signal_connect (
2373 day_view->main_canvas, "drag_leave",
2374 G_CALLBACK (e_day_view_on_main_canvas_drag_leave), day_view);
2375 day_view->priv->main_canvas_drag_leave_handler_id = handler_id;
2377 handler_id = g_signal_connect (
2378 day_view->main_canvas, "drag_begin",
2379 G_CALLBACK (e_day_view_on_drag_begin), day_view);
2380 day_view->priv->main_canvas_drag_begin_handler_id = handler_id;
2382 handler_id = g_signal_connect (
2383 day_view->main_canvas, "drag_end",
2384 G_CALLBACK (e_day_view_on_drag_end), day_view);
2385 day_view->priv->main_canvas_drag_end_handler_id = handler_id;
2387 handler_id = g_signal_connect (
2388 day_view->main_canvas, "drag_data_get",
2389 G_CALLBACK (e_day_view_on_drag_data_get), day_view);
2390 day_view->priv->main_canvas_drag_data_get_handler_id = handler_id;
2392 handler_id = g_signal_connect (
2393 day_view->main_canvas, "drag_data_received",
2394 G_CALLBACK (e_day_view_on_main_canvas_drag_data_received), day_view);
2395 day_view->priv->main_canvas_drag_data_received_handler_id = handler_id;
2397 canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->main_canvas)->root);
2399 day_view->main_canvas_item =
2400 gnome_canvas_item_new (
2401 canvas_group,
2402 e_day_view_main_item_get_type (),
2403 "EDayViewMainItem::day_view", day_view,
2404 NULL);
2406 day_view->drag_rect_item =
2407 gnome_canvas_item_new (
2408 canvas_group,
2409 gnome_canvas_rect_get_type (),
2410 "line_width", 1.0,
2411 NULL);
2412 gnome_canvas_item_hide (day_view->drag_rect_item);
2414 day_view->drag_bar_item =
2415 gnome_canvas_item_new (
2416 canvas_group,
2417 gnome_canvas_rect_get_type (),
2418 "line_width", 1.0,
2419 NULL);
2420 gnome_canvas_item_hide (day_view->drag_bar_item);
2422 day_view->drag_item =
2423 gnome_canvas_item_new (
2424 canvas_group,
2425 e_text_get_type (),
2426 "line_wrap", TRUE,
2427 "clip", TRUE,
2428 "editable", TRUE,
2429 "fill_color_rgba", GNOME_CANVAS_COLOR (0, 0, 0),
2430 NULL);
2431 gnome_canvas_item_hide (day_view->drag_item);
2434 * Times Canvas
2437 /* Keep our own canvas reference so we can
2438 * disconnect signal handlers in dispose(). */
2439 widget = e_canvas_new ();
2440 scrollable = GTK_SCROLLABLE (day_view->main_canvas);
2441 adjustment = gtk_scrollable_get_vadjustment (scrollable);
2442 scrollable = GTK_SCROLLABLE (widget);
2443 gtk_scrollable_set_vadjustment (scrollable, adjustment);
2444 gtk_grid_attach (GTK_GRID (day_view), widget, 0, 1, 1, 1);
2445 g_object_set (G_OBJECT (widget),
2446 "hexpand", FALSE,
2447 "vexpand", TRUE,
2448 "halign", GTK_ALIGN_FILL,
2449 "valign", GTK_ALIGN_FILL,
2450 NULL);
2451 day_view->time_canvas = g_object_ref (widget);
2452 gtk_widget_show (widget);
2454 handler_id = g_signal_connect_after (
2455 day_view->time_canvas, "scroll_event",
2456 G_CALLBACK (e_day_view_on_time_canvas_scroll), day_view);
2457 day_view->priv->time_canvas_scroll_event_handler_id = handler_id;
2459 canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->time_canvas)->root);
2461 day_view->time_canvas_item =
2462 gnome_canvas_item_new (
2463 canvas_group,
2464 e_day_view_time_item_get_type (),
2465 "EDayViewTimeItem::day_view", day_view,
2466 NULL);
2469 * Scrollbar.
2471 scrollable = GTK_SCROLLABLE (day_view->main_canvas);
2472 adjustment = gtk_scrollable_get_hadjustment (scrollable);
2473 day_view->mc_hscrollbar = gtk_scrollbar_new (
2474 GTK_ORIENTATION_HORIZONTAL, adjustment);
2475 gtk_grid_attach (GTK_GRID (day_view), day_view->mc_hscrollbar, 1, 2, 1, 1);
2476 g_object_set (G_OBJECT (day_view->mc_hscrollbar),
2477 "hexpand", FALSE,
2478 "vexpand", FALSE,
2479 "halign", GTK_ALIGN_FILL,
2480 "valign", GTK_ALIGN_START,
2481 NULL);
2482 gtk_widget_show (day_view->mc_hscrollbar);
2484 scrollable = GTK_SCROLLABLE (day_view->top_canvas);
2485 adjustment = gtk_scrollable_get_vadjustment (scrollable);
2486 day_view->tc_vscrollbar = gtk_scrollbar_new (
2487 GTK_ORIENTATION_VERTICAL, adjustment);
2488 gtk_grid_attach (GTK_GRID (day_view), day_view->tc_vscrollbar, 2, 0, 1, 1);
2489 g_object_set (G_OBJECT (day_view->tc_vscrollbar),
2490 "hexpand", FALSE,
2491 "vexpand", FALSE,
2492 "halign", GTK_ALIGN_START,
2493 "valign", GTK_ALIGN_FILL,
2494 NULL);
2495 /* gtk_widget_show (day_view->tc_vscrollbar); */
2497 scrollable = GTK_SCROLLABLE (day_view->main_canvas);
2498 adjustment = gtk_scrollable_get_vadjustment (scrollable);
2499 day_view->vscrollbar = gtk_scrollbar_new (
2500 GTK_ORIENTATION_VERTICAL, adjustment);
2501 gtk_grid_attach (GTK_GRID (day_view), day_view->vscrollbar, 2, 1, 1, 1);
2502 g_object_set (G_OBJECT (day_view->vscrollbar),
2503 "hexpand", FALSE,
2504 "vexpand", TRUE,
2505 "halign", GTK_ALIGN_START,
2506 "valign", GTK_ALIGN_FILL,
2507 NULL);
2508 gtk_widget_show (day_view->vscrollbar);
2510 /* Create the cursors. */
2511 day_view->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR);
2512 day_view->move_cursor = gdk_cursor_new (GDK_FLEUR);
2513 day_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
2514 day_view->resize_height_cursor = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
2515 day_view->last_cursor_set_in_top_canvas = NULL;
2516 day_view->last_cursor_set_in_main_canvas = NULL;
2518 /* Set up the drop sites. */
2519 gtk_drag_dest_set (
2520 day_view->top_canvas, GTK_DEST_DEFAULT_ALL,
2521 target_table, G_N_ELEMENTS (target_table),
2522 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
2524 e_drag_dest_add_calendar_targets (day_view->top_canvas);
2526 gtk_drag_dest_set (
2527 day_view->main_canvas, GTK_DEST_DEFAULT_ALL,
2528 target_table, G_N_ELEMENTS (target_table),
2529 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
2531 e_drag_dest_add_calendar_targets (day_view->main_canvas);
2533 day_view->requires_update = FALSE;
2536 static void
2537 e_day_view_precalc_visible_time_range (ECalendarView *cal_view,
2538 time_t in_start_time,
2539 time_t in_end_time,
2540 time_t *out_start_time,
2541 time_t *out_end_time)
2543 EDayView *day_view;
2544 gint days_shown;
2545 time_t lower;
2546 icaltimezone *zone;
2548 g_return_if_fail (E_IS_DAY_VIEW (cal_view));
2549 g_return_if_fail (out_start_time != NULL);
2550 g_return_if_fail (out_end_time != NULL);
2552 day_view = E_DAY_VIEW (cal_view);
2553 days_shown = e_day_view_get_days_shown (day_view);
2554 zone = e_calendar_view_get_timezone (cal_view);
2556 /* Calculate the first day that should be shown, based on start_time
2557 * and the days_shown setting. If we are showing 1 day it is just the
2558 * start of the day given by start_time, otherwise it is the previous
2559 * work-week start day. */
2560 if (!e_day_view_get_work_week_view (day_view)) {
2561 lower = time_day_begin_with_zone (in_start_time, zone);
2562 } else {
2563 lower = e_day_view_find_work_week_start (day_view, in_start_time);
2566 /* See if we need to change the days shown. */
2567 if (lower == day_view->lower) {
2568 *out_start_time = day_view->lower;
2569 *out_end_time = day_view->upper;
2570 } else {
2571 gint day;
2573 *out_start_time = lower;
2574 *out_end_time = lower;
2576 for (day = 1; day <= days_shown; day++) {
2577 *out_end_time = time_add_day_with_zone (*out_end_time, 1, zone);
2582 static void
2583 time_range_changed_cb (ECalModel *model,
2584 gint64 i64_start_time,
2585 gint64 i64_end_time,
2586 gpointer user_data)
2588 EDayView *day_view = E_DAY_VIEW (user_data);
2589 EDayViewTimeItem *eti;
2590 gint days_shown;
2591 time_t lower, start_time = (time_t) i64_start_time, end_time = (time_t) i64_end_time;
2593 g_return_if_fail (E_IS_DAY_VIEW (day_view));
2595 days_shown = e_day_view_get_days_shown (day_view);
2597 /* Calculate the first day that should be shown, based on start_time
2598 * and the days_shown setting. If we are showing 1 day it is just the
2599 * start of the day given by start_time, otherwise it is the previous
2600 * work-week start day. */
2601 if (!e_day_view_get_work_week_view (day_view)) {
2602 lower = time_day_begin_with_zone (start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
2603 } else {
2604 lower = e_day_view_find_work_week_start (day_view, start_time);
2607 /* See if we need to change the days shown. */
2608 if (lower != day_view->lower)
2609 e_day_view_recalc_day_starts (day_view, lower);
2611 if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2612 e_day_view_free_events (day_view);
2613 day_view->requires_update = TRUE;
2614 return;
2617 /* If we don't show the new selection, don't preserve it */
2618 if (day_view->selection_start_day == -1 || days_shown <= day_view->selection_start_day)
2619 day_view_set_selected_time_range (E_CALENDAR_VIEW (day_view), start_time, end_time);
2621 if (day_view->selection_start_row != -1)
2622 e_day_view_ensure_rows_visible (day_view, day_view->selection_start_row, day_view->selection_start_row);
2624 /* update the time canvas to show proper date in it */
2625 eti = E_DAY_VIEW_TIME_ITEM (day_view->time_canvas_item);
2626 if (eti && e_day_view_time_item_get_second_zone (eti))
2627 gtk_widget_queue_draw (day_view->time_canvas);
2630 static void
2631 process_component (EDayView *day_view,
2632 ECalModelComponent *comp_data)
2634 const gchar *uid;
2635 gchar *rid = NULL;
2636 ECalModel *model;
2637 ECalComponent *comp;
2638 ESourceRegistry *registry;
2639 AddEventData add_event_data;
2641 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
2642 registry = e_cal_model_get_registry (model);
2644 /* If our time hasn't been set yet, just return. */
2645 if (day_view->lower == 0 && day_view->upper == 0)
2646 return;
2648 comp = e_cal_component_new ();
2649 if (!e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp))) {
2650 g_object_unref (comp);
2652 g_message (G_STRLOC ": Could not set icalcomponent on ECalComponent");
2653 return;
2656 e_cal_component_get_uid (comp, &uid);
2657 if (e_cal_component_is_instance (comp))
2658 rid = e_cal_component_get_recurid_as_string (comp);
2659 else
2660 rid = NULL;
2661 /* rid is never used below here, why? */
2663 /* Add the object */
2664 add_event_data.day_view = day_view;
2665 add_event_data.comp_data = comp_data;
2666 e_day_view_add_event (
2667 registry, comp_data->client, comp, comp_data->instance_start,
2668 comp_data->instance_end, &add_event_data);
2670 g_object_unref (comp);
2671 g_free (rid);
2674 static void
2675 update_row (EDayView *day_view,
2676 gint row,
2677 gboolean do_cancel_editing)
2679 ECalModelComponent *comp_data;
2680 ECalModel *model;
2681 gint day, event_num;
2682 const gchar *uid = NULL;
2683 gchar *rid = NULL;
2685 if (do_cancel_editing)
2686 cancel_editing (day_view);
2687 else
2688 e_day_view_stop_editing_event (day_view);
2690 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
2691 comp_data = e_cal_model_get_component_at (model, row);
2692 g_return_if_fail (comp_data != NULL);
2694 uid = icalcomponent_get_uid (comp_data->icalcomp);
2695 if (e_cal_util_component_is_instance (comp_data->icalcomp)) {
2696 icalproperty *prop;
2698 prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_RECURRENCEID_PROPERTY);
2699 if (prop)
2700 rid = icaltime_as_ical_string_r (icalcomponent_get_recurrenceid (comp_data->icalcomp));
2703 if (e_day_view_find_event_from_uid (day_view, comp_data->client, uid, rid, &day, &event_num))
2704 e_day_view_remove_event_cb (day_view, day, event_num, NULL);
2706 g_free (rid);
2708 process_component (day_view, comp_data);
2710 gtk_widget_queue_draw (day_view->top_canvas);
2711 gtk_widget_queue_draw (day_view->main_canvas);
2712 e_day_view_queue_layout (day_view);
2715 static void
2716 model_row_changed_cb (ETableModel *etm,
2717 gint row,
2718 gpointer user_data)
2720 EDayView *day_view = E_DAY_VIEW (user_data);
2722 if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2723 e_day_view_free_events (day_view);
2724 day_view->requires_update = TRUE;
2725 return;
2728 update_row (day_view, row, TRUE);
2731 static void
2732 model_cell_changed_cb (ETableModel *etm,
2733 gint col,
2734 gint row,
2735 gpointer user_data)
2737 EDayView *day_view = E_DAY_VIEW (user_data);
2739 if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2740 e_day_view_free_events (day_view);
2741 day_view->requires_update = TRUE;
2742 return;
2745 update_row (day_view, row, FALSE);
2748 static void
2749 model_rows_inserted_cb (ETableModel *etm,
2750 gint row,
2751 gint count,
2752 gpointer user_data)
2754 EDayView *day_view = E_DAY_VIEW (user_data);
2755 ECalModel *model;
2756 gint i;
2758 if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2759 e_day_view_free_events (day_view);
2760 day_view->requires_update = TRUE;
2761 return;
2764 e_day_view_stop_editing_event (day_view);
2766 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
2767 for (i = 0; i < count; i++) {
2768 ECalModelComponent *comp_data;
2770 comp_data = e_cal_model_get_component_at (model, row + i);
2771 if (comp_data == NULL) {
2772 g_warning ("comp_data is NULL\n");
2773 continue;
2775 process_component (day_view, comp_data);
2778 gtk_widget_queue_draw (day_view->top_canvas);
2779 gtk_widget_queue_draw (day_view->main_canvas);
2780 e_day_view_queue_layout (day_view);
2784 static void
2785 model_comps_deleted_cb (ETableModel *etm,
2786 gpointer data,
2787 gpointer user_data)
2789 EDayView *day_view = E_DAY_VIEW (user_data);
2790 GSList *l, *list = data;
2792 if (!E_CALENDAR_VIEW (day_view)->in_focus) {
2793 e_day_view_free_events (day_view);
2794 day_view->requires_update = TRUE;
2795 return;
2798 e_day_view_stop_editing_event (day_view);
2800 for (l = list; l != NULL; l = g_slist_next (l)) {
2801 ECalModelComponent *comp_data = l->data;
2802 gint day, event_num;
2803 const gchar *uid = NULL;
2804 gchar *rid = NULL;
2806 uid = icalcomponent_get_uid (comp_data->icalcomp);
2807 if (e_cal_util_component_is_instance (comp_data->icalcomp)) {
2808 icalproperty *prop;
2810 prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_RECURRENCEID_PROPERTY);
2811 if (prop)
2812 rid = icaltime_as_ical_string_r (icalcomponent_get_recurrenceid (comp_data->icalcomp));
2815 if (e_day_view_find_event_from_uid (day_view, comp_data->client, uid, rid, &day, &event_num))
2816 e_day_view_remove_event_cb (day_view, day, event_num, NULL);
2818 g_free (rid);
2821 gtk_widget_queue_draw (day_view->top_canvas);
2822 gtk_widget_queue_draw (day_view->main_canvas);
2823 e_day_view_queue_layout (day_view);
2826 static void
2827 timezone_changed_cb (ECalModel *cal_model,
2828 icaltimezone *old_zone,
2829 icaltimezone *new_zone,
2830 gpointer user_data)
2832 struct icaltimetype tt;
2833 time_t lower;
2834 EDayView *day_view = (EDayView *) user_data;
2835 ECalendarView *cal_view = (ECalendarView *) day_view;
2837 g_return_if_fail (E_IS_DAY_VIEW (day_view));
2839 if (!cal_view->in_focus) {
2840 e_day_view_free_events (day_view);
2841 day_view->requires_update = TRUE;
2842 return;
2845 /* If our time hasn't been set yet, just return. */
2846 if (day_view->lower == 0 && day_view->upper == 0)
2847 return;
2849 /* Recalculate the new start of the first day. We just use exactly
2850 * the same time, but with the new timezone. */
2851 tt = icaltime_from_timet_with_zone (
2852 day_view->lower, FALSE,
2853 old_zone);
2855 lower = icaltime_as_timet_with_zone (tt, new_zone);
2857 e_day_view_recalc_day_starts (day_view, lower);
2858 e_day_view_update_query (day_view);
2859 e_day_view_update_timezone_name_labels (day_view);
2862 static void
2863 init_model (EDayView *day_view,
2864 ECalModel *model)
2866 gulong handler_id;
2868 handler_id = g_signal_connect (
2869 model, "time_range_changed",
2870 G_CALLBACK (time_range_changed_cb), day_view);
2871 day_view->priv->time_range_changed_handler_id = handler_id;
2873 handler_id = g_signal_connect (
2874 model, "model_row_changed",
2875 G_CALLBACK (model_row_changed_cb), day_view);
2876 day_view->priv->model_row_changed_handler_id = handler_id;
2878 handler_id = g_signal_connect (
2879 model, "model_cell_changed",
2880 G_CALLBACK (model_cell_changed_cb), day_view);
2881 day_view->priv->model_cell_changed_handler_id = handler_id;
2883 handler_id = g_signal_connect (
2884 model, "model_rows_inserted",
2885 G_CALLBACK (model_rows_inserted_cb), day_view);
2886 day_view->priv->model_rows_inserted_handler_id = handler_id;
2888 handler_id = g_signal_connect (
2889 model, "comps_deleted",
2890 G_CALLBACK (model_comps_deleted_cb), day_view);
2891 day_view->priv->comps_deleted_handler_id = handler_id;
2893 handler_id = g_signal_connect (
2894 model, "timezone_changed",
2895 G_CALLBACK (timezone_changed_cb), day_view);
2896 day_view->priv->timezone_changed_handler_id = handler_id;
2899 /* Turn off the background of the canvas windows. This reduces flicker
2900 * considerably when scrolling. (Why isn't it in GnomeCanvas?). */
2901 static void
2902 e_day_view_on_canvas_realized (GtkWidget *widget,
2903 EDayView *day_view)
2905 GdkWindow *window;
2907 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
2908 gdk_window_set_background_pattern (window, NULL);
2912 * e_day_view_new:
2913 * @Returns: a new #EDayView.
2915 * Creates a new #EDayView.
2917 ECalendarView *
2918 e_day_view_new (ECalModel *model)
2920 ECalendarView *day_view;
2922 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
2924 day_view = g_object_new (E_TYPE_DAY_VIEW, "model", model, NULL);
2925 init_model (E_DAY_VIEW (day_view), model);
2927 return day_view;
2930 static void
2931 e_day_view_set_colors (EDayView *day_view)
2933 GtkWidget *widget = GTK_WIDGET (day_view);
2934 GdkRGBA base_bg, bg_bg, selected_bg, unfocused_selected_bg, dark_bg, light_bg;
2936 e_utils_get_theme_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &base_bg);
2937 e_utils_get_theme_color (widget, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR, &bg_bg);
2938 e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &selected_bg);
2939 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);
2941 e_utils_shade_color (&bg_bg, &dark_bg, E_UTILS_DARKNESS_MULT);
2942 e_utils_shade_color (&bg_bg, &light_bg, E_UTILS_LIGHTNESS_MULT);
2944 e_rgba_to_color (&base_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING]);
2945 e_rgba_to_color (&bg_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING]);
2946 e_rgba_to_color (&selected_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_SELECTED]);
2947 e_rgba_to_color (&unfocused_selected_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_SELECTED_UNFOCUSSED]);
2948 e_rgba_to_color (&dark_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_GRID]);
2949 e_rgba_to_color (&dark_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS]);
2950 e_rgba_to_color (&selected_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS_SELECTED]);
2951 e_rgba_to_color (&light_bg, &day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS_GRID]);
2952 e_rgba_to_color (&selected_bg, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR]);
2953 e_rgba_to_color (&base_bg, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND]);
2954 e_rgba_to_color (&dark_bg, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER]);
2955 e_rgba_to_color (&base_bg, &day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BACKGROUND]);
2956 e_rgba_to_color (&dark_bg, &day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BORDER]);
2958 day_view->colors[E_DAY_VIEW_COLOR_BG_MULTIDAY_TODAY] = get_today_background (day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING]);
2960 bg_bg.red = 0.5;
2961 bg_bg.green = 1.0;
2962 bg_bg.blue = 1.0;
2964 e_rgba_to_color (&bg_bg, &day_view->colors[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE]);
2967 static void
2968 e_day_view_update_top_scroll (EDayView *day_view,
2969 gboolean scroll_to_top)
2971 GtkAllocation allocation;
2972 gint top_rows, top_canvas_height;
2973 gdouble old_x2, old_y2, new_x2, new_y2;
2975 /* Set the height of the top canvas based on the row height and the
2976 * number of rows needed (min 1 + 1 for the dates + 1 space for DnD).*/
2977 top_rows = MAX (1, day_view->rows_in_top_display);
2978 top_canvas_height = (top_rows + 1) * day_view->top_row_height;
2979 if (top_rows <= E_DAY_VIEW_MAX_ROWS_AT_TOP) {
2980 gtk_widget_set_size_request (day_view->top_canvas, -1, top_canvas_height);
2981 gtk_widget_hide (day_view->tc_vscrollbar);
2982 } else {
2983 gtk_widget_set_size_request (day_view->top_canvas, -1, (E_DAY_VIEW_MAX_ROWS_AT_TOP + 1) * day_view->top_row_height);
2984 gtk_widget_show (day_view->tc_vscrollbar);
2987 /* Set the scroll region of the top canvas */
2988 gnome_canvas_get_scroll_region (
2989 GNOME_CANVAS (day_view->top_canvas),
2990 NULL, NULL, &old_x2, &old_y2);
2991 gtk_widget_get_allocation (day_view->top_canvas, &allocation);
2992 new_x2 = allocation.width - 1;
2993 new_y2 = (MAX (1, day_view->rows_in_top_display) + 1) * day_view->top_row_height - 1;
2994 if (old_x2 != new_x2 || old_y2 != new_y2) {
2995 gnome_canvas_set_scroll_region (
2996 GNOME_CANVAS (day_view->top_canvas),
2997 0, 0, new_x2, new_y2);
2999 if (scroll_to_top)
3000 gnome_canvas_scroll_to (GNOME_CANVAS (day_view->top_canvas), 0, 0);
3002 new_y2 = day_view->top_row_height - 1 - 2;
3003 gnome_canvas_get_scroll_region (GNOME_CANVAS (day_view->top_dates_canvas), NULL, NULL, &old_x2, &old_y2);
3005 if (old_x2 != new_x2 || old_y2 != new_y2) {
3006 gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->top_dates_canvas), 0, 0, new_x2, new_y2);
3007 gnome_canvas_scroll_to (GNOME_CANVAS (day_view->top_dates_canvas), 0, 0);
3011 static void
3012 e_day_view_recalc_cell_sizes (EDayView *day_view)
3014 /* An array of dates, one for each month in the year 2000. They must
3015 * all be Sundays. */
3016 static const gint days[12] = { 23, 20, 19, 23, 21, 18,
3017 23, 20, 17, 22, 19, 24 };
3018 gfloat width, offset;
3019 gint day, max_width;
3020 struct tm date_tm;
3021 gchar buffer[128];
3022 GtkAllocation allocation;
3023 PangoContext *pango_context;
3024 PangoLayout *layout;
3025 gint pango_width;
3026 gint days_shown;
3028 days_shown = e_day_view_get_days_shown (day_view);
3030 gtk_widget_get_allocation (day_view->main_canvas, &allocation);
3032 /* Set up Pango prerequisites */
3033 pango_context = gtk_widget_get_pango_context (GTK_WIDGET (day_view));
3034 layout = pango_layout_new (pango_context);
3036 /* Calculate the column sizes, using floating point so that pixels
3037 * get divided evenly. Note that we use one more element than the
3038 * number of columns, to make it easy to get the column widths. */
3039 width = allocation.width;
3040 if (days_shown == 1)
3041 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);
3042 width /= days_shown;
3043 offset = 0;
3044 for (day = 0; day <= days_shown; day++) {
3045 day_view->day_offsets[day] = floor (offset + 0.5);
3046 offset += width;
3049 /* Calculate the days widths based on the offsets. */
3050 for (day = 0; day < days_shown; day++) {
3051 day_view->day_widths[day] = day_view->day_offsets[day + 1] - day_view->day_offsets[day];
3054 /* Determine which date format to use, based on the column widths.
3055 * We want to check the widths using the longest full or abbreviated
3056 * month name and the longest full or abbreviated weekday name, as
3057 * appropriate. */
3058 max_width = day_view->day_widths[0];
3060 memset (&date_tm, 0, sizeof (date_tm));
3061 date_tm.tm_year = 100;
3063 /* Try "Thursday 21 January". */
3064 date_tm.tm_mon = day_view->longest_month_name;
3065 date_tm.tm_mday = days[date_tm.tm_mon]
3066 + day_view->longest_weekday_name;
3067 date_tm.tm_wday = day_view->longest_weekday_name;
3068 date_tm.tm_isdst = -1;
3069 /* strftime format %A = full weekday name, %d = day of month,
3070 * %B = full month name. Don't use any other specifiers. */
3071 e_utf8_strftime (buffer, sizeof (buffer), _("%A %d %B"), &date_tm);
3072 pango_layout_set_text (layout, buffer, -1);
3073 pango_layout_get_pixel_size (layout, &pango_width, NULL);
3075 if (pango_width < max_width) {
3076 day_view->date_format = E_DAY_VIEW_DATE_FULL;
3077 goto exit;
3080 /* Try "Thu 21 Jan". */
3081 date_tm.tm_mon = day_view->longest_abbreviated_month_name;
3082 date_tm.tm_mday = days[date_tm.tm_mon]
3083 + day_view->longest_abbreviated_weekday_name;
3084 date_tm.tm_wday = day_view->longest_abbreviated_weekday_name;
3085 date_tm.tm_isdst = -1;
3086 /* strftime format %a = abbreviated weekday name, %d = day of month,
3087 * %b = abbreviated month name. Don't use any other specifiers. */
3088 e_utf8_strftime (buffer, sizeof (buffer), _("%a %d %b"), &date_tm);
3089 pango_layout_set_text (layout, buffer, -1);
3090 pango_layout_get_pixel_size (layout, &pango_width, NULL);
3092 if (pango_width < max_width) {
3093 day_view->date_format = E_DAY_VIEW_DATE_ABBREVIATED;
3094 goto exit;
3097 /* Try "23 Jan". */
3098 date_tm.tm_mon = day_view->longest_abbreviated_month_name;
3099 date_tm.tm_mday = 23;
3100 date_tm.tm_wday = 0;
3101 date_tm.tm_isdst = -1;
3102 /* strftime format %d = day of month, %b = abbreviated month name.
3103 * Don't use any other specifiers. */
3104 e_utf8_strftime (buffer, sizeof (buffer), _("%d %b"), &date_tm);
3105 pango_layout_set_text (layout, buffer, -1);
3106 pango_layout_get_pixel_size (layout, &pango_width, NULL);
3108 if (pango_width < max_width)
3109 day_view->date_format = E_DAY_VIEW_DATE_NO_WEEKDAY;
3110 else
3111 day_view->date_format = E_DAY_VIEW_DATE_SHORT;
3113 exit:
3114 g_object_unref (layout);
3117 /* This calls a given function for each event instance (in both views).
3118 * If the callback returns FALSE the iteration is stopped.
3119 * Note that it is safe for the callback to remove the event (since we
3120 * step backwards through the arrays). */
3121 static void
3122 e_day_view_foreach_event (EDayView *day_view,
3123 EDayViewForeachEventCallback callback,
3124 gpointer data)
3126 gint day, event_num;
3127 gint days_shown;
3129 days_shown = e_day_view_get_days_shown (day_view);
3131 for (day = 0; day < days_shown; day++) {
3132 for (event_num = day_view->events[day]->len - 1;
3133 event_num >= 0;
3134 event_num--) {
3135 if (!(*callback) (day_view, day, event_num, data))
3136 return;
3140 for (event_num = day_view->long_events->len - 1;
3141 event_num >= 0;
3142 event_num--) {
3143 if (!(*callback) (day_view, E_DAY_VIEW_LONG_EVENT, event_num,
3144 data))
3145 return;
3149 /* This calls a given function for each event instance that matches the given
3150 * uid. If the callback returns FALSE the iteration is stopped.
3151 * Note that it is safe for the callback to remove the event (since we
3152 * step backwards through the arrays). */
3153 static void
3154 e_day_view_foreach_event_with_uid (EDayView *day_view,
3155 const gchar *uid,
3156 EDayViewForeachEventCallback callback,
3157 gpointer data)
3159 EDayViewEvent *event;
3160 gint day, event_num;
3161 gint days_shown;
3162 const gchar *u;
3164 if (!uid)
3165 return;
3167 days_shown = e_day_view_get_days_shown (day_view);
3169 for (day = 0; day < days_shown; day++) {
3170 for (event_num = day_view->events[day]->len - 1;
3171 event_num >= 0;
3172 event_num--) {
3173 event = &g_array_index (day_view->events[day],
3174 EDayViewEvent, event_num);
3176 if (!is_comp_data_valid (event))
3177 continue;
3179 u = icalcomponent_get_uid (event->comp_data->icalcomp);
3180 if (u && !strcmp (uid, u)) {
3181 if (!(*callback) (day_view, day, event_num, data))
3182 return;
3187 for (event_num = day_view->long_events->len - 1;
3188 event_num >= 0;
3189 event_num--) {
3190 event = &g_array_index (day_view->long_events,
3191 EDayViewEvent, event_num);
3193 if (!is_comp_data_valid (event))
3194 continue;
3196 u = icalcomponent_get_uid (event->comp_data->icalcomp);
3197 if (u && !strcmp (uid, u)) {
3198 if (!(*callback) (day_view, E_DAY_VIEW_LONG_EVENT, event_num, data))
3199 return;
3204 static gboolean
3205 e_day_view_remove_event_cb (EDayView *day_view,
3206 gint day,
3207 gint event_num,
3208 gpointer data)
3210 EDayViewEvent *event;
3212 if (day == E_DAY_VIEW_LONG_EVENT) {
3213 if (!is_array_index_in_bounds (day_view->long_events, event_num))
3214 return TRUE;
3216 event = &g_array_index (day_view->long_events,
3217 EDayViewEvent, event_num);
3218 } else {
3219 if (!is_array_index_in_bounds (day_view->events[day], event_num))
3220 return TRUE;
3222 event = &g_array_index (day_view->events[day],
3223 EDayViewEvent, event_num);
3226 if (!event)
3227 return TRUE;
3229 /* If we were editing this event, set editing_event_day to -1 so
3230 * on_editing_stopped doesn't try to update the event. */
3231 if (day_view->editing_event_num == event_num && day_view->editing_event_day == day) {
3232 cancel_editing (day_view);
3233 day_view->editing_event_num = -1;
3234 day_view->editing_event_day = -1;
3235 g_object_notify (G_OBJECT (day_view), "is-editing");
3236 } else if (day_view->editing_event_num > event_num && day_view->editing_event_day == day) {
3237 day_view->editing_event_num--;
3240 if (day_view->popup_event_num == event_num && day_view->popup_event_day == day) {
3241 day_view->popup_event_num = -1;
3242 day_view->popup_event_day = -1;
3243 } else if (day_view->popup_event_num > event_num && day_view->popup_event_day == day) {
3244 day_view->popup_event_num--;
3247 if (event->timeout > 0) {
3248 g_source_remove (event->timeout);
3249 event->timeout = -1;
3252 if (day_view->resize_bars_event_num >= event_num && day_view->resize_bars_event_day == day) {
3253 if (day_view->resize_bars_event_num == event_num) {
3254 day_view->resize_bars_event_num = -1;
3255 day_view->resize_bars_event_day = -1;
3256 } else {
3257 day_view->resize_bars_event_num--;
3261 if (day_view->resize_event_num >= event_num && day_view->resize_event_day == day) {
3262 if (day_view->resize_event_num == event_num) {
3263 e_day_view_abort_resize (day_view);
3264 day_view->resize_event_num = -1;
3265 day_view->resize_event_day = -1;
3266 } else {
3267 day_view->resize_event_num--;
3271 if (day_view->pressed_event_num >= event_num && day_view->pressed_event_day == day) {
3272 if (day_view->pressed_event_num == event_num) {
3273 day_view->pressed_event_num = -1;
3274 day_view->pressed_event_day = -1;
3275 } else {
3276 day_view->pressed_event_num--;
3280 if (day_view->drag_event_num >= event_num && day_view->drag_event_day == day) {
3281 if (day_view->drag_event_num == event_num) {
3282 day_view->drag_event_num = -1;
3283 day_view->drag_event_day = -1;
3284 if (day_view->priv->drag_context) {
3285 gtk_drag_cancel (day_view->priv->drag_context);
3287 } else {
3288 day_view->drag_event_num--;
3292 if (event->canvas_item)
3293 g_object_run_dispose (G_OBJECT (event->canvas_item));
3295 if (is_comp_data_valid (event))
3296 g_object_unref (event->comp_data);
3297 event->comp_data = NULL;
3299 if (day == E_DAY_VIEW_LONG_EVENT) {
3300 g_array_remove_index (day_view->long_events, event_num);
3301 day_view->long_events_need_layout = TRUE;
3302 gtk_widget_grab_focus (GTK_WIDGET (day_view->top_canvas));
3303 } else {
3304 g_array_remove_index (day_view->events[day], event_num);
3305 day_view->need_layout[day] = TRUE;
3306 gtk_widget_grab_focus (GTK_WIDGET (day_view->main_canvas));
3309 return TRUE;
3312 /* Checks if the users participation status is NEEDS-ACTION and shows the summary as bold text */
3313 static void
3314 set_style_from_attendee (EDayViewEvent *event,
3315 ESourceRegistry *registry)
3317 ECalComponent *comp;
3318 GSList *attendees = NULL, *l;
3319 gchar *address;
3320 ECalComponentAttendee *at = NULL;
3322 if (!is_comp_data_valid (event))
3323 return;
3325 comp = e_cal_component_new ();
3326 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
3327 address = itip_get_comp_attendee (
3328 registry, comp, event->comp_data->client);
3329 e_cal_component_get_attendee_list (comp, &attendees);
3330 for (l = attendees; l; l = l->next) {
3331 ECalComponentAttendee *attendee = l->data;
3333 if ((attendee->value && g_strcmp0 (itip_strip_mailto (attendee->value), address) == 0)
3334 || (attendee->sentby && g_strcmp0 (itip_strip_mailto (attendee->sentby), address) == 0)) {
3335 at = attendee;
3336 break;
3340 /* The attendee has not yet accepted the meeting, display the summary as bolded.
3341 * If the attendee is not present, it might have come through a mailing list.
3342 * In that case, we never show the meeting as bold even if it is unaccepted. */
3343 if (at && (at->status == ICAL_PARTSTAT_NEEDSACTION))
3344 gnome_canvas_item_set (event->canvas_item, "bold", TRUE, NULL);
3345 else if (at && at->status == ICAL_PARTSTAT_DECLINED)
3346 gnome_canvas_item_set (event->canvas_item, "strikeout", TRUE, NULL);
3347 else if (at && at->status == ICAL_PARTSTAT_TENTATIVE)
3348 gnome_canvas_item_set (event->canvas_item, "italic", TRUE, NULL);
3349 else if (at && at->status == ICAL_PARTSTAT_DELEGATED)
3350 gnome_canvas_item_set (event->canvas_item, "italic", TRUE, "strikeout", TRUE, NULL);
3352 e_cal_component_free_attendee_list (attendees);
3353 g_free (address);
3354 g_object_unref (comp);
3357 /* This updates the text shown for an event. If the event start or end do not
3358 * lie on a row boundary, the time is displayed before the summary. */
3359 static void
3360 e_day_view_update_event_label (EDayView *day_view,
3361 gint day,
3362 gint event_num)
3364 EDayViewEvent *event;
3365 ECalendarView *cal_view;
3366 ESourceRegistry *registry;
3367 ECalModel *model;
3368 gboolean free_text = FALSE, editing_event = FALSE, short_event = FALSE;
3369 const gchar *summary;
3370 gchar *text;
3371 gint time_divisions;
3372 gint interval;
3374 if (!is_array_index_in_bounds (day_view->events[day], event_num))
3375 return;
3377 event = &g_array_index (day_view->events[day], EDayViewEvent, event_num);
3379 /* If the event isn't visible just return. */
3380 if (!event->canvas_item || !is_comp_data_valid (event))
3381 return;
3383 summary = icalcomponent_get_summary (event->comp_data->icalcomp);
3384 text = summary ? (gchar *) summary : (gchar *) "";
3386 if (day_view->editing_event_day == day
3387 && day_view->editing_event_num == event_num)
3388 editing_event = TRUE;
3390 interval = event->end_minute - event->start_minute;
3392 cal_view = E_CALENDAR_VIEW (day_view);
3393 model = e_calendar_view_get_model (cal_view);
3394 time_divisions = e_calendar_view_get_time_divisions (cal_view);
3396 registry = e_cal_model_get_registry (model);
3398 if ((interval / time_divisions) >= 2)
3399 short_event = FALSE;
3400 else if ((interval % time_divisions) == 0) {
3401 if (((event->end_minute % time_divisions) == 0) ||
3402 ((event->start_minute % time_divisions) == 0)) {
3403 short_event = TRUE;
3405 } else
3406 short_event = FALSE;
3408 if (!editing_event) {
3409 if (!short_event) {
3410 const gchar *description, *location;
3411 gint days_shown;
3413 days_shown = e_day_view_get_days_shown (day_view);
3414 description = icalcomponent_get_description (event->comp_data->icalcomp);
3415 location = icalcomponent_get_location (event->comp_data->icalcomp);
3417 if (description && *description) {
3418 if (location && *location)
3419 text = g_strdup_printf (" \n%s%c(%s)\n\n%s", text, days_shown == 1 ? ' ' : '\n', location, description);
3420 else
3421 text = g_strdup_printf (" \n%s\n\n%s", text, description);
3422 } else if (location && *location)
3423 text = g_strdup_printf (" \n%s%c(%s)", text, days_shown == 1 ? ' ' : '\n', location);
3424 else
3425 text = g_strdup_printf (" \n%s", text);
3427 free_text = TRUE;
3431 gnome_canvas_item_set (
3432 event->canvas_item,
3433 "text", text,
3434 NULL);
3436 if (e_cal_util_component_has_attendee (event->comp_data->icalcomp))
3437 set_style_from_attendee (event, registry);
3439 if (free_text)
3440 g_free (text);
3443 static void
3444 e_day_view_update_long_event_label (EDayView *day_view,
3445 gint event_num)
3447 EDayViewEvent *event;
3448 ECalendarView *cal_view;
3449 ECalModel *model;
3450 ESourceRegistry *registry;
3451 const gchar *summary;
3452 gboolean free_text = FALSE;
3454 cal_view = E_CALENDAR_VIEW (day_view);
3455 model = e_calendar_view_get_model (cal_view);
3457 registry = e_cal_model_get_registry (model);
3459 if (!is_array_index_in_bounds (day_view->long_events, event_num))
3460 return;
3462 event = &g_array_index (day_view->long_events, EDayViewEvent,
3463 event_num);
3465 /* If the event isn't visible just return. */
3466 if (!event->canvas_item || !is_comp_data_valid (event))
3467 return;
3469 summary = e_calendar_view_get_icalcomponent_summary (event->comp_data->client, event->comp_data->icalcomp, &free_text);
3471 gnome_canvas_item_set (
3472 event->canvas_item,
3473 "text", summary ? summary : "",
3474 NULL);
3476 if (free_text)
3477 g_free ((gchar *) summary);
3479 if (e_cal_util_component_has_attendee (event->comp_data->icalcomp))
3480 set_style_from_attendee (event, registry);
3483 /* Finds the day and index of the event with the given canvas item.
3484 * If is is a long event, -1 is returned as the day.
3485 * Returns TRUE if the event was found. */
3486 gboolean
3487 e_day_view_find_event_from_item (EDayView *day_view,
3488 GnomeCanvasItem *item,
3489 gint *day_return,
3490 gint *event_num_return)
3492 EDayViewEvent *event;
3493 gint day, event_num;
3494 gint days_shown;
3496 days_shown = e_day_view_get_days_shown (day_view);
3498 for (day = 0; day < days_shown; day++) {
3499 for (event_num = 0; event_num < day_view->events[day]->len;
3500 event_num++) {
3501 event = &g_array_index (day_view->events[day],
3502 EDayViewEvent, event_num);
3503 if (event->canvas_item == item) {
3504 *day_return = day;
3505 *event_num_return = event_num;
3506 return TRUE;
3511 for (event_num = 0; event_num < day_view->long_events->len;
3512 event_num++) {
3513 event = &g_array_index (day_view->long_events,
3514 EDayViewEvent, event_num);
3515 if (event->canvas_item == item) {
3516 *day_return = E_DAY_VIEW_LONG_EVENT;
3517 *event_num_return = event_num;
3518 return TRUE;
3522 return FALSE;
3525 /* Finds the day and index of the event with the given uid.
3526 * If is is a long event, E_DAY_VIEW_LONG_EVENT is returned as the day.
3527 * Returns TRUE if an event with the uid was found.
3528 * Note that for recurring events there may be several EDayViewEvents, one
3529 * for each instance, all with the same iCalObject and uid. So only use this
3530 * function if you know the event doesn't recur or you are just checking to
3531 * see if any events with the uid exist. */
3532 static gboolean
3533 e_day_view_find_event_from_uid (EDayView *day_view,
3534 ECalClient *client,
3535 const gchar *uid,
3536 const gchar *rid,
3537 gint *day_return,
3538 gint *event_num_return)
3540 EDayViewEvent *event;
3541 gint day, event_num;
3542 gint days_shown;
3543 const gchar *u;
3544 gchar *r = NULL;
3546 if (!uid)
3547 return FALSE;
3549 days_shown = e_day_view_get_days_shown (day_view);
3551 for (day = 0; day < days_shown; day++) {
3552 for (event_num = 0; event_num < day_view->events[day]->len;
3553 event_num++) {
3554 event = &g_array_index (day_view->events[day],
3555 EDayViewEvent, event_num);
3557 if (!is_comp_data_valid (event))
3558 continue;
3560 if (event->comp_data->client != client)
3561 continue;
3563 u = icalcomponent_get_uid (event->comp_data->icalcomp);
3564 if (u && !strcmp (uid, u)) {
3565 if (rid && *rid) {
3566 r = icaltime_as_ical_string_r (icalcomponent_get_recurrenceid (event->comp_data->icalcomp));
3567 if (!r || !*r)
3568 continue;
3569 if (strcmp (rid, r) != 0) {
3570 g_free (r);
3571 continue;
3573 g_free (r);
3576 *day_return = day;
3577 *event_num_return = event_num;
3578 return TRUE;
3583 for (event_num = 0; event_num < day_view->long_events->len;
3584 event_num++) {
3585 event = &g_array_index (day_view->long_events,
3586 EDayViewEvent, event_num);
3588 if (!is_comp_data_valid (event))
3589 continue;
3591 if (event->comp_data->client != client)
3592 continue;
3594 u = icalcomponent_get_uid (event->comp_data->icalcomp);
3595 if (u && !strcmp (uid, u)) {
3596 *day_return = E_DAY_VIEW_LONG_EVENT;
3597 *event_num_return = event_num;
3598 return TRUE;
3602 return FALSE;
3605 static void
3606 e_day_view_set_selected_time_range_in_top_visible (EDayView *day_view,
3607 time_t start_time,
3608 time_t end_time)
3610 gint start_row, start_col, end_row, end_col;
3611 gboolean need_redraw = FALSE, start_in_grid, end_in_grid;
3613 g_return_if_fail (E_IS_DAY_VIEW (day_view));
3615 /* Set the selection. */
3616 start_in_grid = e_day_view_convert_time_to_grid_position (
3617 day_view,
3618 start_time,
3619 &start_col,
3620 &start_row);
3621 end_in_grid = e_day_view_convert_time_to_grid_position (
3622 day_view,
3623 end_time - 60,
3624 &end_col,
3625 &end_row);
3627 if (!start_in_grid)
3628 start_col = 0;
3629 if (!end_in_grid)
3630 end_col = e_day_view_get_days_shown (day_view) - 1;
3632 if (start_row != day_view->selection_start_row
3633 || start_col != day_view->selection_start_day) {
3634 need_redraw = TRUE;
3635 day_view->selection_in_top_canvas = TRUE;
3636 day_view->selection_start_row = -1;
3637 day_view->selection_start_day = start_col;
3640 if (end_row != day_view->selection_end_row
3641 || end_col != day_view->selection_end_day) {
3642 need_redraw = TRUE;
3643 day_view->selection_in_top_canvas = TRUE;
3644 day_view->selection_end_row = -1;
3645 day_view->selection_end_day = end_col;
3648 if (need_redraw) {
3649 gtk_widget_queue_draw (day_view->top_canvas);
3650 gtk_widget_queue_draw (day_view->top_dates_canvas);
3651 gtk_widget_queue_draw (day_view->main_canvas);
3655 static void
3656 e_day_view_set_selected_time_range_visible (EDayView *day_view,
3657 time_t start_time,
3658 time_t end_time)
3660 gint work_day_start_hour;
3661 gint work_day_start_minute;
3662 gint work_day_end_hour;
3663 gint work_day_end_minute;
3664 gint start_row, start_col, end_row, end_col;
3665 gboolean need_redraw = FALSE, start_in_grid, end_in_grid;
3667 g_return_if_fail (E_IS_DAY_VIEW (day_view));
3669 /* Set the selection. */
3670 start_in_grid = e_day_view_convert_time_to_grid_position (
3671 day_view,
3672 start_time,
3673 &start_col,
3674 &start_row);
3675 end_in_grid = e_day_view_convert_time_to_grid_position (
3676 day_view,
3677 end_time - 60,
3678 &end_col,
3679 &end_row);
3681 e_day_view_get_work_day_range_for_day (day_view, start_col,
3682 &work_day_start_hour, &work_day_start_minute,
3683 &work_day_end_hour, &work_day_end_minute);
3685 /* If either of the times isn't in the grid, or the selection covers
3686 * an entire day, we set the selection to 1 row from the start of the
3687 * working day, in the day corresponding to the start time. */
3688 if (!start_in_grid || !end_in_grid
3689 || (start_row == 0 && end_row == day_view->rows - 1)) {
3690 end_col = start_col;
3692 start_row = e_day_view_convert_time_to_row (
3693 day_view, work_day_start_hour, work_day_start_minute);
3694 start_row = CLAMP (start_row, 0, day_view->rows - 1);
3695 end_row = start_row;
3698 if (start_row != day_view->selection_start_row
3699 || start_col != day_view->selection_start_day) {
3700 need_redraw = TRUE;
3701 day_view->selection_in_top_canvas = FALSE;
3702 day_view->selection_start_row = start_row;
3703 day_view->selection_start_day = start_col;
3706 if (end_row != day_view->selection_end_row
3707 || end_col != day_view->selection_end_day) {
3708 need_redraw = TRUE;
3709 day_view->selection_in_top_canvas = FALSE;
3710 day_view->selection_end_row = end_row;
3711 day_view->selection_end_day = end_col;
3714 if (need_redraw) {
3715 gtk_widget_queue_draw (day_view->top_canvas);
3716 gtk_widget_queue_draw (day_view->top_dates_canvas);
3717 gtk_widget_queue_draw (day_view->main_canvas);
3721 /* Finds the start of the working week which includes the given time. */
3722 static time_t
3723 e_day_view_find_work_week_start (EDayView *day_view,
3724 time_t start_time)
3726 GDate date;
3727 ECalModel *model;
3728 guint offset;
3729 GDateWeekday weekday;
3730 GDateWeekday first_work_day;
3731 struct icaltimetype tt = icaltime_null_time ();
3732 icaltimezone *zone;
3734 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
3735 zone = e_cal_model_get_timezone (model);
3737 time_to_gdate_with_zone (&date, start_time, zone);
3739 /* The start of the work-week is the first working day after the
3740 * week start day. */
3742 /* Get the weekday corresponding to start_time. */
3743 weekday = g_date_get_weekday (&date);
3745 /* Calculate the first working day of the week. */
3746 first_work_day = e_cal_model_get_work_day_first (model);
3747 if (first_work_day == G_DATE_BAD_WEEKDAY)
3748 first_work_day = e_cal_model_get_week_start_day (model);
3750 /* Calculate how many days we need to go back to the first workday. */
3751 if (weekday < first_work_day)
3752 offset = (weekday + 7) - first_work_day;
3753 else
3754 offset = weekday - first_work_day;
3756 if (offset > 0)
3757 g_date_subtract_days (&date, offset);
3759 tt.year = g_date_get_year (&date);
3760 tt.month = g_date_get_month (&date);
3761 tt.day = g_date_get_day (&date);
3763 return icaltime_as_timet_with_zone (tt, zone);
3766 static void
3767 e_day_view_recalc_day_starts (EDayView *day_view,
3768 time_t start_time)
3770 gint day;
3771 gchar *str;
3772 gint days_shown;
3773 struct icaltimetype tt;
3774 GDate dt;
3776 days_shown = e_day_view_get_days_shown (day_view);
3777 if (days_shown <= 0)
3778 return;
3780 day_view->day_starts[0] = start_time;
3781 for (day = 1; day <= days_shown; day++) {
3782 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)));
3785 day_view->lower = start_time;
3786 day_view->upper = day_view->day_starts[days_shown];
3788 tt = icaltime_from_timet_with_zone (day_view->day_starts[0], FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
3789 g_date_clear (&dt, 1);
3790 g_date_set_dmy (&dt, tt.day, tt.month, tt.year);
3791 /* To Translators: the %d stands for a week number, it's value between 1 and 52/53 */
3792 str = g_strdup_printf (_("Week %d"), g_date_get_iso8601_week_of_year (&dt));
3793 gtk_label_set_text (GTK_LABEL (day_view->week_number_label), str);
3794 g_free (str);
3796 e_day_view_recalc_work_week (day_view);
3799 /* Whether we are displaying a work-week, in which case the display always
3800 * starts on the first day of the working week. */
3801 gboolean
3802 e_day_view_get_work_week_view (EDayView *day_view)
3804 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE);
3806 return day_view->priv->work_week_view;
3809 void
3810 e_day_view_set_work_week_view (EDayView *day_view,
3811 gboolean work_week_view)
3813 g_return_if_fail (E_IS_DAY_VIEW (day_view));
3815 if (work_week_view == day_view->priv->work_week_view)
3816 return;
3818 day_view->priv->work_week_view = work_week_view;
3820 e_day_view_recalc_work_week (day_view);
3823 gint
3824 e_day_view_get_days_shown (EDayView *day_view)
3826 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), -1);
3828 return day_view->priv->days_shown;
3831 void
3832 e_day_view_set_days_shown (EDayView *day_view,
3833 gint days_shown)
3835 g_return_if_fail (E_IS_DAY_VIEW (day_view));
3836 g_return_if_fail (days_shown >= 1);
3837 g_return_if_fail (days_shown <= E_DAY_VIEW_MAX_DAYS);
3839 if (days_shown == day_view->priv->days_shown)
3840 return;
3842 day_view->priv->days_shown = days_shown;
3844 /* If the date isn't set, just return. */
3845 if (day_view->lower == 0 && day_view->upper == 0)
3846 return;
3848 e_day_view_recalc_day_starts (day_view, day_view->lower);
3849 e_day_view_recalc_cell_sizes (day_view);
3851 e_day_view_update_query (day_view);
3854 static void
3855 e_day_view_recalc_work_week_days_shown (EDayView *day_view)
3857 ECalModel *model;
3858 GDateWeekday first_work_day;
3859 GDateWeekday last_work_day;
3860 gint days_shown;
3862 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
3864 /* Find the first working day in the week. */
3865 first_work_day = e_cal_model_get_work_day_first (model);
3867 if (first_work_day != G_DATE_BAD_WEEKDAY) {
3868 last_work_day = e_cal_model_get_work_day_last (model);
3870 /* Now calculate the days we need to show to include all the
3871 * working days in the week. Add 1 to make it inclusive. */
3872 days_shown = e_weekday_get_days_between (
3873 first_work_day, last_work_day) + 1;
3874 } else {
3875 /* If no working days are set, just use 7. */
3876 days_shown = 7;
3879 e_day_view_set_days_shown (day_view, days_shown);
3882 /* Force a redraw of the Marcus Bains Lines */
3883 void
3884 e_day_view_marcus_bains_update (EDayView *day_view)
3886 g_return_if_fail (E_IS_DAY_VIEW (day_view));
3887 gtk_widget_queue_draw (day_view->main_canvas);
3888 gtk_widget_queue_draw (day_view->time_canvas);
3891 gboolean
3892 e_day_view_marcus_bains_get_show_line (EDayView *day_view)
3894 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE);
3896 return day_view->priv->marcus_bains_show_line;
3899 void
3900 e_day_view_marcus_bains_set_show_line (EDayView *day_view,
3901 gboolean show_line)
3903 g_return_if_fail (E_IS_DAY_VIEW (day_view));
3905 if (show_line == day_view->priv->marcus_bains_show_line)
3906 return;
3908 day_view->priv->marcus_bains_show_line = show_line;
3910 e_day_view_marcus_bains_update (day_view);
3912 g_object_notify (G_OBJECT (day_view), "marcus-bains-show-line");
3915 gboolean
3916 e_day_view_get_draw_flat_events (EDayView *day_view)
3918 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE);
3920 return day_view->priv->draw_flat_events;
3923 void
3924 e_day_view_set_draw_flat_events (EDayView *day_view,
3925 gboolean draw_flat_events)
3927 g_return_if_fail (E_IS_DAY_VIEW (day_view));
3929 if ((day_view->priv->draw_flat_events ? 1 : 0) == (draw_flat_events ? 1 : 0))
3930 return;
3932 day_view->priv->draw_flat_events = draw_flat_events;
3934 gtk_widget_queue_draw (day_view->top_canvas);
3935 gtk_widget_queue_draw (day_view->top_dates_canvas);
3936 gtk_widget_queue_draw (day_view->main_canvas);
3938 g_object_notify (G_OBJECT (day_view), "draw-flat-events");
3941 const gchar *
3942 e_day_view_marcus_bains_get_day_view_color (EDayView *day_view)
3944 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), NULL);
3946 return day_view->priv->marcus_bains_day_view_color;
3949 void
3950 e_day_view_marcus_bains_set_day_view_color (EDayView *day_view,
3951 const gchar *day_view_color)
3953 g_return_if_fail (E_IS_DAY_VIEW (day_view));
3955 g_free (day_view->priv->marcus_bains_day_view_color);
3956 day_view->priv->marcus_bains_day_view_color = g_strdup (day_view_color);
3958 e_day_view_marcus_bains_update (day_view);
3960 g_object_notify (G_OBJECT (day_view), "marcus-bains-day-view-color");
3963 const gchar *
3964 e_day_view_marcus_bains_get_time_bar_color (EDayView *day_view)
3966 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), NULL);
3968 return day_view->priv->marcus_bains_time_bar_color;
3971 void
3972 e_day_view_marcus_bains_set_time_bar_color (EDayView *day_view,
3973 const gchar *time_bar_color)
3975 g_return_if_fail (E_IS_DAY_VIEW (day_view));
3977 g_free (day_view->priv->marcus_bains_time_bar_color);
3978 day_view->priv->marcus_bains_time_bar_color = g_strdup (time_bar_color);
3980 e_day_view_marcus_bains_update (day_view);
3982 g_object_notify (G_OBJECT (day_view), "marcus-bains-time-bar-color");
3985 /* Whether we display event end times in the main canvas. */
3986 gboolean
3987 e_day_view_get_show_event_end_times (EDayView *day_view)
3989 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), TRUE);
3991 return day_view->show_event_end_times;
3994 void
3995 e_day_view_set_show_event_end_times (EDayView *day_view,
3996 gboolean show)
3998 g_return_if_fail (E_IS_DAY_VIEW (day_view));
4000 if (day_view->show_event_end_times != show) {
4001 day_view->show_event_end_times = show;
4002 e_day_view_foreach_event (day_view,
4003 e_day_view_set_show_times_cb, NULL);
4007 /* This is a callback used to update all day event labels. */
4008 static gboolean
4009 e_day_view_set_show_times_cb (EDayView *day_view,
4010 gint day,
4011 gint event_num,
4012 gpointer data)
4014 if (day != E_DAY_VIEW_LONG_EVENT) {
4015 e_day_view_update_event_label (day_view, day, event_num);
4018 return TRUE;
4021 static void
4022 e_day_view_recalc_work_week (EDayView *day_view)
4024 time_t lower;
4026 /* If we aren't showing the work week, just return. */
4027 if (!e_day_view_get_work_week_view (day_view))
4028 return;
4030 e_day_view_recalc_work_week_days_shown (day_view);
4032 /* If the date isn't set, just return. */
4033 if (day_view->lower == 0 && day_view->upper == 0)
4034 return;
4036 lower = e_day_view_find_work_week_start (day_view, day_view->lower);
4037 if (lower != day_view->lower) {
4038 /* Reset the selection, as it may disappear. */
4039 day_view->selection_start_day = -1;
4041 e_day_view_recalc_day_starts (day_view, lower);
4042 e_day_view_update_query (day_view);
4044 /* This updates the date navigator. */
4045 e_day_view_update_calendar_selection_time (day_view);
4049 static gboolean
4050 e_day_view_update_scroll_regions (EDayView *day_view)
4052 GtkAllocation main_canvas_allocation;
4053 GtkAllocation time_canvas_allocation;
4054 gdouble old_x2, old_y2, new_x2, new_y2;
4055 gboolean need_reshape = FALSE;
4057 gtk_widget_get_allocation (
4058 day_view->main_canvas, &main_canvas_allocation);
4059 gtk_widget_get_allocation (
4060 day_view->time_canvas, &time_canvas_allocation);
4062 /* Set the scroll region of the time canvas to its allocated width,
4063 * but with the height the same as the main canvas. */
4064 gnome_canvas_get_scroll_region (
4065 GNOME_CANVAS (day_view->time_canvas),
4066 NULL, NULL, &old_x2, &old_y2);
4067 new_x2 = time_canvas_allocation.width - 1;
4068 new_y2 = MAX (
4069 day_view->rows * day_view->row_height,
4070 main_canvas_allocation.height - 1);
4071 if (old_x2 != new_x2 || old_y2 != new_y2)
4072 gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->time_canvas),
4073 0, 0, new_x2, new_y2);
4075 /* Set the scroll region of the main canvas to its allocated width,
4076 * but with the height depending on the number of rows needed. */
4077 gnome_canvas_get_scroll_region (
4078 GNOME_CANVAS (day_view->main_canvas),
4079 NULL, NULL, &old_x2, &old_y2);
4080 new_x2 = main_canvas_allocation.width - 1;
4082 if (e_day_view_get_days_shown (day_view) == 1)
4083 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);
4085 if (old_x2 != new_x2 || old_y2 != new_y2) {
4086 need_reshape = TRUE;
4087 gnome_canvas_set_scroll_region (
4088 GNOME_CANVAS (day_view->main_canvas),
4089 0, 0, new_x2, new_y2);
4092 if (new_x2 <= main_canvas_allocation.width - 1)
4093 gtk_widget_hide (day_view->mc_hscrollbar);
4094 else
4095 gtk_widget_show (day_view->mc_hscrollbar);
4097 return need_reshape;
4100 /* This recalculates the number of rows to display, based on the time range
4101 * shown and the minutes per row. */
4102 static void
4103 e_day_view_recalc_num_rows (EDayView *day_view)
4105 ECalendarView *cal_view;
4106 gint time_divisions;
4107 gint hours, minutes, total_minutes;
4109 cal_view = E_CALENDAR_VIEW (day_view);
4110 time_divisions = e_calendar_view_get_time_divisions (cal_view);
4112 hours = day_view->last_hour_shown - day_view->first_hour_shown;
4113 /* This could be negative but it works out OK. */
4114 minutes = day_view->last_minute_shown - day_view->first_minute_shown;
4115 total_minutes = hours * 60 + minutes;
4116 day_view->rows = total_minutes / time_divisions;
4119 /* Converts an hour and minute to a row in the canvas. Note that if we aren't
4120 * showing all 24 hours of the day, the returned row may be negative or
4121 * greater than day_view->rows. */
4122 gint
4123 e_day_view_convert_time_to_row (EDayView *day_view,
4124 gint hour,
4125 gint minute)
4127 ECalendarView *cal_view;
4128 gint time_divisions;
4129 gint total_minutes, start_minute, offset;
4131 cal_view = E_CALENDAR_VIEW (day_view);
4132 time_divisions = e_calendar_view_get_time_divisions (cal_view);
4134 total_minutes = hour * 60 + minute;
4135 start_minute = day_view->first_hour_shown * 60
4136 + day_view->first_minute_shown;
4137 offset = total_minutes - start_minute;
4138 if (offset < 0)
4139 return -1;
4140 else
4141 return offset / time_divisions;
4144 /* Converts an hour and minute to a y coordinate in the canvas. */
4145 gint
4146 e_day_view_convert_time_to_position (EDayView *day_view,
4147 gint hour,
4148 gint minute)
4150 ECalendarView *cal_view;
4151 gint time_divisions;
4152 gint total_minutes, start_minute, offset;
4154 cal_view = E_CALENDAR_VIEW (day_view);
4155 time_divisions = e_calendar_view_get_time_divisions (cal_view);
4157 total_minutes = hour * 60 + minute;
4158 start_minute = day_view->first_hour_shown * 60
4159 + day_view->first_minute_shown;
4160 offset = total_minutes - start_minute;
4162 return offset * day_view->row_height / time_divisions;
4165 static gboolean
4166 e_day_view_on_top_canvas_button_press (GtkWidget *widget,
4167 GdkEvent *button_event,
4168 EDayView *day_view)
4170 gint event_x, event_y, day, event_num;
4171 ECalendarViewPosition pos;
4172 GtkLayout *layout;
4173 GdkWindow *window;
4174 GdkDevice *event_device;
4175 guint event_button = 0;
4176 guint32 event_time;
4178 layout = GTK_LAYOUT (widget);
4179 window = gtk_layout_get_bin_window (layout);
4181 gdk_event_get_button (button_event, &event_button);
4182 event_device = gdk_event_get_device (button_event);
4183 event_time = gdk_event_get_time (button_event);
4185 if (day_view->resize_event_num != -1)
4186 day_view->resize_event_num = -1;
4188 if (day_view->drag_event_num != -1)
4189 day_view->drag_event_num = -1;
4191 /* Convert the coords to the main canvas window, or return if the
4192 * window is not found. */
4193 if (!e_day_view_convert_event_coords (
4194 day_view, button_event, window, &event_x, &event_y))
4195 return FALSE;
4197 pos = e_day_view_convert_position_in_top_canvas (
4198 day_view,
4199 event_x, event_y,
4200 &day, &event_num);
4202 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
4203 return FALSE;
4205 if (pos != E_CALENDAR_VIEW_POS_NONE)
4206 return e_day_view_on_long_event_button_press (
4207 day_view,
4208 event_num,
4209 button_event,
4210 pos,
4211 event_x,
4212 event_y);
4214 e_day_view_stop_editing_event (day_view);
4216 if (event_button == 1) {
4217 GdkGrabStatus grab_status;
4219 if (button_event->type == GDK_2BUTTON_PRESS) {
4220 time_t dtstart, dtend;
4222 day_view_get_selected_time_range ((ECalendarView *) day_view, &dtstart, &dtend);
4223 if (dtstart < day_view->before_click_dtend && dtend > day_view->before_click_dtstart) {
4224 dtstart = day_view->before_click_dtstart;
4225 dtend = day_view->before_click_dtend;
4226 day_view_set_selected_time_range ((ECalendarView *) day_view, dtstart, dtend);
4229 e_cal_ops_new_component_editor_from_model (
4230 e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), NULL,
4231 dtstart, dtend, calendar_config_get_prefer_meeting (), TRUE);
4232 return TRUE;
4235 if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4236 gtk_widget_grab_focus (GTK_WIDGET (day_view));
4238 grab_status = gdk_device_grab (
4239 event_device,
4240 window,
4241 GDK_OWNERSHIP_NONE,
4242 FALSE,
4243 GDK_POINTER_MOTION_MASK |
4244 GDK_BUTTON_RELEASE_MASK,
4245 NULL,
4246 event_time);
4248 if (grab_status == GDK_GRAB_SUCCESS) {
4249 g_warn_if_fail (day_view->grabbed_pointer == NULL);
4250 day_view->grabbed_pointer = g_object_ref (event_device);
4252 if (event_time - day_view->bc_event_time > 250)
4253 day_view_get_selected_time_range (
4254 E_CALENDAR_VIEW (day_view),
4255 &day_view->before_click_dtstart,
4256 &day_view->before_click_dtend);
4257 day_view->bc_event_time = event_time;
4258 e_day_view_start_selection (day_view, day, -1);
4260 } else if (event_button == 3) {
4261 if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4262 gtk_widget_grab_focus (GTK_WIDGET (day_view));
4264 if (day < day_view->selection_start_day || day > day_view->selection_end_day) {
4265 e_day_view_start_selection (day_view, day, -1);
4266 e_day_view_finish_selection (day_view);
4269 e_day_view_on_event_right_click (day_view, button_event, -1, -1);
4272 return TRUE;
4275 static gboolean
4276 e_day_view_convert_event_coords (EDayView *day_view,
4277 GdkEvent *event,
4278 GdkWindow *window,
4279 gint *x_return,
4280 gint *y_return)
4282 gint event_x, event_y, win_x, win_y;
4283 GdkWindow *event_window;;
4285 /* Get the event window, x & y from the appropriate event struct. */
4286 switch (event->type) {
4287 case GDK_BUTTON_PRESS:
4288 case GDK_2BUTTON_PRESS:
4289 case GDK_3BUTTON_PRESS:
4290 case GDK_BUTTON_RELEASE:
4291 event_x = event->button.x;
4292 event_y = event->button.y;
4293 event_window = event->button.window;
4294 break;
4295 case GDK_MOTION_NOTIFY:
4296 event_x = event->motion.x;
4297 event_y = event->motion.y;
4298 event_window = event->motion.window;
4299 break;
4300 case GDK_ENTER_NOTIFY:
4301 case GDK_LEAVE_NOTIFY:
4302 event_x = event->crossing.x;
4303 event_y = event->crossing.y;
4304 event_window = event->crossing.window;
4305 break;
4306 default:
4307 /* Shouldn't get here. */
4308 g_return_val_if_reached (FALSE);
4311 while (event_window && event_window != window
4312 && event_window != gdk_get_default_root_window ()) {
4313 gdk_window_get_position (event_window, &win_x, &win_y);
4314 event_x += win_x;
4315 event_y += win_y;
4316 event_window = gdk_window_get_parent (event_window);
4319 *x_return = event_x;
4320 *y_return = event_y;
4322 return (event_window == window) ? TRUE : FALSE;
4325 static gboolean
4326 e_day_view_on_main_canvas_button_press (GtkWidget *widget,
4327 GdkEvent *button_event,
4328 EDayView *day_view)
4330 gint event_x, event_y, row, day, event_num;
4331 ECalendarViewPosition pos;
4332 GtkLayout *layout;
4333 GdkWindow *window;
4334 GdkDevice *event_device;
4335 guint event_button = 0;
4336 guint32 event_time;
4338 layout = GTK_LAYOUT (widget);
4339 window = gtk_layout_get_bin_window (layout);
4341 gdk_event_get_button (button_event, &event_button);
4342 event_device = gdk_event_get_device (button_event);
4343 event_time = gdk_event_get_time (button_event);
4345 if (day_view->resize_event_num != -1)
4346 day_view->resize_event_num = -1;
4348 if (day_view->drag_event_num != -1)
4349 day_view->drag_event_num = -1;
4351 /* Convert the coords to the main canvas window, or return if the
4352 * window is not found. */
4353 if (!e_day_view_convert_event_coords (
4354 day_view, button_event, window, &event_x, &event_y))
4355 return FALSE;
4357 /* Find out where the mouse is. */
4358 pos = e_day_view_convert_position_in_main_canvas (
4359 day_view,
4360 event_x, event_y,
4361 &day, &row,
4362 &event_num);
4364 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
4365 return FALSE;
4367 if (pos != E_CALENDAR_VIEW_POS_NONE)
4368 return e_day_view_on_event_button_press (
4369 day_view,
4370 day,
4371 event_num,
4372 button_event,
4373 pos,
4374 event_x,
4375 event_y);
4377 e_day_view_stop_editing_event (day_view);
4379 /* Start the selection drag. */
4380 if (event_button == 1) {
4381 GdkGrabStatus grab_status;
4383 if (button_event->type == GDK_2BUTTON_PRESS) {
4384 time_t dtstart, dtend;
4386 day_view_get_selected_time_range ((ECalendarView *) day_view, &dtstart, &dtend);
4387 if (dtstart < day_view->before_click_dtend && dtend > day_view->before_click_dtstart) {
4388 dtstart = day_view->before_click_dtstart;
4389 dtend = day_view->before_click_dtend;
4390 day_view_set_selected_time_range ((ECalendarView *) day_view, dtstart, dtend);
4392 e_cal_ops_new_component_editor_from_model (
4393 e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), NULL,
4394 dtstart, dtend, calendar_config_get_prefer_meeting (), FALSE);
4395 return TRUE;
4398 if (!gtk_widget_has_focus (GTK_WIDGET (day_view)) && !gtk_widget_has_focus (GTK_WIDGET (day_view->main_canvas)))
4399 gtk_widget_grab_focus (GTK_WIDGET (day_view));
4401 grab_status = gdk_device_grab (
4402 event_device,
4403 window,
4404 GDK_OWNERSHIP_NONE,
4405 FALSE,
4406 GDK_POINTER_MOTION_MASK |
4407 GDK_BUTTON_RELEASE_MASK,
4408 NULL,
4409 event_time);
4411 if (grab_status == GDK_GRAB_SUCCESS) {
4412 g_warn_if_fail (day_view->grabbed_pointer == NULL);
4413 day_view->grabbed_pointer = g_object_ref (event_device);
4415 if (event_time - day_view->bc_event_time > 250)
4416 day_view_get_selected_time_range (
4417 E_CALENDAR_VIEW (day_view),
4418 &day_view->before_click_dtstart,
4419 &day_view->before_click_dtend);
4420 day_view->bc_event_time = event_time;
4421 e_day_view_start_selection (day_view, day, row);
4422 g_signal_emit_by_name (day_view, "selected_time_changed");
4424 } else if (event_button == 3) {
4425 if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4426 gtk_widget_grab_focus (GTK_WIDGET (day_view));
4428 if ((day < day_view->selection_start_day || day > day_view->selection_end_day)
4429 || (day == day_view->selection_start_day && row < day_view->selection_start_row)
4430 || (day == day_view->selection_end_day && row > day_view->selection_end_row)) {
4431 e_day_view_start_selection (day_view, day, row);
4432 e_day_view_finish_selection (day_view);
4435 e_day_view_on_event_right_click (day_view, button_event, -1, -1);
4438 return TRUE;
4441 static gboolean
4442 e_day_view_on_main_canvas_scroll (GtkWidget *widget,
4443 GdkEventScroll *scroll,
4444 EDayView *day_view)
4446 switch (scroll->direction) {
4447 case GDK_SCROLL_UP:
4448 e_day_view_scroll (day_view, E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4449 return TRUE;
4450 case GDK_SCROLL_DOWN:
4451 e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4452 return TRUE;
4453 case GDK_SCROLL_SMOOTH:
4454 if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) {
4455 e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE * scroll->delta_y);
4456 return TRUE;
4458 break;
4459 default:
4460 break;
4463 return FALSE;
4466 static gboolean
4467 e_day_view_on_top_canvas_scroll (GtkWidget *widget,
4468 GdkEventScroll *scroll,
4469 EDayView *day_view)
4471 switch (scroll->direction) {
4472 case GDK_SCROLL_UP:
4473 e_day_view_top_scroll (day_view, E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4474 return TRUE;
4475 case GDK_SCROLL_DOWN:
4476 e_day_view_top_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4477 return TRUE;
4478 case GDK_SCROLL_SMOOTH:
4479 if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) {
4480 e_day_view_top_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE * scroll->delta_y);
4481 return TRUE;
4483 break;
4484 default:
4485 break;
4488 return FALSE;
4491 static gboolean
4492 e_day_view_on_time_canvas_scroll (GtkWidget *widget,
4493 GdkEventScroll *scroll,
4494 EDayView *day_view)
4496 GtkWidget *tool_window = g_object_get_data ((GObject *) day_view, "tooltip-window");
4498 if (tool_window) {
4499 gtk_widget_destroy (tool_window);
4500 g_object_set_data (G_OBJECT (day_view), "tooltip-window", NULL);
4503 switch (scroll->direction) {
4504 case GDK_SCROLL_UP:
4505 e_day_view_scroll (day_view, E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4506 return TRUE;
4507 case GDK_SCROLL_DOWN:
4508 e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE);
4509 return TRUE;
4510 case GDK_SCROLL_SMOOTH:
4511 if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) {
4512 e_day_view_scroll (day_view, -E_DAY_VIEW_WHEEL_MOUSE_STEP_SIZE * scroll->delta_y);
4513 return TRUE;
4515 break;
4516 default:
4517 break;
4520 return FALSE;
4523 static gboolean
4524 e_day_view_on_long_event_button_press (EDayView *day_view,
4525 gint event_num,
4526 GdkEvent *button_event,
4527 ECalendarViewPosition pos,
4528 gint event_x,
4529 gint event_y)
4531 guint event_button = 0;
4533 gdk_event_get_button (button_event, &event_button);
4535 if (event_button == 1) {
4536 if (button_event->type == GDK_BUTTON_PRESS) {
4537 e_day_view_on_long_event_click (
4538 day_view, event_num,
4539 button_event, pos,
4540 event_x, event_y);
4541 return TRUE;
4542 } else if (button_event->type == GDK_2BUTTON_PRESS) {
4543 e_day_view_on_event_double_click (
4544 day_view, -1,
4545 event_num);
4546 g_signal_stop_emission_by_name (day_view->top_canvas, "button_press_event");
4547 return TRUE;
4549 } else if (event_button == 3) {
4550 EDayViewEvent *e;
4552 if (!is_array_index_in_bounds (day_view->long_events, event_num))
4553 return TRUE;
4555 e = &g_array_index (day_view->long_events, EDayViewEvent, event_num);
4557 e_day_view_set_selected_time_range_in_top_visible (day_view, e->start, e->end);
4559 e_day_view_on_event_right_click (
4560 day_view, button_event,
4561 E_DAY_VIEW_LONG_EVENT,
4562 event_num);
4564 return TRUE;
4566 return FALSE;
4569 static gboolean
4570 e_day_view_on_event_button_press (EDayView *day_view,
4571 gint day,
4572 gint event_num,
4573 GdkEvent *button_event,
4574 ECalendarViewPosition pos,
4575 gint event_x,
4576 gint event_y)
4578 guint event_button = 0;
4580 gdk_event_get_button (button_event, &event_button);
4582 if (event_button == 1) {
4583 if (button_event->type == GDK_BUTTON_PRESS) {
4584 e_day_view_on_event_click (
4585 day_view, day, event_num,
4586 button_event, pos,
4587 event_x, event_y);
4588 return TRUE;
4589 } else if (button_event->type == GDK_2BUTTON_PRESS) {
4590 e_day_view_on_event_double_click (
4591 day_view, day,
4592 event_num);
4594 g_signal_stop_emission_by_name (day_view->main_canvas, "button_press_event");
4595 return TRUE;
4597 } else if (event_button == 3) {
4598 EDayViewEvent *e;
4600 if (!is_array_index_in_bounds (day_view->events[day], event_num))
4601 return TRUE;
4603 e = &g_array_index (day_view->events[day], EDayViewEvent, event_num);
4605 e_day_view_set_selected_time_range_visible (day_view, e->start, e->end);
4607 e_day_view_on_event_right_click (
4608 day_view, button_event, day, event_num);
4610 return TRUE;
4612 return FALSE;
4615 static void
4616 e_day_view_on_long_event_click (EDayView *day_view,
4617 gint event_num,
4618 GdkEvent *button_event,
4619 ECalendarViewPosition pos,
4620 gint event_x,
4621 gint event_y)
4623 EDayViewEvent *event;
4624 GtkLayout *layout;
4625 GdkWindow *window;
4626 gint start_day, end_day, day;
4627 gint item_x, item_y, item_w, item_h;
4629 if (!is_array_index_in_bounds (day_view->long_events, event_num))
4630 return;
4632 event = &g_array_index (day_view->long_events, EDayViewEvent,
4633 event_num);
4635 if (!is_comp_data_valid (event))
4636 return;
4638 /* Ignore clicks on the EText while editing. */
4639 if (pos == E_CALENDAR_VIEW_POS_EVENT
4640 && E_TEXT (event->canvas_item)->editing) {
4641 GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (
4642 event->canvas_item, button_event);
4643 return;
4646 if ((e_cal_util_component_is_instance (event->comp_data->icalcomp) ||
4647 !e_cal_util_component_has_recurrences (event->comp_data->icalcomp))
4648 && (pos == E_CALENDAR_VIEW_POS_LEFT_EDGE
4649 || pos == E_CALENDAR_VIEW_POS_RIGHT_EDGE)) {
4650 GdkGrabStatus grab_status;
4651 GdkDevice *event_device;
4652 guint32 event_time;
4654 if (!e_day_view_find_long_event_days (
4655 event,
4656 e_day_view_get_days_shown (day_view),
4657 day_view->day_starts,
4658 &start_day, &end_day))
4659 return;
4661 /* Grab the keyboard focus, so the event being edited is saved
4662 * and we can use the Escape key to abort the resize. */
4663 if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4664 gtk_widget_grab_focus (GTK_WIDGET (day_view));
4666 layout = GTK_LAYOUT (day_view->top_canvas);
4667 window = gtk_layout_get_bin_window (layout);
4669 event_device = gdk_event_get_device (button_event);
4670 event_time = gdk_event_get_time (button_event);
4672 grab_status = gdk_device_grab (
4673 event_device,
4674 window,
4675 GDK_OWNERSHIP_NONE,
4676 FALSE,
4677 GDK_POINTER_MOTION_MASK |
4678 GDK_BUTTON_RELEASE_MASK,
4679 NULL,
4680 event_time);
4682 if (grab_status == GDK_GRAB_SUCCESS) {
4683 g_warn_if_fail (day_view->grabbed_pointer == NULL);
4684 day_view->grabbed_pointer = g_object_ref (event_device);
4686 day_view->resize_event_day = E_DAY_VIEW_LONG_EVENT;
4687 day_view->resize_event_num = event_num;
4688 day_view->resize_drag_pos = pos;
4689 day_view->resize_start_row = start_day;
4690 day_view->resize_end_row = end_day;
4692 /* Raise the event's item, above the rect as well. */
4693 gnome_canvas_item_raise_to_top (event->canvas_item);
4695 } else if (e_day_view_get_long_event_position (day_view, event_num,
4696 &start_day, &end_day,
4697 &item_x, &item_y,
4698 &item_w, &item_h)) {
4699 /* Remember the item clicked and the mouse position,
4700 * so we can start a drag if the mouse moves. */
4701 day_view->pressed_event_day = E_DAY_VIEW_LONG_EVENT;
4702 day_view->pressed_event_num = event_num;
4704 day_view->drag_event_x = event_x;
4705 day_view->drag_event_y = event_y;
4707 pos = e_day_view_convert_position_in_top_canvas (
4708 day_view,
4709 event_x, event_y,
4710 &day, NULL);
4712 if (pos != E_CALENDAR_VIEW_POS_NONE && pos != E_CALENDAR_VIEW_POS_OUTSIDE)
4713 day_view->drag_event_offset = day - start_day;
4717 static void
4718 e_day_view_on_event_click (EDayView *day_view,
4719 gint day,
4720 gint event_num,
4721 GdkEvent *button_event,
4722 ECalendarViewPosition pos,
4723 gint event_x,
4724 gint event_y)
4726 EDayViewEvent *event;
4727 ECalendarView *cal_view;
4728 GtkLayout *layout;
4729 GdkWindow *window;
4730 gint time_divisions;
4731 gint tmp_day, row, start_row;
4733 cal_view = E_CALENDAR_VIEW (day_view);
4734 time_divisions = e_calendar_view_get_time_divisions (cal_view);
4736 if (!is_array_index_in_bounds (day_view->events[day], event_num))
4737 return;
4739 event = &g_array_index (day_view->events[day], EDayViewEvent,
4740 event_num);
4742 if (!is_comp_data_valid (event))
4743 return;
4745 /* Ignore clicks on the EText while editing. */
4746 if (pos == E_CALENDAR_VIEW_POS_EVENT
4747 && E_TEXT (event->canvas_item)->editing) {
4748 GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (
4749 event->canvas_item, button_event);
4750 return;
4753 if ((e_cal_util_component_is_instance (event->comp_data->icalcomp) ||
4754 !e_cal_util_component_has_recurrences (event->comp_data->icalcomp))
4755 && (pos == E_CALENDAR_VIEW_POS_TOP_EDGE
4756 || pos == E_CALENDAR_VIEW_POS_BOTTOM_EDGE)) {
4757 GdkGrabStatus grab_status;
4758 GdkDevice *event_device;
4759 guint32 event_time;
4761 if (event && (!event->is_editable || e_client_is_readonly (E_CLIENT (event->comp_data->client)))) {
4762 return;
4765 /* Grab the keyboard focus, so the event being edited is saved
4766 * and we can use the Escape key to abort the resize. */
4767 if (!gtk_widget_has_focus (GTK_WIDGET (day_view)))
4768 gtk_widget_grab_focus (GTK_WIDGET (day_view));
4770 layout = GTK_LAYOUT (day_view->main_canvas);
4771 window = gtk_layout_get_bin_window (layout);
4773 event_device = gdk_event_get_device (button_event);
4774 event_time = gdk_event_get_time (button_event);
4776 grab_status = gdk_device_grab (
4777 event_device,
4778 window,
4779 GDK_OWNERSHIP_NONE,
4780 FALSE,
4781 GDK_POINTER_MOTION_MASK |
4782 GDK_BUTTON_RELEASE_MASK,
4783 NULL,
4784 event_time);
4786 if (grab_status == GDK_GRAB_SUCCESS) {
4787 g_warn_if_fail (day_view->grabbed_pointer == NULL);
4788 day_view->grabbed_pointer = g_object_ref (event_device);
4790 day_view->resize_event_day = day;
4791 day_view->resize_event_num = event_num;
4792 day_view->resize_drag_pos = pos;
4793 day_view->resize_start_row = event->start_minute / time_divisions;
4794 day_view->resize_end_row = (event->end_minute - 1) / time_divisions;
4795 if (day_view->resize_end_row < day_view->resize_start_row)
4796 day_view->resize_end_row = day_view->resize_start_row;
4798 day_view->resize_bars_event_day = day;
4799 day_view->resize_bars_event_num = event_num;
4801 e_day_view_reshape_main_canvas_resize_bars (day_view);
4803 /* Raise the event's item, above the rect as well. */
4804 gnome_canvas_item_raise_to_top (event->canvas_item);
4807 } else {
4808 /* Remember the item clicked and the mouse position,
4809 * so we can start a drag if the mouse moves. */
4810 day_view->pressed_event_day = day;
4811 day_view->pressed_event_num = event_num;
4813 day_view->drag_event_x = event_x;
4814 day_view->drag_event_y = event_y;
4816 pos = e_day_view_convert_position_in_main_canvas (
4817 day_view,
4818 event_x, event_y,
4819 &tmp_day, &row,
4820 NULL);
4822 if (pos != E_CALENDAR_VIEW_POS_NONE && pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
4823 start_row = event->start_minute / time_divisions;
4824 day_view->drag_event_offset = row - start_row;
4829 static void
4830 e_day_view_on_event_double_click (EDayView *day_view,
4831 gint day,
4832 gint event_num)
4834 EDayViewEvent *event;
4836 if (day == -1) {
4837 if (!is_array_index_in_bounds (day_view->long_events, event_num))
4838 return;
4840 event = &g_array_index (day_view->long_events, EDayViewEvent,
4841 event_num);
4842 } else {
4843 if (!is_array_index_in_bounds (day_view->events[day], event_num))
4844 return;
4846 event = &g_array_index (day_view->events[day], EDayViewEvent,
4847 event_num);
4850 if (!is_comp_data_valid (event))
4851 return;
4853 e_calendar_view_edit_appointment ((ECalendarView *) day_view, event->comp_data->client, event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT);
4856 static void
4857 e_day_view_show_popup_menu (EDayView *day_view,
4858 GdkEvent *button_event,
4859 gint day,
4860 gint event_num)
4862 EDayViewEvent *pevent = NULL;
4864 if (event_num >= 0)
4865 pevent = tooltip_get_view_event (day_view, day, event_num);
4867 if (pevent && pevent->canvas_item)
4868 tooltip_destroy (day_view, pevent->canvas_item);
4870 day_view->popup_event_day = day;
4871 day_view->popup_event_num = event_num;
4873 e_calendar_view_popup_event (E_CALENDAR_VIEW (day_view), button_event);
4876 /* Restarts a query for the day view */
4877 static void
4878 e_day_view_update_query (EDayView *day_view)
4880 gint rows, r;
4882 if (!E_CALENDAR_VIEW (day_view)->in_focus) {
4883 e_day_view_free_events (day_view);
4884 day_view->requires_update = TRUE;
4885 return;
4888 day_view->requires_update = FALSE;
4890 e_day_view_stop_editing_event (day_view);
4892 gtk_widget_queue_draw (day_view->top_canvas);
4893 gtk_widget_queue_draw (day_view->top_dates_canvas);
4894 gtk_widget_queue_draw (day_view->main_canvas);
4895 e_day_view_free_events (day_view);
4896 e_day_view_queue_layout (day_view);
4898 rows = e_table_model_row_count (E_TABLE_MODEL (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view))));
4899 for (r = 0; r < rows; r++) {
4900 ECalModelComponent *comp_data;
4902 comp_data = e_cal_model_get_component_at (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), r);
4903 g_return_if_fail (comp_data != NULL);
4904 process_component (day_view, comp_data);
4908 static void
4909 e_day_view_on_event_right_click (EDayView *day_view,
4910 GdkEvent *button_event,
4911 gint day,
4912 gint event_num)
4914 e_day_view_show_popup_menu (day_view, button_event, day, event_num);
4917 static gboolean
4918 e_day_view_on_top_canvas_button_release (GtkWidget *widget,
4919 GdkEvent *button_event,
4920 EDayView *day_view)
4922 GdkDevice *event_device;
4923 guint32 event_time;
4925 event_device = gdk_event_get_device (button_event);
4926 event_time = gdk_event_get_time (button_event);
4928 if (day_view->grabbed_pointer == event_device) {
4929 gdk_device_ungrab (day_view->grabbed_pointer, event_time);
4930 g_object_unref (day_view->grabbed_pointer);
4931 day_view->grabbed_pointer = NULL;
4934 if (day_view->selection_is_being_dragged) {
4935 e_day_view_finish_selection (day_view);
4936 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
4937 e_day_view_finish_long_event_resize (day_view);
4938 } else if (day_view->pressed_event_day != -1) {
4939 e_day_view_start_editing_event (
4940 day_view,
4941 day_view->pressed_event_day,
4942 day_view->pressed_event_num,
4943 NULL);
4946 day_view->pressed_event_day = -1;
4948 return FALSE;
4951 static gboolean
4952 e_day_view_on_main_canvas_button_release (GtkWidget *widget,
4953 GdkEvent *button_event,
4954 EDayView *day_view)
4956 GdkDevice *event_device;
4957 guint32 event_time;
4959 event_device = gdk_event_get_device (button_event);
4960 event_time = gdk_event_get_time (button_event);
4962 if (day_view->grabbed_pointer == event_device) {
4963 gdk_device_ungrab (day_view->grabbed_pointer, event_time);
4964 g_object_unref (day_view->grabbed_pointer);
4965 day_view->grabbed_pointer = NULL;
4968 if (day_view->selection_is_being_dragged) {
4969 e_day_view_finish_selection (day_view);
4970 e_day_view_stop_auto_scroll (day_view);
4971 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
4972 e_day_view_finish_resize (day_view);
4973 e_day_view_stop_auto_scroll (day_view);
4974 } else if (day_view->pressed_event_day != -1) {
4975 e_day_view_start_editing_event (
4976 day_view,
4977 day_view->pressed_event_day,
4978 day_view->pressed_event_num,
4979 NULL);
4982 day_view->pressed_event_day = -1;
4984 return FALSE;
4987 void
4988 e_day_view_update_calendar_selection_time (EDayView *day_view)
4990 time_t start, end;
4992 day_view_get_selected_time_range ((ECalendarView *) day_view, &start, &end);
4995 static gboolean
4996 e_day_view_on_top_canvas_motion (GtkWidget *widget,
4997 GdkEventMotion *mevent,
4998 EDayView *day_view)
5000 EDayViewEvent *event = NULL;
5001 ECalendarViewPosition pos;
5002 gint event_x, event_y, canvas_x, canvas_y;
5003 gint day, event_num;
5004 GdkCursor *cursor;
5005 GdkWindow *window;
5007 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
5009 /* Convert the coords to the main canvas window, or return if the
5010 * window is not found. */
5011 if (!e_day_view_convert_event_coords (
5012 day_view, (GdkEvent *) mevent, window, &event_x, &event_y))
5013 return FALSE;
5015 canvas_x = event_x;
5016 canvas_y = event_y;
5018 pos = e_day_view_convert_position_in_top_canvas (
5019 day_view,
5020 canvas_x, canvas_y,
5021 &day, &event_num);
5022 if (event_num != -1) {
5023 if (!is_array_index_in_bounds (day_view->long_events, event_num))
5024 return FALSE;
5026 event = &g_array_index (day_view->long_events, EDayViewEvent,
5027 event_num);
5030 if (day_view->selection_is_being_dragged) {
5031 e_day_view_update_selection (day_view, day, -1);
5032 return TRUE;
5033 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
5034 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
5035 e_day_view_update_long_event_resize (day_view, day);
5036 return TRUE;
5038 } else if (day_view->pressed_event_day == E_DAY_VIEW_LONG_EVENT) {
5039 GtkTargetList *target_list;
5041 if (!is_array_index_in_bounds (day_view->long_events, day_view->pressed_event_num))
5042 return FALSE;
5044 event = &g_array_index (day_view->long_events, EDayViewEvent,
5045 day_view->pressed_event_num);
5047 if (!is_comp_data_valid (event))
5048 return FALSE;
5050 if (!e_cal_util_component_has_recurrences (event->comp_data->icalcomp)
5051 && gtk_drag_check_threshold (widget, day_view->drag_event_x, day_view->drag_event_y, canvas_x, canvas_y)) {
5052 day_view->drag_event_day = day_view->pressed_event_day;
5053 day_view->drag_event_num = day_view->pressed_event_num;
5054 day_view->pressed_event_day = -1;
5056 /* Hide the horizontal bars. */
5057 if (day_view->resize_bars_event_day != -1) {
5058 day_view->resize_bars_event_day = -1;
5059 day_view->resize_bars_event_num = -1;
5062 target_list = gtk_target_list_new (
5063 target_table, G_N_ELEMENTS (target_table));
5064 e_target_list_add_calendar_targets (target_list, 0);
5065 g_clear_object (&day_view->priv->drag_context);
5066 day_view->priv->drag_context = gtk_drag_begin (
5067 widget, target_list,
5068 GDK_ACTION_COPY | GDK_ACTION_MOVE,
5069 1, (GdkEvent *) mevent);
5070 gtk_target_list_unref (target_list);
5072 if (day_view->priv->drag_context)
5073 g_object_ref (day_view->priv->drag_context);
5075 } else {
5076 cursor = day_view->normal_cursor;
5078 /* Recurring events can't be resized. */
5079 if (event && is_comp_data_valid (event) && !e_cal_util_component_has_recurrences (event->comp_data->icalcomp)) {
5080 switch (pos) {
5081 case E_CALENDAR_VIEW_POS_LEFT_EDGE:
5082 case E_CALENDAR_VIEW_POS_RIGHT_EDGE:
5083 cursor = day_view->resize_width_cursor;
5084 break;
5085 default:
5086 break;
5090 /* Only set the cursor if it is different to last one set. */
5091 if (day_view->last_cursor_set_in_top_canvas != cursor) {
5092 GdkWindow *window;
5094 day_view->last_cursor_set_in_top_canvas = cursor;
5096 window = gtk_widget_get_window (widget);
5097 gdk_window_set_cursor (window, cursor);
5100 if (event && E_IS_TEXT (event->canvas_item) && E_TEXT (event->canvas_item)->editing) {
5101 GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (event->canvas_item, (GdkEvent *) mevent);
5105 return FALSE;
5108 static gboolean
5109 e_day_view_on_main_canvas_motion (GtkWidget *widget,
5110 GdkEventMotion *mevent,
5111 EDayView *day_view)
5113 EDayViewEvent *event = NULL;
5114 ECalendarViewPosition pos;
5115 gint event_x, event_y, canvas_x, canvas_y;
5116 gint row, day, event_num;
5117 GdkWindow *window;
5118 GdkCursor *cursor;
5120 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
5122 /* Convert the coords to the main canvas window, or return if the
5123 * window is not found. */
5124 if (!e_day_view_convert_event_coords (
5125 day_view, (GdkEvent *) mevent, window, &event_x, &event_y))
5126 return FALSE;
5128 canvas_x = event_x;
5129 canvas_y = event_y;
5131 pos = e_day_view_convert_position_in_main_canvas (
5132 day_view,
5133 canvas_x, canvas_y,
5134 &day, &row,
5135 &event_num);
5136 if (event_num != -1) {
5137 if (!is_array_index_in_bounds (day_view->events[day], event_num))
5138 return FALSE;
5140 event = &g_array_index (day_view->events[day], EDayViewEvent,
5141 event_num);
5144 if (day_view->selection_is_being_dragged) {
5145 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
5146 e_day_view_update_selection (day_view, day, row);
5147 e_day_view_check_auto_scroll (
5148 day_view,
5149 event_x, event_y);
5150 return TRUE;
5152 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
5153 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
5154 e_day_view_update_resize (day_view, row);
5155 e_day_view_check_auto_scroll (
5156 day_view,
5157 event_x, event_y);
5158 return TRUE;
5160 } else if (day_view->pressed_event_day != -1
5161 && day_view->pressed_event_day != E_DAY_VIEW_LONG_EVENT) {
5162 GtkTargetList *target_list;
5164 if (gtk_drag_check_threshold (widget, day_view->drag_event_x, day_view->drag_event_y, canvas_x, canvas_y)) {
5165 day_view->drag_event_day = day_view->pressed_event_day;
5166 day_view->drag_event_num = day_view->pressed_event_num;
5167 day_view->pressed_event_day = -1;
5169 /* Hide the horizontal bars. */
5170 if (day_view->resize_bars_event_day != -1) {
5171 day_view->resize_bars_event_day = -1;
5172 day_view->resize_bars_event_num = -1;
5175 target_list = gtk_target_list_new (
5176 target_table, G_N_ELEMENTS (target_table));
5177 e_target_list_add_calendar_targets (target_list, 0);
5178 g_clear_object (&day_view->priv->drag_context);
5179 day_view->priv->drag_context = gtk_drag_begin (
5180 widget, target_list,
5181 GDK_ACTION_COPY | GDK_ACTION_MOVE,
5182 1, (GdkEvent *) mevent);
5183 gtk_target_list_unref (target_list);
5185 if (day_view->priv->drag_context)
5186 g_object_ref (day_view->priv->drag_context);
5188 } else {
5189 cursor = day_view->normal_cursor;
5191 /* Check if the event is editable and client is not readonly while changing the cursor */
5192 if (event && event->is_editable && is_comp_data_valid (event) && !e_client_is_readonly (E_CLIENT (event->comp_data->client))) {
5194 switch (pos) {
5195 case E_CALENDAR_VIEW_POS_LEFT_EDGE:
5196 cursor = day_view->move_cursor;
5197 break;
5198 case E_CALENDAR_VIEW_POS_TOP_EDGE:
5199 case E_CALENDAR_VIEW_POS_BOTTOM_EDGE:
5200 cursor = day_view->resize_height_cursor;
5201 break;
5202 default:
5203 break;
5207 /* Only set the cursor if it is different to last one set. */
5208 if (day_view->last_cursor_set_in_main_canvas != cursor) {
5209 GdkWindow *window;
5211 day_view->last_cursor_set_in_main_canvas = cursor;
5213 window = gtk_widget_get_window (widget);
5214 gdk_window_set_cursor (window, cursor);
5217 if (event && E_IS_TEXT (event->canvas_item) && E_TEXT (event->canvas_item)->editing) {
5218 GNOME_CANVAS_ITEM_GET_CLASS (event->canvas_item)->event (event->canvas_item, (GdkEvent *) mevent);
5222 return FALSE;
5225 /* This sets the selection to a single cell. If day is -1 then the current
5226 * start day is reused. If row is -1 then the selection is in the top canvas.
5228 void
5229 e_day_view_start_selection (EDayView *day_view,
5230 gint day,
5231 gint row)
5233 if (day == -1) {
5234 day = day_view->selection_start_day;
5235 if (day == -1)
5236 day = 0;
5239 day_view->selection_start_day = day;
5240 day_view->selection_end_day = day;
5242 day_view->selection_start_row = row;
5243 day_view->selection_end_row = row;
5245 day_view->selection_is_being_dragged = TRUE;
5246 day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
5247 day_view->selection_in_top_canvas = (row == -1) ? TRUE : FALSE;
5249 /* FIXME: Optimise? */
5250 gtk_widget_queue_draw (day_view->top_canvas);
5251 gtk_widget_queue_draw (day_view->main_canvas);
5254 /* Updates the selection during a drag. If day is -1 the selection day is
5255 * unchanged. */
5256 void
5257 e_day_view_update_selection (EDayView *day_view,
5258 gint day,
5259 gint row)
5261 gboolean need_redraw = FALSE;
5263 day_view->selection_in_top_canvas = (row == -1) ? TRUE : FALSE;
5265 if (day == -1)
5266 day = (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
5267 ? day_view->selection_start_day
5268 : day_view->selection_end_day;
5270 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) {
5271 if (row != day_view->selection_start_row
5272 || day != day_view->selection_start_day) {
5273 need_redraw = TRUE;
5274 day_view->selection_start_row = row;
5275 day_view->selection_start_day = day;
5277 } else {
5278 if (row != day_view->selection_end_row
5279 || day != day_view->selection_end_day) {
5280 need_redraw = TRUE;
5281 day_view->selection_end_row = row;
5282 day_view->selection_end_day = day;
5286 e_day_view_normalize_selection (day_view);
5288 /* FIXME: Optimise? */
5289 if (need_redraw) {
5290 gtk_widget_queue_draw (day_view->top_canvas);
5291 gtk_widget_queue_draw (day_view->main_canvas);
5295 static void
5296 e_day_view_normalize_selection (EDayView *day_view)
5298 gint tmp_row, tmp_day;
5300 /* Switch the drag position if necessary. */
5301 if (day_view->selection_start_day > day_view->selection_end_day
5302 || (day_view->selection_start_day == day_view->selection_end_day
5303 && day_view->selection_start_row > day_view->selection_end_row)) {
5304 tmp_row = day_view->selection_start_row;
5305 tmp_day = day_view->selection_start_day;
5306 day_view->selection_start_day = day_view->selection_end_day;
5307 day_view->selection_start_row = day_view->selection_end_row;
5308 day_view->selection_end_day = tmp_day;
5309 day_view->selection_end_row = tmp_row;
5310 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
5311 day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
5312 else
5313 day_view->selection_drag_pos = E_DAY_VIEW_DRAG_START;
5317 void
5318 e_day_view_finish_selection (EDayView *day_view)
5320 day_view->selection_is_being_dragged = FALSE;
5321 e_day_view_update_calendar_selection_time (day_view);
5324 static void
5325 e_day_view_update_long_event_resize (EDayView *day_view,
5326 gint day)
5328 gint event_num;
5329 gboolean need_reshape = FALSE;
5331 event_num = day_view->resize_event_num;
5333 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE) {
5334 day = MIN (day, day_view->resize_end_row);
5335 if (day != day_view->resize_start_row) {
5336 need_reshape = TRUE;
5337 day_view->resize_start_row = day;
5340 } else {
5341 day = MAX (day, day_view->resize_start_row);
5342 if (day != day_view->resize_end_row) {
5343 need_reshape = TRUE;
5344 day_view->resize_end_row = day;
5348 /* FIXME: Optimise? */
5349 if (need_reshape) {
5350 e_day_view_reshape_long_event (day_view, event_num);
5351 gtk_widget_queue_draw (day_view->top_canvas);
5355 static void
5356 e_day_view_update_resize (EDayView *day_view,
5357 gint row)
5359 /* Same thing again? */
5360 EDayViewEvent *event;
5361 gint day, event_num;
5362 gboolean need_reshape = FALSE;
5364 if (day_view->resize_event_num == -1)
5365 return;
5367 day = day_view->resize_event_day;
5368 event_num = day_view->resize_event_num;
5370 if (!is_array_index_in_bounds (day_view->events[day], event_num))
5371 return;
5373 event = &g_array_index (day_view->events[day], EDayViewEvent,
5374 event_num);
5376 if (event && (!event->is_editable || !is_comp_data_valid (event) || e_client_is_readonly (E_CLIENT (event->comp_data->client)))) {
5377 return;
5380 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) {
5381 row = MIN (row, day_view->resize_end_row);
5382 if (row != day_view->resize_start_row) {
5383 need_reshape = TRUE;
5384 day_view->resize_start_row = row;
5387 } else {
5388 row = MAX (row, day_view->resize_start_row);
5389 if (row != day_view->resize_end_row) {
5390 need_reshape = TRUE;
5391 day_view->resize_end_row = row;
5395 /* FIXME: Optimise? */
5396 if (need_reshape) {
5397 e_day_view_reshape_day_event (day_view, day, event_num);
5398 e_day_view_reshape_main_canvas_resize_bars (day_view);
5399 gtk_widget_queue_draw (day_view->main_canvas);
5403 /* This converts the resize start or end row back to a time and updates the
5404 * event. */
5405 static void
5406 e_day_view_finish_long_event_resize (EDayView *day_view)
5408 EDayViewEvent *event;
5409 gint event_num;
5410 ECalComponent *comp;
5411 ECalComponentDateTime date;
5412 struct icaltimetype itt;
5413 time_t dt;
5414 ECalModel *model;
5415 ECalClient *client;
5416 ESourceRegistry *registry;
5417 ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
5418 gint is_date;
5420 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
5421 registry = e_cal_model_get_registry (model);
5423 event_num = day_view->resize_event_num;
5425 if (!is_array_index_in_bounds (day_view->long_events, event_num))
5426 return;
5428 event = &g_array_index (day_view->long_events, EDayViewEvent,
5429 event_num);
5431 if (!is_comp_data_valid (event))
5432 return;
5434 client = event->comp_data->client;
5436 /* We use a temporary copy of the comp since we don't want to
5437 * change the original comp here. Otherwise we would not detect that
5438 * the event's time had changed in the "update_event" callback. */
5439 comp = e_cal_component_new ();
5440 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
5442 if (e_cal_component_has_attendees (comp) &&
5443 !itip_organizer_is_user (registry, comp, client)) {
5444 g_object_unref (comp);
5445 e_day_view_abort_resize (day_view);
5446 return;
5449 date.value = &itt;
5450 date.tzid = NULL;
5452 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE) {
5453 ECalComponentDateTime ecdt;
5455 e_cal_component_get_dtstart (comp, &ecdt);
5456 is_date = ecdt.value && ecdt.value->is_date;
5457 if (!is_date)
5458 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5459 dt = day_view->day_starts[day_view->resize_start_row];
5460 *date.value = icaltime_from_timet_with_zone (dt, is_date,
5461 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5462 cal_comp_set_dtstart_with_oldzone (client, comp, &date);
5463 e_cal_component_free_datetime (&ecdt);
5464 date.tzid = NULL; /* do not reuse it later */
5465 } else {
5466 ECalComponentDateTime ecdt;
5468 e_cal_component_get_dtend (comp, &ecdt);
5469 is_date = ecdt.value && ecdt.value->is_date;
5470 if (!is_date)
5471 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5472 dt = day_view->day_starts[day_view->resize_end_row + 1];
5473 *date.value = icaltime_from_timet_with_zone (dt, is_date,
5474 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5475 cal_comp_set_dtend_with_oldzone (client, comp, &date);
5476 e_cal_component_free_datetime (&ecdt);
5477 date.tzid = NULL; /* do not reuse it later */
5480 e_cal_component_commit_sequence (comp);
5481 if (e_cal_component_has_recurrences (comp)) {
5482 if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
5483 gtk_widget_queue_draw (day_view->top_canvas);
5484 goto out;
5487 if (mod == E_CAL_OBJ_MOD_THIS) {
5488 /* set the correct DTSTART/DTEND on the individual recurrence */
5489 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) {
5490 *date.value = icaltime_from_timet_with_zone (
5491 event->comp_data->instance_end, FALSE,
5492 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5493 cal_comp_set_dtend_with_oldzone (client, comp, &date);
5494 } else {
5495 *date.value = icaltime_from_timet_with_zone (
5496 event->comp_data->instance_start, FALSE,
5497 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5498 cal_comp_set_dtstart_with_oldzone (client, comp, &date);
5501 e_cal_component_set_rdate_list (comp, NULL);
5502 e_cal_component_set_rrule_list (comp, NULL);
5503 e_cal_component_set_exdate_list (comp, NULL);
5504 e_cal_component_set_exrule_list (comp, NULL);
5506 } else if (e_cal_component_is_instance (comp))
5507 mod = E_CAL_OBJ_MOD_THIS;
5509 e_cal_component_commit_sequence (comp);
5511 e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp),
5512 mod, E_CAL_OPS_SEND_FLAG_ASK | E_CAL_OPS_SEND_FLAG_IS_NEW_COMPONENT);
5514 out:
5515 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
5517 g_object_unref (comp);
5520 /* This converts the resize start or end row back to a time and updates the
5521 * event. */
5522 static void
5523 e_day_view_finish_resize (EDayView *day_view)
5525 EDayViewEvent *event;
5526 gint day, event_num;
5527 ECalComponent *comp;
5528 ECalComponentDateTime date;
5529 struct icaltimetype itt;
5530 time_t dt;
5531 ECalModel *model;
5532 ECalClient *client;
5533 ESourceRegistry *registry;
5534 ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
5535 GtkWindow *toplevel;
5536 GtkResponseType send = GTK_RESPONSE_NO;
5537 gboolean only_new_attendees = FALSE;
5538 gboolean strip_alarms = TRUE;
5540 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
5541 registry = e_cal_model_get_registry (model);
5543 if (day_view->resize_event_num == -1)
5544 return;
5546 day = day_view->resize_event_day;
5547 event_num = day_view->resize_event_num;
5549 if (!is_array_index_in_bounds (day_view->events[day], event_num))
5550 return;
5552 event = &g_array_index (day_view->events[day], EDayViewEvent,
5553 event_num);
5555 if (!is_comp_data_valid (event))
5556 return;
5558 client = event->comp_data->client;
5560 /* We use a temporary shallow copy of the ico since we don't want to
5561 * change the original ico here. Otherwise we would not detect that
5562 * the event's time had changed in the "update_event" callback. */
5563 comp = e_cal_component_new ();
5564 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
5566 if (e_cal_component_has_attendees (comp) &&
5567 !itip_organizer_is_user (registry, comp, client)) {
5568 g_object_unref (comp);
5569 e_day_view_abort_resize (day_view);
5570 return;
5573 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view)));
5575 if (itip_has_any_attendees (comp) &&
5576 (itip_organizer_is_user (registry, comp, client) ||
5577 itip_sentby_is_user (registry, comp, client)))
5578 send = e_cal_dialogs_send_dragged_or_resized_component (
5579 toplevel, client, comp, &strip_alarms, &only_new_attendees);
5581 if (send == GTK_RESPONSE_CANCEL) {
5582 e_day_view_abort_resize (day_view);
5583 goto out;
5586 date.value = &itt;
5587 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5589 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) {
5590 dt = e_day_view_convert_grid_position_to_time (day_view, day, day_view->resize_start_row);
5591 *date.value = icaltime_from_timet_with_zone (dt, FALSE,
5592 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5593 cal_comp_set_dtstart_with_oldzone (client, comp, &date);
5594 } else {
5595 dt = e_day_view_convert_grid_position_to_time (day_view, day, day_view->resize_end_row + 1);
5596 *date.value = icaltime_from_timet_with_zone (dt, FALSE,
5597 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5598 cal_comp_set_dtend_with_oldzone (client, comp, &date);
5601 e_cal_component_commit_sequence (comp);
5603 if (day_view->last_edited_comp_string != NULL) {
5604 g_free (day_view->last_edited_comp_string);
5605 day_view->last_edited_comp_string = NULL;
5608 day_view->last_edited_comp_string = e_cal_component_get_as_string (comp);
5610 /* Hide the horizontal bars. */
5611 day_view->resize_bars_event_day = -1;
5612 day_view->resize_bars_event_num = -1;
5614 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
5616 if (e_cal_component_has_recurrences (comp)) {
5617 if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
5618 gtk_widget_queue_draw (day_view->main_canvas);
5619 goto out;
5622 if (mod == E_CAL_OBJ_MOD_THIS) {
5623 /* set the correct DTSTART/DTEND on the individual recurrence */
5624 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) {
5625 *date.value = icaltime_from_timet_with_zone (
5626 event->comp_data->instance_end, FALSE,
5627 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5628 cal_comp_set_dtend_with_oldzone (client, comp, &date);
5629 } else {
5630 *date.value = icaltime_from_timet_with_zone (
5631 event->comp_data->instance_start, FALSE,
5632 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
5633 cal_comp_set_dtstart_with_oldzone (client, comp, &date);
5636 e_cal_component_set_rdate_list (comp, NULL);
5637 e_cal_component_set_rrule_list (comp, NULL);
5638 e_cal_component_set_exdate_list (comp, NULL);
5639 e_cal_component_set_exrule_list (comp, NULL);
5641 } else if (e_cal_component_is_instance (comp))
5642 mod = E_CAL_OBJ_MOD_THIS;
5644 e_cal_component_commit_sequence (comp);
5646 e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp), mod,
5647 (send == GTK_RESPONSE_YES ? E_CAL_OPS_SEND_FLAG_SEND : E_CAL_OPS_SEND_FLAG_DONT_SEND) |
5648 (strip_alarms ? E_CAL_OPS_SEND_FLAG_STRIP_ALARMS : 0) |
5649 (only_new_attendees ? E_CAL_OPS_SEND_FLAG_ONLY_NEW_ATTENDEES : 0));
5651 out:
5652 g_object_unref (comp);
5655 static void
5656 e_day_view_abort_resize (EDayView *day_view)
5658 GdkWindow *window;
5659 gint day, event_num;
5661 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_NONE)
5662 return;
5664 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
5666 day = day_view->resize_event_day;
5667 event_num = day_view->resize_event_num;
5669 if (day == E_DAY_VIEW_LONG_EVENT) {
5670 e_day_view_reshape_long_event (day_view, event_num);
5671 gtk_widget_queue_draw (day_view->top_canvas);
5673 day_view->last_cursor_set_in_top_canvas = day_view->normal_cursor;
5674 window = gtk_widget_get_window (day_view->top_canvas);
5675 gdk_window_set_cursor (window, day_view->normal_cursor);
5676 } else {
5677 e_day_view_reshape_day_event (day_view, day, event_num);
5678 e_day_view_reshape_main_canvas_resize_bars (day_view);
5679 gtk_widget_queue_draw (day_view->main_canvas);
5681 day_view->last_cursor_set_in_main_canvas = day_view->normal_cursor;
5682 window = gtk_widget_get_window (day_view->main_canvas);
5683 gdk_window_set_cursor (window, day_view->normal_cursor);
5687 static void
5688 e_day_view_free_events (EDayView *day_view)
5690 gint day;
5691 gboolean did_editing = day_view->editing_event_day != -1;
5693 /* Reset all our indices. */
5694 day_view->editing_event_day = -1;
5695 day_view->popup_event_day = -1;
5696 day_view->resize_bars_event_day = -1;
5697 day_view->resize_event_day = -1;
5698 day_view->pressed_event_day = -1;
5699 day_view->drag_event_day = -1;
5700 day_view->editing_event_num = -1;
5701 day_view->popup_event_num = -1;
5703 g_clear_object (&day_view->priv->drag_context);
5705 e_day_view_free_event_array (day_view, day_view->long_events);
5707 for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
5708 e_day_view_free_event_array (day_view, day_view->events[day]);
5710 if (did_editing)
5711 g_object_notify (G_OBJECT (day_view), "is-editing");
5714 static void
5715 e_day_view_free_event_array (EDayView *day_view,
5716 GArray *array)
5718 EDayViewEvent *event;
5719 gint event_num;
5721 for (event_num = 0; event_num < array->len; event_num++) {
5722 event = &g_array_index (array, EDayViewEvent, event_num);
5723 if (event->canvas_item)
5724 g_object_run_dispose (G_OBJECT (event->canvas_item));
5726 if (is_comp_data_valid (event))
5727 g_object_unref (event->comp_data);
5729 if (event->timeout > 0) {
5730 g_source_remove (event->timeout);
5731 event->timeout = -1;
5735 g_array_set_size (array, 0);
5738 /* This adds one event to the view, adding it to the appropriate array. */
5739 static void
5740 e_day_view_add_event (ESourceRegistry *registry,
5741 ECalClient *client,
5742 ECalComponent *comp,
5743 time_t start,
5744 time_t end,
5745 gpointer data)
5748 EDayViewEvent event;
5749 gint day, offset;
5750 gint days_shown;
5751 struct icaltimetype start_tt, end_tt;
5752 AddEventData *add_event_data;
5753 icaltimezone *zone;
5755 add_event_data = data;
5757 /*if (end < start || start >= add_event_data->day_view->upper || end < add_event_data->day_view->lower) {
5758 g_print ("%s: day_view: %p\n", G_STRFUNC, add_event_data->day_view);
5759 g_print ("\tDay view lower: %s", ctime (&add_event_data->day_view->lower));
5760 g_print ("\tDay view upper: %s", ctime (&add_event_data->day_view->upper));
5761 g_print ("\tEvent start: %s", ctime (&start));
5762 g_print ("\tEvent end : %s\n", ctime (&end));
5765 /* Check that the event times are valid. */
5766 g_return_if_fail (start <= end);
5767 g_return_if_fail (start < add_event_data->day_view->upper);
5769 if (end != start || end < add_event_data->day_view->lower)
5770 g_return_if_fail (end > add_event_data->day_view->lower);
5772 zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->day_view));
5773 start_tt = icaltime_from_timet_with_zone (start, FALSE, zone);
5774 end_tt = icaltime_from_timet_with_zone (end, FALSE, zone);
5776 if (add_event_data->comp_data) {
5777 event.comp_data = g_object_ref (add_event_data->comp_data);
5778 } else {
5779 event.comp_data = g_object_new (E_TYPE_CAL_MODEL_COMPONENT, NULL);
5780 event.comp_data->is_new_component = TRUE;
5781 event.comp_data->client = g_object_ref (client);
5782 e_cal_component_abort_sequence (comp);
5783 event.comp_data->icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
5786 event.start = start;
5787 event.tooltip = NULL;
5788 event.color = NULL;
5789 event.timeout = -1;
5790 event.end = end;
5791 event.canvas_item = NULL;
5792 event.comp_data->instance_start = start;
5793 event.comp_data->instance_end = end;
5795 /* Calculate the start & end minute, relative to the top of the
5796 * display. */
5797 offset = add_event_data->day_view->first_hour_shown * 60
5798 + add_event_data->day_view->first_minute_shown;
5799 event.start_minute = start_tt.hour * 60 + start_tt.minute - offset;
5800 event.end_minute = end_tt.hour * 60 + end_tt.minute - offset;
5802 event.start_row_or_col = 0;
5803 event.num_columns = 0;
5805 event.different_timezone = FALSE;
5806 if (!cal_comp_util_compare_event_timezones (comp, event.comp_data->client, zone))
5807 event.different_timezone = TRUE;
5809 if (!e_cal_component_has_attendees (comp) ||
5810 itip_organizer_is_user (registry, comp, event.comp_data->client) ||
5811 itip_sentby_is_user (registry, comp, event.comp_data->client))
5812 event.is_editable = TRUE;
5813 else
5814 event.is_editable = FALSE;
5816 days_shown = e_day_view_get_days_shown (add_event_data->day_view);
5818 /* Find out which array to add the event to. */
5819 for (day = 0; day < days_shown; day++) {
5820 if (start >= add_event_data->day_view->day_starts[day]
5821 && end <= add_event_data->day_view->day_starts[day + 1]) {
5823 if (start == end && start == add_event_data->day_view->day_starts[day + 1])
5824 continue;
5826 /* Special case for when the appointment ends at
5827 * midnight, i.e. the start of the next day. */
5828 if (end == add_event_data->day_view->day_starts[day + 1] && start != end) {
5830 /* If the event last the entire day, then we
5831 * skip it here so it gets added to the top
5832 * canvas. */
5833 if (start == add_event_data->day_view->day_starts[day])
5834 break;
5836 event.end_minute = 24 * 60;
5839 g_array_append_val (add_event_data->day_view->events[day], event);
5840 add_event_data->day_view->events_sorted[day] = FALSE;
5841 add_event_data->day_view->need_layout[day] = TRUE;
5842 return;
5846 /* The event wasn't within one day so it must be a long event,
5847 * i.e. shown in the top canvas. */
5848 g_array_append_val (add_event_data->day_view->long_events, event);
5849 add_event_data->day_view->long_events_sorted = FALSE;
5850 add_event_data->day_view->long_events_need_layout = TRUE;
5851 return;
5854 /* This lays out the short (less than 1 day) events in the columns.
5855 * Any long events are simply skipped. */
5856 void
5857 e_day_view_check_layout (EDayView *day_view)
5859 ECalendarView *cal_view;
5860 gint time_divisions;
5861 gint day, rows_in_top_display;
5862 gint days_shown;
5863 gint max_cols = -1;
5865 days_shown = e_day_view_get_days_shown (day_view);
5867 cal_view = E_CALENDAR_VIEW (day_view);
5868 time_divisions = e_calendar_view_get_time_divisions (cal_view);
5870 /* Don't bother if we aren't visible. */
5871 if (!E_CALENDAR_VIEW (day_view)->in_focus) {
5872 e_day_view_free_events (day_view);
5873 day_view->requires_update = TRUE;
5874 return;
5877 /* Make sure the events are sorted (by start and size). */
5878 e_day_view_ensure_events_sorted (day_view);
5880 for (day = 0; day < days_shown; day++) {
5881 if (day_view->need_layout[day]) {
5882 gint cols;
5884 cols = e_day_view_layout_day_events (
5885 day_view->events[day],
5886 day_view->rows,
5887 time_divisions,
5888 day_view->cols_per_row[day],
5889 days_shown == 1 ? -1 :
5890 E_DAY_VIEW_MULTI_DAY_MAX_COLUMNS);
5892 max_cols = MAX (cols, max_cols);
5895 if (day_view->need_layout[day]
5896 || day_view->need_reshape[day]) {
5897 e_day_view_reshape_day_events (day_view, day);
5899 if (day_view->resize_bars_event_day == day)
5900 e_day_view_reshape_main_canvas_resize_bars (day_view);
5903 day_view->need_layout[day] = FALSE;
5904 day_view->need_reshape[day] = FALSE;
5907 if (day_view->long_events_need_layout) {
5908 e_day_view_layout_long_events (
5909 day_view->long_events,
5910 days_shown,
5911 day_view->day_starts,
5912 &rows_in_top_display);
5915 if (day_view->long_events_need_layout
5916 || day_view->long_events_need_reshape)
5917 e_day_view_reshape_long_events (day_view);
5919 if (day_view->long_events_need_layout
5920 && day_view->rows_in_top_display != rows_in_top_display) {
5921 day_view->rows_in_top_display = rows_in_top_display;
5922 e_day_view_update_top_scroll (day_view, FALSE);
5925 day_view->long_events_need_layout = FALSE;
5926 day_view->long_events_need_reshape = FALSE;
5928 if (max_cols != -1 && max_cols != day_view->max_cols) {
5929 day_view->max_cols = max_cols;
5930 e_day_view_recalc_main_canvas_size (day_view);
5934 static void
5935 e_day_view_reshape_long_events (EDayView *day_view)
5937 EDayViewEvent *event;
5938 gint event_num;
5940 for (event_num = 0; event_num < day_view->long_events->len;
5941 event_num++) {
5942 event = &g_array_index (day_view->long_events, EDayViewEvent,
5943 event_num);
5945 if (event->num_columns == 0) {
5946 if (event->canvas_item) {
5947 g_object_run_dispose (G_OBJECT (event->canvas_item));
5948 event->canvas_item = NULL;
5950 } else {
5951 e_day_view_reshape_long_event (day_view, event_num);
5956 static void
5957 e_day_view_reshape_long_event (EDayView *day_view,
5958 gint event_num)
5960 EDayViewEvent *event;
5961 gint start_day, end_day, item_x, item_y, item_w, item_h;
5962 gint text_x, text_w, num_icons, icons_width, width, time_width;
5963 ECalComponent *comp;
5964 gint min_text_x, max_text_w, text_width, line_len;
5965 gchar *text, *end_of_line;
5966 gboolean show_icons = TRUE, use_max_width = FALSE;
5967 PangoContext *pango_context;
5968 PangoLayout *layout;
5970 if (!is_array_index_in_bounds (day_view->long_events, event_num))
5971 return;
5973 event = &g_array_index (day_view->long_events, EDayViewEvent,
5974 event_num);
5976 if (!e_day_view_get_long_event_position (day_view, event_num,
5977 &start_day, &end_day,
5978 &item_x, &item_y,
5979 &item_w, &item_h)) {
5980 if (event->canvas_item) {
5981 g_object_run_dispose (G_OBJECT (event->canvas_item));
5982 event->canvas_item = NULL;
5984 return;
5987 if (!is_comp_data_valid (event))
5988 return;
5990 /* Take off the border and padding. */
5991 item_x += E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD;
5992 item_w -= (E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD) * 2;
5993 item_y += E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD;
5994 item_h -= (E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD) * 2;
5996 /* We don't show the icons while resizing, since we'd have to
5997 * draw them on top of the resize rect. Nor when editing. */
5998 num_icons = 0;
5999 comp = e_cal_component_new ();
6000 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
6002 /* Set up Pango prerequisites */
6003 pango_context = gtk_widget_get_pango_context (GTK_WIDGET (day_view));
6004 layout = pango_layout_new (pango_context);
6006 if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE
6007 && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT
6008 && day_view->resize_event_num == event_num)
6009 show_icons = FALSE;
6011 if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT
6012 && day_view->editing_event_num == event_num) {
6013 show_icons = FALSE;
6014 use_max_width = TRUE;
6017 if (show_icons) {
6018 if (e_cal_component_has_alarms (comp))
6019 num_icons++;
6020 if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp))
6021 num_icons++;
6022 if (event->different_timezone)
6023 num_icons++;
6025 if (e_cal_component_has_attendees (comp))
6026 num_icons++;
6027 if (e_cal_component_has_attachments (comp))
6028 num_icons++;
6029 num_icons += cal_comp_util_get_n_icons (comp, NULL);
6032 if (!event->canvas_item) {
6033 GdkColor color;
6035 color = e_day_view_get_text_color (day_view, event);
6037 event->canvas_item =
6038 gnome_canvas_item_new (
6039 GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_canvas)->root),
6040 e_text_get_type (),
6041 "clip", TRUE,
6042 "max_lines", 1,
6043 "editable", TRUE,
6044 "use_ellipsis", TRUE,
6045 "fill_color_gdk", &color,
6046 "im_context", E_CANVAS (day_view->top_canvas)->im_context,
6047 NULL);
6048 g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num));
6049 g_object_set_data (G_OBJECT (event->canvas_item), "event-day", GINT_TO_POINTER (E_DAY_VIEW_LONG_EVENT));
6050 g_signal_connect (
6051 event->canvas_item, "event",
6052 G_CALLBACK (e_day_view_on_text_item_event), day_view);
6053 g_signal_emit_by_name (day_view, "event_added", event);
6055 e_day_view_update_long_event_label (day_view, event_num);
6056 } else if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->canvas_item), "event-num")) != event_num) {
6057 g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num));
6060 /* Calculate its position. We first calculate the ideal position which
6061 * is centered with the icons. We then make sure we haven't gone off
6062 * the left edge of the available space. Finally we make sure we don't
6063 * go off the right edge. */
6064 icons_width = (E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD)
6065 * num_icons + E_DAY_VIEW_LONG_EVENT_ICON_R_PAD;
6066 time_width = e_day_view_get_time_string_width (day_view);
6068 if (use_max_width) {
6069 text_x = item_x;
6070 text_w = item_w;
6071 } else {
6072 /* Get the requested size of the label. */
6073 g_object_get (event->canvas_item, "text", &text, NULL);
6074 text_width = 0;
6075 if (text) {
6076 end_of_line = strchr (text, '\n');
6077 if (end_of_line)
6078 line_len = end_of_line - text;
6079 else
6080 line_len = strlen (text);
6081 pango_layout_set_text (layout, text, line_len);
6082 pango_layout_get_pixel_size (layout, &text_width, NULL);
6083 g_free (text);
6086 width = text_width + icons_width;
6087 text_x = item_x + (item_w - width) / 2;
6089 min_text_x = item_x;
6090 if (event->start > day_view->day_starts[start_day])
6091 min_text_x += time_width + E_DAY_VIEW_LONG_EVENT_TIME_X_PAD;
6093 text_x = MAX (text_x, min_text_x);
6095 max_text_w = item_x + item_w - text_x;
6096 if (event->end < day_view->day_starts[end_day + 1])
6097 max_text_w -= time_width + E_DAY_VIEW_LONG_EVENT_TIME_X_PAD;
6099 text_w = MIN (width, max_text_w);
6101 /* Now take out the space for the icons. */
6102 text_x += icons_width;
6103 text_w -= icons_width;
6106 text_w = MAX (text_w, 0);
6107 gnome_canvas_item_set (
6108 event->canvas_item,
6109 "clip_width", (gdouble) text_w,
6110 "clip_height", (gdouble) item_h,
6111 NULL);
6112 e_canvas_item_move_absolute (
6113 event->canvas_item,
6114 text_x, item_y);
6116 g_object_unref (layout);
6117 g_object_unref (comp);
6120 /* This creates or updates the sizes of the canvas items for one day of the
6121 * main canvas. */
6122 static void
6123 e_day_view_reshape_day_events (EDayView *day_view,
6124 gint day)
6126 gint event_num;
6128 for (event_num = 0; event_num < day_view->events[day]->len;
6129 event_num++) {
6130 EDayViewEvent *event;
6131 gchar *current_comp_string;
6133 e_day_view_reshape_day_event (day_view, day, event_num);
6134 event = &g_array_index (day_view->events[day], EDayViewEvent, event_num);
6136 if (!is_comp_data_valid (event))
6137 continue;
6139 current_comp_string = icalcomponent_as_ical_string_r (event->comp_data->icalcomp);
6140 if (day_view->last_edited_comp_string == NULL) {
6141 g_free (current_comp_string);
6142 continue;
6145 if (strncmp (current_comp_string, day_view->last_edited_comp_string,50) == 0) {
6146 e_canvas_item_grab_focus (event->canvas_item, TRUE);
6147 g_free (day_view->last_edited_comp_string);
6148 day_view-> last_edited_comp_string = NULL;
6150 g_free (current_comp_string);
6154 static void
6155 e_day_view_reshape_day_event (EDayView *day_view,
6156 gint day,
6157 gint event_num)
6159 EDayViewEvent *event;
6160 gint item_x, item_y, item_w, item_h;
6161 gint num_icons, icons_offset;
6163 if (!is_array_index_in_bounds (day_view->events[day], event_num))
6164 return;
6166 event = &g_array_index (day_view->events[day], EDayViewEvent,
6167 event_num);
6169 if (!e_day_view_get_event_position (day_view, day, event_num,
6170 &item_x, &item_y,
6171 &item_w, &item_h)) {
6172 if (event->canvas_item) {
6173 g_object_run_dispose (G_OBJECT (event->canvas_item));
6174 event->canvas_item = NULL;
6176 } else {
6177 /* Skip the border and padding. */
6178 item_x += E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD;
6179 item_w -= E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD * 2;
6180 item_y += E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD;
6181 item_h -= (E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD) * 2;
6183 /* We don't show the icons while resizing, since we'd have to
6184 * draw them on top of the resize rect. */
6185 icons_offset = 0;
6186 num_icons = 0;
6187 if (is_comp_data_valid (event) && (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_NONE
6188 || day_view->resize_event_day != day
6189 || day_view->resize_event_num != event_num)) {
6190 ECalComponent *comp;
6192 comp = e_cal_component_new ();
6193 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
6195 if (e_cal_component_has_alarms (comp))
6196 num_icons++;
6197 if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp))
6198 num_icons++;
6199 if (e_cal_component_has_attachments (comp))
6200 num_icons++;
6201 if (event->different_timezone)
6202 num_icons++;
6203 if (e_cal_component_has_attendees (comp))
6204 num_icons++;
6206 num_icons += cal_comp_util_get_n_icons (comp, NULL);
6207 g_object_unref (comp);
6210 if (num_icons > 0) {
6211 if (item_h >= (E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD) * num_icons)
6212 icons_offset = E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD * 2;
6213 else if (item_h <= (E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD) * 2 || num_icons == 1)
6214 icons_offset = (E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD) * num_icons + E_DAY_VIEW_ICON_X_PAD;
6215 else
6216 icons_offset = E_DAY_VIEW_ICON_X_PAD;
6219 if (!event->canvas_item) {
6220 GdkColor color;
6222 color = e_day_view_get_text_color (day_view, event);
6224 event->canvas_item = gnome_canvas_item_new (
6225 GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->main_canvas)->root),
6226 e_text_get_type (),
6227 "line_wrap", TRUE,
6228 "editable", TRUE,
6229 "clip", TRUE,
6230 "use_ellipsis", TRUE,
6231 "fill_color_gdk", &color,
6232 "im_context", E_CANVAS (day_view->main_canvas)->im_context,
6233 NULL);
6234 g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num));
6235 g_object_set_data (G_OBJECT (event->canvas_item), "event-day", GINT_TO_POINTER (day));
6236 g_signal_connect (
6237 event->canvas_item, "event",
6238 G_CALLBACK (e_day_view_on_text_item_event), day_view);
6239 g_signal_emit_by_name (day_view, "event_added", event);
6241 e_day_view_update_event_label (day_view, day, event_num);
6242 } else if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (event->canvas_item), "event-num")) != event_num) {
6243 g_object_set_data (G_OBJECT (event->canvas_item), "event-num", GINT_TO_POINTER (event_num));
6246 item_w = MAX (item_w, 0);
6247 gnome_canvas_item_set (
6248 event->canvas_item,
6249 "clip_width", (gdouble) item_w,
6250 "clip_height", (gdouble) item_h,
6251 "x_offset", (gdouble) icons_offset,
6252 NULL);
6253 e_canvas_item_move_absolute (
6254 event->canvas_item,
6255 item_x, item_y);
6259 /* This creates or resizes the horizontal bars used to resize events in the
6260 * main canvas. */
6261 static void
6262 e_day_view_reshape_main_canvas_resize_bars (EDayView *day_view)
6264 gint day, event_num;
6265 gint item_x, item_y, item_w, item_h;
6266 gdouble x, y, w, h;
6268 day = day_view->resize_bars_event_day;
6269 event_num = day_view->resize_bars_event_num;
6271 /* If we're not editing an event, or the event is not shown,
6272 * hide the resize bars. */
6273 if (day != -1 && day == day_view->drag_event_day
6274 && event_num == day_view->drag_event_num) {
6275 g_object_get (
6276 day_view->drag_rect_item,
6277 "x1", &x,
6278 "y1", &y,
6279 "x2", &w,
6280 "y2", &h,
6281 NULL);
6282 w -= x;
6283 x++;
6284 h -= y;
6285 } else if (day != -1
6286 && e_day_view_get_event_position (day_view, day, event_num,
6287 &item_x, &item_y,
6288 &item_w, &item_h)) {
6289 x = item_x + E_DAY_VIEW_BAR_WIDTH;
6290 y = item_y;
6291 w = item_w - E_DAY_VIEW_BAR_WIDTH;
6292 h = item_h;
6294 gtk_widget_queue_draw (day_view->main_canvas);
6295 } else {
6296 return;
6300 static void
6301 e_day_view_ensure_events_sorted (EDayView *day_view)
6303 gint day;
6304 gint days_shown;
6306 days_shown = e_day_view_get_days_shown (day_view);
6308 /* Sort the long events. */
6309 if (!day_view->long_events_sorted) {
6310 qsort (
6311 day_view->long_events->data,
6312 day_view->long_events->len,
6313 sizeof (EDayViewEvent),
6314 e_day_view_event_sort_func);
6315 day_view->long_events_sorted = TRUE;
6318 /* Sort the events for each day. */
6319 for (day = 0; day < days_shown; day++) {
6320 if (!day_view->events_sorted[day]) {
6321 qsort (
6322 day_view->events[day]->data,
6323 day_view->events[day]->len,
6324 sizeof (EDayViewEvent),
6325 e_day_view_event_sort_func);
6326 day_view->events_sorted[day] = TRUE;
6331 gint
6332 e_day_view_event_sort_func (gconstpointer arg1,
6333 gconstpointer arg2)
6335 EDayViewEvent *event1, *event2;
6337 event1 = (EDayViewEvent *) arg1;
6338 event2 = (EDayViewEvent *) arg2;
6340 if (event1->start < event2->start)
6341 return -1;
6342 if (event1->start > event2->start)
6343 return 1;
6345 if (event1->end > event2->end)
6346 return -1;
6347 if (event1->end < event2->end)
6348 return 1;
6350 return 0;
6353 static gboolean
6354 e_day_view_do_key_press (GtkWidget *widget,
6355 GdkEventKey *event)
6357 EDayView *day_view;
6358 guint keyval;
6359 gboolean stop_emission;
6361 g_return_val_if_fail (widget != NULL, FALSE);
6362 g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
6363 g_return_val_if_fail (event != NULL, FALSE);
6365 day_view = E_DAY_VIEW (widget);
6366 keyval = event->keyval;
6368 /* The Escape key aborts a resize operation. */
6369 if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
6370 if (keyval == GDK_KEY_Escape) {
6371 if (day_view->grabbed_pointer != NULL) {
6372 gdk_device_ungrab (
6373 day_view->grabbed_pointer,
6374 event->time);
6375 g_object_unref (day_view->grabbed_pointer);
6376 day_view->grabbed_pointer = NULL;
6378 e_day_view_abort_resize (day_view);
6380 return FALSE;
6383 /* Alt + Arrow Keys to move a selected event through time lines */
6384 if (((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK)
6385 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6386 &&((event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK)) {
6387 if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up)
6388 return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_UP);
6389 else if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
6390 return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_DOWN);
6391 else if (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left)
6392 return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_LEFT);
6393 else if (keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right)
6394 return e_day_view_event_move ((ECalendarView *) day_view, E_CAL_VIEW_MOVE_RIGHT);
6397 /*Go to the start/end of a work day*/
6398 if ((keyval == GDK_KEY_Home)
6399 &&((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK)
6400 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6401 &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) {
6402 e_day_view_goto_start_of_work_day (day_view);
6403 return TRUE;
6405 if ((keyval == GDK_KEY_End)
6406 &&((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK)
6407 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6408 &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) {
6409 e_day_view_goto_end_of_work_day (day_view);
6410 return TRUE;
6413 /* In DayView, Shift+Home/End, Change the duration to the time that begins/ends the current work day */
6414 if ((keyval == GDK_KEY_Home)
6415 &&((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6416 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6417 &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) {
6418 e_day_view_change_duration_to_start_of_work_day (day_view);
6419 return TRUE;
6421 if ((keyval == GDK_KEY_End)
6422 &&((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6423 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
6424 &&((event->state & GDK_MOD1_MASK) != GDK_MOD1_MASK)) {
6425 e_day_view_change_duration_to_end_of_work_day (day_view);
6426 return TRUE;
6429 /* Handle the cursor keys for moving & extending the selection. */
6430 stop_emission = TRUE;
6431 if (event->state & GDK_SHIFT_MASK) {
6432 switch (keyval) {
6433 case GDK_KEY_Up:
6434 e_day_view_cursor_key_up_shifted (day_view, event);
6435 break;
6436 case GDK_KEY_Down:
6437 e_day_view_cursor_key_down_shifted (day_view, event);
6438 break;
6439 case GDK_KEY_Left:
6440 e_day_view_cursor_key_left_shifted (day_view, event);
6441 break;
6442 case GDK_KEY_Right:
6443 e_day_view_cursor_key_right_shifted (day_view, event);
6444 break;
6445 default:
6446 stop_emission = FALSE;
6447 break;
6449 } else if (!(event->state & GDK_MOD1_MASK)) {
6450 switch (keyval) {
6451 case GDK_KEY_Up:
6452 e_day_view_cursor_key_up (day_view, event);
6453 break;
6454 case GDK_KEY_Down:
6455 e_day_view_cursor_key_down (day_view, event);
6456 break;
6457 case GDK_KEY_Left:
6458 e_day_view_cursor_key_left (day_view, event);
6459 break;
6460 case GDK_KEY_Right:
6461 e_day_view_cursor_key_right (day_view, event);
6462 break;
6463 case GDK_KEY_Page_Up:
6464 e_day_view_scroll (day_view, E_DAY_VIEW_PAGE_STEP);
6465 break;
6466 case GDK_KEY_Page_Down:
6467 e_day_view_scroll (day_view, -E_DAY_VIEW_PAGE_STEP);
6468 break;
6469 default:
6470 stop_emission = FALSE;
6471 break;
6474 else
6475 stop_emission = FALSE;
6476 if (stop_emission)
6477 return TRUE;
6479 if (day_view->selection_start_day == -1)
6480 return FALSE;
6482 /* We only want to start an edit with a return key or a simple
6483 * character. */
6484 if ((keyval != GDK_KEY_Return && keyval != GDK_KEY_KP_Enter) &&
6485 (((keyval >= 0x20) && (keyval <= 0xFF)
6486 && (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
6487 || (event->length == 0)
6488 || (keyval == GDK_KEY_Tab)
6489 || (keyval == GDK_KEY_Escape)
6490 || (keyval == GDK_KEY_Delete)
6491 || (keyval == GDK_KEY_KP_Delete))) {
6492 return FALSE;
6495 e_day_view_add_new_event_in_selected_range (day_view, event, FALSE);
6497 return TRUE;
6500 /* Select the time that begins a work day*/
6501 static void
6502 e_day_view_goto_start_of_work_day (EDayView *day_view)
6504 gint work_day_start_hour;
6505 gint work_day_start_minute;
6506 gint work_day_end_hour;
6507 gint work_day_end_minute;
6509 if (day_view->selection_in_top_canvas)
6510 return;
6512 e_day_view_get_work_day_range_for_day (day_view, day_view->selection_start_day,
6513 &work_day_start_hour, &work_day_start_minute,
6514 &work_day_end_hour, &work_day_end_minute);
6516 day_view->selection_start_row =
6517 e_day_view_convert_time_to_row (
6518 day_view, work_day_start_hour, work_day_start_minute);
6519 day_view->selection_end_row = day_view->selection_start_row;
6520 day_view->selection_end_day = day_view->selection_start_day;
6522 e_day_view_ensure_rows_visible (
6523 day_view,
6524 day_view->selection_start_row,
6525 day_view->selection_end_row);
6527 e_day_view_update_calendar_selection_time (day_view);
6529 gtk_widget_queue_draw (day_view->top_canvas);
6530 gtk_widget_queue_draw (day_view->top_dates_canvas);
6531 gtk_widget_queue_draw (day_view->main_canvas);
6534 /* Select the time that ends a work day*/
6535 static void
6536 e_day_view_goto_end_of_work_day (EDayView *day_view)
6538 gint work_day_start_hour;
6539 gint work_day_start_minute;
6540 gint work_day_end_hour;
6541 gint work_day_end_minute;
6543 if (day_view->selection_in_top_canvas)
6544 return;
6546 e_day_view_get_work_day_range_for_day (day_view, day_view->selection_end_day,
6547 &work_day_start_hour, &work_day_start_minute,
6548 &work_day_end_hour, &work_day_end_minute);
6550 day_view->selection_start_row =
6551 e_day_view_convert_time_to_row (
6552 day_view, work_day_end_hour - 1, work_day_end_minute + 30);
6553 day_view->selection_end_row = day_view->selection_start_row;
6554 day_view->selection_start_day = day_view->selection_end_day;
6556 e_day_view_ensure_rows_visible (
6557 day_view,
6558 day_view->selection_start_row,
6559 day_view->selection_end_row);
6561 e_day_view_update_calendar_selection_time (day_view);
6563 gtk_widget_queue_draw (day_view->top_canvas);
6564 gtk_widget_queue_draw (day_view->top_dates_canvas);
6565 gtk_widget_queue_draw (day_view->main_canvas);
6568 /* Change the duration to the time that begins the current work day */
6569 static void
6570 e_day_view_change_duration_to_start_of_work_day (EDayView *day_view)
6572 gint work_start_row;
6573 gint work_day_start_hour;
6574 gint work_day_start_minute;
6575 gint work_day_end_hour;
6576 gint work_day_end_minute;
6578 g_return_if_fail (day_view != NULL);
6580 if (day_view->selection_in_top_canvas)
6581 return;
6583 e_day_view_get_work_day_range_for_day (day_view, day_view->selection_start_day,
6584 &work_day_start_hour, &work_day_start_minute,
6585 &work_day_end_hour, &work_day_end_minute);
6587 work_start_row = e_day_view_convert_time_to_row (day_view, work_day_start_hour, work_day_start_minute);
6589 if (day_view->selection_start_row < work_start_row)
6590 day_view->selection_end_row = work_start_row - 1;
6591 else
6592 day_view->selection_start_row = work_start_row;
6594 e_day_view_ensure_rows_visible (
6595 day_view,
6596 day_view->selection_start_row,
6597 day_view->selection_end_row);
6599 e_day_view_update_calendar_selection_time (day_view);
6601 gtk_widget_queue_draw (day_view->top_canvas);
6602 gtk_widget_queue_draw (day_view->top_dates_canvas);
6603 gtk_widget_queue_draw (day_view->main_canvas);
6606 /* Change the duration to the time that ends the current work day */
6607 static void
6608 e_day_view_change_duration_to_end_of_work_day (EDayView *day_view)
6610 gint selection_start_row, work_end_row;
6611 gint work_day_start_hour;
6612 gint work_day_start_minute;
6613 gint work_day_end_hour;
6614 gint work_day_end_minute;
6616 g_return_if_fail (day_view != NULL);
6618 if (day_view->selection_in_top_canvas)
6619 return;
6621 e_day_view_get_work_day_range_for_day (day_view, day_view->selection_start_day,
6622 &work_day_start_hour, &work_day_start_minute,
6623 &work_day_end_hour, &work_day_end_minute);
6625 work_end_row = e_day_view_convert_time_to_row (
6626 day_view, work_day_end_hour - 1, work_day_end_minute + 30);
6627 selection_start_row = day_view->selection_start_row;
6629 if (selection_start_row <= work_end_row) {
6630 day_view->selection_end_row = work_end_row;
6631 } else {
6632 day_view->selection_start_row = work_end_row + 1;
6633 day_view->selection_end_row = selection_start_row;
6636 e_day_view_ensure_rows_visible (
6637 day_view,
6638 day_view->selection_start_row,
6639 day_view->selection_end_row);
6641 e_day_view_update_calendar_selection_time (day_view);
6643 gtk_widget_queue_draw (day_view->top_canvas);
6644 gtk_widget_queue_draw (day_view->top_dates_canvas);
6645 gtk_widget_queue_draw (day_view->main_canvas);
6648 static void
6649 e_day_view_cursor_key_up_shifted (EDayView *day_view,
6650 GdkEventKey *event)
6652 gint *row;
6654 if (day_view->selection_in_top_canvas)
6655 return;
6657 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
6658 row = &day_view->selection_start_row;
6659 else
6660 row = &day_view->selection_end_row;
6662 if (*row == 0)
6663 return;
6665 *row = *row - 1;
6667 e_day_view_ensure_rows_visible (day_view, *row, *row);
6669 e_day_view_normalize_selection (day_view);
6671 e_day_view_update_calendar_selection_time (day_view);
6673 /* FIXME: Optimise? */
6674 gtk_widget_queue_draw (day_view->top_canvas);
6675 gtk_widget_queue_draw (day_view->main_canvas);
6679 * e_day_view_get_extreme_event
6680 * @day_view: the day view widget operates on
6681 * @start_day, @end_day: range of search, both inclusive
6682 * @first: %TURE indicate to return the data for the first event in the range,
6683 * %FALSE to return data for the last event in the range.
6684 * @day_out: out value, day of the event found. -1 for no event found.
6685 * @event_num_out: out value, event number of the event found.
6686 * -1 for no event found.
6688 * Get day and event_num value for the first or last event found in the day range.
6690 * Return value: %TRUE, if a event found.
6692 static gboolean
6693 e_day_view_get_extreme_event (EDayView *day_view,
6694 gint start_day,
6695 gint end_day,
6696 gboolean first,
6697 gint *day_out,
6698 gint *event_num_out)
6700 gint loop_day;
6702 g_return_val_if_fail (day_view != NULL, FALSE);
6703 g_return_val_if_fail (start_day >= 0, FALSE);
6704 g_return_val_if_fail (end_day <= E_DAY_VIEW_LONG_EVENT, FALSE);
6705 g_return_val_if_fail (day_out && event_num_out, FALSE);
6707 if (start_day > end_day)
6708 return FALSE;
6709 if (first) {
6710 for (loop_day = start_day; loop_day <= end_day; ++loop_day)
6711 if (day_view->events[loop_day]->len > 0) {
6712 *day_out = loop_day;
6713 *event_num_out = 0;
6714 return TRUE;
6717 else {
6718 for (loop_day = end_day; loop_day >= start_day; --loop_day)
6719 if (day_view->events[loop_day]->len > 0) {
6720 *day_out = loop_day;
6721 *event_num_out =
6722 day_view->events[loop_day]->len - 1;
6723 return TRUE;
6726 *day_out = -1;
6727 *event_num_out = -1;
6728 return FALSE;
6732 * e_day_view_get_extreme_long_event
6733 * @day_view: the day view widget operates on
6734 * @first: %TURE indicate to return the data for the first event in the range,
6735 * %FALSE to return data for the last event in the range.
6736 * @event_num_out: out value, event number of the event found.
6737 * -1 for no event found.
6739 * Similar to e_day_view_get_extreme_event, but run for long events.
6741 * Return value: %TRUE, if a event found.
6743 static gboolean
6744 e_day_view_get_extreme_long_event (EDayView *day_view,
6745 gboolean first,
6746 gint *day_out,
6747 gint *event_num_out)
6749 g_return_val_if_fail (day_view != NULL, FALSE);
6750 g_return_val_if_fail (day_out && event_num_out, FALSE);
6752 if (first && (day_view->long_events->len > 0)) {
6753 *day_out = E_DAY_VIEW_LONG_EVENT;
6754 *event_num_out = 0;
6755 return TRUE;
6757 if ((!first) && (day_view->long_events->len > 0)) {
6758 *day_out = E_DAY_VIEW_LONG_EVENT;
6759 *event_num_out = day_view->long_events->len - 1;
6760 return TRUE;
6762 *day_out = -1;
6763 *event_num_out = -1;
6764 return FALSE;
6768 * e_day_view_get_next_tab_event
6769 * @day_view: the day view widget operates on
6770 * @direction: GTK_DIR_TAB_BACKWARD or GTK_DIR_TAB_FORWARD
6771 * @day_out: out value, day of the event found. -1 for no event found.
6772 * @event_num_out: out value, event number of the event found.
6773 * -1 for no event found.
6775 * Decide on which event the focus should go next.
6776 * if ((day_out == -1) && (event_num_out == -1)) is true, focus should go
6777 * to day_view widget itself.
6779 * Return value: %TRUE, if a event found.
6781 static gboolean
6782 e_day_view_get_next_tab_event (EDayView *day_view,
6783 GtkDirectionType direction,
6784 gint *day_out,
6785 gint *event_num_out)
6787 gint new_day;
6788 gint new_event_num;
6789 gint days_shown;
6791 g_return_val_if_fail (day_view != NULL, FALSE);
6792 g_return_val_if_fail (day_out != NULL, FALSE);
6793 g_return_val_if_fail (event_num_out != NULL, FALSE);
6795 days_shown = e_day_view_get_days_shown (day_view);
6796 *day_out = -1;
6797 *event_num_out = -1;
6799 g_return_val_if_fail (days_shown > 0, FALSE);
6801 switch (direction) {
6802 case GTK_DIR_TAB_BACKWARD:
6803 new_event_num = day_view->editing_event_num - 1;
6804 break;
6805 case GTK_DIR_TAB_FORWARD:
6806 new_event_num = day_view->editing_event_num + 1;
6807 break;
6808 default:
6809 return FALSE;
6812 new_day = day_view->editing_event_day;
6814 /* not current editing event, set to first long event if there is one
6816 if (new_day == -1) {
6817 if (direction == GTK_DIR_TAB_FORWARD) {
6818 if (e_day_view_get_extreme_long_event (day_view, TRUE,
6819 day_out,
6820 event_num_out))
6821 return TRUE;
6823 /* no long event, set to first event if there is
6825 e_day_view_get_extreme_event (
6826 day_view, 0,
6827 days_shown - 1, TRUE,
6828 day_out, event_num_out);
6829 /* go to event if found, or day view widget
6831 return TRUE;
6833 else {
6834 if (e_day_view_get_extreme_event (day_view, 0,
6835 days_shown - 1, FALSE,
6836 day_out, event_num_out))
6837 return TRUE;
6839 /* no event, set to last long event if there is
6841 e_day_view_get_extreme_long_event (
6842 day_view, FALSE,
6843 day_out,
6844 event_num_out);
6846 /* go to long event if found, or day view widget
6848 return TRUE;
6851 /* go backward from the first long event */
6852 else if ((new_day == E_DAY_VIEW_LONG_EVENT) && (new_event_num < 0)) {
6853 /* let focus go to day view widget in this case
6855 return TRUE;
6857 /* go forward from the last long event */
6858 else if ((new_day == E_DAY_VIEW_LONG_EVENT) &&
6859 (new_event_num >= day_view->long_events->len)) {
6860 e_day_view_get_extreme_event (
6861 day_view, 0,
6862 days_shown - 1, TRUE,
6863 day_out, event_num_out);
6864 /* go to the next main item event if found or day view widget
6866 return TRUE;
6869 /* go backward from the first event in current editting day */
6870 else if ((new_day < E_DAY_VIEW_LONG_EVENT) && (new_event_num < 0)) {
6871 /* try to find a event from the previous day in days shown
6873 if (e_day_view_get_extreme_event (day_view, 0,
6874 new_day - 1, FALSE,
6875 day_out, event_num_out))
6876 return TRUE;
6877 /* try to find a long event
6879 e_day_view_get_extreme_long_event (
6880 day_view, FALSE,
6881 day_out, event_num_out);
6882 /* go to a long event if found, or day view widget
6884 return TRUE;
6886 /* go forward from the last event in current editting day */
6887 else if ((new_day < E_DAY_VIEW_LONG_EVENT) &&
6888 (new_event_num >= day_view->events[new_day]->len)) {
6889 /* try to find a event from the next day in days shown
6891 e_day_view_get_extreme_event (
6892 day_view, (new_day + 1),
6893 days_shown - 1, TRUE,
6894 day_out, event_num_out);
6895 /* go to a event found, or day view widget
6897 return TRUE;
6899 /* in the normal case
6901 *day_out = new_day;
6902 *event_num_out = new_event_num;
6903 return TRUE;
6906 static void
6907 e_day_view_cursor_key_down_shifted (EDayView *day_view,
6908 GdkEventKey *event)
6910 gint *row;
6912 if (day_view->selection_in_top_canvas)
6913 return;
6915 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
6916 row = &day_view->selection_start_row;
6917 else
6918 row = &day_view->selection_end_row;
6920 if (*row >= day_view->rows - 1)
6921 return;
6923 *row = *row + 1;
6925 e_day_view_ensure_rows_visible (day_view, *row, *row);
6927 e_day_view_normalize_selection (day_view);
6929 e_day_view_update_calendar_selection_time (day_view);
6931 /* FIXME: Optimise? */
6932 gtk_widget_queue_draw (day_view->top_canvas);
6933 gtk_widget_queue_draw (day_view->main_canvas);
6936 static void
6937 e_day_view_cursor_key_left_shifted (EDayView *day_view,
6938 GdkEventKey *event)
6940 gint *day;
6942 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
6943 day = &day_view->selection_start_day;
6944 else
6945 day = &day_view->selection_end_day;
6947 if (*day == 0)
6948 return;
6950 *day = *day - 1;
6952 e_day_view_normalize_selection (day_view);
6954 e_day_view_update_calendar_selection_time (day_view);
6956 /* FIXME: Optimise? */
6957 gtk_widget_queue_draw (day_view->top_canvas);
6958 gtk_widget_queue_draw (day_view->main_canvas);
6961 static void
6962 e_day_view_cursor_key_right_shifted (EDayView *day_view,
6963 GdkEventKey *event)
6965 gint *day;
6967 if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
6968 day = &day_view->selection_start_day;
6969 else
6970 day = &day_view->selection_end_day;
6972 if (*day >= e_day_view_get_days_shown (day_view) - 1)
6973 return;
6975 *day = *day + 1;
6977 e_day_view_normalize_selection (day_view);
6979 e_day_view_update_calendar_selection_time (day_view);
6981 /* FIXME: Optimise? */
6982 gtk_widget_queue_draw (day_view->top_canvas);
6983 gtk_widget_queue_draw (day_view->main_canvas);
6986 static void
6987 e_day_view_cursor_key_up (EDayView *day_view,
6988 GdkEventKey *event)
6990 if (day_view->selection_start_day == -1) {
6991 day_view->selection_start_day = 0;
6992 day_view->selection_start_row = 0;
6994 day_view->selection_end_day = day_view->selection_start_day;
6996 if (day_view->selection_in_top_canvas) {
6997 return;
6998 } else if (day_view->selection_start_row == 0) {
6999 day_view->selection_in_top_canvas = TRUE;
7000 day_view->selection_start_row = -1;
7001 } else {
7002 day_view->selection_start_row--;
7004 day_view->selection_end_row = day_view->selection_start_row;
7006 if (!day_view->selection_in_top_canvas)
7007 e_day_view_ensure_rows_visible (
7008 day_view,
7009 day_view->selection_start_row,
7010 day_view->selection_end_row);
7012 g_signal_emit_by_name (day_view, "selected_time_changed");
7013 e_day_view_update_calendar_selection_time (day_view);
7015 /* FIXME: Optimise? */
7016 gtk_widget_queue_draw (day_view->top_canvas);
7017 gtk_widget_queue_draw (day_view->main_canvas);
7020 static void
7021 e_day_view_cursor_key_down (EDayView *day_view,
7022 GdkEventKey *event)
7024 if (day_view->selection_start_day == -1) {
7025 day_view->selection_start_day = 0;
7026 day_view->selection_start_row = 0;
7028 day_view->selection_end_day = day_view->selection_start_day;
7030 if (day_view->selection_in_top_canvas) {
7031 day_view->selection_in_top_canvas = FALSE;
7032 day_view->selection_start_row = 0;
7033 } else if (day_view->selection_start_row >= day_view->rows - 1) {
7034 return;
7035 } else {
7036 day_view->selection_start_row++;
7038 day_view->selection_end_row = day_view->selection_start_row;
7040 if (!day_view->selection_in_top_canvas)
7041 e_day_view_ensure_rows_visible (
7042 day_view,
7043 day_view->selection_start_row,
7044 day_view->selection_end_row);
7046 g_signal_emit_by_name (day_view, "selected_time_changed");
7047 e_day_view_update_calendar_selection_time (day_view);
7049 /* FIXME: Optimise? */
7050 gtk_widget_queue_draw (day_view->top_canvas);
7051 gtk_widget_queue_draw (day_view->main_canvas);
7054 static void
7055 e_day_view_cursor_key_left (EDayView *day_view,
7056 GdkEventKey *event)
7058 if (day_view->selection_start_day == 0) {
7059 e_calendar_view_move_view_range (E_CALENDAR_VIEW (day_view), E_CALENDAR_VIEW_MOVE_PREVIOUS, 0);
7060 } else {
7061 day_view->selection_start_day--;
7062 day_view->selection_end_day--;
7064 e_day_view_update_calendar_selection_time (day_view);
7066 /* FIXME: Optimise? */
7067 gtk_widget_queue_draw (day_view->top_canvas);
7068 gtk_widget_queue_draw (day_view->main_canvas);
7070 g_signal_emit_by_name (day_view, "selected_time_changed");
7073 static void
7074 e_day_view_cursor_key_right (EDayView *day_view,
7075 GdkEventKey *event)
7077 gint days_shown;
7079 days_shown = e_day_view_get_days_shown (day_view);
7081 if (day_view->selection_end_day == days_shown - 1) {
7082 e_calendar_view_move_view_range (E_CALENDAR_VIEW (day_view), E_CALENDAR_VIEW_MOVE_NEXT, 0);
7083 } else {
7084 day_view->selection_start_day++;
7085 day_view->selection_end_day++;
7087 e_day_view_update_calendar_selection_time (day_view);
7089 /* FIXME: Optimise? */
7090 gtk_widget_queue_draw (day_view->top_canvas);
7091 gtk_widget_queue_draw (day_view->main_canvas);
7093 g_signal_emit_by_name (day_view, "selected_time_changed");
7096 /* Scrolls the main canvas up or down. The pages_to_scroll argument
7097 * is multiplied with the adjustment's page size and added to the adjustment's
7098 * value, while ensuring we stay within the bounds. A positive value will
7099 * scroll the canvas down and a negative value will scroll it up. */
7100 static void
7101 e_day_view_scroll (EDayView *day_view,
7102 gfloat pages_to_scroll)
7104 GtkAdjustment *adjustment;
7105 GtkScrollable *scrollable;
7106 gdouble new_value;
7107 gdouble page_size;
7108 gdouble lower;
7109 gdouble upper;
7110 gdouble value;
7112 scrollable = GTK_SCROLLABLE (day_view->main_canvas);
7113 adjustment = gtk_scrollable_get_vadjustment (scrollable);
7115 page_size = gtk_adjustment_get_page_size (adjustment);
7116 lower = gtk_adjustment_get_lower (adjustment);
7117 upper = gtk_adjustment_get_upper (adjustment);
7118 value = gtk_adjustment_get_value (adjustment);
7120 new_value = value - page_size * pages_to_scroll;
7121 new_value = CLAMP (new_value, lower, upper - page_size);
7122 gtk_adjustment_set_value (adjustment, new_value);
7125 static void
7126 e_day_view_top_scroll (EDayView *day_view,
7127 gfloat pages_to_scroll)
7129 GtkAdjustment *adjustment;
7130 GtkScrollable *scrollable;
7131 gdouble new_value;
7132 gdouble page_size;
7133 gdouble lower;
7134 gdouble upper;
7135 gdouble value;
7137 scrollable = GTK_SCROLLABLE (day_view->top_canvas);
7138 adjustment = gtk_scrollable_get_vadjustment (scrollable);
7140 page_size = gtk_adjustment_get_page_size (adjustment);
7141 lower = gtk_adjustment_get_lower (adjustment);
7142 upper = gtk_adjustment_get_upper (adjustment);
7143 value = gtk_adjustment_get_value (adjustment);
7145 new_value = value - page_size * pages_to_scroll;
7146 new_value = CLAMP (new_value, lower, upper - page_size);
7147 gtk_adjustment_set_value (adjustment, new_value);
7150 void
7151 e_day_view_ensure_rows_visible (EDayView *day_view,
7152 gint start_row,
7153 gint end_row)
7155 GtkAdjustment *adjustment;
7156 GtkScrollable *scrollable;
7157 gdouble max_value;
7158 gdouble min_value;
7159 gdouble page_size;
7160 gdouble value;
7162 scrollable = GTK_SCROLLABLE (day_view->main_canvas);
7163 adjustment = gtk_scrollable_get_vadjustment (scrollable);
7165 value = gtk_adjustment_get_value (adjustment);
7166 page_size = gtk_adjustment_get_page_size (adjustment);
7168 min_value = (end_row + 1) * day_view->row_height - page_size;
7169 if (value < min_value)
7170 value = min_value;
7172 max_value = start_row * day_view->row_height;
7173 if (value > max_value)
7174 value = max_value;
7176 gtk_adjustment_set_value (adjustment, value);
7179 static void
7180 e_day_view_start_editing_event (EDayView *day_view,
7181 gint day,
7182 gint event_num,
7183 GdkEventKey *key_event)
7185 EDayViewEvent *event;
7186 ETextEventProcessor *event_processor = NULL;
7187 ETextEventProcessorCommand command;
7189 /* If we are already editing the event, just return. */
7190 if (day == day_view->editing_event_day
7191 && event_num == day_view->editing_event_num)
7192 return;
7194 if (day == E_DAY_VIEW_LONG_EVENT) {
7195 if (!is_array_index_in_bounds (day_view->long_events, event_num))
7196 return;
7198 event = &g_array_index (day_view->long_events, EDayViewEvent,
7199 event_num);
7200 } else {
7201 if (!is_array_index_in_bounds (day_view->events[day], event_num))
7202 return;
7204 event = &g_array_index (day_view->events[day], EDayViewEvent,
7205 event_num);
7208 if (!is_comp_data_valid (event))
7209 return;
7211 if (e_client_is_readonly (E_CLIENT (event->comp_data->client)))
7212 return;
7214 /* If the event is not shown, don't try to edit it. */
7215 if (!event->canvas_item)
7216 return;
7218 /* We must grab the focus before setting the initial text, since
7219 * grabbing the focus will result in a call to
7220 * e_day_view_on_editing_started (), which will reset the text to get
7221 * rid of the start and end times. */
7222 e_canvas_item_grab_focus (event->canvas_item, TRUE);
7224 if (key_event) {
7225 if (gtk_im_context_filter_keypress (((EText *)(event->canvas_item))->im_context, key_event)) {
7226 ((EText *)(event->canvas_item))->need_im_reset = TRUE;
7227 } else if (key_event->keyval != GDK_KEY_Return && key_event->keyval != GDK_KEY_KP_Enter) {
7228 gchar *initial_text;
7230 initial_text = e_utf8_from_gtk_event_key (GTK_WIDGET (day_view), key_event->keyval, key_event->string);
7231 gnome_canvas_item_set (
7232 event->canvas_item,
7233 "text", initial_text,
7234 NULL);
7235 if (initial_text)
7236 g_free (initial_text);
7240 /* Try to move the cursor to the end of the text. */
7241 g_object_get (
7242 event->canvas_item,
7243 "event_processor", &event_processor,
7244 NULL);
7245 if (event_processor) {
7246 command.action = E_TEP_MOVE;
7247 command.position = E_TEP_END_OF_BUFFER;
7248 g_signal_emit_by_name (
7249 event_processor,
7250 "command", &command);
7254 /* This stops the current edit. If accept is TRUE the event summary is updated,
7255 * else the edit is cancelled. */
7256 static void
7257 e_day_view_stop_editing_event (EDayView *day_view)
7259 GtkWidget *toplevel;
7261 /* Check we are editing an event. */
7262 if (day_view->editing_event_day == -1)
7263 return;
7265 /* Set focus to the toplevel so the item loses focus. */
7266 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (day_view));
7267 if (toplevel && GTK_IS_WINDOW (toplevel))
7268 gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
7271 /* Cancels the current edition by resetting the appointment's text to its original value */
7272 static void
7273 cancel_editing (EDayView *day_view)
7275 gint day, event_num;
7276 EDayViewEvent *event;
7277 const gchar *summary;
7279 day = day_view->editing_event_day;
7280 event_num = day_view->editing_event_num;
7282 if (day == -1)
7283 return;
7285 if (day == E_DAY_VIEW_LONG_EVENT) {
7286 if (!is_array_index_in_bounds (day_view->long_events, event_num))
7287 return;
7289 event = &g_array_index (day_view->long_events, EDayViewEvent, event_num);
7290 } else {
7291 if (!is_array_index_in_bounds (day_view->events[day], event_num))
7292 return;
7294 event = &g_array_index (day_view->events[day], EDayViewEvent, event_num);
7297 if (!is_comp_data_valid (event))
7298 return;
7300 /* Reset the text to what was in the component */
7302 summary = icalcomponent_get_summary (event->comp_data->icalcomp);
7303 g_object_set (
7304 event->canvas_item,
7305 "text", summary ? summary : "",
7306 NULL);
7308 /* Stop editing */
7309 e_day_view_stop_editing_event (day_view);
7312 static EDayViewEvent *
7313 tooltip_get_view_event (EDayView *day_view,
7314 gint day,
7315 gint event_num)
7317 EDayViewEvent *pevent;
7319 if (day == E_DAY_VIEW_LONG_EVENT) {
7320 if (!is_array_index_in_bounds (day_view->long_events, event_num))
7321 return NULL;
7323 pevent = &g_array_index (day_view->long_events, EDayViewEvent,
7324 event_num);
7325 } else {
7326 if (!is_array_index_in_bounds (day_view->events[day], event_num))
7327 return NULL;
7329 pevent = &g_array_index (day_view->events[day], EDayViewEvent,
7330 event_num);
7333 return pevent;
7336 static void
7337 tooltip_destroy (EDayView *day_view,
7338 GnomeCanvasItem *item)
7340 GtkWidget *tooltip = g_object_get_data (G_OBJECT (day_view), "tooltip-window");
7342 if (tooltip) {
7343 gtk_widget_destroy (tooltip);
7344 g_object_set_data (G_OBJECT (day_view), "tooltip-window", NULL);
7347 if (item) {
7348 EDayViewEvent *pevent;
7349 gint event_num, day;
7351 e_day_view_check_layout (day_view);
7353 event_num = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-num"));
7354 day = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-day"));
7355 pevent = tooltip_get_view_event (day_view, day, event_num);
7356 if (pevent) {
7357 pevent->tooltip = NULL;
7358 if (pevent->timeout != -1) {
7359 g_source_remove (pevent->timeout);
7360 pevent->timeout = -1;
7366 static gboolean
7367 e_day_view_on_text_item_event (GnomeCanvasItem *item,
7368 GdkEvent *event,
7369 EDayView *day_view)
7371 switch (event->type) {
7372 case GDK_KEY_PRESS:
7373 tooltip_destroy (day_view, item);
7374 if (!E_TEXT (item)->preedit_len && event && (
7375 event->key.keyval == GDK_KEY_Return ||
7376 event->key.keyval == GDK_KEY_KP_Enter)) {
7377 day_view->resize_event_num = -1;
7379 /* We set the keyboard focus to the EDayView, so the
7380 * EText item loses it and stops the edit. */
7381 gtk_widget_grab_focus (GTK_WIDGET (day_view));
7383 /* Stop the signal last or we will also stop any
7384 * other events getting to the EText item. */
7385 g_signal_stop_emission_by_name (item, "event");
7386 return TRUE;
7387 } else if (event->key.keyval == GDK_KEY_Escape) {
7388 cancel_editing (day_view);
7389 g_signal_stop_emission_by_name (item, "event");
7390 /* focus should go to day view when stop editing */
7391 gtk_widget_grab_focus (GTK_WIDGET (day_view));
7392 return TRUE;
7393 } else if ((event->key.keyval == GDK_KEY_Up)
7394 && (event->key.state & GDK_SHIFT_MASK)
7395 && (event->key.state & GDK_CONTROL_MASK)
7396 && !(event->key.state & GDK_MOD1_MASK)) {
7397 e_day_view_change_event_end_time_up (day_view);
7398 return TRUE;
7399 } else if ((event->key.keyval == GDK_KEY_Down)
7400 && (event->key.state & GDK_SHIFT_MASK)
7401 && (event->key.state & GDK_CONTROL_MASK)
7402 && !(event->key.state & GDK_MOD1_MASK)) {
7403 e_day_view_change_event_end_time_down (day_view);
7404 return TRUE;
7406 break;
7407 case GDK_2BUTTON_PRESS:
7408 break;
7410 case GDK_BUTTON_RELEASE:
7411 if (day_view->resize_event_num != -1)
7412 day_view->resize_event_num = -1;
7414 if (day_view->drag_event_num != -1)
7415 day_view->drag_event_num = -1;
7416 tooltip_destroy (day_view, item);
7417 /* Only let the EText handle the event while editing. */
7418 if (!E_TEXT (item)->editing)
7419 g_signal_stop_emission_by_name (item, "event");
7420 break;
7422 case GDK_BUTTON_PRESS:
7423 tooltip_destroy (day_view, item);
7424 /* Only let the EText handle the event while editing. */
7425 if (!E_TEXT (item)->editing)
7426 g_signal_stop_emission_by_name (item, "event");
7427 break;
7428 case GDK_FOCUS_CHANGE:
7429 if (event->focus_change.in)
7430 e_day_view_on_editing_started (day_view, item);
7431 else
7432 e_day_view_on_editing_stopped (day_view, item);
7434 return FALSE;
7435 case GDK_ENTER_NOTIFY:
7437 EDayViewEvent *pevent;
7438 ECalendarViewEventData *data;
7439 gint event_x, event_y, row, day, event_num;
7440 ECalendarViewPosition pos;
7441 gboolean main_canvas = TRUE;
7442 GdkWindow *window;
7443 GtkLayout *layout;
7445 if (day_view->editing_event_num != -1)
7446 break;
7448 if (day_view->resize_event_num != -1)
7449 break;
7451 if (day_view->drag_event_num != -1)
7452 break;
7454 /* Convert the coords to the main canvas window, or return if the
7455 * window is not found. */
7456 layout = GTK_LAYOUT (day_view->main_canvas);
7457 window = gtk_layout_get_bin_window (layout);
7458 if (!e_day_view_convert_event_coords (
7459 day_view, (GdkEvent *) event,
7460 window, &event_x, &event_y)) {
7462 main_canvas = FALSE;
7464 layout = GTK_LAYOUT (day_view->top_canvas);
7465 window = gtk_layout_get_bin_window (layout);
7466 if (!e_day_view_convert_event_coords (
7467 day_view, (GdkEvent *) event,
7468 window, &event_x, &event_y)) {
7469 return FALSE;
7472 /* Find out where the mouse is. */
7473 if (main_canvas) {
7474 pos = e_day_view_convert_position_in_main_canvas (
7475 day_view,
7476 event_x, event_y,
7477 &day, &row,
7478 &event_num);
7479 } else {
7480 gint tmp;
7482 pos = e_day_view_convert_position_in_top_canvas (
7483 day_view,
7484 event_x, event_y,
7485 &tmp, &event_num);
7486 day = E_DAY_VIEW_LONG_EVENT;
7489 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
7490 break;
7492 /* even when returns position inside, or other, then the day and/or event_num
7493 * can be unknown, thus check for this here, otherwise it will crash later */
7494 if (day == -1 || event_num == -1)
7495 break;
7497 pevent = tooltip_get_view_event (day_view, day, event_num);
7498 if (!pevent)
7499 break;
7501 g_object_set_data (G_OBJECT (item), "event-num", GINT_TO_POINTER (event_num));
7502 g_object_set_data (G_OBJECT (item), "event-day", GINT_TO_POINTER (day));
7504 data = g_malloc (sizeof (ECalendarViewEventData));
7505 pevent->x = ((GdkEventCrossing *) event)->x_root;
7506 pevent->y = ((GdkEventCrossing *) event)->y_root;
7507 pevent->tooltip = NULL;
7509 data->cal_view = (ECalendarView *) day_view;
7510 data->day = day;
7511 data->event_num = event_num;
7512 data->get_view_event = (ECalendarViewEvent * (*)(ECalendarView *, int, gint)) tooltip_get_view_event;
7513 pevent->timeout = e_named_timeout_add_full (
7514 G_PRIORITY_DEFAULT, 500,
7515 (GSourceFunc) e_calendar_view_get_tooltips,
7516 data, (GDestroyNotify) g_free);
7518 return TRUE;
7520 case GDK_LEAVE_NOTIFY:
7521 tooltip_destroy (day_view, item);
7522 return TRUE;
7523 case GDK_MOTION_NOTIFY:
7525 EDayViewEvent *pevent;
7526 gint event_num, day;
7528 e_day_view_check_layout (day_view);
7530 event_num = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-num"));
7531 day = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "event-day"));
7533 pevent = tooltip_get_view_event (day_view, day, event_num);
7534 if (!pevent)
7535 break;
7537 pevent->x = ((GdkEventMotion *) event)->x_root;
7538 pevent->y = ((GdkEventMotion *) event)->y_root;
7539 pevent->tooltip = (GtkWidget *) g_object_get_data (G_OBJECT (day_view), "tooltip-window");
7541 if (pevent->tooltip) {
7542 e_calendar_view_move_tip (pevent->tooltip, pevent->x + 16, pevent->y + 16);
7545 return TRUE;
7547 default:
7548 break;
7551 return FALSE;
7554 static gboolean
7555 e_day_view_event_move (ECalendarView *cal_view,
7556 ECalViewMoveDirection direction)
7558 EDayViewEvent *event;
7559 EDayView *day_view;
7560 gint time_divisions;
7561 gint day, event_num, resize_start_row, resize_end_row;
7562 time_t start_dt, end_dt;
7563 struct icaltimetype start_time, end_time;
7565 day_view = E_DAY_VIEW (cal_view);
7566 day = day_view->editing_event_day;
7567 event_num = day_view->editing_event_num;
7569 time_divisions = e_calendar_view_get_time_divisions (cal_view);
7571 if ((day == -1) || (day == E_DAY_VIEW_LONG_EVENT))
7572 return FALSE;
7574 if (!is_array_index_in_bounds (day_view->events[day], event_num))
7575 return FALSE;
7577 event = &g_array_index (day_view->events[day], EDayViewEvent,
7578 event_num);
7579 day_view->resize_event_day = day;
7580 day_view->resize_event_num = event_num;
7581 day_view->resize_bars_event_day = day;
7582 day_view->resize_bars_event_num = event_num;
7583 resize_start_row = event->start_minute / time_divisions;
7584 resize_end_row = (event->end_minute - 1) / time_divisions;
7585 if (resize_end_row < resize_start_row)
7586 resize_end_row = resize_start_row;
7588 switch (direction) {
7589 case E_CAL_VIEW_MOVE_UP:
7590 if (resize_start_row <= 0)
7591 return FALSE;
7592 resize_start_row--;
7593 resize_end_row--;
7594 start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row);
7595 end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1);
7596 break;
7597 case E_CAL_VIEW_MOVE_DOWN:
7598 if (resize_end_row >= day_view->rows - 1)
7599 return FALSE;
7600 resize_start_row++;
7601 resize_end_row++;
7602 start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row);
7603 end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1);
7604 break;
7605 case E_CAL_VIEW_MOVE_LEFT:
7606 if (day <= 0)
7607 return TRUE;
7608 start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row);
7609 end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1);
7610 start_time = icaltime_from_timet_with_zone (start_dt, 0, NULL);
7611 end_time = icaltime_from_timet_with_zone (end_dt, 0, NULL);
7612 icaltime_adjust (&start_time ,-1,0,0,0);
7613 icaltime_adjust (&end_time ,-1,0,0,0);
7614 start_dt = icaltime_as_timet (start_time);
7615 end_dt = icaltime_as_timet (end_time);
7616 break;
7617 case E_CAL_VIEW_MOVE_RIGHT:
7618 if (day + 1 >= e_day_view_get_days_shown (day_view))
7619 return TRUE;
7620 start_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_start_row);
7621 end_dt = e_day_view_convert_grid_position_to_time (day_view, day, resize_end_row + 1);
7622 start_time = icaltime_from_timet_with_zone (start_dt, 0, NULL);
7623 end_time = icaltime_from_timet_with_zone (end_dt, 0, NULL);
7624 icaltime_adjust (&start_time ,1,0,0,0);
7625 icaltime_adjust (&end_time ,1,0,0,0);
7626 start_dt = icaltime_as_timet (start_time);
7627 end_dt = icaltime_as_timet (end_time);
7628 break;
7629 default:
7630 return FALSE;
7633 e_day_view_change_event_time (day_view, start_dt, end_dt);
7634 e_day_view_ensure_rows_visible (day_view, resize_start_row, resize_end_row);
7636 return TRUE;
7639 static void
7640 e_day_view_change_event_time (EDayView *day_view,
7641 time_t start_dt,
7642 time_t end_dt)
7644 EDayViewEvent *event;
7645 gint day, event_num;
7646 ECalComponent *comp;
7647 ECalComponentDateTime date;
7648 struct icaltimetype itt;
7649 ECalModel *model;
7650 ECalClient *client;
7651 ESourceRegistry *registry;
7652 ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
7654 day = day_view->editing_event_day;
7655 event_num = day_view->editing_event_num;
7657 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
7658 registry = e_cal_model_get_registry (model);
7660 if (!is_array_index_in_bounds (day_view->events[day], event_num))
7661 return;
7663 event = &g_array_index (day_view->events[day], EDayViewEvent,
7664 event_num);
7666 if (!is_comp_data_valid (event))
7667 return;
7669 client = event->comp_data->client;
7671 /* We use a temporary shallow copy of the ico since we don't want to
7672 * change the original ico here. Otherwise we would not detect that
7673 * the event's time had changed in the "update_event" callback. */
7674 comp = e_cal_component_new ();
7675 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
7677 if (e_cal_component_has_attendees (comp) &&
7678 !itip_organizer_is_user (registry, comp, client)) {
7679 g_object_unref (comp);
7680 return;
7683 date.value = &itt;
7684 /* FIXME: Should probably keep the timezone of the original start
7685 * and end times. */
7686 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
7688 *date.value = icaltime_from_timet_with_zone (start_dt, FALSE,
7689 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
7690 cal_comp_set_dtstart_with_oldzone (client, comp, &date);
7691 *date.value = icaltime_from_timet_with_zone (end_dt, FALSE,
7692 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
7693 cal_comp_set_dtend_with_oldzone (client, comp, &date);
7695 e_cal_component_commit_sequence (comp);
7697 if (day_view->last_edited_comp_string != NULL) {
7698 g_free (day_view->last_edited_comp_string);
7699 day_view->last_edited_comp_string = NULL;
7702 day_view->last_edited_comp_string = e_cal_component_get_as_string (comp);
7704 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
7706 if (e_cal_component_has_recurrences (comp)) {
7707 if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
7708 gtk_widget_queue_draw (day_view->top_canvas);
7709 goto out;
7712 if (mod == E_CAL_OBJ_MOD_THIS) {
7713 e_cal_component_set_rdate_list (comp, NULL);
7714 e_cal_component_set_rrule_list (comp, NULL);
7715 e_cal_component_set_exdate_list (comp, NULL);
7716 e_cal_component_set_exrule_list (comp, NULL);
7718 } else if (e_cal_component_is_instance (comp))
7719 mod = E_CAL_OBJ_MOD_THIS;
7721 e_cal_component_commit_sequence (comp);
7723 e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp),
7724 mod, E_CAL_OPS_SEND_FLAG_ASK | E_CAL_OPS_SEND_FLAG_IS_NEW_COMPONENT);
7726 out:
7727 g_object_unref (comp);
7730 static void
7731 e_day_view_change_event_end_time_up (EDayView *day_view)
7733 EDayViewEvent *event;
7734 ECalendarView *cal_view;
7735 gint time_divisions;
7736 gint day, event_num, resize_start_row, resize_end_row;
7738 day = day_view->editing_event_day;
7739 event_num = day_view->editing_event_num;
7740 if ((day == -1) || (day == E_DAY_VIEW_LONG_EVENT))
7741 return;
7743 if (!is_array_index_in_bounds (day_view->events[day], event_num))
7744 return;
7746 cal_view = E_CALENDAR_VIEW (day_view);
7747 time_divisions = e_calendar_view_get_time_divisions (cal_view);
7749 event = &g_array_index (day_view->events[day], EDayViewEvent,
7750 event_num);
7751 day_view->resize_event_day = day;
7752 day_view->resize_event_num = event_num;
7753 day_view->resize_bars_event_day = day;
7754 day_view->resize_bars_event_num = event_num;
7755 resize_start_row = event->start_minute / time_divisions;
7756 resize_end_row = (event->end_minute - 1) / time_divisions;
7757 if (resize_end_row < resize_start_row)
7758 resize_end_row = resize_start_row;
7759 if (resize_end_row == resize_start_row)
7760 return;
7761 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_BOTTOM_EDGE;
7762 resize_end_row--;
7763 day_view->resize_start_row = resize_start_row;
7764 day_view->resize_end_row = resize_end_row;
7765 e_day_view_finish_resize (day_view);
7766 e_day_view_ensure_rows_visible (day_view, resize_start_row, resize_end_row);
7769 static void
7770 e_day_view_change_event_end_time_down (EDayView *day_view)
7772 EDayViewEvent *event;
7773 ECalendarView *cal_view;
7774 gint time_divisions;
7775 gint day, event_num, resize_start_row, resize_end_row;
7777 cal_view = E_CALENDAR_VIEW (day_view);
7778 time_divisions = e_calendar_view_get_time_divisions (cal_view);
7780 day = day_view->editing_event_day;
7781 event_num = day_view->editing_event_num;
7782 if ((day == -1) || (day == E_DAY_VIEW_LONG_EVENT))
7783 return;
7785 if (!is_array_index_in_bounds (day_view->events[day], event_num))
7786 return;
7788 event = &g_array_index (day_view->events[day], EDayViewEvent,
7789 event_num);
7790 day_view->resize_event_day = day;
7791 day_view->resize_event_num = event_num;
7792 day_view->resize_bars_event_day = day;
7793 day_view->resize_bars_event_num = event_num;
7794 resize_start_row = event->start_minute / time_divisions;
7795 resize_end_row = (event->end_minute - 1) / time_divisions;
7796 if (resize_end_row < resize_start_row)
7797 resize_end_row = resize_start_row;
7798 if (resize_end_row == day_view->rows -1)
7799 return;
7800 day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_BOTTOM_EDGE;
7801 resize_end_row++;
7802 day_view->resize_start_row = resize_start_row;
7803 day_view->resize_end_row = resize_end_row;
7804 e_day_view_finish_resize (day_view);
7805 e_day_view_ensure_rows_visible (day_view, resize_start_row, resize_end_row);
7808 static void
7809 e_day_view_on_editing_started (EDayView *day_view,
7810 GnomeCanvasItem *item)
7812 GtkAllocation allocation;
7813 gint day, event_num;
7815 if (!e_day_view_find_event_from_item (day_view, item,
7816 &day, &event_num))
7817 return;
7819 /* FIXME: This is a temporary workaround for a bug which seems to stop
7820 * us getting focus_out signals. It is not a complete fix since if we
7821 * don't get focus_out signals we don't save the appointment text so
7822 * this may be lost. */
7823 if (day_view->editing_event_day == day
7824 && day_view->editing_event_num == event_num)
7825 return;
7827 day_view->editing_event_day = day;
7828 day_view->editing_event_num = event_num;
7830 gtk_widget_get_allocation (day_view->top_canvas, &allocation);
7832 if (day == E_DAY_VIEW_LONG_EVENT) {
7833 gint item_x, item_y, item_w, item_h, scroll_y;
7834 gint start_day, end_day;
7836 e_day_view_reshape_long_event (day_view, event_num);
7838 if (e_day_view_get_long_event_position (day_view, event_num,
7839 &start_day, &end_day,
7840 &item_x, &item_y,
7841 &item_w, &item_h)) {
7842 GtkAdjustment *adjustment;
7843 GtkScrollable *scrollable;
7845 scrollable = GTK_SCROLLABLE (day_view->top_canvas);
7846 adjustment = gtk_scrollable_get_vadjustment (scrollable);
7848 /* and ensure it's visible too */
7849 /*item_y = (event_num * (day_view->top_row_height + 1)) - 1;*/
7850 scroll_y = gtk_adjustment_get_value (adjustment);
7851 if (item_y + day_view->top_row_height > allocation.height + scroll_y || item_y < scroll_y)
7852 gnome_canvas_scroll_to (GNOME_CANVAS (day_view->top_canvas), 0, item_y);
7854 } else {
7855 day_view->resize_bars_event_day = day;
7856 day_view->resize_bars_event_num = event_num;
7857 e_day_view_update_event_label (day_view, day, event_num);
7858 e_day_view_reshape_main_canvas_resize_bars (day_view);
7861 g_signal_emit_by_name (day_view, "selection_changed");
7863 g_object_notify (G_OBJECT (day_view), "is-editing");
7866 static void
7867 e_day_view_on_editing_stopped (EDayView *day_view,
7868 GnomeCanvasItem *item)
7870 gint day, event_num;
7871 EDayViewEvent *event;
7872 gchar *text = NULL;
7873 ECalComponentText summary;
7874 ECalComponent *comp;
7875 ECalClient *client;
7876 gboolean on_server;
7878 /* Note: the item we are passed here isn't reliable, so we just stop
7879 * the edit of whatever item was being edited. We also receive this
7880 * event twice for some reason. */
7881 day = day_view->editing_event_day;
7882 event_num = day_view->editing_event_num;
7884 /* If no item is being edited, just return. */
7885 if (day == -1)
7886 return;
7888 if (day == E_DAY_VIEW_LONG_EVENT) {
7889 if (!is_array_index_in_bounds (day_view->long_events, event_num))
7890 return;
7892 event = &g_array_index (day_view->long_events, EDayViewEvent,
7893 event_num);
7894 } else {
7895 if (!is_array_index_in_bounds (day_view->events[day], event_num))
7896 return;
7898 event = &g_array_index (day_view->events[day], EDayViewEvent,
7899 event_num);
7903 if (!is_comp_data_valid (event))
7904 return;
7906 /* Reset the edit fields. */
7907 day_view->editing_event_day = -1;
7908 day_view->editing_event_num = -1;
7910 day_view->resize_bars_event_day = -1;
7911 day_view->resize_bars_event_num = -1;
7913 g_object_set (event->canvas_item, "handle_popup", FALSE, NULL);
7914 g_object_get (
7915 event->canvas_item,
7916 "text", &text,
7917 NULL);
7918 g_return_if_fail (text != NULL);
7920 comp = e_cal_component_new ();
7921 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
7923 client = event->comp_data->client;
7924 on_server = !event->comp_data->is_new_component;
7926 if (string_is_empty (text) && !on_server) {
7927 const gchar *uid;
7929 e_cal_component_get_uid (comp, &uid);
7931 e_day_view_foreach_event_with_uid (day_view, uid,
7932 e_day_view_remove_event_cb, NULL);
7933 e_day_view_check_layout (day_view);
7934 gtk_widget_queue_draw (day_view->top_canvas);
7935 gtk_widget_queue_draw (day_view->main_canvas);
7936 goto out;
7939 /* Only update the summary if necessary. */
7940 e_cal_component_get_summary (comp, &summary);
7941 if (summary.value && !strcmp (text, summary.value)) {
7942 if (day == E_DAY_VIEW_LONG_EVENT)
7943 e_day_view_reshape_long_event (day_view, event_num);
7944 else
7945 e_day_view_update_event_label (
7946 day_view, day,
7947 event_num);
7948 } else if (summary.value || !string_is_empty (text)) {
7949 icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
7951 summary.value = text;
7952 summary.altrep = NULL;
7953 e_cal_component_set_summary (comp, &summary);
7954 e_cal_component_commit_sequence (comp);
7956 if (!on_server) {
7957 e_cal_ops_create_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), client, icalcomp,
7958 e_calendar_view_component_created_cb, g_object_ref (day_view), g_object_unref);
7960 /* we remove the object since we either got the update from the server or failed */
7961 e_day_view_remove_event_cb (day_view, day, event_num, NULL);
7962 } else {
7963 ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
7965 if (e_cal_component_has_recurrences (comp)) {
7966 if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
7967 goto out;
7970 if (mod == E_CAL_OBJ_MOD_THIS) {
7971 ECalComponentDateTime olddt, dt;
7972 icaltimetype itt;
7974 dt.value = &itt;
7976 e_cal_component_get_dtstart (comp, &olddt);
7977 if (olddt.value->zone) {
7978 *dt.value = icaltime_from_timet_with_zone (
7979 event->comp_data->instance_start,
7980 olddt.value->is_date,
7981 olddt.value->zone);
7982 } else {
7983 *dt.value = icaltime_from_timet_with_zone (
7984 event->comp_data->instance_start,
7985 olddt.value->is_date,
7986 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
7988 dt.tzid = olddt.tzid;
7989 e_cal_component_set_dtstart (comp, &dt);
7990 dt.tzid = NULL;
7991 e_cal_component_free_datetime (&olddt);
7993 e_cal_component_get_dtend (comp, &olddt);
7994 if (olddt.value->zone) {
7995 *dt.value = icaltime_from_timet_with_zone (
7996 event->comp_data->instance_end,
7997 olddt.value->is_date,
7998 olddt.value->zone);
7999 } else {
8000 *dt.value = icaltime_from_timet_with_zone (
8001 event->comp_data->instance_end,
8002 olddt.value->is_date,
8003 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
8005 dt.tzid = olddt.tzid;
8006 e_cal_component_set_dtend (comp, &dt);
8007 dt.tzid = NULL;
8008 e_cal_component_free_datetime (&olddt);
8010 e_cal_component_set_rdate_list (comp, NULL);
8011 e_cal_component_set_rrule_list (comp, NULL);
8012 e_cal_component_set_exdate_list (comp, NULL);
8013 e_cal_component_set_exrule_list (comp, NULL);
8015 e_cal_component_commit_sequence (comp);
8017 } else if (e_cal_component_is_instance (comp))
8018 mod = E_CAL_OBJ_MOD_THIS;
8020 e_cal_ops_modify_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)),
8021 client, e_cal_component_get_icalcomponent (comp), mod, E_CAL_OPS_SEND_FLAG_ASK);
8025 gtk_widget_queue_draw (day_view->main_canvas);
8027 out:
8029 g_object_unref (comp);
8030 g_free (text);
8032 g_signal_emit_by_name (day_view, "selection_changed");
8034 g_object_notify (G_OBJECT (day_view), "is-editing");
8037 /* FIXME: It is possible that we may produce an invalid time due to daylight
8038 * saving times (i.e. when clocks go forward there is a range of time which
8039 * is not valid). I don't know the best way to handle daylight saving time. */
8040 static time_t
8041 e_day_view_convert_grid_position_to_time (EDayView *day_view,
8042 gint col,
8043 gint row)
8045 ECalendarView *cal_view;
8046 gint time_divisions;
8047 struct icaltimetype tt;
8048 time_t val;
8049 gint minutes;
8051 cal_view = E_CALENDAR_VIEW (day_view);
8052 time_divisions = e_calendar_view_get_time_divisions (cal_view);
8054 /* Calulate the number of minutes since the start of the day. */
8055 minutes = day_view->first_hour_shown * 60
8056 + day_view->first_minute_shown
8057 + row * time_divisions;
8059 /* A special case for midnight, where we can use the start of the
8060 * next day. */
8061 if (minutes == 60 * 24)
8062 return day_view->day_starts[col + 1];
8064 /* Create an icaltimetype and convert to a time_t. */
8065 tt = icaltime_from_timet_with_zone (
8066 day_view->day_starts[col],
8067 FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
8068 tt.hour = minutes / 60;
8069 tt.minute = minutes % 60;
8070 tt.second = 0;
8072 val = icaltime_as_timet_with_zone (tt, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
8073 return val;
8076 static gboolean
8077 e_day_view_convert_time_to_grid_position (EDayView *day_view,
8078 time_t time,
8079 gint *col,
8080 gint *row)
8082 ECalendarView *cal_view;
8083 struct icaltimetype tt;
8084 gint time_divisions;
8085 gint day, minutes;
8086 gint days_shown;
8088 *col = *row = 0;
8090 cal_view = E_CALENDAR_VIEW (day_view);
8091 time_divisions = e_calendar_view_get_time_divisions (cal_view);
8093 if (time < day_view->lower || time >= day_view->upper)
8094 return FALSE;
8096 days_shown = e_day_view_get_days_shown (day_view);
8098 /* We can find the column easily using the day_starts array. */
8099 for (day = 1; day <= days_shown; day++) {
8100 if (time < day_view->day_starts[day]) {
8101 *col = day - 1;
8102 break;
8106 /* To find the row we need to convert the time to an icaltimetype,
8107 * calculate the offset in minutes from the top of the display and
8108 * divide it by the mins per row setting. */
8109 tt = icaltime_from_timet_with_zone (time, FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
8111 minutes = tt.hour * 60 + tt.minute;
8112 minutes -= day_view->first_hour_shown * 60 + day_view->first_minute_shown;
8114 *row = minutes / time_divisions;
8116 if (*row < 0 || *row >= day_view->rows)
8117 return FALSE;
8119 return TRUE;
8122 /* This starts or stops auto-scrolling when dragging a selection or resizing
8123 * an event. */
8124 void
8125 e_day_view_check_auto_scroll (EDayView *day_view,
8126 gint event_x,
8127 gint event_y)
8129 GtkAllocation allocation;
8130 gint scroll_x, scroll_y;
8132 gnome_canvas_get_scroll_offsets (
8133 GNOME_CANVAS (day_view->main_canvas),
8134 &scroll_x, &scroll_y);
8136 event_x -= scroll_x;
8137 event_y -= scroll_y;
8139 day_view->last_mouse_x = event_x;
8140 day_view->last_mouse_y = event_y;
8142 gtk_widget_get_allocation (day_view->main_canvas, &allocation);
8144 if (event_y < E_DAY_VIEW_AUTO_SCROLL_OFFSET)
8145 e_day_view_start_auto_scroll (day_view, TRUE);
8146 else if (event_y >= allocation.height - E_DAY_VIEW_AUTO_SCROLL_OFFSET)
8147 e_day_view_start_auto_scroll (day_view, FALSE);
8148 else
8149 e_day_view_stop_auto_scroll (day_view);
8152 static void
8153 e_day_view_start_auto_scroll (EDayView *day_view,
8154 gboolean scroll_up)
8156 if (day_view->auto_scroll_timeout_id == 0) {
8157 day_view->auto_scroll_timeout_id = e_named_timeout_add (
8158 E_DAY_VIEW_AUTO_SCROLL_TIMEOUT,
8159 e_day_view_auto_scroll_handler, day_view);
8160 day_view->auto_scroll_delay = E_DAY_VIEW_AUTO_SCROLL_DELAY;
8162 day_view->auto_scroll_up = scroll_up;
8165 void
8166 e_day_view_stop_auto_scroll (EDayView *day_view)
8168 if (day_view->auto_scroll_timeout_id != 0) {
8169 g_source_remove (day_view->auto_scroll_timeout_id);
8170 day_view->auto_scroll_timeout_id = 0;
8174 static gboolean
8175 e_day_view_auto_scroll_handler (gpointer data)
8177 EDayView *day_view;
8178 ECalendarViewPosition pos;
8179 gint scroll_x, scroll_y, new_scroll_y, canvas_x, canvas_y, row, day;
8180 GtkAdjustment *adjustment;
8181 GtkScrollable *scrollable;
8182 gdouble step_increment;
8183 gdouble page_size;
8184 gdouble upper;
8186 g_return_val_if_fail (E_IS_DAY_VIEW (data), FALSE);
8188 day_view = E_DAY_VIEW (data);
8190 if (day_view->auto_scroll_delay > 0) {
8191 day_view->auto_scroll_delay--;
8192 return TRUE;
8195 gnome_canvas_get_scroll_offsets (
8196 GNOME_CANVAS (day_view->main_canvas),
8197 &scroll_x, &scroll_y);
8199 scrollable = GTK_SCROLLABLE (day_view->main_canvas);
8200 adjustment = gtk_scrollable_get_vadjustment (scrollable);
8202 step_increment = gtk_adjustment_get_step_increment (adjustment);
8203 page_size = gtk_adjustment_get_page_size (adjustment);
8204 upper = gtk_adjustment_get_upper (adjustment);
8206 if (day_view->auto_scroll_up)
8207 new_scroll_y = MAX (scroll_y - step_increment, 0);
8208 else
8209 new_scroll_y = MIN (
8210 scroll_y + step_increment,
8211 upper - page_size);
8213 if (new_scroll_y != scroll_y) {
8214 /* NOTE: This reduces flicker, but only works if we don't use
8215 * canvas items which have X windows. */
8216 gnome_canvas_scroll_to (
8217 GNOME_CANVAS (day_view->main_canvas),
8218 scroll_x, new_scroll_y);
8221 canvas_x = day_view->last_mouse_x + scroll_x;
8222 canvas_y = day_view->last_mouse_y + new_scroll_y;
8224 /* The last_mouse_x position is set to -1 when we are selecting using
8225 * the time column. In this case we set canvas_x to 0 and we ignore
8226 * the resulting day. */
8227 if (day_view->last_mouse_x == -1)
8228 canvas_x = 0;
8230 /* Update the selection/resize/drag if necessary. */
8231 pos = e_day_view_convert_position_in_main_canvas (
8232 day_view,
8233 canvas_x, canvas_y,
8234 &day, &row, NULL);
8236 if (day_view->last_mouse_x == -1)
8237 day = -1;
8239 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
8240 if (day_view->selection_is_being_dragged) {
8241 e_day_view_update_selection (day_view, day, row);
8242 } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
8243 e_day_view_update_resize (day_view, row);
8244 } else if (day_view->drag_item->flags & GNOME_CANVAS_ITEM_VISIBLE) {
8245 e_day_view_update_main_canvas_drag (day_view, row, day);
8249 return TRUE;
8252 gboolean
8253 e_day_view_get_event_rows (EDayView *day_view,
8254 gint day,
8255 gint event_num,
8256 gint *start_row_out,
8257 gint *end_row_out)
8259 ECalendarView *cal_view;
8260 EDayViewEvent *event;
8261 gint time_divisions;
8262 gint start_row, end_row;
8264 g_return_val_if_fail (day >= 0, FALSE);
8265 g_return_val_if_fail (day < E_DAY_VIEW_LONG_EVENT, FALSE);
8266 g_return_val_if_fail (event_num >= 0, FALSE);
8268 if (!is_array_index_in_bounds (day_view->events[day], event_num))
8269 return FALSE;
8271 cal_view = E_CALENDAR_VIEW (day_view);
8272 time_divisions = e_calendar_view_get_time_divisions (cal_view);
8274 event = &g_array_index (day_view->events[day], EDayViewEvent,
8275 event_num);
8276 start_row = event->start_minute / time_divisions;
8277 end_row = (event->end_minute - 1) / time_divisions;
8278 if (end_row < start_row)
8279 end_row = start_row;
8281 *start_row_out = start_row;
8282 *end_row_out = end_row;
8283 return TRUE;
8286 gboolean
8287 e_day_view_get_event_position (EDayView *day_view,
8288 gint day,
8289 gint event_num,
8290 gint *item_x,
8291 gint *item_y,
8292 gint *item_w,
8293 gint *item_h)
8295 EDayViewEvent *event;
8296 gint start_row, end_row, cols_in_row, start_col, num_columns;
8298 if (!is_array_index_in_bounds (day_view->events[day], event_num))
8299 return FALSE;
8301 event = &g_array_index (day_view->events[day], EDayViewEvent,
8302 event_num);
8304 /* If the event is flagged as not displayed, return FALSE. */
8305 if (event->num_columns == 0)
8306 return FALSE;
8308 e_day_view_get_event_rows (day_view, day, event_num, &start_row, &end_row);
8310 cols_in_row = day_view->cols_per_row[day][start_row];
8311 start_col = event->start_row_or_col;
8312 num_columns = event->num_columns;
8314 if (cols_in_row == 0)
8315 return FALSE;
8317 /* If the event is being resize, use the resize position. */
8318 if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE
8319 && day_view->resize_event_day == day
8320 && day_view->resize_event_num == event_num) {
8321 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE)
8322 start_row = day_view->resize_start_row;
8323 else if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_BOTTOM_EDGE)
8324 end_row = day_view->resize_end_row;
8327 *item_x = day_view->day_offsets[day]
8328 + day_view->day_widths[day] * start_col / cols_in_row;
8329 *item_w = day_view->day_widths[day] * num_columns / cols_in_row
8330 - E_DAY_VIEW_GAP_WIDTH;
8331 *item_w = MAX (*item_w, 0);
8332 *item_y = start_row * day_view->row_height;
8334 /* This makes the event end on the grid line of the next row,
8335 * which maybe looks nicer if you have 2 events on consecutive rows. */
8336 *item_h = (end_row - start_row + 1) * day_view->row_height + 1;
8338 return TRUE;
8341 gboolean
8342 e_day_view_get_long_event_position (EDayView *day_view,
8343 gint event_num,
8344 gint *start_day,
8345 gint *end_day,
8346 gint *item_x,
8347 gint *item_y,
8348 gint *item_w,
8349 gint *item_h)
8351 EDayViewEvent *event;
8352 gint days_shown;
8354 days_shown = e_day_view_get_days_shown (day_view);
8356 if (!is_array_index_in_bounds (day_view->long_events, event_num))
8357 return FALSE;
8359 event = &g_array_index (day_view->long_events, EDayViewEvent,
8360 event_num);
8362 /* If the event is flagged as not displayed, return FALSE. */
8363 if (event->num_columns == 0)
8364 return FALSE;
8366 if (!e_day_view_find_long_event_days (event,
8367 days_shown,
8368 day_view->day_starts,
8369 start_day, end_day))
8370 return FALSE;
8372 /* If the event is being resize, use the resize position. */
8373 if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE
8374 && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT
8375 && day_view->resize_event_num == event_num) {
8376 if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE)
8377 *start_day = day_view->resize_start_row;
8378 else if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_RIGHT_EDGE)
8379 *end_day = day_view->resize_end_row;
8382 *item_x = day_view->day_offsets[*start_day] + E_DAY_VIEW_BAR_WIDTH;
8383 if (days_shown == 1) {
8384 GtkAllocation allocation;
8386 gtk_widget_get_allocation (day_view->top_canvas, &allocation);
8387 *item_w = allocation.width;
8388 } else
8389 *item_w = day_view->day_offsets[*end_day + 1];
8390 *item_w = MAX (*item_w - *item_x - E_DAY_VIEW_GAP_WIDTH, 0);
8391 *item_y = (event->start_row_or_col) * day_view->top_row_height;
8392 *item_h = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP;
8393 return TRUE;
8396 /* Converts a position within the entire top canvas to a day & event and
8397 * a place within the event if appropriate. If event_num_return is NULL, it
8398 * simply returns the grid position without trying to find the event. */
8399 static ECalendarViewPosition
8400 e_day_view_convert_position_in_top_canvas (EDayView *day_view,
8401 gint x,
8402 gint y,
8403 gint *day_return,
8404 gint *event_num_return)
8406 EDayViewEvent *event;
8407 gint day, row, col;
8408 gint event_num, start_day, end_day, item_x, item_y, item_w, item_h;
8409 gint days_shown;
8411 days_shown = e_day_view_get_days_shown (day_view);
8413 *day_return = -1;
8414 if (event_num_return)
8415 *event_num_return = -1;
8417 if (x < 0 || y < 0)
8418 return E_CALENDAR_VIEW_POS_OUTSIDE;
8420 row = y / day_view->top_row_height;
8422 day = -1;
8423 for (col = 1; col <= days_shown; col++) {
8424 if (x < day_view->day_offsets[col]) {
8425 day = col - 1;
8426 break;
8429 if (day == -1)
8430 return E_CALENDAR_VIEW_POS_OUTSIDE;
8432 *day_return = day;
8434 /* If only the grid position is wanted, return. */
8435 if (event_num_return == NULL)
8436 return E_CALENDAR_VIEW_POS_NONE;
8438 for (event_num = 0; event_num < day_view->long_events->len;
8439 event_num++) {
8440 event = &g_array_index (day_view->long_events, EDayViewEvent,
8441 event_num);
8443 if (event->start_row_or_col != row)
8444 continue;
8446 if (!e_day_view_get_long_event_position (day_view, event_num,
8447 &start_day, &end_day,
8448 &item_x, &item_y,
8449 &item_w, &item_h))
8450 continue;
8452 if (x < item_x)
8453 continue;
8455 if (x >= item_x + item_w)
8456 continue;
8458 *event_num_return = event_num;
8460 if (x < item_x + E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH
8461 + E_DAY_VIEW_LONG_EVENT_X_PAD)
8462 return E_CALENDAR_VIEW_POS_LEFT_EDGE;
8464 if (x >= item_x + item_w - E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH
8465 - E_DAY_VIEW_LONG_EVENT_X_PAD)
8466 return E_CALENDAR_VIEW_POS_RIGHT_EDGE;
8468 return E_CALENDAR_VIEW_POS_EVENT;
8471 return E_CALENDAR_VIEW_POS_NONE;
8474 /* Converts a position within the entire main canvas to a day, row, event and
8475 * a place within the event if appropriate. If event_num_return is NULL, it
8476 * simply returns the grid position without trying to find the event. */
8477 static ECalendarViewPosition
8478 e_day_view_convert_position_in_main_canvas (EDayView *day_view,
8479 gint x,
8480 gint y,
8481 gint *day_return,
8482 gint *row_return,
8483 gint *event_num_return)
8485 gint day, row, col, event_num;
8486 gint item_x, item_y, item_w, item_h;
8487 gint days_shown;
8489 days_shown = e_day_view_get_days_shown (day_view);
8491 *day_return = -1;
8492 *row_return = -1;
8493 if (event_num_return)
8494 *event_num_return = -1;
8496 /* Check the position is inside the canvas, and determine the day
8497 * and row. */
8498 if (x < 0 || y < 0)
8499 return E_CALENDAR_VIEW_POS_OUTSIDE;
8501 row = y / day_view->row_height;
8502 if (row >= day_view->rows)
8503 return E_CALENDAR_VIEW_POS_OUTSIDE;
8505 day = -1;
8506 for (col = 1; col <= days_shown; col++) {
8507 if (x < day_view->day_offsets[col]) {
8508 day = col - 1;
8509 break;
8512 if (day == -1)
8513 return E_CALENDAR_VIEW_POS_OUTSIDE;
8515 *day_return = day;
8516 *row_return = row;
8518 /* If only the grid position is wanted, return. */
8519 if (event_num_return == NULL)
8520 return E_CALENDAR_VIEW_POS_NONE;
8522 /* Check the selected item first, since the horizontal resizing bars
8523 * may be above other events. */
8524 if (day_view->resize_bars_event_day == day) {
8525 if (e_day_view_get_event_position (day_view, day,
8526 day_view->resize_bars_event_num,
8527 &item_x, &item_y,
8528 &item_w, &item_h)) {
8529 if (x >= item_x && x < item_x + item_w) {
8530 *event_num_return = day_view->resize_bars_event_num;
8531 if (y >= item_y - E_DAY_VIEW_BAR_HEIGHT
8532 && y < item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT)
8533 return E_CALENDAR_VIEW_POS_TOP_EDGE;
8534 if (y >= item_y + item_h - E_DAY_VIEW_EVENT_BORDER_HEIGHT
8535 && y < item_y + item_h + E_DAY_VIEW_BAR_HEIGHT)
8536 return E_CALENDAR_VIEW_POS_BOTTOM_EDGE;
8541 /* Try to find the event at the found position. */
8542 *event_num_return = -1;
8543 for (event_num = 0; event_num < day_view->events[day]->len;
8544 event_num++) {
8545 if (!e_day_view_get_event_position (day_view, day, event_num,
8546 &item_x, &item_y,
8547 &item_w, &item_h))
8548 continue;
8550 if (x < item_x || x >= item_x + item_w
8551 || y < item_y || y >= item_y + item_h)
8552 continue;
8554 *event_num_return = event_num;
8556 if (x < item_x + E_DAY_VIEW_BAR_WIDTH)
8557 return E_CALENDAR_VIEW_POS_LEFT_EDGE;
8559 if (y < item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT
8560 + E_DAY_VIEW_EVENT_Y_PAD)
8561 return E_CALENDAR_VIEW_POS_TOP_EDGE;
8563 if (y >= item_y + item_h - E_DAY_VIEW_EVENT_BORDER_HEIGHT
8564 - E_DAY_VIEW_EVENT_Y_PAD)
8565 return E_CALENDAR_VIEW_POS_BOTTOM_EDGE;
8567 return E_CALENDAR_VIEW_POS_EVENT;
8570 return E_CALENDAR_VIEW_POS_NONE;
8573 static gboolean
8574 e_day_view_on_top_canvas_drag_motion (GtkWidget *widget,
8575 GdkDragContext *context,
8576 gint x,
8577 gint y,
8578 guint time,
8579 EDayView *day_view)
8581 gint scroll_x, scroll_y;
8583 gnome_canvas_get_scroll_offsets (
8584 GNOME_CANVAS (widget),
8585 &scroll_x, &scroll_y);
8586 day_view->drag_event_x = x + scroll_x;
8587 day_view->drag_event_y = y + scroll_y;
8589 e_day_view_reshape_top_canvas_drag_item (day_view);
8591 return TRUE;
8594 static void
8595 e_day_view_reshape_top_canvas_drag_item (EDayView *day_view)
8597 ECalendarViewPosition pos;
8598 gint x, y, day;
8600 /* Calculate the day & start row of the event being dragged, using
8601 * the current mouse position. */
8602 x = day_view->drag_event_x;
8603 y = day_view->drag_event_y;
8604 pos = e_day_view_convert_position_in_top_canvas (
8605 day_view, x, y,
8606 &day, NULL);
8607 /* This shouldn't really happen in a drag. */
8608 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
8609 return;
8611 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT)
8612 day -= day_view->drag_event_offset;
8613 day = MAX (day, 0);
8615 e_day_view_update_top_canvas_drag (day_view, day);
8618 static void
8619 e_day_view_update_top_canvas_drag (EDayView *day_view,
8620 gint day)
8622 EDayViewEvent *event = NULL;
8623 gint row, num_days, start_day, end_day;
8624 gdouble item_x, item_y, item_w, item_h;
8625 gint days_shown;
8626 gchar *text;
8628 days_shown = e_day_view_get_days_shown (day_view);
8630 /* Calculate the event's position. If the event is in the same
8631 * position we started in, we use the same columns. */
8632 row = day_view->rows_in_top_display + 1;
8633 num_days = 1;
8635 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
8636 if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num))
8637 return;
8639 event = &g_array_index (day_view->long_events, EDayViewEvent,
8640 day_view->drag_event_num);
8641 row = event->start_row_or_col + 1;
8643 if (!e_day_view_find_long_event_days (event,
8644 days_shown,
8645 day_view->day_starts,
8646 &start_day, &end_day))
8647 return;
8649 num_days = end_day - start_day + 1;
8651 /* Make sure we don't go off the screen. */
8652 day = MIN (day, days_shown - num_days);
8654 } else if (day_view->drag_event_day != -1) {
8655 if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num))
8656 return;
8658 event = &g_array_index (day_view->events[day_view->drag_event_day],
8659 EDayViewEvent,
8660 day_view->drag_event_num);
8663 /* If the position hasn't changed, just return. */
8664 if (day_view->drag_last_day == day
8665 && (day_view->drag_long_event_item->flags & GNOME_CANVAS_ITEM_VISIBLE))
8666 return;
8668 day_view->drag_last_day = day;
8670 item_x = day_view->day_offsets[day] + E_DAY_VIEW_BAR_WIDTH;
8671 item_w = day_view->day_offsets[day + num_days] - item_x
8672 - E_DAY_VIEW_GAP_WIDTH;
8673 item_y = row * day_view->top_row_height;
8674 item_h = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP;
8676 /* Set the positions of the event & associated items. */
8677 gnome_canvas_item_set (
8678 day_view->drag_long_event_rect_item,
8679 "x1", item_x,
8680 "y1", item_y,
8681 "x2", item_x + item_w - 1,
8682 "y2", item_y + item_h - 1,
8683 NULL);
8685 gnome_canvas_item_set (
8686 day_view->drag_long_event_item,
8687 "clip_width", item_w - (E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD) * 2,
8688 "clip_height", item_h - (E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD) * 2,
8689 NULL);
8690 e_canvas_item_move_absolute (
8691 day_view->drag_long_event_item,
8692 item_x + E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD,
8693 item_y + E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD);
8695 if (!(day_view->drag_long_event_rect_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8696 gnome_canvas_item_raise_to_top (day_view->drag_long_event_rect_item);
8697 gnome_canvas_item_show (day_view->drag_long_event_rect_item);
8700 /* Set the text, if necessary. We don't want to set the text every
8701 * time it moves, so we check if it is currently invisible and only
8702 * set the text then. */
8703 if (!(day_view->drag_long_event_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8704 const gchar *summary;
8706 if (event && is_comp_data_valid (event)) {
8707 summary = icalcomponent_get_summary (event->comp_data->icalcomp);
8708 text = g_strdup (summary);
8709 } else {
8710 text = NULL;
8713 gnome_canvas_item_set (
8714 day_view->drag_long_event_item,
8715 "text", text ? text : "",
8716 NULL);
8717 gnome_canvas_item_raise_to_top (day_view->drag_long_event_item);
8718 gnome_canvas_item_show (day_view->drag_long_event_item);
8720 g_free (text);
8724 static gboolean
8725 e_day_view_on_main_canvas_drag_motion (GtkWidget *widget,
8726 GdkDragContext *context,
8727 gint x,
8728 gint y,
8729 guint time,
8730 EDayView *day_view)
8732 gint scroll_x, scroll_y;
8734 gnome_canvas_get_scroll_offsets (
8735 GNOME_CANVAS (widget),
8736 &scroll_x, &scroll_y);
8738 day_view->drag_event_x = x + scroll_x;
8739 day_view->drag_event_y = y + scroll_y;
8741 e_day_view_reshape_main_canvas_drag_item (day_view);
8742 e_day_view_reshape_main_canvas_resize_bars (day_view);
8744 e_day_view_check_auto_scroll (day_view, day_view->drag_event_x, day_view->drag_event_y);
8746 return TRUE;
8749 static void
8750 e_day_view_reshape_main_canvas_drag_item (EDayView *day_view)
8752 ECalendarViewPosition pos;
8753 gint x, y, day, row;
8755 /* Calculate the day & start row of the event being dragged, using
8756 * the current mouse position. */
8757 x = day_view->drag_event_x;
8758 y = day_view->drag_event_y;
8759 pos = e_day_view_convert_position_in_main_canvas (
8760 day_view, x, y,
8761 &day, &row, NULL);
8762 /* This shouldn't really happen in a drag. */
8763 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
8764 return;
8766 if (day_view->drag_event_day != -1
8767 && day_view->drag_event_day != E_DAY_VIEW_LONG_EVENT)
8768 row -= day_view->drag_event_offset;
8769 row = MAX (row, 0);
8771 e_day_view_update_main_canvas_drag (day_view, row, day);
8774 static void
8775 e_day_view_update_main_canvas_drag (EDayView *day_view,
8776 gint row,
8777 gint day)
8779 EDayViewEvent *event = NULL;
8780 ECalendarView *cal_view;
8781 gint time_divisions;
8782 gint cols_in_row, start_col, num_columns, num_rows, start_row, end_row;
8783 gdouble item_x, item_y, item_w, item_h;
8784 gchar *text;
8786 cal_view = E_CALENDAR_VIEW (day_view);
8787 time_divisions = e_calendar_view_get_time_divisions (cal_view);
8789 /* If the position hasn't changed, just return. */
8790 if (day_view->drag_last_day == day
8791 && day_view->drag_last_row == row
8792 && (day_view->drag_item->flags & GNOME_CANVAS_ITEM_VISIBLE))
8793 return;
8795 day_view->drag_last_day = day;
8796 day_view->drag_last_row = row;
8798 /* Calculate the event's position. If the event is in the same
8799 * position we started in, we use the same columns. */
8800 cols_in_row = 1;
8801 start_row = 0;
8802 start_col = 0;
8803 num_columns = 1;
8804 num_rows = 1;
8806 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
8807 if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num))
8808 return;
8810 event = &g_array_index (day_view->long_events, EDayViewEvent,
8811 day_view->drag_event_num);
8812 } else if (day_view->drag_event_day != -1) {
8813 if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num))
8814 return;
8816 event = &g_array_index (day_view->events[day_view->drag_event_day],
8817 EDayViewEvent,
8818 day_view->drag_event_num);
8819 start_row = event->start_minute / time_divisions;
8820 end_row = (event->end_minute - 1) / time_divisions;
8821 if (end_row < start_row)
8822 end_row = start_row;
8824 num_rows = end_row - start_row + 1;
8826 if (day_view->drag_event_day == day && start_row == row) {
8827 cols_in_row = day_view->cols_per_row[day][row];
8828 start_col = event->start_row_or_col;
8829 num_columns = event->num_columns;
8833 item_x = day_view->day_offsets[day]
8834 + day_view->day_widths[day] * start_col / cols_in_row;
8835 item_w = day_view->day_widths[day] * num_columns / cols_in_row
8836 - E_DAY_VIEW_GAP_WIDTH;
8837 item_y = row * day_view->row_height;
8838 item_h = num_rows * day_view->row_height;
8840 /* Set the positions of the event & associated items. */
8841 gnome_canvas_item_set (
8842 day_view->drag_rect_item,
8843 "x1", item_x + E_DAY_VIEW_BAR_WIDTH - 1,
8844 "y1", item_y,
8845 "x2", item_x + item_w - 1,
8846 "y2", item_y + item_h - 1,
8847 NULL);
8849 gnome_canvas_item_set (
8850 day_view->drag_bar_item,
8851 "x1", item_x,
8852 "y1", item_y,
8853 "x2", item_x + E_DAY_VIEW_BAR_WIDTH - 1,
8854 "y2", item_y + item_h - 1,
8855 NULL);
8857 gnome_canvas_item_set (
8858 day_view->drag_item,
8859 "clip_width", item_w - E_DAY_VIEW_BAR_WIDTH - E_DAY_VIEW_EVENT_X_PAD * 2,
8860 "clip_height", item_h - (E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD) * 2,
8861 NULL);
8862 e_canvas_item_move_absolute (
8863 day_view->drag_item,
8864 item_x + E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD,
8865 item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD);
8867 if (!(day_view->drag_bar_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8868 gnome_canvas_item_raise_to_top (day_view->drag_bar_item);
8869 gnome_canvas_item_show (day_view->drag_bar_item);
8872 if (!(day_view->drag_rect_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8873 gnome_canvas_item_raise_to_top (day_view->drag_rect_item);
8874 gnome_canvas_item_show (day_view->drag_rect_item);
8877 /* Set the text, if necessary. We don't want to set the text every
8878 * time it moves, so we check if it is currently invisible and only
8879 * set the text then. */
8880 if (!(day_view->drag_item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
8881 const gchar *summary;
8883 if (event && is_comp_data_valid (event)) {
8884 summary = icalcomponent_get_summary (event->comp_data->icalcomp);
8885 text = g_strdup (summary);
8886 } else {
8887 text = NULL;
8890 gnome_canvas_item_set (
8891 day_view->drag_item,
8892 "text", text ? text : "",
8893 NULL);
8894 gnome_canvas_item_raise_to_top (day_view->drag_item);
8895 gnome_canvas_item_show (day_view->drag_item);
8897 g_free (text);
8901 static void
8902 e_day_view_on_top_canvas_drag_leave (GtkWidget *widget,
8903 GdkDragContext *context,
8904 guint time,
8905 EDayView *day_view)
8907 day_view->drag_last_day = -1;
8909 gnome_canvas_item_hide (day_view->drag_long_event_rect_item);
8910 gnome_canvas_item_hide (day_view->drag_long_event_item);
8913 static void
8914 e_day_view_on_main_canvas_drag_leave (GtkWidget *widget,
8915 GdkDragContext *context,
8916 guint time,
8917 EDayView *day_view)
8919 day_view->drag_last_day = -1;
8921 e_day_view_stop_auto_scroll (day_view);
8923 gnome_canvas_item_hide (day_view->drag_rect_item);
8924 gnome_canvas_item_hide (day_view->drag_bar_item);
8925 gnome_canvas_item_hide (day_view->drag_item);
8927 /* Hide the resize bars if they are being used in the drag. */
8928 if (day_view->drag_event_day == day_view->resize_bars_event_day
8929 && day_view->drag_event_num == day_view->resize_bars_event_num) {
8933 static void
8934 e_day_view_on_drag_begin (GtkWidget *widget,
8935 GdkDragContext *context,
8936 EDayView *day_view)
8938 EDayViewEvent *event;
8939 gint day, event_num;
8941 day = day_view->drag_event_day;
8942 event_num = day_view->drag_event_num;
8944 /* These should both be set. */
8945 if (day == -1) {
8946 g_warn_if_reached ();
8947 return;
8950 g_return_if_fail (event_num != -1);
8952 if (day == E_DAY_VIEW_LONG_EVENT) {
8953 if (!is_array_index_in_bounds (day_view->long_events, event_num))
8954 return;
8956 event = &g_array_index (day_view->long_events, EDayViewEvent,
8957 event_num);
8958 } else {
8959 if (!is_array_index_in_bounds (day_view->events[day], event_num))
8960 return;
8962 event = &g_array_index (day_view->events[day], EDayViewEvent,
8963 event_num);
8966 /* Hide the text item, since it will be shown in the special drag
8967 * items. */
8968 gnome_canvas_item_hide (event->canvas_item);
8971 static void
8972 e_day_view_on_drag_end (GtkWidget *widget,
8973 GdkDragContext *context,
8974 EDayView *day_view)
8976 EDayViewEvent *event;
8977 gint day, event_num;
8979 day = day_view->drag_event_day;
8980 event_num = day_view->drag_event_num;
8982 /* If the calendar has already been updated in drag_data_received()
8983 * we just return. */
8984 if (day == -1 || event_num == -1)
8985 return;
8987 if (day == E_DAY_VIEW_LONG_EVENT) {
8988 if (!is_array_index_in_bounds (day_view->long_events, event_num))
8989 return;
8991 event = &g_array_index (day_view->long_events, EDayViewEvent,
8992 event_num);
8993 gtk_widget_queue_draw (day_view->top_canvas);
8994 } else {
8995 if (!is_array_index_in_bounds (day_view->events[day], event_num))
8996 return;
8998 event = &g_array_index (day_view->events[day], EDayViewEvent,
8999 event_num);
9000 gtk_widget_queue_draw (day_view->main_canvas);
9003 /* Show the text item again. */
9004 gnome_canvas_item_show (event->canvas_item);
9006 day_view->drag_event_day = -1;
9007 day_view->drag_event_num = -1;
9008 g_clear_object (&day_view->priv->drag_context);
9011 static void
9012 e_day_view_on_drag_data_get (GtkWidget *widget,
9013 GdkDragContext *context,
9014 GtkSelectionData *selection_data,
9015 guint info,
9016 guint time,
9017 EDayView *day_view)
9019 EDayViewEvent *event;
9020 icalcomponent *vcal;
9021 gint day, event_num;
9022 gchar *comp_str;
9024 day = day_view->drag_event_day;
9025 event_num = day_view->drag_event_num;
9027 /* These should both be set. */
9028 if (day == -1) {
9029 g_warn_if_reached ();
9030 return;
9033 g_return_if_fail (event_num != -1);
9035 if (day == E_DAY_VIEW_LONG_EVENT) {
9036 if (!is_array_index_in_bounds (day_view->long_events, event_num))
9037 return;
9039 event = &g_array_index (day_view->long_events,
9040 EDayViewEvent, event_num);
9041 } else {
9042 if (!is_array_index_in_bounds (day_view->events[day], event_num))
9043 return;
9045 event = &g_array_index (day_view->events[day],
9046 EDayViewEvent, event_num);
9049 if (!is_comp_data_valid (event))
9050 return;
9052 vcal = e_cal_util_new_top_level ();
9053 e_cal_util_add_timezones_from_component (
9054 vcal, event->comp_data->icalcomp);
9055 icalcomponent_add_component (
9056 vcal, icalcomponent_new_clone (event->comp_data->icalcomp));
9058 comp_str = icalcomponent_as_ical_string_r (vcal);
9059 if (comp_str) {
9060 ESource *source;
9061 const gchar *source_uid;
9062 GdkAtom target;
9063 gchar *tmp;
9065 source = e_client_get_source (E_CLIENT (event->comp_data->client));
9066 source_uid = e_source_get_uid (source);
9068 tmp = g_strconcat (source_uid, "\n", comp_str, NULL);
9069 target = gtk_selection_data_get_target (selection_data);
9070 gtk_selection_data_set (
9071 selection_data, target, 8,
9072 (guchar *) tmp, strlen (tmp));
9074 g_free (tmp);
9077 icalcomponent_free (vcal);
9078 g_free (comp_str);
9081 static void
9082 e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget,
9083 GdkDragContext *context,
9084 gint x,
9085 gint y,
9086 GtkSelectionData *selection_data,
9087 guint info,
9088 guint time,
9089 EDayView *day_view)
9091 EDayViewEvent *event = NULL;
9092 ECalendarViewPosition pos;
9093 gint day, start_day, end_day, num_days;
9094 gint start_offset, end_offset;
9095 ECalComponent *comp;
9096 ECalComponentDateTime date;
9097 ESourceRegistry *registry;
9098 struct icaltimetype itt;
9099 time_t dt;
9100 gboolean all_day_event;
9101 ECalModel *model;
9102 ECalendarView *cal_view;
9103 gboolean drag_from_same_window;
9104 const guchar *data;
9105 gint format, length;
9106 gint days_shown;
9107 GtkResponseType send = GTK_RESPONSE_NO;
9108 gboolean only_new_attendees = FALSE;
9109 gboolean strip_alarms = TRUE;
9111 data = gtk_selection_data_get_data (selection_data);
9112 format = gtk_selection_data_get_format (selection_data);
9113 length = gtk_selection_data_get_length (selection_data);
9115 days_shown = e_day_view_get_days_shown (day_view);
9117 if (day_view->drag_event_day != -1)
9118 drag_from_same_window = TRUE;
9119 else
9120 drag_from_same_window = FALSE;
9122 cal_view = E_CALENDAR_VIEW (day_view);
9123 model = e_calendar_view_get_model (cal_view);
9125 registry = e_cal_model_get_registry (model);
9127 /* Note that we only support DnD within the EDayView at present. */
9128 if (length >= 0 && format == 8 && day_view->drag_event_day != -1) {
9129 /* We are dragging in the same window */
9131 pos = e_day_view_convert_position_in_top_canvas (
9132 day_view,
9133 x, y, &day,
9134 NULL);
9135 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
9136 ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
9137 ECalClient *client;
9138 GtkWindow *toplevel;
9140 num_days = 1;
9141 start_offset = 0;
9142 end_offset = 0;
9144 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
9145 if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num))
9146 return;
9148 event = &g_array_index (day_view->long_events, EDayViewEvent,
9149 day_view->drag_event_num);
9151 if (!is_comp_data_valid (event))
9152 return;
9154 day -= day_view->drag_event_offset;
9155 day = MAX (day, 0);
9157 e_day_view_find_long_event_days (
9158 event,
9159 days_shown,
9160 day_view->day_starts,
9161 &start_day,
9162 &end_day);
9163 num_days = end_day - start_day + 1;
9164 /* Make sure we don't go off the screen. */
9165 day = MIN (day, days_shown - num_days);
9167 start_offset = event->start_minute;
9168 end_offset = event->end_minute;
9169 } else {
9170 if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num))
9171 return;
9173 event = &g_array_index (day_view->events[day_view->drag_event_day],
9174 EDayViewEvent,
9175 day_view->drag_event_num);
9177 if (!is_comp_data_valid (event))
9178 return;
9181 client = event->comp_data->client;
9183 /* We clone the event since we don't want to change
9184 * the original comp here.
9185 * Otherwise we would not detect that the event's time
9186 * had changed in the "update_event" callback. */
9188 comp = e_cal_component_new ();
9189 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
9191 if (e_cal_component_has_attendees (comp) &&
9192 !itip_organizer_is_user (registry, comp, client)) {
9193 g_object_unref (comp);
9194 return;
9197 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view)));
9199 if (itip_has_any_attendees (comp) &&
9200 (itip_organizer_is_user (registry, comp, client) ||
9201 itip_sentby_is_user (registry, comp, client)))
9202 send = e_cal_dialogs_send_dragged_or_resized_component (
9203 toplevel, client, comp, &strip_alarms, &only_new_attendees);
9205 if (send == GTK_RESPONSE_CANCEL) {
9206 e_day_view_abort_resize (day_view);
9207 g_object_unref (comp);
9208 return;
9211 if (start_offset == 0 && end_offset == 0)
9212 all_day_event = TRUE;
9213 else
9214 all_day_event = FALSE;
9216 date.value = &itt;
9218 dt = day_view->day_starts[day] + start_offset * 60;
9219 itt = icaltime_from_timet_with_zone (
9220 dt, FALSE,
9221 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
9222 if (all_day_event) {
9223 itt.is_date = TRUE;
9224 date.tzid = NULL;
9225 } else {
9226 /* FIXME: Should probably keep the timezone of
9227 * the original start and end times. */
9228 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
9230 cal_comp_set_dtstart_with_oldzone (client, comp, &date);
9232 if (end_offset == 0)
9233 dt = day_view->day_starts[day + num_days];
9234 else
9235 dt = day_view->day_starts[day + num_days - 1] + end_offset * 60;
9236 itt = icaltime_from_timet_with_zone (
9237 dt, FALSE,
9238 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
9239 if (all_day_event) {
9240 itt.is_date = TRUE;
9241 date.tzid = NULL;
9242 } else {
9243 /* FIXME: Should probably keep the timezone of
9244 * the original start and end times. */
9245 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
9247 cal_comp_set_dtend_with_oldzone (client, comp, &date);
9249 gtk_drag_finish (context, TRUE, TRUE, time);
9251 /* Reset this since it will be invalid. */
9252 day_view->drag_event_day = -1;
9253 g_clear_object (&day_view->priv->drag_context);
9255 /* Show the text item again, just in case it hasn't
9256 * moved. If we don't do this it may not appear. */
9257 if (event->canvas_item)
9258 gnome_canvas_item_show (event->canvas_item);
9260 e_cal_component_commit_sequence (comp);
9261 if (e_cal_component_has_recurrences (comp)) {
9262 if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
9263 gtk_widget_queue_draw (day_view->top_canvas);
9264 g_object_unref (comp);
9265 return;
9268 if (mod == E_CAL_OBJ_MOD_THIS) {
9269 e_cal_component_set_rdate_list (comp, NULL);
9270 e_cal_component_set_rrule_list (comp, NULL);
9271 e_cal_component_set_exdate_list (comp, NULL);
9272 e_cal_component_set_exrule_list (comp, NULL);
9274 } else if (e_cal_component_is_instance (comp))
9275 mod = E_CAL_OBJ_MOD_THIS;
9277 e_cal_component_commit_sequence (comp);
9279 e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp), mod,
9280 (send == GTK_RESPONSE_YES ? E_CAL_OPS_SEND_FLAG_SEND : E_CAL_OPS_SEND_FLAG_DONT_SEND) |
9281 (strip_alarms ? E_CAL_OPS_SEND_FLAG_STRIP_ALARMS : 0) |
9282 (only_new_attendees ? E_CAL_OPS_SEND_FLAG_ONLY_NEW_ATTENDEES : 0));
9284 g_object_unref (comp);
9286 return;
9290 if (length >= 0 && format == 8 && !drag_from_same_window) {
9291 /* We are dragging between different window */
9292 icalcomponent *icalcomp;
9293 icalcomponent_kind kind;
9295 pos = e_day_view_convert_position_in_top_canvas (
9296 day_view,
9297 x, y, &day,
9298 NULL);
9299 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
9300 goto error;
9302 icalcomp = icalparser_parse_string ((const gchar *) data);
9303 if (!icalcomp)
9304 goto error;
9306 /* check the type of the component */
9307 kind = icalcomponent_isa (icalcomp);
9308 icalcomponent_free (icalcomp);
9310 if (kind != ICAL_VCALENDAR_COMPONENT && kind != ICAL_VEVENT_COMPONENT)
9311 goto error;
9313 e_cal_ops_paste_components (model, (const gchar *) data);
9315 gtk_drag_finish (context, TRUE, TRUE, time);
9316 return;
9319 error:
9320 gtk_drag_finish (context, FALSE, FALSE, time);
9323 static void
9324 e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget,
9325 GdkDragContext *context,
9326 gint x,
9327 gint y,
9328 GtkSelectionData *selection_data,
9329 guint info,
9330 guint time,
9331 EDayView *day_view)
9333 ECalendarView *cal_view;
9334 EDayViewEvent *event = NULL;
9335 ECalendarViewPosition pos;
9336 gint time_divisions;
9337 gint day, row, start_row, end_row, num_rows, scroll_x, scroll_y;
9338 gint start_offset, end_offset;
9339 ECalModel *model;
9340 ECalComponent *comp;
9341 ECalComponentDateTime date;
9342 ESourceRegistry *registry;
9343 struct icaltimetype itt;
9344 time_t dt;
9345 gboolean drag_from_same_window;
9346 const guchar *data;
9347 gint format, length;
9348 GtkResponseType send = GTK_RESPONSE_NO;
9349 gboolean only_new_attendees = FALSE;
9350 gboolean strip_alarms = TRUE;
9352 cal_view = E_CALENDAR_VIEW (day_view);
9353 model = e_calendar_view_get_model (cal_view);
9354 time_divisions = e_calendar_view_get_time_divisions (cal_view);
9356 registry = e_cal_model_get_registry (model);
9358 data = gtk_selection_data_get_data (selection_data);
9359 format = gtk_selection_data_get_format (selection_data);
9360 length = gtk_selection_data_get_length (selection_data);
9362 if (day_view->drag_event_day != -1)
9363 drag_from_same_window = TRUE;
9364 else
9365 drag_from_same_window = FALSE;
9367 gnome_canvas_get_scroll_offsets (
9368 GNOME_CANVAS (widget),
9369 &scroll_x, &scroll_y);
9370 x += scroll_x;
9371 y += scroll_y;
9373 /* Note that we only support DnD within the EDayView at present. */
9374 if (length >= 0 && format == 8 && (day_view->drag_event_day != -1)) {
9375 /* We are dragging in the same window */
9377 pos = e_day_view_convert_position_in_main_canvas (
9378 day_view,
9379 x, y, &day,
9380 &row, NULL);
9381 if (pos != E_CALENDAR_VIEW_POS_OUTSIDE) {
9382 ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
9383 ECalClient *client;
9384 GtkWindow *toplevel;
9386 num_rows = 1;
9387 start_offset = 0;
9388 end_offset = 0;
9390 if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
9391 if (!is_array_index_in_bounds (day_view->long_events, day_view->drag_event_num))
9392 return;
9394 event = &g_array_index (day_view->long_events, EDayViewEvent,
9395 day_view->drag_event_num);
9397 if (!is_comp_data_valid (event))
9398 return;
9399 } else {
9400 if (!is_array_index_in_bounds (day_view->events[day_view->drag_event_day], day_view->drag_event_num))
9401 return;
9403 event = &g_array_index (day_view->events[day_view->drag_event_day],
9404 EDayViewEvent,
9405 day_view->drag_event_num);
9407 if (!is_comp_data_valid (event))
9408 return;
9410 row -= day_view->drag_event_offset;
9412 /* Calculate time offset from start row. */
9413 start_row = event->start_minute / time_divisions;
9414 end_row = (event->end_minute - 1) / time_divisions;
9415 if (end_row < start_row)
9416 end_row = start_row;
9418 num_rows = end_row - start_row + 1;
9420 start_offset = event->start_minute % time_divisions;
9421 end_offset = event->end_minute % time_divisions;
9422 if (end_offset != 0)
9423 end_offset = time_divisions - end_offset;
9426 client = event->comp_data->client;
9428 /* We use a temporary shallow copy of comp since we
9429 * don't want to change the original comp here.
9430 * Otherwise we would not detect that the event's time
9431 * had changed in the "update_event" callback. */
9432 comp = e_cal_component_new ();
9433 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
9435 if (e_cal_component_has_attendees (comp) &&
9436 !itip_organizer_is_user (registry, comp, client)) {
9437 g_object_unref (comp);
9438 return;
9441 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (day_view)));
9443 if (itip_has_any_attendees (comp) &&
9444 (itip_organizer_is_user (registry, comp, client) ||
9445 itip_sentby_is_user (registry, comp, client)))
9446 send = e_cal_dialogs_send_dragged_or_resized_component (
9447 toplevel, client, comp, &strip_alarms, &only_new_attendees);
9449 if (send == GTK_RESPONSE_CANCEL) {
9450 e_day_view_abort_resize (day_view);
9451 g_object_unref (comp);
9452 return;
9455 date.value = &itt;
9456 date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
9458 dt = e_day_view_convert_grid_position_to_time (day_view, day, row) + start_offset * 60;
9459 *date.value = icaltime_from_timet_with_zone (dt, FALSE,
9460 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
9461 cal_comp_set_dtstart_with_oldzone (client, comp, &date);
9462 dt = e_day_view_convert_grid_position_to_time (day_view, day, row + num_rows) - end_offset * 60;
9463 *date.value = icaltime_from_timet_with_zone (dt, FALSE,
9464 e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)));
9465 cal_comp_set_dtend_with_oldzone (client, comp, &date);
9466 e_cal_component_abort_sequence (comp);
9468 gtk_drag_finish (context, TRUE, TRUE, time);
9470 /* Reset this since it will be invalid. */
9471 day_view->drag_event_day = -1;
9472 g_clear_object (&day_view->priv->drag_context);
9474 /* Show the text item again, just in case it hasn't
9475 * moved. If we don't do this it may not appear. */
9476 if (event->canvas_item)
9477 gnome_canvas_item_show (event->canvas_item);
9479 e_cal_component_commit_sequence (comp);
9480 if (e_cal_component_has_recurrences (comp)) {
9481 if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
9482 gtk_widget_queue_draw (day_view->main_canvas);
9483 g_object_unref (comp);
9484 return;
9487 if (mod == E_CAL_OBJ_MOD_THIS) {
9488 e_cal_component_set_rdate_list (comp, NULL);
9489 e_cal_component_set_rrule_list (comp, NULL);
9490 e_cal_component_set_exdate_list (comp, NULL);
9491 e_cal_component_set_exrule_list (comp, NULL);
9493 } else if (e_cal_component_is_instance (comp))
9494 mod = E_CAL_OBJ_MOD_THIS;
9496 e_cal_component_commit_sequence (comp);
9498 e_cal_ops_modify_component (model, client, e_cal_component_get_icalcomponent (comp), mod,
9499 (send == GTK_RESPONSE_YES ? E_CAL_OPS_SEND_FLAG_SEND : E_CAL_OPS_SEND_FLAG_DONT_SEND) |
9500 (strip_alarms ? E_CAL_OPS_SEND_FLAG_STRIP_ALARMS : 0) |
9501 (only_new_attendees ? E_CAL_OPS_SEND_FLAG_ONLY_NEW_ATTENDEES : 0));
9503 g_object_unref (comp);
9505 return;
9509 if (length >= 0 && format == 8 && !drag_from_same_window) {
9510 /* We are dragging between different window */
9511 icalcomponent *icalcomp;
9512 icalcomponent_kind kind;
9514 pos = e_day_view_convert_position_in_main_canvas (
9515 day_view,
9516 x, y, &day,
9517 &row, NULL);
9518 if (pos == E_CALENDAR_VIEW_POS_OUTSIDE)
9519 goto error;
9521 icalcomp = icalparser_parse_string ((const gchar *) data);
9522 if (!icalcomp)
9523 goto error;
9525 /* check the type of the component */
9526 kind = icalcomponent_isa (icalcomp);
9527 icalcomponent_free (icalcomp);
9529 if (kind != ICAL_VCALENDAR_COMPONENT && kind != ICAL_VEVENT_COMPONENT)
9530 goto error;
9532 e_cal_ops_paste_components (model, (const gchar *) data);
9534 gtk_drag_finish (context, TRUE, TRUE, time);
9535 return;
9538 error:
9539 gtk_drag_finish (context, FALSE, FALSE, time);
9542 /* Converts an hour from 0-23 to the preferred time format, and returns the
9543 * suffix to add and the width of it in the normal font. */
9544 void
9545 e_day_view_convert_time_to_display (EDayView *day_view,
9546 gint hour,
9547 gint *display_hour,
9548 const gchar **suffix,
9549 gint *suffix_width)
9551 ECalModel *model;
9553 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
9555 /* Calculate the actual hour number to display. For 12-hour
9556 * format we convert 0-23 to 12-11am/12-11pm. */
9557 *display_hour = hour;
9558 if (e_cal_model_get_use_24_hour_format (model)) {
9559 *suffix = "";
9560 *suffix_width = 0;
9561 } else {
9562 if (hour < 12) {
9563 *suffix = day_view->am_string;
9564 *suffix_width = day_view->am_string_width;
9565 } else {
9566 *display_hour -= 12;
9567 *suffix = day_view->pm_string;
9568 *suffix_width = day_view->pm_string_width;
9571 /* 12-hour uses 12:00 rather than 0:00. */
9572 if (*display_hour == 0)
9573 *display_hour = 12;
9577 gint
9578 e_day_view_get_time_string_width (EDayView *day_view)
9580 ECalModel *model;
9581 gint time_width;
9583 model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
9584 time_width = day_view->digit_width * 4 + day_view->colon_width;
9586 if (!e_cal_model_get_use_24_hour_format (model))
9587 time_width += MAX (day_view->am_string_width,
9588 day_view->pm_string_width);
9590 return time_width;
9593 /* Queues a layout, unless one is already queued. */
9594 static void
9595 e_day_view_queue_layout (EDayView *day_view)
9597 if (day_view->layout_timeout_id == 0) {
9598 day_view->layout_timeout_id = e_named_timeout_add (
9599 E_DAY_VIEW_LAYOUT_TIMEOUT,
9600 e_day_view_layout_timeout_cb, day_view);
9604 /* Removes any queued layout. */
9605 static void
9606 e_day_view_cancel_layout (EDayView *day_view)
9608 if (day_view->layout_timeout_id != 0) {
9609 g_source_remove (day_view->layout_timeout_id);
9610 day_view->layout_timeout_id = 0;
9614 static gboolean
9615 e_day_view_layout_timeout_cb (gpointer data)
9617 EDayView *day_view = E_DAY_VIEW (data);
9619 gtk_widget_queue_draw (day_view->top_canvas);
9620 gtk_widget_queue_draw (day_view->top_dates_canvas);
9621 gtk_widget_queue_draw (day_view->main_canvas);
9622 e_day_view_check_layout (day_view);
9624 day_view->layout_timeout_id = 0;
9625 return FALSE;
9628 /* Returns the number of selected events (0 or 1 at present). */
9629 gint
9630 e_day_view_get_num_events_selected (EDayView *day_view)
9632 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), 0);
9634 return (day_view->editing_event_day != -1) ? 1 : 0;
9637 gboolean
9638 e_day_view_is_editing (EDayView *day_view)
9640 g_return_val_if_fail (E_IS_DAY_VIEW (day_view), FALSE);
9642 return day_view->editing_event_day != -1;
9645 static void
9646 day_view_update_timezone_name_label (GtkWidget *label,
9647 icaltimezone *zone)
9649 const gchar *location, *dash;
9650 gchar *markup;
9652 g_return_if_fail (GTK_IS_LABEL (label));
9654 if (!zone) {
9655 location = NULL;
9656 } else {
9657 location = icaltimezone_get_location (zone);
9658 if (location && *location)
9659 location = _(location);
9660 if (!location || !*location)
9661 location = icaltimezone_get_tzid (zone);
9664 if (!location)
9665 location = "";
9667 gtk_widget_set_tooltip_text (label, location);
9669 dash = strchr (location, '/');
9670 if (dash && *dash && dash[1])
9671 location = dash + 1;
9673 markup = g_markup_printf_escaped ("<small>%s</small>", location);
9674 gtk_label_set_markup (GTK_LABEL (label), markup);
9675 g_free (markup);
9678 void
9679 e_day_view_update_timezone_name_labels (EDayView *day_view)
9681 icaltimezone *zone;
9683 g_return_if_fail (E_IS_DAY_VIEW (day_view));
9685 zone = e_cal_model_get_timezone (day_view->priv->model);
9686 day_view_update_timezone_name_label (day_view->priv->timezone_name_1_label, zone);
9688 zone = e_day_view_time_item_get_second_zone (E_DAY_VIEW_TIME_ITEM (day_view->time_canvas_item));
9689 if (!zone) {
9690 gtk_widget_hide (day_view->priv->timezone_name_2_label);
9691 } else {
9692 day_view_update_timezone_name_label (day_view->priv->timezone_name_2_label, zone);
9693 gtk_widget_show (day_view->priv->timezone_name_2_label);