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