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)