1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
13 *
14 *
15 * Authors:
16 * Damon Chaplin <damon@ximian.com>
17 * Rodrigo Moya <rodrigo@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 /*
24 * EWeekView - displays the Week & Month views of the calendar.
25 */
26
27 #include "evolution-config.h"
28
29 #include "e-week-view.h"
30
31 #include <math.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <glib/gi18n.h>
34 #include <libgnomecanvas/libgnomecanvas.h>
35
36 #include "calendar-config.h"
37 #include "comp-util.h"
38 #include "e-cal-dialogs.h"
39 #include "e-cal-model-calendar.h"
40 #include "e-cal-ops.h"
41 #include "e-week-view-event-item.h"
42 #include "e-week-view-layout.h"
43 #include "e-week-view-main-item.h"
44 #include "e-week-view-titles-item.h"
45 #include "ea-calendar.h"
46 #include "itip-utils.h"
47 #include "misc.h"
48 #include "print.h"
49 #include "ea-week-view.h"
50
51 /* Images */
52 #include "data/xpm/jump.xpm"
53
54 #define E_WEEK_VIEW_GET_PRIVATE(obj) \
55 (G_TYPE_INSTANCE_GET_PRIVATE \
56 ((obj), E_TYPE_WEEK_VIEW, EWeekViewPrivate))
57
58 #define E_WEEK_VIEW_SMALL_FONT_PTSIZE 7
59
60 #define E_WEEK_VIEW_JUMP_BUTTON_WIDTH 16
61 #define E_WEEK_VIEW_JUMP_BUTTON_HEIGHT 8
62
63 #define E_WEEK_VIEW_JUMP_BUTTON_X_PAD 3
64 #define E_WEEK_VIEW_JUMP_BUTTON_Y_PAD 3
65
66 #define E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS -1
67
68 /* The timeout before we do a layout, so we don't do a layout for each event
69 * we get from the server. */
70 #define E_WEEK_VIEW_LAYOUT_TIMEOUT 100
71
72 struct _EWeekViewPrivate {
73 /* The first day shown in the view. */
74 GDate first_day_shown;
75
76 /* If we are displaying multiple weeks in rows. If this is
77 * FALSE only one week is shown, with a different layout. */
78 gboolean multi_week_view;
79
80 /* How many weeks we are showing. This is only relevant if
81 * multi_week_view is TRUE. */
82 gint weeks_shown;
83
84 /* If Sat & Sun are compressed. Only applicable in month view,
85 * since they are always compressed into 1 cell in week view. */
86 gboolean compress_weekend;
87
88 /* Whether we show event end times. */
89 gboolean show_event_end_times;
90
91 /* Whether to update the base date when the time range changes. */
92 gboolean update_base_date;
93
94 /* The first day of the week we display. This will usually be
95 * week_start_day, but if the Sat & Sun are compressed and the
96 * week starts on Sunday then we have to use Saturday. */
97 GDateWeekday display_start_day;
98
99 gulong notify_week_start_day_id;
100
101 gboolean show_icons_month_view;
102 gboolean draw_flat_events;
103 gboolean days_left_to_right;
104 };
105
106 typedef struct {
107 EWeekView *week_view;
108 ECalModelComponent *comp_data;
109 } AddEventData;
110
111 static void e_week_view_set_colors (EWeekView *week_view);
112 static void e_week_view_recalc_cell_sizes (EWeekView *week_view);
113 static gboolean e_week_view_get_next_tab_event (EWeekView *week_view,
114 GtkDirectionType direction,
115 gint current_event_num,
116 gint current_span_num,
117 gint *next_event_num,
118 gint *next_span_num);
119 static void e_week_view_update_query (EWeekView *week_view);
120
121 static gboolean e_week_view_on_button_press (GtkWidget *widget,
122 GdkEvent *button_event,
123 EWeekView *week_view);
124 static gboolean e_week_view_on_button_release (GtkWidget *widget,
125 GdkEvent *button_event,
126 EWeekView *week_view);
127 static gboolean e_week_view_on_scroll (GtkWidget *widget,
128 GdkEventScroll *scroll,
129 EWeekView *week_view);
130 static gboolean e_week_view_on_motion (GtkWidget *widget,
131 GdkEventMotion *event,
132 EWeekView *week_view);
133 static gint e_week_view_convert_position_to_day (EWeekView *week_view,
134 gint x,
135 gint y);
136 static void e_week_view_update_selection (EWeekView *week_view,
137 gint day);
138
139 static void e_week_view_free_events (EWeekView *week_view);
140 static void e_week_view_add_event (ECalClient *client,
141 ECalComponent *comp,
142 time_t start,
143 time_t end,
144 gboolean prepend,
145 gpointer data);
146 static void e_week_view_check_layout (EWeekView *week_view);
147 static void e_week_view_ensure_events_sorted (EWeekView *week_view);
148 static void e_week_view_reshape_events (EWeekView *week_view);
149 static void e_week_view_reshape_event_span (EWeekView *week_view,
150 gint event_num,
151 gint span_num);
152 static void e_week_view_recalc_day_starts (EWeekView *week_view,
153 time_t lower);
154 static void e_week_view_on_editing_started (EWeekView *week_view,
155 GnomeCanvasItem *item);
156 static void e_week_view_on_editing_stopped (EWeekView *week_view,
157 GnomeCanvasItem *item);
158 static gboolean e_week_view_find_event_from_uid (EWeekView *week_view,
159 ECalClient *client,
160 const gchar *uid,
161 const gchar *rid,
162 gint *event_num_return);
163 typedef gboolean (* EWeekViewForeachEventCallback) (EWeekView *week_view,
164 gint event_num,
165 gpointer data);
166 static void e_week_view_foreach_event_with_uid (EWeekView *week_view,
167 const gchar *uid,
168 EWeekViewForeachEventCallback callback,
169 gpointer data);
170 static gboolean e_week_view_on_text_item_event (GnomeCanvasItem *item,
171 GdkEvent *event,
172 EWeekView *week_view);
173 static gboolean e_week_view_event_move (ECalendarView *cal_view, ECalViewMoveDirection direction);
174 static gint e_week_view_get_day_offset_of_event (EWeekView *week_view, time_t event_time);
175 static void e_week_view_change_event_time (EWeekView *week_view, time_t start_dt, time_t end_dt, gboolean is_all_day);
176 static gboolean e_week_view_on_jump_button_event (GnomeCanvasItem *item,
177 GdkEvent *event,
178 EWeekView *week_view);
179 static gboolean e_week_view_do_key_press (GtkWidget *widget,
180 GdkEventKey *event);
181 static gint e_week_view_get_adjust_days_for_move_up (EWeekView *week_view, gint
182 current_day);
183 static gint e_week_view_get_adjust_days_for_move_down (EWeekView *week_view,gint current_day);
184 static gint e_week_view_get_adjust_days_for_move_left (EWeekView *week_view,gint current_day);
185 static gint e_week_view_get_adjust_days_for_move_right (EWeekView *week_view,gint current_day);
186
187 static gboolean e_week_view_remove_event_cb (EWeekView *week_view,
188 gint event_num,
189 gpointer data);
190 static gboolean e_week_view_recalc_display_start_day (EWeekView *week_view);
191
192 static void e_week_view_queue_layout (EWeekView *week_view);
193 static void e_week_view_cancel_layout (EWeekView *week_view);
194 static gboolean e_week_view_layout_timeout_cb (gpointer data);
195
196 G_DEFINE_TYPE (EWeekView, e_week_view, E_TYPE_CALENDAR_VIEW)
197
198 enum {
199 PROP_0,
200 PROP_COMPRESS_WEEKEND,
201 PROP_DRAW_FLAT_EVENTS,
202 PROP_DAYS_LEFT_TO_RIGHT,
203 PROP_SHOW_EVENT_END_TIMES,
204 PROP_SHOW_ICONS_MONTH_VIEW,
205 PROP_IS_EDITING
206 };
207
208 static gint map_left[] = {0, 1, 2, 0, 1, 2, 2};
209 static gint map_right[] = {3, 4, 5, 3, 4, 5, 6};
210
211 static void
e_week_view_set_popup_event(EWeekView * week_view,gint event_num)212 e_week_view_set_popup_event (EWeekView *week_view,
213 gint event_num)
214 {
215 if (week_view->popup_event_num != event_num) {
216 week_view->popup_event_num = event_num;
217
218 g_signal_emit_by_name (week_view, "selection-changed");
219 }
220 }
221
222 static void
week_view_process_component(EWeekView * week_view,ECalModelComponent * comp_data)223 week_view_process_component (EWeekView *week_view,
224 ECalModelComponent *comp_data)
225 {
226 ECalComponent *comp = NULL;
227 AddEventData add_event_data;
228
229 /* If we don't have a valid date set yet, just return. */
230 if (!g_date_valid (&week_view->priv->first_day_shown))
231 return;
232
233 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (comp_data->icalcomp));
234 if (!comp) {
235 g_message (G_STRLOC ": Could not set ICalComponent on ECalComponent");
236 return;
237 }
238
239 /* Add the object */
240 add_event_data.week_view = week_view;
241 add_event_data.comp_data = comp_data;
242 e_week_view_add_event (comp_data->client, comp, comp_data->instance_start, comp_data->instance_end, FALSE, &add_event_data);
243
244 g_object_unref (comp);
245 }
246
247 static void
week_view_update_row(EWeekView * week_view,gint row)248 week_view_update_row (EWeekView *week_view,
249 gint row)
250 {
251 ECalModelComponent *comp_data;
252 ECalModel *model;
253 gint event_num;
254 const gchar *uid;
255 gchar *rid;
256
257 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
258 comp_data = e_cal_model_get_component_at (model, row);
259 g_return_if_fail (comp_data != NULL);
260
261 uid = i_cal_component_get_uid (comp_data->icalcomp);
262 rid = e_cal_util_component_get_recurid_as_string (comp_data->icalcomp);
263
264 if (e_week_view_find_event_from_uid (week_view, comp_data->client, uid, rid, &event_num))
265 e_week_view_remove_event_cb (week_view, event_num, NULL);
266
267 g_free (rid);
268
269 week_view_process_component (week_view, comp_data);
270
271 gtk_widget_queue_draw (week_view->main_canvas);
272 e_week_view_queue_layout (week_view);
273 }
274
275 static void
week_view_model_cell_changed_cb(EWeekView * week_view,gint col,gint row)276 week_view_model_cell_changed_cb (EWeekView *week_view,
277 gint col,
278 gint row)
279 {
280 if (!E_CALENDAR_VIEW (week_view)->in_focus) {
281 e_week_view_free_events (week_view);
282 week_view->requires_update = TRUE;
283 return;
284 }
285
286 week_view_update_row (week_view, row);
287 }
288
289 static void
week_view_model_comps_deleted_cb(EWeekView * week_view,gpointer data)290 week_view_model_comps_deleted_cb (EWeekView *week_view,
291 gpointer data)
292 {
293 GSList *l, *list = data;
294
295 /* FIXME Stop editing? */
296 if (!E_CALENDAR_VIEW (week_view)->in_focus) {
297 e_week_view_free_events (week_view);
298 week_view->requires_update = TRUE;
299 return;
300 }
301
302 for (l = list; l != NULL; l = g_slist_next (l)) {
303 gint event_num;
304 const gchar *uid;
305 gchar *rid;
306 ECalModelComponent *comp_data = l->data;
307
308 uid = i_cal_component_get_uid (comp_data->icalcomp);
309 rid = e_cal_util_component_get_recurid_as_string (comp_data->icalcomp);
310
311 if (e_week_view_find_event_from_uid (week_view, comp_data->client, uid, rid, &event_num))
312 e_week_view_remove_event_cb (week_view, event_num, NULL);
313
314 g_free (rid);
315 }
316
317 gtk_widget_queue_draw (week_view->main_canvas);
318 e_week_view_queue_layout (week_view);
319 }
320
321 static void
week_view_model_row_changed_cb(EWeekView * week_view,gint row)322 week_view_model_row_changed_cb (EWeekView *week_view,
323 gint row)
324 {
325 if (!E_CALENDAR_VIEW (week_view)->in_focus) {
326 e_week_view_free_events (week_view);
327 week_view->requires_update = TRUE;
328 return;
329 }
330
331 week_view_update_row (week_view, row);
332 }
333
334 static void
week_view_model_rows_inserted_cb(EWeekView * week_view,gint row,gint count)335 week_view_model_rows_inserted_cb (EWeekView *week_view,
336 gint row,
337 gint count)
338 {
339 ECalModel *model;
340 gint i;
341
342 if (!E_CALENDAR_VIEW (week_view)->in_focus) {
343 e_week_view_free_events (week_view);
344 week_view->requires_update = TRUE;
345 return;
346 }
347
348 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
349
350 for (i = 0; i < count; i++) {
351 ECalModelComponent *comp_data;
352
353 comp_data = e_cal_model_get_component_at (model, row + i);
354 if (comp_data == NULL) {
355 g_warning ("comp_data is NULL\n");
356 continue;
357 }
358 week_view_process_component (week_view, comp_data);
359 }
360
361 gtk_widget_queue_draw (week_view->main_canvas);
362 e_week_view_queue_layout (week_view);
363 }
364
365 static void
e_week_view_precalc_visible_time_range(ECalendarView * cal_view,time_t in_start_time,time_t in_end_time,time_t * out_start_time,time_t * out_end_time)366 e_week_view_precalc_visible_time_range (ECalendarView *cal_view,
367 time_t in_start_time,
368 time_t in_end_time,
369 time_t *out_start_time,
370 time_t *out_end_time)
371 {
372 EWeekView *week_view;
373 GDate date, base_date;
374 GDateWeekday weekday;
375 GDateWeekday display_start_day;
376 guint day_offset, week_start_offset;
377 gint num_days;
378 ICalTimezone *zone;
379
380 g_return_if_fail (E_IS_WEEK_VIEW (cal_view));
381 g_return_if_fail (out_start_time != NULL);
382 g_return_if_fail (out_end_time != NULL);
383
384 week_view = E_WEEK_VIEW (cal_view);
385 zone = e_calendar_view_get_timezone (cal_view);
386
387 time_to_gdate_with_zone (&date, in_start_time, zone);
388
389 weekday = g_date_get_weekday (&date);
390 display_start_day = e_week_view_get_display_start_day (week_view);
391
392 /* Convert it to an offset from the start of the display. */
393 week_start_offset = e_weekday_get_days_between (display_start_day, weekday);
394
395 /* Set the day_offset to the result, so we move back to the
396 * start of the week. */
397 day_offset = week_start_offset;
398
399 /* Calculate the base date, i.e. the first day shown when the
400 * scrollbar adjustment value is 0. */
401 base_date = date;
402 g_date_subtract_days (&base_date, day_offset);
403
404 num_days = e_week_view_get_weeks_shown (week_view) * 7;
405
406 /* See if we need to update the first day shown. */
407 if (!g_date_valid (&week_view->priv->first_day_shown)
408 || g_date_compare (&week_view->priv->first_day_shown, &base_date)) {
409 GDate end_date, in_end_date;
410 gint day;
411
412 end_date = date;
413 g_date_add_days (&end_date, num_days);
414 g_date_subtract_days (&end_date, day_offset);
415
416 time_to_gdate_with_zone (&in_end_date, in_end_time, zone);
417
418 while (g_date_days_between (&end_date, &in_end_date) >= 6) {
419 g_date_add_days (&end_date, 7);
420 num_days += 7;
421 }
422
423 in_start_time = time_add_day_with_zone (in_start_time, -((gint) day_offset), zone);
424 in_start_time = time_day_begin_with_zone (in_start_time, zone);
425
426 *out_start_time = in_start_time;
427 *out_end_time = in_start_time;
428
429 for (day = 1; day <= num_days; day++) {
430 *out_end_time = time_add_day_with_zone (*out_end_time, 1, zone);
431 }
432 } else {
433 *out_start_time = week_view->day_starts[0];
434 *out_end_time = week_view->day_starts[num_days];
435 }
436 }
437
438 static void
week_view_time_range_changed_cb(EWeekView * week_view,gint64 i64_start_time,gint64 i64_end_time,ECalModel * model)439 week_view_time_range_changed_cb (EWeekView *week_view,
440 gint64 i64_start_time,
441 gint64 i64_end_time,
442 ECalModel *model)
443 {
444 time_t start_time = (time_t) i64_start_time;
445 GDate date, base_date;
446 GDateWeekday weekday;
447 GDateWeekday display_start_day;
448 guint day_offset, week_start_offset;
449 gint num_days;
450 gboolean update_adjustment_value = FALSE;
451
452 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
453
454 time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
455
456 weekday = g_date_get_weekday (&date);
457 display_start_day = e_week_view_get_display_start_day (week_view);
458
459 /* Convert it to an offset from the start of the display. */
460 week_start_offset = e_weekday_get_days_between (
461 display_start_day, weekday);
462
463 /* Set the day_offset to the result, so we move back to the
464 * start of the week. */
465 day_offset = week_start_offset;
466
467 /* Calculate the base date, i.e. the first day shown when the
468 * scrollbar adjustment value is 0. */
469 base_date = date;
470 g_date_subtract_days (&base_date, day_offset);
471
472 /* See if we need to update the base date. */
473 if (!g_date_valid (&week_view->base_date)
474 || e_week_view_get_update_base_date (week_view)) {
475 week_view->base_date = base_date;
476 update_adjustment_value = TRUE;
477 }
478
479 /* See if we need to update the first day shown. */
480 if (!g_date_valid (&week_view->priv->first_day_shown)
481 || g_date_compare (&week_view->priv->first_day_shown, &base_date)) {
482 week_view->priv->first_day_shown = base_date;
483 start_time = time_add_day_with_zone (
484 start_time, -((gint) day_offset),
485 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
486 start_time = time_day_begin_with_zone (
487 start_time,
488 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
489 e_week_view_recalc_day_starts (week_view, start_time);
490 }
491
492 /* Reset the adjustment value to 0 if the base address has changed.
493 * Note that we do this after updating first_day_shown so that our
494 * signal handler will not try to reload the events. */
495 if (update_adjustment_value) {
496 GtkRange *range;
497 GtkAdjustment *adjustment;
498
499 range = GTK_RANGE (week_view->vscrollbar);
500 adjustment = gtk_range_get_adjustment (range);
501 gtk_adjustment_set_value (adjustment, 0);
502 }
503
504 if (!E_CALENDAR_VIEW (week_view)->in_focus) {
505 e_week_view_free_events (week_view);
506 week_view->requires_update = TRUE;
507 return;
508 }
509
510 gtk_widget_queue_draw (week_view->main_canvas);
511
512 num_days = e_week_view_get_weeks_shown (week_view) * 7;
513
514 /* FIXME Preserve selection if possible */
515 if (week_view->selection_start_day == -1 ||
516 num_days <= week_view->selection_start_day)
517 e_calendar_view_set_selected_time_range (
518 E_CALENDAR_VIEW (week_view), start_time, start_time);
519 }
520
521 static void
timezone_changed_cb(ECalModel * cal_model,ICalTimezone * old_zone,ICalTimezone * new_zone,gpointer user_data)522 timezone_changed_cb (ECalModel *cal_model,
523 ICalTimezone *old_zone,
524 ICalTimezone *new_zone,
525 gpointer user_data)
526 {
527 ECalendarView *cal_view = (ECalendarView *) user_data;
528 GDate *first_day_shown;
529 ICalTime *tt = NULL;
530 time_t lower;
531 EWeekView *week_view = (EWeekView *) cal_view;
532
533 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
534
535 first_day_shown = &week_view->priv->first_day_shown;
536
537 if (!cal_view->in_focus) {
538 e_week_view_free_events (week_view);
539 week_view->requires_update = TRUE;
540 return;
541 }
542
543 /* If we don't have a valid date set yet, just return. */
544 if (!g_date_valid (first_day_shown))
545 return;
546
547 tt = i_cal_time_new_null_time ();
548
549 /* Recalculate the new start of the first week. We just use exactly
550 * the same time, but with the new timezone. */
551 i_cal_time_set_date (tt,
552 g_date_get_year (first_day_shown),
553 g_date_get_month (first_day_shown),
554 g_date_get_day (first_day_shown));
555
556 lower = i_cal_time_as_timet_with_zone (tt, new_zone);
557
558 g_clear_object (&tt);
559
560 e_week_view_recalc_day_starts (week_view, lower);
561 e_week_view_update_query (week_view);
562 }
563
564 static void
week_view_notify_week_start_day_cb(EWeekView * week_view)565 week_view_notify_week_start_day_cb (EWeekView *week_view)
566 {
567 GDate *first_day_shown;
568
569 first_day_shown = &week_view->priv->first_day_shown;
570
571 e_week_view_recalc_display_start_day (week_view);
572
573 /* Recalculate the days shown and reload if necessary. */
574 if (g_date_valid (first_day_shown))
575 e_week_view_set_first_day_shown (week_view, first_day_shown);
576
577 gtk_widget_queue_draw (week_view->titles_canvas);
578 gtk_widget_queue_draw (week_view->main_canvas);
579 }
580
581 static void
month_scroll_by_week_changed_cb(GSettings * settings,const gchar * key,gpointer user_data)582 month_scroll_by_week_changed_cb (GSettings *settings,
583 const gchar *key,
584 gpointer user_data)
585 {
586 EWeekView *week_view = user_data;
587
588 g_return_if_fail (week_view != NULL);
589 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
590
591 if (e_week_view_get_multi_week_view (week_view) &&
592 week_view->month_scroll_by_week != calendar_config_get_month_scroll_by_week ()) {
593 week_view->priv->multi_week_view = FALSE;
594 e_week_view_set_multi_week_view (week_view, TRUE);
595 }
596 }
597
598 /* FIXME: This is also needed in e-day-view-time-item.c. We should probably use
599 * pango's approximation function, but it needs a language tag. Find out how to
600 * get one of those properly. */
601 static gint
get_digit_width(PangoLayout * layout)602 get_digit_width (PangoLayout *layout)
603 {
604 gint digit;
605 gint max_digit_width = 1;
606
607 for (digit = '0'; digit <= '9'; digit++) {
608 gchar digit_char;
609 gint digit_width;
610
611 digit_char = digit;
612
613 pango_layout_set_text (layout, &digit_char, 1);
614 pango_layout_get_pixel_size (layout, &digit_width, NULL);
615
616 max_digit_width = MAX (max_digit_width, digit_width);
617 }
618
619 return max_digit_width;
620 }
621
622 static gint
get_string_width(PangoLayout * layout,const gchar * string)623 get_string_width (PangoLayout *layout,
624 const gchar *string)
625 {
626 gint width;
627
628 pango_layout_set_text (layout, string, -1);
629 pango_layout_get_pixel_size (layout, &width, NULL);
630 return width;
631 }
632
633 typedef struct {
634 EWeekView *week_view;
635 time_t dtstart, dtend;
636 gchar *initial_text;
637 gboolean paste_clipboard;
638 } NewEventInRangeData;
639
640 static void
new_event_in_rage_data_free(gpointer ptr)641 new_event_in_rage_data_free (gpointer ptr)
642 {
643 NewEventInRangeData *ned = ptr;
644
645 if (ned) {
646 g_clear_object (&ned->week_view);
647 g_free (ned->initial_text);
648 g_slice_free (NewEventInRangeData, ned);
649 }
650 }
651
652 static void
week_view_new_event_in_selected_range_cb(ECalModel * model,ECalClient * client,ICalComponent * default_component,gpointer user_data)653 week_view_new_event_in_selected_range_cb (ECalModel *model,
654 ECalClient *client,
655 ICalComponent *default_component,
656 gpointer user_data)
657 {
658 NewEventInRangeData *ned = user_data;
659 ECalComponent *comp = NULL;
660 gint event_num;
661 ECalComponentDateTime *date;
662 const gchar *uid;
663 AddEventData add_event_data;
664 EWeekViewEvent *wvevent;
665 EWeekViewEventSpan *span;
666 ICalTimezone *zone;
667
668 /* Check if the client is read only */
669 if (e_client_is_readonly (E_CLIENT (client)))
670 goto exit;
671
672 /* Add a new event covering the selected range. */
673 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (default_component));
674 g_return_if_fail (comp != NULL);
675
676 uid = i_cal_component_get_uid (default_component);
677
678 zone = e_cal_model_get_timezone (model);
679
680 /* We use DATE values now, so we don't need the timezone. */
681 /*date.tzid = icaltimezone_get_tzid (e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));*/
682
683 date = e_cal_component_datetime_new_take (i_cal_time_new_from_timet_with_zone (ned->dtstart, TRUE, zone), NULL);
684 e_cal_component_set_dtstart (comp, date);
685 e_cal_component_datetime_free (date);
686
687 date = e_cal_component_datetime_new_take (i_cal_time_new_from_timet_with_zone (ned->dtend, TRUE, zone), NULL);
688 e_cal_component_set_dtend (comp, date);
689 e_cal_component_datetime_free (date);
690
691 /* Editor default in week/month view */
692 e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
693
694 /* We add the event locally and start editing it. We don't send it
695 * to the server until the user finishes editing it. */
696 add_event_data.week_view = ned->week_view;
697 add_event_data.comp_data = NULL;
698 e_week_view_add_event (client, comp, ned->dtstart, ned->dtend, TRUE, &add_event_data);
699 e_week_view_check_layout (ned->week_view);
700 gtk_widget_queue_draw (ned->week_view->main_canvas);
701
702 if (!e_week_view_find_event_from_uid (ned->week_view, client, uid, NULL, &event_num)) {
703 g_warning ("Couldn't find event to start editing.\n");
704 goto exit;
705 }
706
707 if (!is_array_index_in_bounds (ned->week_view->events, event_num))
708 goto exit;
709
710 wvevent = &g_array_index (ned->week_view->events, EWeekViewEvent, event_num);
711
712 if (!is_array_index_in_bounds (ned->week_view->spans, wvevent->spans_index + 0))
713 goto exit;
714
715 span = &g_array_index (ned->week_view->spans, EWeekViewEventSpan, wvevent->spans_index + 0);
716
717 /* If the event can't be fit on the screen, don't try to edit it. */
718 if (!span->text_item) {
719 e_week_view_foreach_event_with_uid (ned->week_view, uid, e_week_view_remove_event_cb, NULL);
720 goto exit;
721 }
722
723 e_week_view_start_editing_event (ned->week_view, event_num, 0, ned->initial_text);
724
725 if (ned->paste_clipboard) {
726 wvevent = &g_array_index (ned->week_view->events, EWeekViewEvent, ned->week_view->editing_event_num);
727
728 if (!is_array_index_in_bounds (ned->week_view->spans, wvevent->spans_index + ned->week_view->editing_span_num))
729 return;
730
731 span = &g_array_index (ned->week_view->spans, EWeekViewEventSpan, wvevent->spans_index + ned->week_view->editing_span_num);
732
733 if (span->text_item &&
734 E_IS_TEXT (span->text_item) &&
735 E_TEXT (span->text_item)->editing) {
736 e_text_paste_clipboard (E_TEXT (span->text_item));
737 }
738 }
739
740 exit:
741 g_clear_object (&comp);
742 }
743
744 static void
e_week_view_add_new_event_in_selected_range(EWeekView * week_view,const gchar * initial_text,gboolean paste_clipboard)745 e_week_view_add_new_event_in_selected_range (EWeekView *week_view,
746 const gchar *initial_text,
747 gboolean paste_clipboard)
748 {
749 NewEventInRangeData *ned;
750 ECalModel *model;
751 const gchar *source_uid;
752
753 ned = g_slice_new0 (NewEventInRangeData);
754 ned->week_view = g_object_ref (week_view);
755 ned->initial_text = g_strdup (initial_text);
756 ned->dtstart = week_view->day_starts[week_view->selection_start_day];
757 ned->dtend = week_view->day_starts[week_view->selection_end_day + 1];
758 ned->paste_clipboard = paste_clipboard;
759
760 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
761 source_uid = e_cal_model_get_default_source_uid (model);
762
763 e_cal_ops_get_default_component (model, source_uid, TRUE,
764 week_view_new_event_in_selected_range_cb, ned, new_event_in_rage_data_free);
765 }
766
767 static void
week_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)768 week_view_set_property (GObject *object,
769 guint property_id,
770 const GValue *value,
771 GParamSpec *pspec)
772 {
773 switch (property_id) {
774 case PROP_COMPRESS_WEEKEND:
775 e_week_view_set_compress_weekend (
776 E_WEEK_VIEW (object),
777 g_value_get_boolean (value));
778 return;
779
780 case PROP_DAYS_LEFT_TO_RIGHT:
781 e_week_view_set_days_left_to_right (
782 E_WEEK_VIEW (object),
783 g_value_get_boolean (value));
784 return;
785
786 case PROP_DRAW_FLAT_EVENTS:
787 e_week_view_set_draw_flat_events (
788 E_WEEK_VIEW (object),
789 g_value_get_boolean (value));
790 return;
791
792 case PROP_SHOW_EVENT_END_TIMES:
793 e_week_view_set_show_event_end_times (
794 E_WEEK_VIEW (object),
795 g_value_get_boolean (value));
796 return;
797
798 case PROP_SHOW_ICONS_MONTH_VIEW:
799 e_week_view_set_show_icons_month_view (
800 E_WEEK_VIEW (object),
801 g_value_get_boolean (value));
802 return;
803 }
804
805 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
806 }
807
808 static void
week_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)809 week_view_get_property (GObject *object,
810 guint property_id,
811 GValue *value,
812 GParamSpec *pspec)
813 {
814 switch (property_id) {
815 case PROP_COMPRESS_WEEKEND:
816 g_value_set_boolean (
817 value,
818 e_week_view_get_compress_weekend (
819 E_WEEK_VIEW (object)));
820 return;
821
822 case PROP_DAYS_LEFT_TO_RIGHT:
823 g_value_set_boolean (
824 value,
825 e_week_view_get_days_left_to_right (
826 E_WEEK_VIEW (object)));
827 return;
828
829 case PROP_DRAW_FLAT_EVENTS:
830 g_value_set_boolean (
831 value,
832 e_week_view_get_draw_flat_events (
833 E_WEEK_VIEW (object)));
834 return;
835
836 case PROP_SHOW_EVENT_END_TIMES:
837 g_value_set_boolean (
838 value,
839 e_week_view_get_show_event_end_times (
840 E_WEEK_VIEW (object)));
841 return;
842
843 case PROP_SHOW_ICONS_MONTH_VIEW:
844 g_value_set_boolean (
845 value,
846 e_week_view_get_show_icons_month_view (
847 E_WEEK_VIEW (object)));
848 return;
849
850 case PROP_IS_EDITING:
851 g_value_set_boolean (value, e_week_view_is_editing (E_WEEK_VIEW (object)));
852 return;
853 }
854
855 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
856 }
857
858 static void
week_view_dispose(GObject * object)859 week_view_dispose (GObject *object)
860 {
861 EWeekView *week_view;
862 ECalModel *model;
863
864 week_view = E_WEEK_VIEW (object);
865 model = e_calendar_view_get_model (E_CALENDAR_VIEW (object));
866
867 e_week_view_cancel_layout (week_view);
868
869 if (model) {
870 g_signal_handlers_disconnect_by_data (model, object);
871 e_signal_disconnect_notify_handler (model, &week_view->priv->notify_week_start_day_id);
872 }
873
874 if (week_view->events) {
875 e_week_view_free_events (week_view);
876 g_array_free (week_view->events, TRUE);
877 week_view->events = NULL;
878 }
879
880 g_clear_pointer (&week_view->small_font_desc, pango_font_description_free);
881 g_clear_object (&week_view->normal_cursor);
882 g_clear_object (&week_view->move_cursor);
883 g_clear_object (&week_view->resize_width_cursor);
884
885 calendar_config_remove_notification (
886 month_scroll_by_week_changed_cb, week_view);
887
888 /* Chain up to parent's dispose() method. */
889 G_OBJECT_CLASS (e_week_view_parent_class)->dispose (object);
890 }
891
892 static void
week_view_constructed(GObject * object)893 week_view_constructed (GObject *object)
894 {
895 EWeekView *week_view;
896 ECalModel *model;
897 ECalendarView *calendar_view;
898 PangoContext *pango_context;
899
900 /* Chain up to parent's constructed() method. */
901 G_OBJECT_CLASS (e_week_view_parent_class)->constructed (object);
902
903 week_view = E_WEEK_VIEW (object);
904 calendar_view = E_CALENDAR_VIEW (object);
905 model = e_calendar_view_get_model (calendar_view);
906
907 pango_context = gtk_widget_get_pango_context (GTK_WIDGET (week_view));
908 g_warn_if_fail (pango_context != NULL);
909 week_view->small_font_desc = pango_font_description_copy (pango_context_get_font_description (pango_context));
910 pango_font_description_set_size (
911 week_view->small_font_desc,
912 E_WEEK_VIEW_SMALL_FONT_PTSIZE * PANGO_SCALE);
913
914 e_week_view_recalc_display_start_day (E_WEEK_VIEW (object));
915
916 week_view->priv->notify_week_start_day_id = e_signal_connect_notify_swapped (
917 model, "notify::week-start-day",
918 G_CALLBACK (week_view_notify_week_start_day_cb), object);
919
920 g_signal_connect_swapped (
921 model, "comps-deleted",
922 G_CALLBACK (week_view_model_comps_deleted_cb), object);
923
924 g_signal_connect_swapped (
925 model, "model-cell-changed",
926 G_CALLBACK (week_view_model_cell_changed_cb), object);
927
928 g_signal_connect_swapped (
929 model, "model-row-changed",
930 G_CALLBACK (week_view_model_row_changed_cb), object);
931
932 g_signal_connect_swapped (
933 model, "model-rows-inserted",
934 G_CALLBACK (week_view_model_rows_inserted_cb), object);
935
936 g_signal_connect_swapped (
937 model, "time-range-changed",
938 G_CALLBACK (week_view_time_range_changed_cb), object);
939
940 g_signal_connect (
941 model, "timezone-changed",
942 G_CALLBACK (timezone_changed_cb), object);
943 }
944
945 static GdkColor
e_week_view_get_text_color(EWeekView * week_view,EWeekViewEvent * event)946 e_week_view_get_text_color (EWeekView *week_view,
947 EWeekViewEvent *event)
948 {
949 GdkColor color;
950 GdkRGBA rgba;
951
952 if (is_comp_data_valid (event) &&
953 e_cal_model_get_rgba_for_component (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), event->comp_data, &rgba)) {
954 } else {
955 gdouble cc = 65535.0;
956
957 rgba.red = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].red / cc;
958 rgba.green = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].green / cc;
959 rgba.blue = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].blue / cc;
960 rgba.alpha = 1.0;
961
962 }
963
964 rgba = e_utils_get_text_color_for_background (&rgba);
965 e_rgba_to_color (&rgba, &color);
966
967 return color;
968 }
969
970 static void
week_view_update_style_settings(EWeekView * week_view)971 week_view_update_style_settings (EWeekView *week_view)
972 {
973 gint day, day_width, max_day_width, max_abbr_day_width;
974 gint month, month_width, max_month_width, max_abbr_month_width;
975 gint span_num;
976 const gchar *name;
977 PangoFontDescription *font_desc;
978 PangoContext *pango_context;
979 PangoFontMetrics *font_metrics;
980 PangoLayout *layout;
981 PangoAttrList *tnum;
982 PangoAttribute *attr;
983 EWeekViewEventSpan *span;
984
985 e_week_view_set_colors (week_view);
986 e_week_view_check_layout (week_view);
987
988 if (week_view->spans) {
989 for (span_num = 0; span_num < week_view->spans->len; span_num++) {
990 span = &g_array_index (week_view->spans, EWeekViewEventSpan, span_num);
991 if (span->text_item && span->background_item) {
992 gint event_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (span->background_item), "event-num"));
993 EWeekViewEvent *event = NULL;
994
995 if (is_array_index_in_bounds (week_view->events, event_num))
996 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
997
998 if (event) {
999 GdkColor text_color;
1000
1001 text_color = e_week_view_get_text_color (week_view, event);
1002
1003 gnome_canvas_item_set (
1004 span->text_item,
1005 "fill_color_gdk", &text_color,
1006 NULL);
1007 }
1008 }
1009 }
1010 }
1011
1012 /* Set up Pango prerequisites */
1013 pango_context = gtk_widget_get_pango_context (GTK_WIDGET (week_view));
1014 font_desc = pango_font_description_copy (pango_context_get_font_description (pango_context));
1015 font_metrics = pango_context_get_metrics (
1016 pango_context, font_desc,
1017 pango_context_get_language (pango_context));
1018 layout = pango_layout_new (pango_context);
1019 tnum = pango_attr_list_new ();
1020 attr = pango_attr_font_features_new ("tnum=1");
1021 pango_attr_list_insert_before (tnum, attr);
1022 pango_layout_set_attributes (layout, tnum);
1023 pango_attr_list_unref (tnum);
1024
1025 /* Recalculate the height of each row based on the font size. */
1026 week_view->row_height = PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1027 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) +
1028 E_WEEK_VIEW_EVENT_BORDER_HEIGHT * 2 + E_WEEK_VIEW_EVENT_TEXT_Y_PAD * 2;
1029 week_view->row_height = MAX (week_view->row_height, E_WEEK_VIEW_ICON_HEIGHT + E_WEEK_VIEW_ICON_Y_PAD + E_WEEK_VIEW_EVENT_BORDER_HEIGHT * 2);
1030
1031 /* Check that the small font is smaller than the default font.
1032 * If it isn't, we won't use it. */
1033 if (week_view->small_font_desc) {
1034 if (PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1035 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
1036 <= E_WEEK_VIEW_SMALL_FONT_PTSIZE)
1037 week_view->use_small_font = FALSE;
1038 }
1039
1040 /* Set the height of the top canvas. */
1041 gtk_widget_set_size_request (
1042 week_view->titles_canvas, -1,
1043 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
1044 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) + 5);
1045
1046 /* Save the sizes of various strings in the font, so we can quickly
1047 * decide which date formats to use. */
1048
1049 max_day_width = 0;
1050 max_abbr_day_width = 0;
1051 for (day = 0; day < 7; day++) {
1052 name = e_get_weekday_name (day + 1, FALSE);
1053 day_width = get_string_width (layout, name);
1054 week_view->day_widths[day] = day_width;
1055 max_day_width = MAX (max_day_width, day_width);
1056
1057 name = e_get_weekday_name (day + 1, TRUE);
1058 day_width = get_string_width (layout, name);
1059 week_view->abbr_day_widths[day] = day_width;
1060 max_abbr_day_width = MAX (max_abbr_day_width, day_width);
1061 }
1062
1063 max_month_width = 0;
1064 max_abbr_month_width = 0;
1065 for (month = 0; month < 12; month++) {
1066 name = e_get_month_name (month + 1, FALSE);
1067 month_width = get_string_width (layout, name);
1068 week_view->month_widths[month] = month_width;
1069 max_month_width = MAX (max_month_width, month_width);
1070
1071 name = e_get_month_name (month + 1, TRUE);
1072 month_width = get_string_width (layout, name);
1073 week_view->abbr_month_widths[month] = month_width;
1074 max_abbr_month_width = MAX (max_abbr_month_width, month_width);
1075 }
1076
1077 week_view->space_width = get_string_width (layout, " ");
1078 week_view->colon_width = get_string_width (layout, ":");
1079 week_view->slash_width = get_string_width (layout, "/");
1080 week_view->digit_width = get_digit_width (layout);
1081 if (week_view->small_font_desc) {
1082 pango_layout_set_font_description (layout, week_view->small_font_desc);
1083 week_view->small_digit_width = get_digit_width (layout);
1084 pango_layout_set_font_description (layout, font_desc);
1085 }
1086 week_view->max_day_width = max_day_width;
1087 week_view->max_abbr_day_width = max_abbr_day_width;
1088 week_view->max_month_width = max_month_width;
1089 week_view->max_abbr_month_width = max_abbr_month_width;
1090
1091 week_view->am_string_width = get_string_width (
1092 layout,
1093 week_view->am_string);
1094 week_view->pm_string_width = get_string_width (
1095 layout,
1096 week_view->pm_string);
1097
1098 g_object_unref (layout);
1099 pango_font_metrics_unref (font_metrics);
1100 pango_font_description_free (font_desc);
1101 }
1102
1103 static void
week_view_realize(GtkWidget * widget)1104 week_view_realize (GtkWidget *widget)
1105 {
1106 EWeekView *week_view;
1107
1108 if (GTK_WIDGET_CLASS (e_week_view_parent_class)->realize)
1109 (*GTK_WIDGET_CLASS (e_week_view_parent_class)->realize)(widget);
1110
1111 week_view = E_WEEK_VIEW (widget);
1112
1113 week_view_update_style_settings (week_view);
1114
1115 /* Create the pixmaps. */
1116 week_view->reminder_icon =
1117 e_icon_factory_get_icon ("stock_bell", GTK_ICON_SIZE_MENU);
1118 week_view->recurrence_icon =
1119 e_icon_factory_get_icon ("view-refresh", GTK_ICON_SIZE_MENU);
1120 week_view->timezone_icon =
1121 e_icon_factory_get_icon ("stock_timezone", GTK_ICON_SIZE_MENU);
1122 week_view->attach_icon =
1123 e_icon_factory_get_icon ("mail-attachment", GTK_ICON_SIZE_MENU);
1124 week_view->meeting_icon =
1125 e_icon_factory_get_icon ("stock_people", GTK_ICON_SIZE_MENU);
1126 }
1127
1128 static void
week_view_unrealize(GtkWidget * widget)1129 week_view_unrealize (GtkWidget *widget)
1130 {
1131 EWeekView *week_view;
1132
1133 week_view = E_WEEK_VIEW (widget);
1134
1135 g_object_unref (week_view->reminder_icon);
1136 week_view->reminder_icon = NULL;
1137 g_object_unref (week_view->recurrence_icon);
1138 week_view->recurrence_icon = NULL;
1139 g_object_unref (week_view->timezone_icon);
1140 week_view->timezone_icon = NULL;
1141 g_object_unref (week_view->attach_icon);
1142 week_view->attach_icon = NULL;
1143 g_object_unref (week_view->meeting_icon);
1144 week_view->meeting_icon = NULL;
1145
1146 if (GTK_WIDGET_CLASS (e_week_view_parent_class)->unrealize)
1147 (*GTK_WIDGET_CLASS (e_week_view_parent_class)->unrealize)(widget);
1148 }
1149
1150 static void
week_view_style_updated(GtkWidget * widget)1151 week_view_style_updated (GtkWidget *widget)
1152 {
1153 if (GTK_WIDGET_CLASS (e_week_view_parent_class)->style_updated)
1154 (*GTK_WIDGET_CLASS (e_week_view_parent_class)->style_updated) (widget);
1155
1156 week_view_update_style_settings (E_WEEK_VIEW (widget));
1157 }
1158
1159 static void
week_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1160 week_view_size_allocate (GtkWidget *widget,
1161 GtkAllocation *allocation)
1162 {
1163 EWeekView *week_view;
1164 GtkAllocation canvas_allocation;
1165 gdouble old_x2, old_y2, new_x2, new_y2;
1166
1167 week_view = E_WEEK_VIEW (widget);
1168
1169 (*GTK_WIDGET_CLASS (e_week_view_parent_class)->size_allocate) (widget, allocation);
1170
1171 e_week_view_recalc_cell_sizes (week_view);
1172
1173 /* Set the scroll region of the top canvas to its allocated size. */
1174 gnome_canvas_get_scroll_region (
1175 GNOME_CANVAS (week_view->titles_canvas),
1176 NULL, NULL, &old_x2, &old_y2);
1177 gtk_widget_get_allocation (
1178 week_view->titles_canvas, &canvas_allocation);
1179 new_x2 = canvas_allocation.width - 1;
1180 new_y2 = canvas_allocation.height - 1;
1181 if (old_x2 != new_x2 || old_y2 != new_y2)
1182 gnome_canvas_set_scroll_region (
1183 GNOME_CANVAS (week_view->titles_canvas),
1184 0, 0, new_x2, new_y2);
1185
1186 /* Set the scroll region of the main canvas to its allocated width,
1187 * but with the height depending on the number of rows needed. */
1188 gnome_canvas_get_scroll_region (
1189 GNOME_CANVAS (week_view->main_canvas),
1190 NULL, NULL, &old_x2, &old_y2);
1191 gtk_widget_get_allocation (
1192 week_view->main_canvas, &canvas_allocation);
1193 new_x2 = canvas_allocation.width - 1;
1194 new_y2 = canvas_allocation.height - 1;
1195 if (old_x2 != new_x2 || old_y2 != new_y2)
1196 gnome_canvas_set_scroll_region (
1197 GNOME_CANVAS (week_view->main_canvas),
1198 0, 0, new_x2, new_y2);
1199
1200 /* Flag that we need to reshape the events. */
1201 if (old_x2 != new_x2 || old_y2 != new_y2) {
1202 week_view->events_need_reshape = TRUE;
1203 e_week_view_check_layout (week_view);
1204 }
1205 }
1206
1207 static gint
week_view_focus_in(GtkWidget * widget,GdkEventFocus * event)1208 week_view_focus_in (GtkWidget *widget,
1209 GdkEventFocus *event)
1210 {
1211 EWeekView *week_view;
1212
1213 g_return_val_if_fail (widget != NULL, FALSE);
1214 g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
1215 g_return_val_if_fail (event != NULL, FALSE);
1216
1217 week_view = E_WEEK_VIEW (widget);
1218
1219 /* XXX Can't access flags directly anymore, but is it really needed?
1220 * If so, could we call gtk_widget_send_focus_change() instead? */
1221 #if 0
1222 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1223 #endif
1224
1225 if (E_CALENDAR_VIEW (week_view)->in_focus && week_view->requires_update) {
1226 time_t my_start = 0, my_end = 0, model_start = 0, model_end = 0;
1227
1228 week_view->requires_update = FALSE;
1229
1230 e_cal_model_get_time_range (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), &model_start, &model_end);
1231
1232 if (e_calendar_view_get_visible_time_range (E_CALENDAR_VIEW (week_view), &my_start, &my_end) &&
1233 model_start == my_start && model_end == my_end) {
1234 /* update only when the same time range is set in a view and in a model;
1235 * otherwise time range change invokes also query update */
1236 e_week_view_update_query (week_view);
1237 }
1238 }
1239
1240 gtk_widget_queue_draw (week_view->main_canvas);
1241
1242 return FALSE;
1243 }
1244
1245 static gint
week_view_focus_out(GtkWidget * widget,GdkEventFocus * event)1246 week_view_focus_out (GtkWidget *widget,
1247 GdkEventFocus *event)
1248 {
1249 EWeekView *week_view;
1250
1251 g_return_val_if_fail (widget != NULL, FALSE);
1252 g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
1253 g_return_val_if_fail (event != NULL, FALSE);
1254
1255 week_view = E_WEEK_VIEW (widget);
1256
1257 /* XXX Can't access flags directly anymore, but is it really needed?
1258 * If so, could we call gtk_widget_send_focus_change() instead? */
1259 #if 0
1260 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1261 #endif
1262
1263 gtk_widget_queue_draw (week_view->main_canvas);
1264
1265 return FALSE;
1266 }
1267
1268 static gboolean
week_view_key_press(GtkWidget * widget,GdkEventKey * event)1269 week_view_key_press (GtkWidget *widget,
1270 GdkEventKey *event)
1271 {
1272 gboolean handled = FALSE;
1273 handled = e_week_view_do_key_press (widget, event);
1274
1275 /* if not handled, try key bindings */
1276 if (!handled)
1277 handled = GTK_WIDGET_CLASS (e_week_view_parent_class)->key_press_event (widget, event);
1278 return handled;
1279 }
1280
1281 static gboolean
week_view_focus(GtkWidget * widget,GtkDirectionType direction)1282 week_view_focus (GtkWidget *widget,
1283 GtkDirectionType direction)
1284 {
1285 EWeekView *week_view;
1286 gint new_event_num;
1287 gint new_span_num;
1288 gint event_loop;
1289 gboolean editable = FALSE;
1290 static gint last_focus_event_num = -1, last_focus_span_num = -1;
1291
1292 g_return_val_if_fail (widget != NULL, FALSE);
1293 g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
1294
1295 week_view = E_WEEK_VIEW (widget);
1296
1297 e_week_view_check_layout (week_view);
1298
1299 if (week_view->focused_jump_button == E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS) {
1300 last_focus_event_num = week_view->editing_event_num;
1301 last_focus_span_num = week_view->editing_span_num;
1302 }
1303
1304 /* if there is not event, just grab week_view */
1305 if (week_view->events->len == 0) {
1306 gtk_widget_grab_focus (widget);
1307 return TRUE;
1308 }
1309
1310 for (event_loop = 0; event_loop < week_view->events->len;
1311 ++event_loop) {
1312 if (!e_week_view_get_next_tab_event (week_view, direction,
1313 last_focus_event_num,
1314 last_focus_span_num,
1315 &new_event_num,
1316 &new_span_num))
1317 return FALSE;
1318
1319 if (new_event_num == -1) {
1320 /* focus should go to week_view widget
1321 */
1322 gtk_widget_grab_focus (widget);
1323 return TRUE;
1324 }
1325
1326 editable = e_week_view_start_editing_event (
1327 week_view,
1328 new_event_num,
1329 new_span_num,
1330 NULL);
1331 last_focus_event_num = new_event_num;
1332 last_focus_span_num = new_span_num;
1333
1334 if (editable)
1335 break;
1336 else {
1337 /* check if we should go to the jump button */
1338
1339 EWeekViewEvent *event;
1340 EWeekViewEventSpan *span;
1341 gint current_day;
1342
1343 if (!is_array_index_in_bounds (week_view->events, new_event_num))
1344 break;
1345
1346 event = &g_array_index (week_view->events,
1347 EWeekViewEvent,
1348 new_event_num);
1349
1350 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + new_span_num))
1351 break;
1352
1353 span = &g_array_index (week_view->spans,
1354 EWeekViewEventSpan,
1355 event->spans_index + new_span_num);
1356 current_day = span->start_day;
1357
1358 if ((week_view->focused_jump_button != current_day) &&
1359 e_week_view_is_jump_button_visible (week_view, current_day)) {
1360
1361 /* focus go to the jump button */
1362 e_week_view_stop_editing_event (week_view);
1363 gnome_canvas_item_grab_focus (week_view->jump_buttons[current_day]);
1364 return TRUE;
1365 }
1366 }
1367 }
1368 return editable;
1369 }
1370
1371 static gboolean
week_view_popup_menu(GtkWidget * widget)1372 week_view_popup_menu (GtkWidget *widget)
1373 {
1374 EWeekView *week_view = E_WEEK_VIEW (widget);
1375
1376 e_week_view_show_popup_menu (
1377 week_view, NULL,
1378 week_view->editing_event_num);
1379
1380 return TRUE;
1381 }
1382
1383 static GList *
week_view_get_selected_events(ECalendarView * cal_view)1384 week_view_get_selected_events (ECalendarView *cal_view)
1385 {
1386 EWeekViewEvent *event = NULL;
1387 GList *list = NULL;
1388 EWeekView *week_view = (EWeekView *) cal_view;
1389
1390 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), NULL);
1391
1392 if (week_view->editing_event_num != -1) {
1393 if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num)) {
1394 week_view->editing_event_num = -1;
1395 g_object_notify (G_OBJECT (week_view), "is-editing");
1396 return NULL;
1397 }
1398
1399 event = &g_array_index (week_view->events, EWeekViewEvent,
1400 week_view->editing_event_num);
1401 } else if (week_view->popup_event_num != -1) {
1402 if (!is_array_index_in_bounds (week_view->events, week_view->popup_event_num))
1403 return NULL;
1404
1405 event = &g_array_index (week_view->events, EWeekViewEvent,
1406 week_view->popup_event_num);
1407 }
1408
1409 if (event)
1410 list = g_list_prepend (list, event);
1411
1412 return list;
1413 }
1414
1415 static gboolean
week_view_get_selected_time_range(ECalendarView * cal_view,time_t * start_time,time_t * end_time)1416 week_view_get_selected_time_range (ECalendarView *cal_view,
1417 time_t *start_time,
1418 time_t *end_time)
1419 {
1420 gint start_day, end_day;
1421 EWeekView *week_view = E_WEEK_VIEW (cal_view);
1422
1423 start_day = week_view->selection_start_day;
1424 end_day = week_view->selection_end_day;
1425
1426 if (start_day == -1) {
1427 start_day = 0;
1428 end_day = 0;
1429 }
1430
1431 if (start_time)
1432 *start_time = week_view->day_starts[start_day];
1433
1434 if (end_time)
1435 *end_time = week_view->day_starts[end_day + 1];
1436
1437 return TRUE;
1438 }
1439
1440 /* This sets the selected time range. The EWeekView will show the corresponding
1441 * month and the days between start_time and end_time will be selected.
1442 * To select a single day, use the same value for start_time & end_time. */
1443 static void
week_view_set_selected_time_range(ECalendarView * cal_view,time_t start_time,time_t end_time)1444 week_view_set_selected_time_range (ECalendarView *cal_view,
1445 time_t start_time,
1446 time_t end_time)
1447 {
1448 GDate date, end_date;
1449 gint num_days;
1450 EWeekView *week_view = E_WEEK_VIEW (cal_view);
1451
1452 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
1453
1454 if (!g_date_valid (&week_view->priv->first_day_shown)) {
1455 /* This view has not been initialized/shown yet, thus skip this. */
1456 return;
1457 }
1458
1459 time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
1460
1461 /* Set the selection to the given days. */
1462 week_view->selection_start_day = g_date_get_julian (&date)
1463 - g_date_get_julian (&week_view->priv->first_day_shown);
1464 if (end_time == start_time
1465 || end_time <= time_add_day_with_zone (start_time, 1,
1466 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view))))
1467 week_view->selection_end_day = week_view->selection_start_day;
1468 else {
1469 time_to_gdate_with_zone (&end_date, end_time - 60, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
1470 week_view->selection_end_day = g_date_get_julian (&end_date)
1471 - g_date_get_julian (&week_view->priv->first_day_shown);
1472 }
1473
1474 /* Make sure the selection is valid. */
1475 num_days = (e_week_view_get_weeks_shown (week_view) * 7) - 1;
1476 week_view->selection_start_day = CLAMP (
1477 week_view->selection_start_day, 0, num_days);
1478 week_view->selection_end_day = CLAMP (
1479 week_view->selection_end_day,
1480 week_view->selection_start_day,
1481 num_days);
1482
1483 gtk_widget_queue_draw (week_view->main_canvas);
1484 }
1485
1486 static gboolean
week_view_get_visible_time_range(ECalendarView * cal_view,time_t * start_time,time_t * end_time)1487 week_view_get_visible_time_range (ECalendarView *cal_view,
1488 time_t *start_time,
1489 time_t *end_time)
1490 {
1491 gint num_days;
1492 EWeekView *week_view = E_WEEK_VIEW (cal_view);
1493
1494 /* If we don't have a valid date set yet, return FALSE. */
1495 if (!g_date_valid (&week_view->priv->first_day_shown))
1496 return FALSE;
1497
1498 num_days = e_week_view_get_weeks_shown (week_view) * 7;
1499
1500 *start_time = week_view->day_starts[0];
1501 *end_time = week_view->day_starts[num_days];
1502
1503 return TRUE;
1504 }
1505
1506 static void
week_view_paste_text(ECalendarView * cal_view)1507 week_view_paste_text (ECalendarView *cal_view)
1508 {
1509 EWeekViewEvent *event;
1510 EWeekViewEventSpan *span;
1511 EWeekView *week_view;
1512
1513 g_return_if_fail (E_IS_WEEK_VIEW (cal_view));
1514
1515 week_view = E_WEEK_VIEW (cal_view);
1516
1517 if (week_view->editing_event_num == -1) {
1518 e_week_view_add_new_event_in_selected_range (week_view, NULL, TRUE);
1519 return;
1520 }
1521
1522 if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
1523 return;
1524
1525 event = &g_array_index (week_view->events, EWeekViewEvent,
1526 week_view->editing_event_num);
1527
1528 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + week_view->editing_span_num))
1529 return;
1530
1531 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
1532 event->spans_index + week_view->editing_span_num);
1533
1534 if (span->text_item &&
1535 E_IS_TEXT (span->text_item) &&
1536 E_TEXT (span->text_item)->editing) {
1537 e_text_paste_clipboard (E_TEXT (span->text_item));
1538 }
1539 }
1540
1541 static void
week_view_cursor_key_up(EWeekView * week_view)1542 week_view_cursor_key_up (EWeekView *week_view)
1543 {
1544 if (week_view->selection_start_day == -1)
1545 return;
1546
1547 week_view->selection_start_day--;
1548
1549 if (week_view->selection_start_day < 0) {
1550 e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_UP);
1551 week_view->selection_start_day = 6;
1552 }
1553
1554 week_view->selection_end_day = week_view->selection_start_day;
1555 g_signal_emit_by_name (week_view, "selected_time_changed");
1556 gtk_widget_queue_draw (week_view->main_canvas);
1557 }
1558
1559 static void
week_view_cursor_key_down(EWeekView * week_view)1560 week_view_cursor_key_down (EWeekView *week_view)
1561 {
1562 if (week_view->selection_start_day == -1)
1563 return;
1564
1565 week_view->selection_start_day++;
1566
1567 if (week_view->selection_start_day > 6) {
1568 e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_DOWN);
1569 week_view->selection_start_day = 0;
1570 }
1571
1572 week_view->selection_end_day = week_view->selection_start_day;
1573 g_signal_emit_by_name (week_view, "selected_time_changed");
1574 gtk_widget_queue_draw (week_view->main_canvas);
1575 }
1576
1577 static void
week_view_cursor_key_left(EWeekView * week_view)1578 week_view_cursor_key_left (EWeekView *week_view)
1579 {
1580 if (week_view->selection_start_day == -1)
1581 return;
1582
1583 week_view->selection_start_day = map_left[week_view->selection_start_day];
1584 week_view->selection_end_day = week_view->selection_start_day;
1585 g_signal_emit_by_name (week_view, "selected_time_changed");
1586 gtk_widget_queue_draw (week_view->main_canvas);
1587 }
1588
1589 static void
week_view_cursor_key_right(EWeekView * week_view)1590 week_view_cursor_key_right (EWeekView *week_view)
1591 {
1592 if (week_view->selection_start_day == -1)
1593 return;
1594
1595 week_view->selection_start_day = map_right[week_view->selection_start_day];
1596 week_view->selection_end_day = week_view->selection_start_day;
1597 g_signal_emit_by_name (week_view, "selected_time_changed");
1598 gtk_widget_queue_draw (week_view->main_canvas);
1599 }
1600
1601 static void
e_week_view_class_init(EWeekViewClass * class)1602 e_week_view_class_init (EWeekViewClass *class)
1603 {
1604 GObjectClass *object_class;
1605 GtkWidgetClass *widget_class;
1606 ECalendarViewClass *view_class;
1607
1608 g_type_class_add_private (class, sizeof (EWeekViewPrivate));
1609
1610 object_class = G_OBJECT_CLASS (class);
1611 object_class->set_property = week_view_set_property;
1612 object_class->get_property = week_view_get_property;
1613 object_class->dispose = week_view_dispose;
1614 object_class->constructed = week_view_constructed;
1615
1616 widget_class = GTK_WIDGET_CLASS (class);
1617 widget_class->realize = week_view_realize;
1618 widget_class->unrealize = week_view_unrealize;
1619 widget_class->style_updated = week_view_style_updated;
1620 widget_class->size_allocate = week_view_size_allocate;
1621 widget_class->focus_in_event = week_view_focus_in;
1622 widget_class->focus_out_event = week_view_focus_out;
1623 widget_class->key_press_event = week_view_key_press;
1624 widget_class->focus = week_view_focus;
1625 widget_class->popup_menu = week_view_popup_menu;
1626
1627 view_class = E_CALENDAR_VIEW_CLASS (class);
1628 view_class->get_selected_events = week_view_get_selected_events;
1629 view_class->get_selected_time_range = week_view_get_selected_time_range;
1630 view_class->set_selected_time_range = week_view_set_selected_time_range;
1631 view_class->get_visible_time_range = week_view_get_visible_time_range;
1632 view_class->precalc_visible_time_range = e_week_view_precalc_visible_time_range;
1633 view_class->paste_text = week_view_paste_text;
1634
1635 class->cursor_key_up = week_view_cursor_key_up;
1636 class->cursor_key_down = week_view_cursor_key_down;
1637 class->cursor_key_left = week_view_cursor_key_left;
1638 class->cursor_key_right = week_view_cursor_key_right;
1639
1640 /* XXX This property really belongs in EMonthView,
1641 * but too much drawing code is tied to it. */
1642 g_object_class_install_property (
1643 object_class,
1644 PROP_COMPRESS_WEEKEND,
1645 g_param_spec_boolean (
1646 "compress-weekend",
1647 "Compress Weekend",
1648 NULL,
1649 TRUE,
1650 G_PARAM_READWRITE |
1651 G_PARAM_STATIC_STRINGS));
1652
1653 g_object_class_install_property (
1654 object_class,
1655 PROP_DAYS_LEFT_TO_RIGHT,
1656 g_param_spec_boolean (
1657 "days-left-to-right",
1658 "Days Left To Right",
1659 NULL,
1660 FALSE,
1661 G_PARAM_READWRITE |
1662 G_PARAM_STATIC_STRINGS));
1663
1664 g_object_class_install_property (
1665 object_class,
1666 PROP_DRAW_FLAT_EVENTS,
1667 g_param_spec_boolean (
1668 "draw-flat-events",
1669 "Draw Flat Events",
1670 NULL,
1671 TRUE,
1672 G_PARAM_READWRITE |
1673 G_PARAM_STATIC_STRINGS));
1674
1675 g_object_class_install_property (
1676 object_class,
1677 PROP_SHOW_EVENT_END_TIMES,
1678 g_param_spec_boolean (
1679 "show-event-end-times",
1680 "Show Event End Times",
1681 NULL,
1682 TRUE,
1683 G_PARAM_READWRITE |
1684 G_PARAM_STATIC_STRINGS));
1685
1686 g_object_class_install_property (
1687 object_class,
1688 PROP_SHOW_ICONS_MONTH_VIEW,
1689 g_param_spec_boolean (
1690 "show-icons-month-view",
1691 "Show Icons Month View",
1692 NULL,
1693 FALSE,
1694 G_PARAM_READWRITE |
1695 G_PARAM_STATIC_STRINGS));
1696
1697 g_object_class_override_property (
1698 object_class,
1699 PROP_IS_EDITING,
1700 "is-editing");
1701
1702 /* init the accessibility support for e_week_view */
1703 gtk_widget_class_set_accessible_type (widget_class, EA_TYPE_WEEK_VIEW);
1704 }
1705
1706 static void
e_week_view_init(EWeekView * week_view)1707 e_week_view_init (EWeekView *week_view)
1708 {
1709 GnomeCanvasGroup *canvas_group;
1710 GtkAdjustment *adjustment;
1711 GdkPixbuf *pixbuf;
1712 gint i;
1713
1714 week_view->priv = E_WEEK_VIEW_GET_PRIVATE (week_view);
1715 week_view->priv->weeks_shown = 6;
1716 week_view->priv->compress_weekend = TRUE;
1717 week_view->priv->days_left_to_right = FALSE;
1718 week_view->priv->draw_flat_events = TRUE;
1719 week_view->priv->show_event_end_times = TRUE;
1720 week_view->priv->update_base_date = TRUE;
1721 week_view->priv->display_start_day = G_DATE_MONDAY;
1722
1723 gtk_widget_set_can_focus (GTK_WIDGET (week_view), TRUE);
1724
1725 week_view->event_destroyed = FALSE;
1726 week_view->events = g_array_new (
1727 FALSE, FALSE,
1728 sizeof (EWeekViewEvent));
1729 week_view->events_sorted = TRUE;
1730 week_view->events_need_layout = FALSE;
1731 week_view->events_need_reshape = FALSE;
1732
1733 week_view->layout_timeout_id = 0;
1734
1735 week_view->spans = NULL;
1736
1737 week_view->month_scroll_by_week = FALSE;
1738 week_view->scroll_by_week_notif_id = 0;
1739 week_view->rows = 6;
1740 week_view->columns = 2;
1741
1742 g_date_clear (&week_view->base_date, 1);
1743 g_date_clear (&week_view->priv->first_day_shown, 1);
1744
1745 week_view->row_height = 10;
1746 week_view->rows_per_cell = 1;
1747
1748 week_view->selection_start_day = -1;
1749 week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
1750
1751 week_view->pressed_event_num = -1;
1752 week_view->editing_event_num = -1;
1753
1754 week_view->last_edited_comp_string = NULL;
1755
1756 /* Create the small font in constructed. */
1757 week_view->use_small_font = TRUE;
1758 week_view->small_font_desc = NULL;
1759
1760 /* String to use in 12-hour time format for times in the morning. */
1761 week_view->am_string = _("am");
1762
1763 /* String to use in 12-hour time format for times in the afternoon. */
1764 week_view->pm_string = _("pm");
1765
1766 week_view->bc_event_time = 0;
1767 week_view->before_click_dtstart = 0;
1768 week_view->before_click_dtend = 0;
1769
1770 gtk_widget_set_margin_top (GTK_WIDGET (week_view), 1);
1771
1772 /*
1773 * Titles Canvas. Note that we don't show it is only shown in the
1774 * Month view.
1775 */
1776 week_view->titles_canvas = e_canvas_new ();
1777 gtk_grid_attach (GTK_GRID (week_view), week_view->titles_canvas, 1, 0, 1, 1);
1778 g_object_set (G_OBJECT (week_view->titles_canvas),
1779 "hexpand", TRUE,
1780 "vexpand", FALSE,
1781 "halign", GTK_ALIGN_FILL,
1782 "valign", GTK_ALIGN_FILL,
1783 NULL);
1784
1785 canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->titles_canvas)->root);
1786
1787 week_view->titles_canvas_item =
1788 gnome_canvas_item_new (
1789 canvas_group,
1790 e_week_view_titles_item_get_type (),
1791 "EWeekViewTitlesItem::week_view", week_view,
1792 NULL);
1793
1794 /*
1795 * Main Canvas
1796 */
1797 week_view->main_canvas = e_canvas_new ();
1798 gtk_grid_attach (GTK_GRID (week_view), week_view->main_canvas, 1, 1, 1, 1);
1799 g_object_set (G_OBJECT (week_view->main_canvas),
1800 "hexpand", TRUE,
1801 "vexpand", TRUE,
1802 "halign", GTK_ALIGN_FILL,
1803 "valign", GTK_ALIGN_FILL,
1804 NULL);
1805 gtk_widget_show (week_view->main_canvas);
1806
1807 canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root);
1808
1809 week_view->main_canvas_item =
1810 gnome_canvas_item_new (
1811 canvas_group,
1812 e_week_view_main_item_get_type (),
1813 "EWeekViewMainItem::week_view", week_view,
1814 NULL);
1815
1816 g_signal_connect_after (
1817 week_view->main_canvas, "button_press_event",
1818 G_CALLBACK (e_week_view_on_button_press), week_view);
1819 g_signal_connect (
1820 week_view->main_canvas, "button_release_event",
1821 G_CALLBACK (e_week_view_on_button_release), week_view);
1822 g_signal_connect (
1823 week_view->main_canvas, "scroll_event",
1824 G_CALLBACK (e_week_view_on_scroll), week_view);
1825 g_signal_connect (
1826 week_view->main_canvas, "motion_notify_event",
1827 G_CALLBACK (e_week_view_on_motion), week_view);
1828
1829 /* Create the buttons to jump to each days. */
1830 pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) jump_xpm);
1831
1832 for (i = 0; i < E_WEEK_VIEW_MAX_WEEKS * 7; i++) {
1833 week_view->jump_buttons[i] = gnome_canvas_item_new
1834 (canvas_group,
1835 gnome_canvas_pixbuf_get_type (),
1836 "GnomeCanvasPixbuf::pixbuf", pixbuf,
1837 NULL);
1838
1839 g_signal_connect (
1840 week_view->jump_buttons[i], "event",
1841 G_CALLBACK (e_week_view_on_jump_button_event), week_view);
1842 }
1843 week_view->focused_jump_button = E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS;
1844
1845 g_object_unref (pixbuf);
1846
1847 /*
1848 * Scrollbar.
1849 */
1850 adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, -52, 52, 1, 1, 1));
1851
1852 week_view->vscrollbar = gtk_scrollbar_new (
1853 GTK_ORIENTATION_VERTICAL, adjustment);
1854 gtk_grid_attach (GTK_GRID (week_view), week_view->vscrollbar, 2, 1, 1, 1);
1855 g_object_set (G_OBJECT (week_view->vscrollbar),
1856 "hexpand", FALSE,
1857 "vexpand", TRUE,
1858 "halign", GTK_ALIGN_START,
1859 "valign", GTK_ALIGN_FILL,
1860 NULL);
1861 gtk_widget_show (week_view->vscrollbar);
1862
1863 /* Create the cursors. */
1864 week_view->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR);
1865 week_view->move_cursor = gdk_cursor_new (GDK_FLEUR);
1866 week_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
1867 week_view->last_cursor_set = NULL;
1868
1869 week_view->requires_update = FALSE;
1870 }
1871
1872 /**
1873 * e_week_view_new:
1874 * @Returns: a new #EWeekView.
1875 *
1876 * Creates a new #EWeekView.
1877 **/
1878 ECalendarView *
e_week_view_new(ECalModel * model)1879 e_week_view_new (ECalModel *model)
1880 {
1881 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1882
1883 return g_object_new (E_TYPE_WEEK_VIEW, "model", model, NULL);
1884 }
1885
1886 static GdkColor
color_inc(GdkColor c,gint amount)1887 color_inc (GdkColor c,
1888 gint amount)
1889 {
1890 #define dec(x) \
1891 if (x + amount >= 0 \
1892 && x + amount <= 0xFFFF) \
1893 x += amount; \
1894 else if (amount <= 0) \
1895 x = 0; \
1896 else \
1897 x = 0xFFFF;
1898
1899 dec (c.red);
1900 dec (c.green);
1901 dec (c.blue);
1902
1903 #undef dec
1904
1905 return c;
1906 }
1907
1908 static void
e_week_view_set_colors(EWeekView * week_view)1909 e_week_view_set_colors (EWeekView *week_view)
1910 {
1911 GtkWidget *widget = GTK_WIDGET (week_view);
1912 GdkRGBA base_bg, bg_bg, text_fg, selected_bg, selected_fg, unfocused_selected_bg, dark_bg, light_bg;
1913
1914 e_utils_get_theme_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &base_bg);
1915 e_utils_get_theme_color (widget, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR, &bg_bg);
1916 e_utils_get_theme_color (widget, "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR, &text_fg);
1917 e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &selected_bg);
1918 e_utils_get_theme_color (widget, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR, &selected_fg);
1919 e_utils_get_theme_color (widget, "theme_unfocused_selected_bg_color,theme_selected_bg_color", E_UTILS_DEFAULT_THEME_UNFOCUSED_SELECTED_BG_COLOR, &unfocused_selected_bg);
1920
1921 e_utils_shade_color (&bg_bg, &dark_bg, E_UTILS_DARKNESS_MULT);
1922 e_utils_shade_color (&bg_bg, &light_bg, E_UTILS_LIGHTNESS_MULT);
1923
1924 e_rgba_to_color (&bg_bg, &week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS]);
1925 e_rgba_to_color (&base_bg, &week_view->colors[E_WEEK_VIEW_COLOR_ODD_MONTHS]);
1926 e_rgba_to_color (&base_bg, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND]);
1927 e_rgba_to_color (&dark_bg, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER]);
1928 e_rgba_to_color (&text_fg, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_TEXT]);
1929 e_rgba_to_color (&dark_bg, &week_view->colors[E_WEEK_VIEW_COLOR_GRID]);
1930 e_rgba_to_color (&selected_bg, &week_view->colors[E_WEEK_VIEW_COLOR_SELECTED]);
1931 e_rgba_to_color (&unfocused_selected_bg, &week_view->colors[E_WEEK_VIEW_COLOR_SELECTED_UNFOCUSSED]);
1932 e_rgba_to_color (&text_fg, &week_view->colors[E_WEEK_VIEW_COLOR_DATES]);
1933 e_rgba_to_color (&selected_fg, &week_view->colors[E_WEEK_VIEW_COLOR_DATES_SELECTED]);
1934 e_rgba_to_color (&selected_bg, &week_view->colors[E_WEEK_VIEW_COLOR_TODAY]);
1935
1936 week_view->colors[E_WEEK_VIEW_COLOR_TODAY_BACKGROUND] = get_today_background (week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND]);
1937 week_view->colors[E_WEEK_VIEW_COLOR_MONTH_NONWORKING_DAY] = color_inc (week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS], -0x0A0A);
1938 }
1939
1940 static void
e_week_view_recalc_cell_sizes(EWeekView * week_view)1941 e_week_view_recalc_cell_sizes (EWeekView *week_view)
1942 {
1943 gfloat canvas_width, canvas_height, offset;
1944 gint row, col;
1945 GtkAllocation allocation;
1946 GtkWidget *widget;
1947 gint width, height, time_width;
1948 PangoContext *pango_context;
1949 PangoFontMetrics *font_metrics;
1950
1951 if (e_week_view_get_multi_week_view (week_view)) {
1952 week_view->rows =
1953 e_week_view_get_weeks_shown (week_view) * 2;
1954 week_view->columns =
1955 e_week_view_get_compress_weekend (week_view) ? 6 : 7;
1956 } else {
1957 week_view->rows = 6;
1958 week_view->columns = 2;
1959 }
1960
1961 gtk_widget_get_allocation (week_view->main_canvas, &allocation);
1962
1963 /* Calculate the column sizes, using floating point so that pixels
1964 * get divided evenly. Note that we use one more element than the
1965 * number of columns, to make it easy to get the column widths.
1966 * We also add one to the width so that the right border of the last
1967 * column is off the edge of the displayed area. */
1968 canvas_width = allocation.width + 1;
1969 canvas_width /= week_view->columns;
1970 offset = 0;
1971 for (col = 0; col <= week_view->columns; col++) {
1972 week_view->col_offsets[col] = floor (offset + 0.5);
1973 offset += canvas_width;
1974 }
1975
1976 /* Calculate the cell widths based on the offsets. */
1977 for (col = 0; col < week_view->columns; col++) {
1978 week_view->col_widths[col] = week_view->col_offsets[col + 1]
1979 - week_view->col_offsets[col];
1980 }
1981
1982 /* Now do the same for the row heights. */
1983 canvas_height = allocation.height + 1;
1984 canvas_height /= week_view->rows;
1985 offset = 0;
1986 for (row = 0; row <= week_view->rows; row++) {
1987 week_view->row_offsets[row] = floor (offset + 0.5);
1988 offset += canvas_height;
1989 }
1990
1991 /* Calculate the cell heights based on the offsets. */
1992 for (row = 0; row < week_view->rows; row++) {
1993 week_view->row_heights[row] = week_view->row_offsets[row + 1]
1994 - week_view->row_offsets[row];
1995 }
1996
1997 widget = GTK_WIDGET (week_view);
1998
1999 pango_context = gtk_widget_get_pango_context (widget);
2000 if (!pango_context)
2001 return;
2002
2003 font_metrics = pango_context_get_metrics (
2004 pango_context, NULL,
2005 pango_context_get_language (pango_context));
2006
2007 /* Calculate the number of rows of events in each cell, for the large
2008 * cells and the compressed weekend cells. */
2009 if (e_week_view_get_multi_week_view (week_view)) {
2010 week_view->events_y_offset = E_WEEK_VIEW_DATE_T_PAD
2011 + PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
2012 + PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
2013 + E_WEEK_VIEW_DATE_B_PAD;
2014 } else {
2015 week_view->events_y_offset = E_WEEK_VIEW_DATE_T_PAD
2016 + PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics))
2017 + PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics))
2018 + E_WEEK_VIEW_DATE_LINE_T_PAD + 1
2019 + E_WEEK_VIEW_DATE_LINE_B_PAD;
2020 }
2021
2022 height = week_view->row_heights[0];
2023 week_view->rows_per_cell =
2024 (height * 2 - week_view->events_y_offset) /
2025 (week_view->row_height + E_WEEK_VIEW_EVENT_Y_SPACING);
2026 week_view->rows_per_cell = MIN (
2027 week_view->rows_per_cell,
2028 E_WEEK_VIEW_MAX_ROWS_PER_CELL);
2029
2030 week_view->rows_per_compressed_cell =
2031 (height - week_view->events_y_offset) /
2032 (week_view->row_height + E_WEEK_VIEW_EVENT_Y_SPACING);
2033 week_view->rows_per_compressed_cell = MIN (
2034 week_view->rows_per_compressed_cell,
2035 E_WEEK_VIEW_MAX_ROWS_PER_CELL);
2036
2037 /* Determine which time format to use, based on the width of the cells.
2038 * We only allow the time to take up about half of the width. */
2039 width = week_view->col_widths[0];
2040
2041 time_width = e_week_view_get_time_string_width (week_view);
2042
2043 week_view->time_format = E_WEEK_VIEW_TIME_NONE;
2044 if (week_view->use_small_font && week_view->small_font_desc) {
2045 if (e_week_view_get_show_event_end_times (week_view) &&
2046 width / 2 > time_width * 2 + E_WEEK_VIEW_EVENT_TIME_SPACING)
2047 week_view->time_format = E_WEEK_VIEW_TIME_BOTH_SMALL_MIN;
2048 else if (width / 2 > time_width)
2049 week_view->time_format = E_WEEK_VIEW_TIME_START_SMALL_MIN;
2050 } else {
2051 if (e_week_view_get_show_event_end_times (week_view) &&
2052 width / 2 > time_width * 2 + E_WEEK_VIEW_EVENT_TIME_SPACING)
2053 week_view->time_format = E_WEEK_VIEW_TIME_BOTH;
2054 else if (width / 2 > time_width)
2055 week_view->time_format = E_WEEK_VIEW_TIME_START;
2056 }
2057
2058 pango_font_metrics_unref (font_metrics);
2059 }
2060
2061 /**
2062 * e_week_view_get_next_tab_event
2063 * @week_view: the week_view widget operate on
2064 * @direction: GTK_DIR_TAB_BACKWARD or GTK_DIR_TAB_FORWARD.
2065 * @current_event_num and @current_span_num: current status.
2066 * @next_event_num: the event number focus should go next.
2067 * -1 indicates focus should go to week_view widget.
2068 * @next_span_num: always return 0.
2069 **/
2070 static gboolean
e_week_view_get_next_tab_event(EWeekView * week_view,GtkDirectionType direction,gint current_event_num,gint current_span_num,gint * next_event_num,gint * next_span_num)2071 e_week_view_get_next_tab_event (EWeekView *week_view,
2072 GtkDirectionType direction,
2073 gint current_event_num,
2074 gint current_span_num,
2075 gint *next_event_num,
2076 gint *next_span_num)
2077 {
2078 gint event_num;
2079
2080 g_return_val_if_fail (week_view != NULL, FALSE);
2081 g_return_val_if_fail (next_event_num != NULL, FALSE);
2082 g_return_val_if_fail (next_span_num != NULL, FALSE);
2083
2084 if (week_view->events->len <= 0)
2085 return FALSE;
2086
2087 /* we only tab through events not spans */
2088 *next_span_num = 0;
2089
2090 switch (direction) {
2091 case GTK_DIR_TAB_BACKWARD:
2092 event_num = current_event_num - 1;
2093 break;
2094 case GTK_DIR_TAB_FORWARD:
2095 event_num = current_event_num + 1;
2096 break;
2097 default:
2098 return FALSE;
2099 }
2100
2101 if (event_num == -1)
2102 /* backward, out of event range, go to week view widget
2103 */
2104 *next_event_num = -1;
2105 else if (event_num < -1)
2106 /* backward from week_view, go to the last event
2107 */
2108 *next_event_num = week_view->events->len - 1;
2109 else if (event_num >= week_view->events->len)
2110 /* forward, out of event range, go to week view widget
2111 */
2112 *next_event_num = -1;
2113 else
2114 *next_event_num = event_num;
2115 return TRUE;
2116 }
2117
2118 /* Restarts a query for the week view */
2119 static void
e_week_view_update_query(EWeekView * week_view)2120 e_week_view_update_query (EWeekView *week_view)
2121 {
2122 ECalModel *cal_model;
2123 gint rows, r;
2124
2125 if (!E_CALENDAR_VIEW (week_view)->in_focus) {
2126 e_week_view_free_events (week_view);
2127 week_view->requires_update = TRUE;
2128 return;
2129 }
2130
2131 gtk_widget_queue_draw (week_view->main_canvas);
2132 e_week_view_free_events (week_view);
2133 e_week_view_queue_layout (week_view);
2134
2135 cal_model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
2136 rows = e_table_model_row_count (E_TABLE_MODEL (cal_model));
2137
2138 for (r = 0; r < rows; r++) {
2139 ECalModelComponent *comp_data;
2140
2141 comp_data = e_cal_model_get_component_at (cal_model, r);
2142 if (comp_data == NULL) {
2143 g_warning ("comp_data is NULL\n");
2144 continue;
2145 }
2146 week_view_process_component (week_view, comp_data);
2147 }
2148 }
2149
2150 void
e_week_view_set_selected_time_range_visible(EWeekView * week_view,time_t start_time,time_t end_time)2151 e_week_view_set_selected_time_range_visible (EWeekView *week_view,
2152 time_t start_time,
2153 time_t end_time)
2154 {
2155 GDate *first_day_shown;
2156 GDate date, end_date;
2157 gint num_days;
2158
2159 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2160
2161 first_day_shown = &week_view->priv->first_day_shown;
2162
2163 time_to_gdate_with_zone (&date, start_time, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
2164
2165 /* Set the selection to the given days. */
2166 week_view->selection_start_day =
2167 g_date_get_julian (&date) -
2168 g_date_get_julian (first_day_shown);
2169 if (end_time == start_time
2170 || end_time <= time_add_day_with_zone (start_time, 1,
2171 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view))))
2172 week_view->selection_end_day = week_view->selection_start_day;
2173 else {
2174 time_to_gdate_with_zone (&end_date, end_time - 60, e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
2175 week_view->selection_end_day =
2176 g_date_get_julian (&end_date) -
2177 g_date_get_julian (first_day_shown);
2178 }
2179
2180 /* Make sure the selection is valid. */
2181 num_days = (e_week_view_get_weeks_shown (week_view) * 7) - 1;
2182 week_view->selection_start_day = CLAMP (
2183 week_view->selection_start_day, 0, num_days);
2184 week_view->selection_end_day = CLAMP (
2185 week_view->selection_end_day,
2186 week_view->selection_start_day,
2187 num_days);
2188
2189 gtk_widget_queue_draw (week_view->main_canvas);
2190 }
2191
2192 /* Note that the returned date may be invalid if no date has been set yet. */
2193 void
e_week_view_get_first_day_shown(EWeekView * week_view,GDate * date)2194 e_week_view_get_first_day_shown (EWeekView *week_view,
2195 GDate *date)
2196 {
2197 *date = week_view->priv->first_day_shown;
2198 }
2199
2200 /* This sets the first day shown in the view. It will be rounded down to the
2201 * nearest week. */
2202 void
e_week_view_set_first_day_shown(EWeekView * week_view,GDate * date)2203 e_week_view_set_first_day_shown (EWeekView *week_view,
2204 GDate *date)
2205 {
2206 GDate base_date;
2207 GDateWeekday weekday;
2208 GDateWeekday display_start_day;
2209 guint day_offset;
2210 gint num_days;
2211 gboolean update_adjustment_value = FALSE;
2212 guint32 old_selection_start_julian = 0, old_selection_end_julian = 0;
2213
2214 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2215
2216 /* Calculate the old selection range. */
2217 if (week_view->selection_start_day != -1) {
2218 old_selection_start_julian =
2219 g_date_get_julian (&week_view->base_date)
2220 + week_view->selection_start_day;
2221 old_selection_end_julian =
2222 g_date_get_julian (&week_view->base_date)
2223 + week_view->selection_end_day;
2224 }
2225
2226 weekday = g_date_get_weekday (date);
2227 display_start_day = e_week_view_get_display_start_day (week_view);
2228
2229 /* Convert it to an offset from the start of the display. */
2230 day_offset = e_weekday_get_days_between (display_start_day, weekday);
2231
2232 /* Calculate the base date, i.e. the first day shown when the
2233 * scrollbar adjustment value is 0. */
2234 base_date = *date;
2235 g_date_subtract_days (&base_date, day_offset);
2236
2237 /* See if we need to update the base date. */
2238 if (!g_date_valid (&week_view->base_date)
2239 || g_date_compare (&week_view->base_date, &base_date)) {
2240 week_view->base_date = base_date;
2241 update_adjustment_value = TRUE;
2242 }
2243
2244 /* See if we need to update the first day shown. */
2245 if (!g_date_valid (&week_view->priv->first_day_shown)
2246 || g_date_compare (&week_view->priv->first_day_shown, &base_date)) {
2247 ICalTime *start_tt;
2248 time_t start_time;
2249
2250 week_view->priv->first_day_shown = base_date;
2251
2252 start_tt = i_cal_time_new_null_time ();
2253 i_cal_time_set_date (start_tt,
2254 g_date_get_year (&base_date),
2255 g_date_get_month (&base_date),
2256 g_date_get_day (&base_date));
2257
2258 start_time = i_cal_time_as_timet_with_zone (
2259 start_tt,
2260 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
2261
2262 g_clear_object (&start_tt);
2263
2264 e_week_view_recalc_day_starts (week_view, start_time);
2265 e_week_view_update_query (week_view);
2266 }
2267
2268 /* Try to keep the previous selection, but if it is no longer shown
2269 * just select the first day. */
2270 if (week_view->selection_start_day != -1) {
2271 week_view->selection_start_day = old_selection_start_julian
2272 - g_date_get_julian (&base_date);
2273 week_view->selection_end_day = old_selection_end_julian
2274 - g_date_get_julian (&base_date);
2275
2276 /* Make sure the selection is valid. */
2277 num_days = (e_week_view_get_weeks_shown (week_view) * 7) - 1;
2278 week_view->selection_start_day = CLAMP (
2279 week_view->selection_start_day, 0, num_days);
2280 week_view->selection_end_day = CLAMP (
2281 week_view->selection_end_day,
2282 week_view->selection_start_day,
2283 num_days);
2284 }
2285
2286 /* Reset the adjustment value to 0 if the base address has changed.
2287 * Note that we do this after updating first_day_shown so that our
2288 * signal handler will not try to reload the events. */
2289 if (update_adjustment_value) {
2290 GtkRange *range;
2291 GtkAdjustment *adjustment;
2292
2293 range = GTK_RANGE (week_view->vscrollbar);
2294 adjustment = gtk_range_get_adjustment (range);
2295 gtk_adjustment_set_value (adjustment, 0);
2296 }
2297
2298 e_week_view_update_query (week_view);
2299 gtk_widget_queue_draw (week_view->main_canvas);
2300 }
2301
2302 GDateWeekday
e_week_view_get_display_start_day(EWeekView * week_view)2303 e_week_view_get_display_start_day (EWeekView *week_view)
2304 {
2305 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), G_DATE_BAD_WEEKDAY);
2306
2307 return week_view->priv->display_start_day;
2308 }
2309
2310 /* Recalculates the time_t corresponding to the start of each day. */
2311 static void
e_week_view_recalc_day_starts(EWeekView * week_view,time_t lower)2312 e_week_view_recalc_day_starts (EWeekView *week_view,
2313 time_t lower)
2314 {
2315 gint num_days, day;
2316 time_t tmp_time;
2317
2318 num_days = E_WEEK_VIEW_MAX_WEEKS * 7;
2319
2320 tmp_time = lower;
2321 week_view->day_starts[0] = tmp_time;
2322 for (day = 1; day <= num_days; day++) {
2323 tmp_time = time_add_day_with_zone (
2324 tmp_time, 1,
2325 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
2326 week_view->day_starts[day] = tmp_time;
2327 }
2328 }
2329
2330 gboolean
e_week_view_get_multi_week_view(EWeekView * week_view)2331 e_week_view_get_multi_week_view (EWeekView *week_view)
2332 {
2333 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2334
2335 return week_view->priv->multi_week_view;
2336 }
2337
2338 void
e_week_view_set_multi_week_view(EWeekView * week_view,gboolean multi_week_view)2339 e_week_view_set_multi_week_view (EWeekView *week_view,
2340 gboolean multi_week_view)
2341 {
2342 GtkRange *range;
2343 GtkAdjustment *adjustment;
2344 gint page_increment, page_size;
2345
2346 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2347
2348 if (multi_week_view == week_view->priv->multi_week_view)
2349 return;
2350
2351 week_view->priv->multi_week_view = multi_week_view;
2352
2353 if (multi_week_view) {
2354 gtk_widget_show (week_view->titles_canvas);
2355 week_view->month_scroll_by_week = calendar_config_get_month_scroll_by_week ();
2356
2357 calendar_config_add_notification_month_scroll_by_week (
2358 month_scroll_by_week_changed_cb, week_view);
2359
2360 if (week_view->month_scroll_by_week) {
2361 page_increment = 1;
2362 page_size = 5;
2363 } else {
2364 page_increment = 4;
2365 page_size = 5;
2366 }
2367 } else {
2368 gtk_widget_hide (week_view->titles_canvas);
2369 page_increment = page_size = 1;
2370
2371 if (week_view->scroll_by_week_notif_id) {
2372 calendar_config_remove_notification (
2373 month_scroll_by_week_changed_cb, week_view);
2374 week_view->scroll_by_week_notif_id = 0;
2375 }
2376 }
2377
2378 range = GTK_RANGE (week_view->vscrollbar);
2379 adjustment = gtk_range_get_adjustment (range);
2380 gtk_adjustment_set_page_increment (adjustment, page_increment);
2381 gtk_adjustment_set_page_size (adjustment, page_size);
2382
2383 e_week_view_recalc_display_start_day (week_view);
2384 e_week_view_recalc_cell_sizes (week_view);
2385
2386 if (g_date_valid (&week_view->priv->first_day_shown))
2387 e_week_view_set_first_day_shown (
2388 week_view,
2389 &week_view->priv->first_day_shown);
2390 }
2391
2392 gboolean
e_week_view_get_update_base_date(EWeekView * week_view)2393 e_week_view_get_update_base_date (EWeekView *week_view)
2394 {
2395 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2396
2397 return week_view->priv->update_base_date;
2398 }
2399
2400 void
e_week_view_set_update_base_date(EWeekView * week_view,gboolean update_base_date)2401 e_week_view_set_update_base_date (EWeekView *week_view,
2402 gboolean update_base_date)
2403 {
2404 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2405
2406 week_view->priv->update_base_date = update_base_date;
2407 }
2408
2409 gint
e_week_view_get_weeks_shown(EWeekView * week_view)2410 e_week_view_get_weeks_shown (EWeekView *week_view)
2411 {
2412 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), 1);
2413
2414 /* Give a sensible answer for single-week view. */
2415 if (!e_week_view_get_multi_week_view (week_view))
2416 return 1;
2417
2418 return week_view->priv->weeks_shown;
2419 }
2420
2421 void
e_week_view_set_weeks_shown(EWeekView * week_view,gint weeks_shown)2422 e_week_view_set_weeks_shown (EWeekView *week_view,
2423 gint weeks_shown)
2424 {
2425 GtkRange *range;
2426 GtkAdjustment *adjustment;
2427 gint page_increment, page_size;
2428
2429 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2430
2431 weeks_shown = MIN (weeks_shown, E_WEEK_VIEW_MAX_WEEKS);
2432
2433 if (weeks_shown == week_view->priv->weeks_shown)
2434 return;
2435
2436 week_view->priv->weeks_shown = weeks_shown;
2437
2438 if (e_week_view_get_multi_week_view (week_view)) {
2439 if (week_view->month_scroll_by_week) {
2440 page_increment = 1;
2441 page_size = 5;
2442 } else {
2443 page_increment = 4;
2444 page_size = 5;
2445 }
2446
2447 range = GTK_RANGE (week_view->vscrollbar);
2448 adjustment = gtk_range_get_adjustment (range);
2449 gtk_adjustment_set_page_increment (adjustment, page_increment);
2450 gtk_adjustment_set_page_size (adjustment, page_size);
2451
2452 e_week_view_recalc_cell_sizes (week_view);
2453
2454 if (g_date_valid (&week_view->priv->first_day_shown))
2455 e_week_view_set_first_day_shown (
2456 week_view,
2457 &week_view->priv->first_day_shown);
2458
2459 e_week_view_update_query (week_view);
2460 }
2461 }
2462
2463 gboolean
e_week_view_get_compress_weekend(EWeekView * week_view)2464 e_week_view_get_compress_weekend (EWeekView *week_view)
2465 {
2466 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2467
2468 return week_view->priv->compress_weekend;
2469 }
2470
2471 void
e_week_view_set_compress_weekend(EWeekView * week_view,gboolean compress_weekend)2472 e_week_view_set_compress_weekend (EWeekView *week_view,
2473 gboolean compress_weekend)
2474 {
2475 gboolean need_reload = FALSE;
2476
2477 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2478
2479 if (compress_weekend == week_view->priv->compress_weekend)
2480 return;
2481
2482 week_view->priv->compress_weekend = compress_weekend;
2483
2484 /* The option only affects the month view. */
2485 if (!e_week_view_get_multi_week_view (week_view))
2486 return;
2487
2488 e_week_view_recalc_cell_sizes (week_view);
2489
2490 need_reload = e_week_view_recalc_display_start_day (week_view);
2491
2492 /* If the display_start_day has changed we need to recalculate the
2493 * date range shown and reload all events, otherwise we only need to
2494 * do a reshape. */
2495 if (need_reload) {
2496 /* Recalculate the days shown and reload if necessary. */
2497 if (g_date_valid (&week_view->priv->first_day_shown))
2498 e_week_view_set_first_day_shown (
2499 week_view,
2500 &week_view->priv->first_day_shown);
2501 } else {
2502 week_view->events_need_reshape = TRUE;
2503 e_week_view_check_layout (week_view);
2504 }
2505
2506 gtk_widget_queue_draw (week_view->titles_canvas);
2507 gtk_widget_queue_draw (week_view->main_canvas);
2508
2509 g_object_notify (G_OBJECT (week_view), "compress-weekend");
2510 }
2511
2512 gboolean
e_week_view_get_draw_flat_events(EWeekView * week_view)2513 e_week_view_get_draw_flat_events (EWeekView *week_view)
2514 {
2515 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2516
2517 return week_view->priv->draw_flat_events;
2518 }
2519
2520 void
e_week_view_set_draw_flat_events(EWeekView * week_view,gboolean draw_flat_events)2521 e_week_view_set_draw_flat_events (EWeekView *week_view,
2522 gboolean draw_flat_events)
2523 {
2524 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2525
2526 if ((week_view->priv->draw_flat_events ? 1 : 0) == (draw_flat_events ? 1 : 0))
2527 return;
2528
2529 week_view->priv->draw_flat_events = draw_flat_events;
2530
2531 gtk_widget_queue_draw (week_view->titles_canvas);
2532 gtk_widget_queue_draw (week_view->main_canvas);
2533
2534 g_object_notify (G_OBJECT (week_view), "draw-flat-events");
2535 }
2536
2537 gboolean
e_week_view_get_days_left_to_right(EWeekView * week_view)2538 e_week_view_get_days_left_to_right (EWeekView *week_view)
2539 {
2540 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2541
2542 return week_view->priv->days_left_to_right;
2543 }
2544
2545 void
e_week_view_set_days_left_to_right(EWeekView * week_view,gboolean days_left_to_right)2546 e_week_view_set_days_left_to_right (EWeekView *week_view,
2547 gboolean days_left_to_right)
2548 {
2549 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2550
2551 if ((week_view->priv->days_left_to_right ? 1 : 0) == (days_left_to_right ? 1 : 0))
2552 return;
2553
2554 week_view->priv->days_left_to_right = days_left_to_right;
2555
2556 week_view->events_need_layout = TRUE;
2557 week_view->events_need_reshape = TRUE;
2558
2559 gtk_widget_queue_draw (week_view->main_canvas);
2560 e_week_view_queue_layout (week_view);
2561
2562 g_object_notify (G_OBJECT (week_view), "days-left-to-right");
2563 }
2564
2565 /* Whether we display event end times. */
2566 gboolean
e_week_view_get_show_event_end_times(EWeekView * week_view)2567 e_week_view_get_show_event_end_times (EWeekView *week_view)
2568 {
2569 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), TRUE);
2570
2571 return week_view->priv->show_event_end_times;
2572 }
2573
2574 void
e_week_view_set_show_event_end_times(EWeekView * week_view,gboolean show_event_end_times)2575 e_week_view_set_show_event_end_times (EWeekView *week_view,
2576 gboolean show_event_end_times)
2577 {
2578 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2579
2580 if (show_event_end_times == week_view->priv->show_event_end_times)
2581 return;
2582
2583 week_view->priv->show_event_end_times = show_event_end_times;
2584 e_week_view_recalc_cell_sizes (week_view);
2585 week_view->events_need_reshape = TRUE;
2586 e_week_view_check_layout (week_view);
2587
2588 gtk_widget_queue_draw (week_view->titles_canvas);
2589 gtk_widget_queue_draw (week_view->main_canvas);
2590
2591 g_object_notify (G_OBJECT (week_view), "show-event-end-times");
2592 }
2593
2594 gboolean
e_week_view_get_show_icons_month_view(EWeekView * week_view)2595 e_week_view_get_show_icons_month_view (EWeekView *week_view)
2596 {
2597 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), TRUE);
2598
2599 return week_view->priv->show_icons_month_view;
2600 }
2601
2602 void
e_week_view_set_show_icons_month_view(EWeekView * week_view,gboolean show_icons_month_view)2603 e_week_view_set_show_icons_month_view (EWeekView *week_view,
2604 gboolean show_icons_month_view)
2605 {
2606 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
2607
2608 if (show_icons_month_view == week_view->priv->show_icons_month_view)
2609 return;
2610
2611 week_view->priv->show_icons_month_view = show_icons_month_view;
2612
2613 if (e_week_view_get_multi_week_view (week_view)) {
2614 e_week_view_recalc_cell_sizes (week_view);
2615 week_view->events_need_reshape = TRUE;
2616 e_week_view_check_layout (week_view);
2617
2618 gtk_widget_queue_draw (week_view->titles_canvas);
2619 gtk_widget_queue_draw (week_view->main_canvas);
2620 }
2621
2622 g_object_notify (G_OBJECT (week_view), "show-icons-month-view");
2623 }
2624
2625 static gboolean
e_week_view_recalc_display_start_day(EWeekView * week_view)2626 e_week_view_recalc_display_start_day (EWeekView *week_view)
2627 {
2628 ECalModel *model;
2629 GDateWeekday week_start_day;
2630 GDateWeekday display_start_day;
2631 gboolean changed;
2632
2633 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
2634 week_start_day = e_cal_model_get_week_start_day (model);
2635
2636 /* The display start day defaults to week_start_day, but we have
2637 * to use Saturday if the weekend is compressed and week_start_day
2638 * is Sunday. */
2639 display_start_day = week_start_day;
2640
2641 if (display_start_day == G_DATE_SUNDAY &&
2642 e_week_view_get_multi_week_view (week_view) &&
2643 e_week_view_get_compress_weekend (week_view)) {
2644 display_start_day = G_DATE_SATURDAY;
2645 }
2646
2647 changed = (display_start_day != week_view->priv->display_start_day);
2648
2649 week_view->priv->display_start_day = display_start_day;
2650
2651 return changed;
2652 }
2653
2654 /* Checks if the users participation status is NEEDS-ACTION and shows the summary as bold text */
2655 static void
set_style_from_attendee(EWeekViewEvent * event,EWeekViewEventSpan * span,ESourceRegistry * registry)2656 set_style_from_attendee (EWeekViewEvent *event,
2657 EWeekViewEventSpan *span,
2658 ESourceRegistry *registry)
2659 {
2660 ECalComponent *comp;
2661 GSList *attendees = NULL, *l;
2662 gchar *address;
2663 ICalParameterPartstat partstat = I_CAL_PARTSTAT_NONE;
2664
2665 if (!is_comp_data_valid (event))
2666 return;
2667
2668 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
2669 if (!comp)
2670 return;
2671
2672 address = itip_get_comp_attendee (
2673 registry, comp, event->comp_data->client);
2674 attendees = e_cal_component_get_attendees (comp);
2675 for (l = attendees; l && address; l = l->next) {
2676 ECalComponentAttendee *attendee = l->data;
2677 const gchar *value, *sentby;
2678
2679 if (!attendee)
2680 continue;
2681
2682 value = e_cal_component_attendee_get_value (attendee);
2683 if (value)
2684 value = itip_strip_mailto (value);
2685 sentby = e_cal_component_attendee_get_sentby (attendee);
2686 if (sentby)
2687 sentby = itip_strip_mailto (sentby);
2688 if ((value && g_ascii_strcasecmp (value, address) == 0) ||
2689 (sentby && g_ascii_strcasecmp (sentby, address) == 0)) {
2690 partstat = e_cal_component_attendee_get_partstat (attendee);
2691 break;
2692 }
2693 }
2694
2695 if (i_cal_component_get_status (event->comp_data->icalcomp) == I_CAL_STATUS_CANCELLED)
2696 gnome_canvas_item_set (span->text_item, "strikeout", TRUE, NULL);
2697
2698 /* The attendee has not yet accepted the meeting, display the summary as bolded.
2699 * If the attendee is not present, it might have come through a mailing list.
2700 * In that case, we never show the meeting as bold even if it is unaccepted. */
2701 if (partstat == I_CAL_PARTSTAT_NEEDSACTION)
2702 gnome_canvas_item_set (span->text_item, "bold", TRUE, NULL);
2703 else if (partstat == I_CAL_PARTSTAT_DECLINED)
2704 gnome_canvas_item_set (span->text_item, "strikeout", TRUE, NULL);
2705 else if (partstat == I_CAL_PARTSTAT_TENTATIVE)
2706 gnome_canvas_item_set (span->text_item, "italic", TRUE, NULL);
2707 else if (partstat == I_CAL_PARTSTAT_DELEGATED)
2708 gnome_canvas_item_set (span->text_item, "italic", TRUE, "strikeout", TRUE, NULL);
2709
2710 g_slist_free_full (attendees, e_cal_component_attendee_free);
2711 g_free (address);
2712 g_object_unref (comp);
2713 }
2714
2715 /* This calls a given function for each event instance that matches the given
2716 * uid. Note that it is safe for the callback to remove the event (since we
2717 * step backwards through the arrays). */
2718 static void
e_week_view_foreach_event_with_uid(EWeekView * week_view,const gchar * uid,EWeekViewForeachEventCallback callback,gpointer data)2719 e_week_view_foreach_event_with_uid (EWeekView *week_view,
2720 const gchar *uid,
2721 EWeekViewForeachEventCallback callback,
2722 gpointer data)
2723 {
2724 EWeekViewEvent *event;
2725 gint event_num;
2726
2727 for (event_num = week_view->events->len - 1;
2728 event_num >= 0;
2729 event_num--) {
2730 const gchar *u;
2731
2732 event = &g_array_index (week_view->events, EWeekViewEvent,
2733 event_num);
2734
2735 if (!is_comp_data_valid (event))
2736 continue;
2737
2738 u = i_cal_component_get_uid (event->comp_data->icalcomp);
2739 if (u && !strcmp (uid, u)) {
2740 if (!(*callback) (week_view, event_num, data))
2741 return;
2742 }
2743 }
2744 }
2745
2746 static gboolean
e_week_view_remove_event_cb(EWeekView * week_view,gint event_num,gpointer data)2747 e_week_view_remove_event_cb (EWeekView *week_view,
2748 gint event_num,
2749 gpointer data)
2750 {
2751 EWeekViewEvent *event;
2752 EWeekViewEventSpan *span;
2753 gint span_num;
2754
2755 if (!is_array_index_in_bounds (week_view->events, event_num))
2756 return TRUE;
2757
2758 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
2759
2760 /* If we were editing this event, set editing_event_num to -1 so
2761 * on_editing_stopped doesn't try to update the event. */
2762 if (week_view->editing_event_num == event_num) {
2763 week_view->editing_event_num = -1;
2764 g_object_notify (G_OBJECT (week_view), "is-editing");
2765 }
2766
2767 if (week_view->popup_event_num == event_num)
2768 e_week_view_set_popup_event (week_view, -1);
2769
2770 if (is_comp_data_valid (event))
2771 g_object_unref (event->comp_data);
2772 event->comp_data = NULL;
2773
2774 if (week_view->spans) {
2775 /* We leave the span elements in the array, but set the canvas item
2776 * pointers to NULL. */
2777 for (span_num = 0; span_num < event->num_spans; span_num++) {
2778 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
2779 break;
2780
2781 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
2782 event->spans_index + span_num);
2783
2784 if (span->text_item) {
2785 g_object_run_dispose (G_OBJECT (span->text_item));
2786 span->text_item = NULL;
2787 }
2788 if (span->background_item) {
2789 g_object_run_dispose (G_OBJECT (span->background_item));
2790 span->background_item = NULL;
2791 }
2792 }
2793
2794 /* Update event_num numbers for already created spans with event_num higher than our event_num */
2795 for (span_num = 0; span_num < week_view->spans->len; span_num++) {
2796 span = &g_array_index (week_view->spans, EWeekViewEventSpan, span_num);
2797
2798 if (span && span->background_item && E_IS_WEEK_VIEW_EVENT_ITEM (span->background_item)) {
2799 EWeekViewEventItem *wveitem = E_WEEK_VIEW_EVENT_ITEM (span->background_item);
2800 gint wveitem_event_num;
2801
2802 wveitem_event_num =
2803 e_week_view_event_item_get_event_num (wveitem);
2804
2805 if (wveitem_event_num > event_num)
2806 e_week_view_event_item_set_event_num (
2807 wveitem, wveitem_event_num - 1);
2808 }
2809 }
2810 }
2811
2812 g_array_remove_index (week_view->events, event_num);
2813
2814 week_view->events_need_layout = TRUE;
2815
2816 return TRUE;
2817 }
2818
2819 void
e_week_view_get_day_position(EWeekView * week_view,gint day,gint * day_x,gint * day_y,gint * day_w,gint * day_h)2820 e_week_view_get_day_position (EWeekView *week_view,
2821 gint day,
2822 gint *day_x,
2823 gint *day_y,
2824 gint *day_w,
2825 gint *day_h)
2826 {
2827 gint cell_x, cell_y, cell_h;
2828
2829 e_week_view_layout_get_day_position (
2830 day,
2831 e_week_view_get_multi_week_view (week_view),
2832 e_week_view_get_weeks_shown (week_view),
2833 e_week_view_get_display_start_day (week_view),
2834 e_week_view_get_compress_weekend (week_view),
2835 &cell_x, &cell_y, &cell_h);
2836
2837 *day_x = week_view->col_offsets[cell_x];
2838 *day_y = week_view->row_offsets[cell_y];
2839
2840 *day_w = week_view->col_widths[cell_x];
2841 *day_h = week_view->row_heights[cell_y];
2842
2843 while (cell_h > 1) {
2844 *day_h += week_view->row_heights[cell_y + 1];
2845 cell_h--;
2846 cell_y++;
2847 }
2848 }
2849
2850 /* Returns the bounding box for a span of an event. Usually this can easily
2851 * be determined by the start & end days and row of the span, which are set in
2852 * e_week_view_layout_event (). Though we need a special case for the weekends
2853 * when they are compressed, since the span may not fit.
2854 * The bounding box includes the entire width of the days in the view (but
2855 * not the vertical line down the right of the last day), though the displayed
2856 * event doesn't normally extend to the edges of the day.
2857 * It returns FALSE if the span isn't visible. */
2858 gboolean
e_week_view_get_span_position(EWeekView * week_view,gint event_num,gint span_num,gint * span_x,gint * span_y,gint * span_w)2859 e_week_view_get_span_position (EWeekView *week_view,
2860 gint event_num,
2861 gint span_num,
2862 gint *span_x,
2863 gint *span_y,
2864 gint *span_w)
2865 {
2866 EWeekViewEvent *event;
2867 EWeekViewEventSpan *span;
2868 gint num_days;
2869 gint start_x, start_y, start_w, start_h;
2870 gint end_x, end_y, end_w, end_h;
2871
2872 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
2873 g_return_val_if_fail (event_num < week_view->events->len, FALSE);
2874
2875 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
2876
2877 g_return_val_if_fail (span_num < event->num_spans, FALSE);
2878
2879 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
2880 return FALSE;
2881
2882 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
2883 event->spans_index + span_num);
2884
2885 if (!e_week_view_layout_get_span_position (
2886 event, span,
2887 week_view->rows_per_cell,
2888 week_view->rows_per_compressed_cell,
2889 e_week_view_get_display_start_day (week_view),
2890 e_week_view_get_multi_week_view (week_view),
2891 e_week_view_get_compress_weekend (week_view),
2892 &num_days)) {
2893 return FALSE;
2894 }
2895
2896 e_week_view_get_day_position (
2897 week_view, span->start_day,
2898 &start_x, &start_y, &start_w, &start_h);
2899 *span_y = start_y + week_view->events_y_offset
2900 + span->row * (week_view->row_height
2901 + E_WEEK_VIEW_EVENT_Y_SPACING);
2902 if (num_days == 1) {
2903 *span_x = start_x;
2904 *span_w = start_w - 1;
2905 } else {
2906 e_week_view_get_day_position (
2907 week_view,
2908 span->start_day + num_days - 1,
2909 &end_x, &end_y, &end_w, &end_h);
2910 *span_x = start_x;
2911 *span_w = end_x + end_w - start_x - 1;
2912 }
2913
2914 return TRUE;
2915 }
2916
2917 static gboolean
ewv_pass_gdkevent_to_etext(EWeekView * week_view,GdkEvent * gevent)2918 ewv_pass_gdkevent_to_etext (EWeekView *week_view,
2919 GdkEvent *gevent)
2920 {
2921 g_return_val_if_fail (week_view != NULL, FALSE);
2922 g_return_val_if_fail (gevent != NULL, FALSE);
2923
2924 if (week_view->editing_event_num != -1 && week_view->editing_span_num != -1) {
2925 EWeekViewEvent *event;
2926 EWeekViewEventSpan *span;
2927
2928 if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
2929 return FALSE;
2930
2931 event = &g_array_index (week_view->events, EWeekViewEvent, week_view->editing_event_num);
2932
2933 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + week_view->editing_span_num))
2934 return FALSE;
2935
2936 span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + week_view->editing_span_num);
2937
2938 if (span->text_item && E_IS_TEXT (span->text_item)) {
2939 gdouble x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0, ex = 0.0, ey = 0.0;
2940
2941 gdk_event_get_coords (gevent, &ex, &ey);
2942
2943 gnome_canvas_item_get_bounds (span->text_item, &x1, &y1, &x2, &y2);
2944
2945 if (ex >= x1 && ex <= x2 && ey >= y1 && ey <= y2) {
2946 GNOME_CANVAS_ITEM_GET_CLASS (span->text_item)->event (span->text_item, gevent);
2947 return TRUE;
2948 }
2949 }
2950 }
2951
2952 return FALSE;
2953 }
2954
2955 static gboolean
e_week_view_on_button_press(GtkWidget * widget,GdkEvent * button_event,EWeekView * week_view)2956 e_week_view_on_button_press (GtkWidget *widget,
2957 GdkEvent *button_event,
2958 EWeekView *week_view)
2959 {
2960 GnomeCanvasItem *item;
2961 gint event_num = -1, span_num = -1;
2962 guint event_button = 0;
2963 gdouble event_x_win = 0;
2964 gdouble event_y_win = 0;
2965 gint x, y, day;
2966
2967 gdk_event_get_button (button_event, &event_button);
2968 gdk_event_get_coords (button_event, &event_x_win, &event_y_win);
2969
2970 /* Convert the mouse position to a week & day. */
2971 x = (gint) event_x_win;
2972 y = (gint) event_y_win;
2973 day = e_week_view_convert_position_to_day (week_view, x, y);
2974 if (day == -1)
2975 return FALSE;
2976
2977 if (ewv_pass_gdkevent_to_etext (week_view, button_event))
2978 return TRUE;
2979
2980 /* If an event is pressed just return. */
2981 if (week_view->pressed_event_num != -1) {
2982 e_week_view_set_popup_event (week_view, week_view->pressed_event_num);
2983 return FALSE;
2984 }
2985
2986 e_week_view_stop_editing_event (week_view);
2987
2988 item = gnome_canvas_get_item_at (GNOME_CANVAS (widget), x, y);
2989 if (!item || !e_week_view_find_event_from_item (week_view, item, &event_num, &span_num))
2990 event_num = -1;
2991
2992 e_week_view_set_popup_event (week_view, event_num);
2993
2994 if (event_button == 1 && button_event->type == GDK_2BUTTON_PRESS) {
2995 time_t dtstart, dtend;
2996
2997 e_calendar_view_get_selected_time_range ((ECalendarView *) week_view, &dtstart, &dtend);
2998 if (dtstart < week_view->before_click_dtend && dtend > week_view->before_click_dtstart) {
2999 e_calendar_view_set_selected_time_range (
3000 E_CALENDAR_VIEW (week_view),
3001 week_view->before_click_dtstart,
3002 week_view->before_click_dtend);
3003 }
3004 e_calendar_view_new_appointment (E_CALENDAR_VIEW (week_view),
3005 (calendar_config_get_prefer_meeting () ? E_NEW_APPOINTMENT_FLAG_MEETING : 0));
3006 return TRUE;
3007 }
3008
3009 if (event_button == 1) {
3010 GdkGrabStatus grab_status;
3011 GdkWindow *window;
3012 GdkDevice *event_device;
3013 guint32 event_time;
3014
3015 /* Start the selection drag. */
3016 if (!gtk_widget_has_focus (GTK_WIDGET (week_view)) && !gtk_widget_has_focus (GTK_WIDGET (week_view->main_canvas)))
3017 gtk_widget_grab_focus (GTK_WIDGET (week_view));
3018
3019 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
3020
3021 event_device = gdk_event_get_device (button_event);
3022 event_time = gdk_event_get_time (button_event);
3023
3024 grab_status = gdk_device_grab (
3025 event_device,
3026 window,
3027 GDK_OWNERSHIP_NONE,
3028 FALSE,
3029 GDK_POINTER_MOTION_MASK |
3030 GDK_BUTTON_RELEASE_MASK,
3031 NULL,
3032 event_time);
3033
3034 if (grab_status == GDK_GRAB_SUCCESS) {
3035 if (event_time - week_view->bc_event_time > 250)
3036 e_calendar_view_get_selected_time_range (
3037 E_CALENDAR_VIEW (week_view),
3038 &week_view->before_click_dtstart,
3039 &week_view->before_click_dtend);
3040 week_view->bc_event_time = event_time;
3041 week_view->selection_start_day = day;
3042 week_view->selection_end_day = day;
3043 week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_END;
3044 g_signal_emit_by_name (week_view, "selected_time_changed");
3045
3046 /* FIXME: Optimise? */
3047 gtk_widget_queue_draw (week_view->main_canvas);
3048 }
3049 } else if (event_button == 3) {
3050 if (!gtk_widget_has_focus (GTK_WIDGET (week_view)))
3051 gtk_widget_grab_focus (GTK_WIDGET (week_view));
3052
3053 if (day < week_view->selection_start_day || day > week_view->selection_end_day) {
3054 week_view->selection_start_day = day;
3055 week_view->selection_end_day = day;
3056 week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
3057
3058 /* FIXME: Optimise? */
3059 gtk_widget_queue_draw (week_view->main_canvas);
3060 }
3061
3062 e_week_view_show_popup_menu (week_view, button_event, event_num);
3063 }
3064
3065 return TRUE;
3066 }
3067
3068 static gboolean
e_week_view_on_button_release(GtkWidget * widget,GdkEvent * button_event,EWeekView * week_view)3069 e_week_view_on_button_release (GtkWidget *widget,
3070 GdkEvent *button_event,
3071 EWeekView *week_view)
3072 {
3073 GdkDevice *event_device;
3074 guint32 event_time;
3075
3076 event_device = gdk_event_get_device (button_event);
3077 event_time = gdk_event_get_time (button_event);
3078
3079 if (week_view->selection_drag_pos != E_WEEK_VIEW_DRAG_NONE) {
3080 week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
3081 gdk_device_ungrab (event_device, event_time);
3082 } else {
3083 ewv_pass_gdkevent_to_etext (week_view, button_event);
3084 }
3085
3086 return FALSE;
3087 }
3088
3089 static gboolean
e_week_view_on_scroll(GtkWidget * widget,GdkEventScroll * scroll,EWeekView * week_view)3090 e_week_view_on_scroll (GtkWidget *widget,
3091 GdkEventScroll *scroll,
3092 EWeekView *week_view)
3093 {
3094 GtkRange *range;
3095 GtkAdjustment *adjustment;
3096 gdouble page_increment;
3097 gdouble new_value;
3098 gdouble page_size;
3099 gdouble lower;
3100 gdouble upper;
3101 gdouble value;
3102 GtkWidget *tool_window = g_object_get_data (G_OBJECT (week_view), "tooltip-window");
3103 guint timeout;
3104
3105 timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
3106 if (timeout) {
3107 g_source_remove (timeout);
3108 g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
3109 }
3110
3111 if (tool_window) {
3112 gtk_widget_destroy (tool_window);
3113 g_object_set_data (G_OBJECT (week_view), "tooltip-window", NULL);
3114 }
3115
3116 range = GTK_RANGE (week_view->vscrollbar);
3117 adjustment = gtk_range_get_adjustment (range);
3118
3119 page_increment = gtk_adjustment_get_page_increment (adjustment);
3120 page_size = gtk_adjustment_get_page_size (adjustment);
3121 lower = gtk_adjustment_get_lower (adjustment);
3122 upper = gtk_adjustment_get_upper (adjustment);
3123 value = gtk_adjustment_get_value (adjustment);
3124
3125 switch (scroll->direction) {
3126 case GDK_SCROLL_UP:
3127 new_value = value - page_increment;
3128 break;
3129 case GDK_SCROLL_DOWN:
3130 new_value = value + page_increment;
3131 break;
3132 case GDK_SCROLL_SMOOTH:
3133 if (scroll->delta_y < -0.001 || scroll->delta_y > 0.001) {
3134 new_value = value + scroll->delta_y * page_increment;
3135 break;
3136 }
3137 return FALSE;
3138 default:
3139 return FALSE;
3140 }
3141
3142 new_value = CLAMP (new_value, lower, upper - page_size);
3143 gtk_adjustment_set_value (adjustment, new_value);
3144
3145 week_view->events_need_layout = TRUE;
3146 e_week_view_check_layout (week_view);
3147
3148 return TRUE;
3149 }
3150
3151 static gboolean
e_week_view_on_motion(GtkWidget * widget,GdkEventMotion * mevent,EWeekView * week_view)3152 e_week_view_on_motion (GtkWidget *widget,
3153 GdkEventMotion *mevent,
3154 EWeekView *week_view)
3155 {
3156 gint x, y, day;
3157
3158 /* Convert the mouse position to a week & day. */
3159 x = mevent->x;
3160 y = mevent->y;
3161 day = e_week_view_convert_position_to_day (week_view, x, y);
3162 if (day == -1)
3163 return FALSE;
3164
3165 if (week_view->selection_drag_pos != E_WEEK_VIEW_DRAG_NONE) {
3166 e_week_view_update_selection (week_view, day);
3167 return TRUE;
3168 }
3169
3170 ewv_pass_gdkevent_to_etext (week_view, (GdkEvent *) mevent);
3171
3172 return FALSE;
3173 }
3174
3175 /* Converts a position in the canvas window to a day offset from the first
3176 * day displayed. Returns -1 if the position is outside the grid. */
3177 static gint
e_week_view_convert_position_to_day(EWeekView * week_view,gint x,gint y)3178 e_week_view_convert_position_to_day (EWeekView *week_view,
3179 gint x,
3180 gint y)
3181 {
3182 GDateWeekday display_start_day;
3183 gint col, row, grid_x = -1, grid_y = -1, week, day;
3184 gint weekend_col;
3185
3186 display_start_day = e_week_view_get_display_start_day (week_view);
3187
3188 /* First we convert it to a grid position. */
3189 for (col = 0; col <= week_view->columns; col++) {
3190 if (x < week_view->col_offsets[col]) {
3191 grid_x = col - 1;
3192 break;
3193 }
3194 }
3195
3196 for (row = 0; row <= week_view->rows; row++) {
3197 if (y < week_view->row_offsets[row]) {
3198 grid_y = row - 1;
3199 break;
3200 }
3201 }
3202
3203 /* If the mouse is outside the grid return FALSE. */
3204 if (grid_x == -1 || grid_y == -1)
3205 return -1;
3206
3207 /* Now convert the grid position to a week and day. */
3208 if (e_week_view_get_multi_week_view (week_view)) {
3209 week = grid_y / 2;
3210 day = grid_x;
3211
3212 if (e_week_view_get_compress_weekend (week_view)) {
3213 weekend_col = e_weekday_get_days_between (
3214 display_start_day, G_DATE_SATURDAY);
3215 if (grid_x > weekend_col
3216 || (grid_x == weekend_col && grid_y % 2 == 1))
3217 day++;
3218 }
3219 } else {
3220 week = 0;
3221
3222 for (day = 0; day < 7; day++) {
3223 gint day_x = 0, day_y = 0, rows = 0;
3224 e_week_view_layout_get_day_position (
3225 day, FALSE, 1,
3226 e_week_view_get_display_start_day (week_view),
3227 e_week_view_get_compress_weekend (week_view),
3228 &day_x, &day_y, &rows);
3229
3230 if (grid_x == day_x && grid_y >= day_y && grid_y < day_y + rows)
3231 break;
3232 }
3233
3234 if (day == 7)
3235 return -1;
3236 }
3237
3238 return week * 7 + day;
3239 }
3240
3241 static void
e_week_view_update_selection(EWeekView * week_view,gint day)3242 e_week_view_update_selection (EWeekView *week_view,
3243 gint day)
3244 {
3245 gint tmp_day;
3246 gboolean need_redraw = FALSE;
3247
3248 if (week_view->selection_drag_pos == E_WEEK_VIEW_DRAG_START) {
3249 if (day != week_view->selection_start_day) {
3250 need_redraw = TRUE;
3251 week_view->selection_start_day = day;
3252 }
3253 } else {
3254 if (day != week_view->selection_end_day) {
3255 need_redraw = TRUE;
3256 week_view->selection_end_day = day;
3257 }
3258 }
3259
3260 /* Switch the drag position if necessary. */
3261 if (week_view->selection_start_day > week_view->selection_end_day) {
3262 tmp_day = week_view->selection_start_day;
3263 week_view->selection_start_day = week_view->selection_end_day;
3264 week_view->selection_end_day = tmp_day;
3265 if (week_view->selection_drag_pos == E_WEEK_VIEW_DRAG_START)
3266 week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_END;
3267 else
3268 week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_START;
3269 }
3270
3271 /* FIXME: Optimise? */
3272 if (need_redraw) {
3273 gtk_widget_queue_draw (week_view->main_canvas);
3274 }
3275 }
3276
3277 static void
e_week_view_free_events(EWeekView * week_view)3278 e_week_view_free_events (EWeekView *week_view)
3279 {
3280 EWeekViewEvent *event;
3281 EWeekViewEventSpan *span;
3282 gint event_num, span_num, num_days, day;
3283 gboolean did_editing = week_view->editing_event_num != -1;
3284 guint timeout;
3285
3286 /* Reset all our indices. */
3287 week_view->pressed_event_num = -1;
3288 week_view->pressed_span_num = -1;
3289 week_view->editing_event_num = -1;
3290 week_view->editing_span_num = -1;
3291 week_view->popup_event_num = -1;
3292
3293 for (event_num = 0; event_num < week_view->events->len; event_num++) {
3294 event = &g_array_index (week_view->events, EWeekViewEvent,
3295 event_num);
3296
3297 if (is_comp_data_valid (event))
3298 g_object_unref (event->comp_data);
3299 }
3300
3301 g_array_set_size (week_view->events, 0);
3302
3303 /* Destroy all the old canvas items. */
3304 if (week_view->spans) {
3305 for (span_num = 0; span_num < week_view->spans->len;
3306 span_num++) {
3307 span = &g_array_index (week_view->spans,
3308 EWeekViewEventSpan, span_num);
3309 if (span->background_item)
3310 g_object_run_dispose (G_OBJECT (span->background_item));
3311 if (span->text_item)
3312 g_object_run_dispose (G_OBJECT (span->text_item));
3313 }
3314 g_array_free (week_view->spans, TRUE);
3315 week_view->spans = NULL;
3316 }
3317
3318 /* Clear the number of rows used per day. */
3319 num_days = e_week_view_get_weeks_shown (week_view) * 7;
3320 for (day = 0; day <= num_days; day++) {
3321 week_view->rows_per_day[day] = 0;
3322 }
3323
3324 /* Hide all the jump buttons. */
3325 for (day = 0; day < E_WEEK_VIEW_MAX_WEEKS * 7; day++) {
3326 gnome_canvas_item_hide (week_view->jump_buttons[day]);
3327 }
3328
3329 if (did_editing)
3330 g_object_notify (G_OBJECT (week_view), "is-editing");
3331
3332 timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
3333 if (timeout) {
3334 g_source_remove (timeout);
3335 g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
3336 }
3337 }
3338
3339 /* This adds one event to the view, adding it to the appropriate array. */
3340 static void
e_week_view_add_event(ECalClient * client,ECalComponent * comp,time_t start,time_t end,gboolean prepend,gpointer data)3341 e_week_view_add_event (ECalClient *client,
3342 ECalComponent *comp,
3343 time_t start,
3344 time_t end,
3345 gboolean prepend,
3346 gpointer data)
3347
3348 {
3349 AddEventData *add_event_data;
3350 EWeekViewEvent event;
3351 gint num_days;
3352 ICalTime *start_tt, *end_tt;
3353
3354 add_event_data = data;
3355
3356 /* Check that the event times are valid. */
3357 num_days = e_week_view_get_weeks_shown (add_event_data->week_view) * 7;
3358
3359 g_return_if_fail (start <= end);
3360 g_return_if_fail (start < add_event_data->week_view->day_starts[num_days]);
3361
3362 if (end != start || end < add_event_data->week_view->day_starts[0])
3363 g_return_if_fail (end > add_event_data->week_view->day_starts[0]);
3364
3365 start_tt = i_cal_time_new_from_timet_with_zone (
3366 start, FALSE,
3367 e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->week_view)));
3368 end_tt = i_cal_time_new_from_timet_with_zone (
3369 end, FALSE,
3370 e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->week_view)));
3371
3372 if (add_event_data->comp_data) {
3373 event.comp_data = g_object_ref (add_event_data->comp_data);
3374 } else {
3375 event.comp_data = g_object_new (E_TYPE_CAL_MODEL_COMPONENT, NULL);
3376 event.comp_data->is_new_component = TRUE;
3377 event.comp_data->client = g_object_ref (client);
3378 e_cal_component_abort_sequence (comp);
3379 event.comp_data->icalcomp = i_cal_component_clone (e_cal_component_get_icalcomponent (comp));
3380 }
3381
3382 event.start = start;
3383 event.end = end;
3384 event.tooltip = NULL;
3385 event.timeout = -1;
3386 event.color = NULL;
3387 event.spans_index = 0;
3388 event.num_spans = 0;
3389 event.comp_data->instance_start = start;
3390 event.comp_data->instance_end = end;
3391
3392 event.start_minute = i_cal_time_get_hour (start_tt) * 60 + i_cal_time_get_minute (start_tt);
3393 event.end_minute = i_cal_time_get_hour (end_tt) * 60 + i_cal_time_get_minute (end_tt);
3394 if (event.end_minute == 0 && start != end)
3395 event.end_minute = 24 * 60;
3396
3397 event.different_timezone = FALSE;
3398 if (!cal_comp_util_compare_event_timezones (
3399 comp,
3400 event.comp_data->client,
3401 e_calendar_view_get_timezone (E_CALENDAR_VIEW (add_event_data->week_view))))
3402 event.different_timezone = TRUE;
3403
3404 if (prepend)
3405 g_array_prepend_val (add_event_data->week_view->events, event);
3406 else
3407 g_array_append_val (add_event_data->week_view->events, event);
3408 add_event_data->week_view->events_sorted = FALSE;
3409 add_event_data->week_view->events_need_layout = TRUE;
3410
3411 g_clear_object (&start_tt);
3412 g_clear_object (&end_tt);
3413 }
3414
3415 /* This lays out the events, or reshapes them, as necessary. */
3416 static void
e_week_view_check_layout(EWeekView * week_view)3417 e_week_view_check_layout (EWeekView *week_view)
3418 {
3419 /* Don't bother if we aren't visible. */
3420 if (!E_CALENDAR_VIEW (week_view)->in_focus) {
3421 e_week_view_free_events (week_view);
3422 week_view->requires_update = TRUE;
3423 return;
3424 }
3425
3426 /* Make sure the events are sorted (by start and size). */
3427 e_week_view_ensure_events_sorted (week_view);
3428
3429 if (week_view->events_need_layout)
3430 week_view->spans = e_week_view_layout_events (
3431 week_view->events,
3432 week_view->spans,
3433 e_week_view_get_multi_week_view (week_view),
3434 e_week_view_get_weeks_shown (week_view),
3435 e_week_view_get_compress_weekend (week_view),
3436 e_week_view_get_display_start_day (week_view),
3437 week_view->day_starts,
3438 week_view->rows_per_day);
3439
3440 if (week_view->events_need_layout || week_view->events_need_reshape)
3441 e_week_view_reshape_events (week_view);
3442
3443 week_view->events_need_layout = FALSE;
3444 week_view->events_need_reshape = FALSE;
3445 }
3446
3447 static void
e_week_view_ensure_events_sorted(EWeekView * week_view)3448 e_week_view_ensure_events_sorted (EWeekView *week_view)
3449 {
3450 if (!week_view->events_sorted) {
3451 qsort (
3452 week_view->events->data,
3453 week_view->events->len,
3454 sizeof (EWeekViewEvent),
3455 e_week_view_event_sort_func);
3456 week_view->events_sorted = TRUE;
3457 }
3458 }
3459
3460 gint
e_week_view_event_sort_func(gconstpointer arg1,gconstpointer arg2)3461 e_week_view_event_sort_func (gconstpointer arg1,
3462 gconstpointer arg2)
3463 {
3464 EWeekViewEvent *event1, *event2;
3465
3466 event1 = (EWeekViewEvent *) arg1;
3467 event2 = (EWeekViewEvent *) arg2;
3468
3469 if (event1->start < event2->start)
3470 return -1;
3471 if (event1->start > event2->start)
3472 return 1;
3473
3474 if (event1->end > event2->end)
3475 return -1;
3476 if (event1->end < event2->end)
3477 return 1;
3478
3479 return 0;
3480 }
3481
3482 static void
e_week_view_reshape_events(EWeekView * week_view)3483 e_week_view_reshape_events (EWeekView *week_view)
3484 {
3485 EWeekViewEvent *event;
3486 GDateWeekday display_start_day;
3487 gint event_num, span_num;
3488 gint num_days, day, day_x, day_y, day_w, day_h, max_rows;
3489 gboolean is_weekend;
3490
3491 for (event_num = 0; event_num < week_view->events->len; event_num++) {
3492 event = &g_array_index (week_view->events, EWeekViewEvent,
3493 event_num);
3494 if (!is_comp_data_valid (event))
3495 continue;
3496
3497 for (span_num = 0; span_num < event->num_spans; span_num++) {
3498 gchar *current_comp_string;
3499
3500 e_week_view_reshape_event_span (
3501 week_view, event_num, span_num);
3502 if (week_view->last_edited_comp_string == NULL)
3503 continue;
3504 current_comp_string = i_cal_component_as_ical_string (event->comp_data->icalcomp);
3505 if (strncmp (current_comp_string, week_view->last_edited_comp_string, 50) == 0) {
3506 EWeekViewEventSpan *span;
3507
3508 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num)) {
3509 g_free (current_comp_string);
3510 continue;
3511 }
3512
3513 span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num);
3514 e_canvas_item_grab_focus (span->text_item, TRUE);
3515 g_free (week_view->last_edited_comp_string);
3516 week_view->last_edited_comp_string = NULL;
3517 }
3518 g_free (current_comp_string);
3519 }
3520 }
3521
3522 /* Reshape the jump buttons and show/hide them as appropriate. */
3523 num_days = e_week_view_get_weeks_shown (week_view) * 7;
3524 display_start_day = e_week_view_get_display_start_day (week_view);
3525 for (day = 0; day < num_days; day++) {
3526 switch (e_weekday_add_days (display_start_day, day)) {
3527 case G_DATE_SATURDAY:
3528 case G_DATE_SUNDAY:
3529 is_weekend = TRUE;
3530 break;
3531 default:
3532 is_weekend = FALSE;
3533 break;
3534 }
3535
3536 if (!is_weekend || (
3537 e_week_view_get_multi_week_view (week_view)
3538 && !e_week_view_get_compress_weekend (week_view)))
3539 max_rows = week_view->rows_per_cell;
3540 else
3541 max_rows = week_view->rows_per_compressed_cell;
3542
3543 /* Determine whether the jump button should be shown. */
3544 if (week_view->rows_per_day[day] <= max_rows) {
3545 gnome_canvas_item_hide (week_view->jump_buttons[day]);
3546 } else {
3547 cairo_matrix_t matrix;
3548
3549 e_week_view_get_day_position (
3550 week_view, day,
3551 &day_x, &day_y,
3552 &day_w, &day_h);
3553
3554 cairo_matrix_init_translate (
3555 &matrix,
3556 day_x + day_w - E_WEEK_VIEW_JUMP_BUTTON_X_PAD - E_WEEK_VIEW_JUMP_BUTTON_WIDTH,
3557 day_y + day_h - E_WEEK_VIEW_JUMP_BUTTON_Y_PAD - E_WEEK_VIEW_JUMP_BUTTON_HEIGHT);
3558 gnome_canvas_item_set_matrix (week_view->jump_buttons[day], &matrix);
3559
3560 gnome_canvas_item_show (week_view->jump_buttons[day]);
3561 gnome_canvas_item_raise_to_top (week_view->jump_buttons[day]);
3562 }
3563 }
3564
3565 for (day = num_days; day < E_WEEK_VIEW_MAX_WEEKS * 7; day++) {
3566 gnome_canvas_item_hide (week_view->jump_buttons[day]);
3567 }
3568 }
3569
3570 static EWeekViewEvent *
tooltip_get_view_event(EWeekView * week_view,gint day,gint event_num)3571 tooltip_get_view_event (EWeekView *week_view,
3572 gint day,
3573 gint event_num)
3574 {
3575 EWeekViewEvent *pevent;
3576
3577 if (!is_array_index_in_bounds (week_view->events, event_num))
3578 return NULL;
3579
3580 pevent = &g_array_index (week_view->events, EWeekViewEvent, event_num);
3581
3582 return pevent;
3583 }
3584
3585 static void
tooltip_destroy(EWeekView * week_view,GnomeCanvasItem * item)3586 tooltip_destroy (EWeekView *week_view,
3587 GnomeCanvasItem *item)
3588 {
3589 gint event_num;
3590 EWeekViewEvent *pevent;
3591 guint timeout;
3592
3593 e_week_view_check_layout (week_view);
3594
3595 event_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "event-num"));
3596
3597 timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
3598 if (timeout) {
3599 g_source_remove (timeout);
3600 g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
3601 }
3602
3603 pevent = tooltip_get_view_event (week_view, -1, event_num);
3604 if (pevent) {
3605 if (pevent->tooltip && g_object_get_data (G_OBJECT (week_view), "tooltip-window")) {
3606 gtk_widget_destroy (pevent->tooltip);
3607 pevent->tooltip = NULL;
3608 }
3609
3610 g_object_set_data (G_OBJECT (week_view), "tooltip-window", NULL);
3611 }
3612 }
3613
3614 static gboolean
e_week_view_handle_tooltip_timeout(gpointer user_data)3615 e_week_view_handle_tooltip_timeout (gpointer user_data)
3616 {
3617 ECalendarViewEventData *data = user_data;
3618
3619 g_return_val_if_fail (data != NULL, FALSE);
3620
3621 return e_calendar_view_get_tooltips (data);
3622 }
3623
3624 static void
e_week_view_destroy_tooltip_timeout_data(gpointer ptr)3625 e_week_view_destroy_tooltip_timeout_data (gpointer ptr)
3626 {
3627 ECalendarViewEventData *data = ptr;
3628
3629 if (data) {
3630 g_object_set_data ((GObject *) data->cal_view, "tooltip-timeout", NULL);
3631 g_object_unref (data->cal_view);
3632 g_free (data);
3633 }
3634 }
3635
3636 static gboolean
tooltip_event_cb(GnomeCanvasItem * item,GdkEvent * event,EWeekView * view)3637 tooltip_event_cb (GnomeCanvasItem *item,
3638 GdkEvent *event,
3639 EWeekView *view)
3640 {
3641 gint event_num;
3642 guint event_button = 0;
3643 EWeekViewEvent *pevent;
3644
3645 e_week_view_check_layout (view);
3646
3647 event_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "event-num"));
3648 pevent = tooltip_get_view_event (view, -1, event_num);
3649
3650 switch (event->type) {
3651 case GDK_ENTER_NOTIFY:
3652 if (view->editing_event_num == -1) {
3653 ECalendarViewEventData *data;
3654
3655 g_return_val_if_fail (pevent != NULL, FALSE);
3656 data = g_malloc (sizeof (ECalendarViewEventData));
3657
3658 pevent->x = ((GdkEventCrossing *) event)->x_root;
3659 pevent->y = ((GdkEventCrossing *) event)->y_root;
3660 pevent->tooltip = NULL;
3661
3662 data->cal_view = E_CALENDAR_VIEW (g_object_ref (view));
3663 data->day = -1;
3664 data->event_num = event_num;
3665 data->get_view_event = (ECalendarViewEvent * (*)(ECalendarView *, int, gint)) tooltip_get_view_event;
3666 pevent->timeout = e_named_timeout_add_full (
3667 G_PRIORITY_DEFAULT, 500,
3668 e_week_view_handle_tooltip_timeout,
3669 data, e_week_view_destroy_tooltip_timeout_data);
3670 g_object_set_data ((GObject *) view, "tooltip-timeout", GUINT_TO_POINTER (pevent->timeout));
3671
3672 return TRUE;
3673 } else {
3674 return FALSE;
3675 }
3676 case GDK_MOTION_NOTIFY:
3677 g_return_val_if_fail (pevent != NULL, FALSE);
3678
3679 pevent->x = ((GdkEventMotion *) event)->x_root;
3680 pevent->y = ((GdkEventMotion *) event)->y_root;
3681 pevent->tooltip = (GtkWidget *) g_object_get_data (G_OBJECT (view), "tooltip-window");
3682
3683 if (pevent->tooltip) {
3684 e_calendar_view_move_tip (pevent->tooltip, pevent->x, pevent->y);
3685 }
3686
3687 return TRUE;
3688 case GDK_LEAVE_NOTIFY:
3689 case GDK_KEY_PRESS:
3690 case GDK_BUTTON_PRESS:
3691 tooltip_destroy (view, item);
3692 if (gdk_event_get_button (event, &event_button) && event_button == 1)
3693 e_week_view_set_popup_event (view, event_num);
3694
3695 return FALSE;
3696 default:
3697 return FALSE;
3698 }
3699 }
3700
3701 static gchar *
dup_comp_summary(ECalClient * client,ICalComponent * icomp)3702 dup_comp_summary (ECalClient *client,
3703 ICalComponent *icomp)
3704 {
3705 const gchar *location;
3706 gchar *summary, *my_summary;
3707
3708 g_return_val_if_fail (icomp != NULL, NULL);
3709
3710 my_summary = e_calendar_view_dup_component_summary (icomp);
3711
3712 location = i_cal_component_get_location (icomp);
3713
3714 if (location && *location) {
3715 /* Translators: the first '%s' is replaced with a component summary;
3716 the second '%s' is replaced with an event location.
3717 Example: "Meet John Doe (Central Park)" */
3718 summary = g_strdup_printf (C_("SummaryWithLocation", "%s (%s)"), my_summary ? my_summary : "", location);
3719
3720 g_free (my_summary);
3721 } else {
3722 summary = my_summary;
3723 }
3724
3725 return summary;
3726 }
3727
3728 static void
e_week_view_on_text_item_notify_text_width(GObject * etext,GParamSpec * param,gpointer user_data)3729 e_week_view_on_text_item_notify_text_width (GObject *etext,
3730 GParamSpec *param,
3731 gpointer user_data)
3732 {
3733 EWeekView *week_view = user_data;
3734 gint event_num = 0, span_num;
3735
3736 g_return_if_fail (E_IS_WEEK_VIEW (week_view));
3737
3738 if (!e_week_view_find_event_from_item (week_view, GNOME_CANVAS_ITEM (etext), &event_num, &span_num))
3739 return;
3740
3741 e_week_view_reshape_event_span (week_view, event_num, span_num);
3742 }
3743
3744 static void
e_week_view_reshape_event_span(EWeekView * week_view,gint event_num,gint span_num)3745 e_week_view_reshape_event_span (EWeekView *week_view,
3746 gint event_num,
3747 gint span_num)
3748 {
3749 ECalendarView *cal_view;
3750 ECalModel *model;
3751 ESourceRegistry *registry;
3752 EWeekViewEvent *event;
3753 EWeekViewEventSpan *span;
3754 gint span_x, span_y, span_w, num_icons, icons_width, time_width;
3755 gint min_text_x, max_text_w, width;
3756 gboolean show_icons = TRUE, use_max_width = FALSE;
3757 gboolean one_day_event;
3758 ECalComponent *comp;
3759 gdouble text_x, text_y, text_w, text_h;
3760 gchar *text, *end_of_line;
3761 gint line_len, text_width;
3762 PangoContext *pango_context;
3763 PangoFontMetrics *font_metrics;
3764 PangoLayout *layout;
3765
3766 cal_view = E_CALENDAR_VIEW (week_view);
3767 model = e_calendar_view_get_model (cal_view);
3768
3769 registry = e_cal_model_get_registry (model);
3770
3771 if (!is_array_index_in_bounds (week_view->events, event_num))
3772 return;
3773
3774 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
3775
3776 if (!is_comp_data_valid (event))
3777 return;
3778
3779 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
3780 return;
3781
3782 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
3783 event->spans_index + span_num);
3784 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
3785 if (!comp)
3786 return;
3787
3788 one_day_event = e_week_view_is_one_day_event (week_view, event_num);
3789
3790 /* If the span will not be visible destroy the canvas items and
3791 * return. */
3792 if (!e_week_view_get_span_position (week_view, event_num, span_num,
3793 &span_x, &span_y, &span_w)) {
3794 if (span->background_item)
3795 g_object_run_dispose (G_OBJECT (span->background_item));
3796 if (span->text_item)
3797 g_object_run_dispose (G_OBJECT (span->text_item));
3798 span->background_item = NULL;
3799 span->text_item = NULL;
3800
3801 g_object_unref (comp);
3802 return;
3803 }
3804
3805 /* Set up Pango prerequisites */
3806 pango_context = gtk_widget_get_pango_context (GTK_WIDGET (week_view));
3807 font_metrics = pango_context_get_metrics (
3808 pango_context, NULL,
3809 pango_context_get_language (pango_context));
3810 layout = pango_layout_new (pango_context);
3811
3812 /* If we are editing a long event we don't show the icons and the EText
3813 * item uses the maximum width available. */
3814 if (!one_day_event && week_view->editing_event_num == event_num
3815 && week_view->editing_span_num == span_num) {
3816 show_icons = FALSE;
3817 use_max_width = TRUE;
3818 } else if (e_week_view_get_multi_week_view (week_view)) {
3819 show_icons = e_week_view_get_show_icons_month_view (week_view);
3820 }
3821
3822 /* Calculate how many icons we need to show. */
3823 num_icons = 0;
3824 if (show_icons) {
3825 if (e_cal_component_has_alarms (comp))
3826 num_icons++;
3827 if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp))
3828 num_icons++;
3829 if (e_cal_component_has_attachments (comp))
3830 num_icons++;
3831 if (e_cal_component_has_attendees (comp))
3832 num_icons++;
3833 if (event->different_timezone)
3834 num_icons++;
3835 num_icons += cal_comp_util_get_n_icons (comp, NULL);
3836 }
3837
3838 /* Create the background canvas item if necessary. */
3839 if (!span->background_item) {
3840 span->background_item =
3841 gnome_canvas_item_new (
3842 GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root),
3843 e_week_view_event_item_get_type (),
3844 NULL);
3845 }
3846
3847 g_object_set_data ((GObject *) span->background_item, "event-num", GINT_TO_POINTER (event_num));
3848 g_signal_connect (
3849 span->background_item, "event",
3850 G_CALLBACK (tooltip_event_cb), week_view);
3851
3852 gnome_canvas_item_set (
3853 span->background_item,
3854 "event_num", event_num,
3855 "span_num", span_num,
3856 NULL);
3857
3858 /* Create the text item if necessary. */
3859 if (!span->text_item) {
3860 gchar *summary;
3861 GdkColor color;
3862
3863 color = e_week_view_get_text_color (week_view, event);
3864 summary = dup_comp_summary (event->comp_data->client, event->comp_data->icalcomp);
3865
3866 span->text_item =
3867 gnome_canvas_item_new (
3868 GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root),
3869 e_text_get_type (),
3870 "clip", TRUE,
3871 "max_lines", 1,
3872 "editable", TRUE,
3873 "text", summary ? summary : "",
3874 "use_ellipsis", TRUE,
3875 "fill_color_gdk", &color,
3876 "im_context", E_CANVAS (week_view->main_canvas)->im_context,
3877 NULL);
3878
3879 g_free (summary);
3880
3881 if (e_cal_util_component_has_attendee (event->comp_data->icalcomp))
3882 set_style_from_attendee (event, span, registry);
3883 else if (i_cal_component_get_status (event->comp_data->icalcomp) == I_CAL_STATUS_CANCELLED)
3884 gnome_canvas_item_set (span->text_item, "strikeout", TRUE, NULL);
3885
3886 g_signal_connect (
3887 span->text_item, "event",
3888 G_CALLBACK (e_week_view_on_text_item_event), week_view);
3889 g_signal_connect (
3890 span->text_item, "notify::text-width",
3891 G_CALLBACK (e_week_view_on_text_item_notify_text_width), week_view);
3892 g_signal_emit_by_name (
3893 G_OBJECT (week_view),
3894 "event_added", event);
3895
3896 }
3897
3898 g_object_set_data (G_OBJECT (span->text_item), "event-num", GINT_TO_POINTER (event_num));
3899
3900 /* Calculate the position of the text item.
3901 * For events < 1 day it starts after the times & icons and ends at the
3902 * right edge of the span.
3903 * For events >= 1 day we need to determine whether times are shown at
3904 * the start and end of the span, then try to center the text item with
3905 * the icons in the middle, but making sure we don't go over the times.
3906 */
3907
3908 /* Calculate the space necessary to display a time, e.g. "13:00". */
3909 time_width = e_week_view_get_time_string_width (week_view);
3910
3911 /* Calculate the space needed for the icons. */
3912 if (num_icons > 0) {
3913 icons_width = (E_WEEK_VIEW_ICON_WIDTH + E_WEEK_VIEW_ICON_X_PAD)
3914 * num_icons - E_WEEK_VIEW_ICON_X_PAD + E_WEEK_VIEW_ICON_R_PAD;
3915 } else {
3916 icons_width = 0;
3917 }
3918
3919 /* The y position and height are the same for both event types. */
3920 text_y = span_y + E_WEEK_VIEW_EVENT_BORDER_HEIGHT
3921 + E_WEEK_VIEW_EVENT_TEXT_Y_PAD;
3922
3923 text_h =
3924 PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) +
3925 PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics));
3926
3927 if (one_day_event) {
3928 gboolean hide_end_time;
3929
3930 hide_end_time = event->start_minute == event->end_minute;
3931
3932 /* Note that 1-day events don't have a border. Although we
3933 * still use the border height to position the events
3934 * vertically so they still line up neatly (see above),
3935 * we don't use the border width or edge padding at all. */
3936 text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD;
3937
3938 switch (week_view->time_format) {
3939 case E_WEEK_VIEW_TIME_BOTH_SMALL_MIN:
3940 case E_WEEK_VIEW_TIME_BOTH:
3941 /* These have 2 time strings with a small space between
3942 * them and some space before the EText item. */
3943 text_x += time_width * (hide_end_time ? 1 : 2)
3944 + (hide_end_time ? 0 : E_WEEK_VIEW_EVENT_TIME_SPACING)
3945 + E_WEEK_VIEW_EVENT_TIME_X_PAD;
3946 break;
3947 case E_WEEK_VIEW_TIME_START_SMALL_MIN:
3948 case E_WEEK_VIEW_TIME_START:
3949 /* These have just 1 time string with some space
3950 * before the EText item. */
3951 text_x += time_width + E_WEEK_VIEW_EVENT_TIME_X_PAD;
3952 break;
3953 case E_WEEK_VIEW_TIME_NONE:
3954 break;
3955 }
3956
3957 /* The icons_width includes space on the right of the icons. */
3958 text_x += icons_width;
3959
3960 /* The width of the EText item extends right to the edge of the
3961 * event, just inside the border. */
3962 text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD - text_x;
3963
3964 } else {
3965 if (use_max_width) {
3966 /* When we are editing the event we use all the
3967 * available width. */
3968 text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD
3969 + E_WEEK_VIEW_EVENT_BORDER_WIDTH
3970 + E_WEEK_VIEW_EVENT_EDGE_X_PAD;
3971 text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD
3972 - E_WEEK_VIEW_EVENT_BORDER_WIDTH
3973 - E_WEEK_VIEW_EVENT_EDGE_X_PAD - text_x;
3974 } else {
3975 gdouble item_text_width = 0.0;
3976
3977 g_object_get (span->text_item, "text-width", &item_text_width, NULL);
3978 text_width = (gint) item_text_width;
3979
3980 if (text_width <= 0) {
3981 text = NULL;
3982 /* Get the width of the text of the event. This is a
3983 * bit of a hack. It would be better if EText could
3984 * tell us this. */
3985 g_object_get (span->text_item, "text", &text, NULL);
3986 text_width = 0;
3987 if (text) {
3988 /* It should only have one line of text in it.
3989 * I'm not sure we need this any more. */
3990 end_of_line = strchr (text, '\n');
3991 if (end_of_line)
3992 line_len = end_of_line - text;
3993 else
3994 line_len = strlen (text);
3995
3996 pango_layout_set_text (layout, text, line_len);
3997 pango_layout_get_pixel_size (layout, &text_width, NULL);
3998 g_free (text);
3999 }
4000 }
4001
4002 /* Add on the width of the icons and find the default
4003 * position, which centers the icons + text. */
4004 width = text_width + icons_width;
4005 text_x = span_x + (span_w - width) / 2;
4006
4007 /* Now calculate the left-most valid position, and make
4008 * sure we don't go to the left of that. */
4009 min_text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD
4010 + E_WEEK_VIEW_EVENT_BORDER_WIDTH
4011 + E_WEEK_VIEW_EVENT_EDGE_X_PAD;
4012 /* See if we will want to display the start time, and
4013 * if so take that into account. */
4014 if (event->start > week_view->day_starts[span->start_day])
4015 min_text_x += time_width
4016 + E_WEEK_VIEW_EVENT_TIME_X_PAD;
4017
4018 /* Now make sure we don't go to the left of the minimum
4019 * position. */
4020 text_x = MAX (text_x, min_text_x);
4021
4022 /* Now calculate the largest valid width, using the
4023 * calculated x position, and make sure we don't
4024 * exceed that. */
4025 max_text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD
4026 - E_WEEK_VIEW_EVENT_BORDER_WIDTH
4027 - E_WEEK_VIEW_EVENT_EDGE_X_PAD - text_x;
4028 if (event->end < week_view->day_starts[span->start_day
4029 + span->num_days])
4030 max_text_w -= time_width
4031 + E_WEEK_VIEW_EVENT_TIME_X_PAD;
4032
4033 text_w = MIN (width, max_text_w);
4034
4035 /* Now take out the space for the icons. */
4036 text_x += icons_width;
4037 text_w -= icons_width;
4038 }
4039 }
4040
4041 /* Make sure we don't try to use a negative width. */
4042 text_w = MAX (text_w, 0);
4043
4044 gnome_canvas_item_set (
4045 span->text_item,
4046 "clip_width", (gdouble) text_w,
4047 "clip_height", (gdouble) text_h,
4048 NULL);
4049 e_canvas_item_move_absolute (span->text_item, text_x, text_y);
4050 gnome_canvas_item_request_update (span->background_item);
4051
4052 g_object_unref (comp);
4053 g_object_unref (layout);
4054 pango_font_metrics_unref (font_metrics);
4055 }
4056
4057 gboolean
e_week_view_start_editing_event(EWeekView * week_view,gint event_num,gint span_num,gchar * initial_text)4058 e_week_view_start_editing_event (EWeekView *week_view,
4059 gint event_num,
4060 gint span_num,
4061 gchar *initial_text)
4062 {
4063 EWeekViewEvent *event;
4064 EWeekViewEventSpan *span;
4065 ETextEventProcessor *event_processor = NULL;
4066 ETextEventProcessorCommand command;
4067 ECalModelComponent *comp_data;
4068 const gchar *summary;
4069
4070 /* If we are already editing the event, just return. */
4071 if (event_num == week_view->editing_event_num
4072 && span_num == week_view->editing_span_num)
4073 return TRUE;
4074
4075 if (!is_array_index_in_bounds (week_view->events, event_num))
4076 return FALSE;
4077
4078 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4079
4080 if (!is_comp_data_valid (event))
4081 return FALSE;
4082
4083 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4084 return FALSE;
4085
4086 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
4087 event->spans_index + span_num);
4088
4089 if (e_client_is_readonly (E_CLIENT (event->comp_data->client)) ||
4090 (!initial_text && !e_calendar_view_get_allow_direct_summary_edit (E_CALENDAR_VIEW (week_view))))
4091 return FALSE;
4092
4093 /* If the event is not shown, don't try to edit it. */
4094 if (!span->text_item)
4095 return FALSE;
4096
4097 if (week_view->editing_event_num >= 0) {
4098 EWeekViewEvent *editing;
4099
4100 if (!is_array_index_in_bounds (week_view->events, week_view->editing_event_num))
4101 return FALSE;
4102
4103 editing = &g_array_index (week_view->events, EWeekViewEvent, week_view->editing_event_num);
4104
4105 /* do not change to other part of same component - the event is spread into more days */
4106 if (editing && editing->comp_data == event->comp_data)
4107 return FALSE;
4108 }
4109
4110 summary = i_cal_component_get_summary (event->comp_data->icalcomp);
4111 if (!summary)
4112 summary = "";
4113
4114 gnome_canvas_item_set (
4115 span->text_item,
4116 "text", initial_text ? initial_text : summary,
4117 NULL);
4118
4119 /* Save the comp_data value because we use that as our invariant */
4120 comp_data = event->comp_data;
4121
4122 e_canvas_item_grab_focus (span->text_item, TRUE);
4123
4124 /* If the above focus caused things to redraw, then find the
4125 * the event and the span again */
4126 if (event_num < week_view->events->len)
4127 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4128
4129 if (event_num >= week_view->events->len || event->comp_data != comp_data) {
4130 /* When got in because of other comp_data, then be sure we go through all events */
4131 event_num = week_view->events->len;
4132
4133 /* Unfocussing can cause a removal but not a new
4134 * addition so just run backwards through the
4135 * events */
4136 for (event_num--; event_num >= 0; event_num--) {
4137 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4138 if (event->comp_data == comp_data)
4139 break;
4140 }
4141 g_return_val_if_fail (event_num >= 0, FALSE);
4142 }
4143
4144 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4145 return FALSE;
4146
4147 span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num);
4148
4149 /* Try to move the cursor to the end of the text. */
4150 g_object_get (span->text_item, "event_processor", &event_processor, NULL);
4151 if (event_processor) {
4152 command.action = E_TEP_MOVE;
4153 command.position = E_TEP_END_OF_BUFFER;
4154 g_signal_emit_by_name (
4155 event_processor,
4156 "command", &command);
4157 }
4158 return TRUE;
4159 }
4160
4161 /* This stops any current edit. */
4162 void
e_week_view_stop_editing_event(EWeekView * week_view)4163 e_week_view_stop_editing_event (EWeekView *week_view)
4164 {
4165 GtkWidget *toplevel;
4166
4167 /* Check we are editing an event. */
4168 if (week_view->editing_event_num == -1)
4169 return;
4170
4171 /* Set focus to the toplevel so the item loses focus. */
4172 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (week_view));
4173 if (toplevel && GTK_IS_WINDOW (toplevel))
4174 gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
4175 }
4176
4177 /* Cancels the current edition by resetting the appointment's text to its original value */
4178 static void
cancel_editing(EWeekView * week_view)4179 cancel_editing (EWeekView *week_view)
4180 {
4181 gint event_num, span_num;
4182 EWeekViewEvent *event;
4183 EWeekViewEventSpan *span;
4184 const gchar *summary;
4185
4186 event_num = week_view->editing_event_num;
4187 span_num = week_view->editing_span_num;
4188
4189 g_return_if_fail (event_num != -1);
4190
4191 if (!is_array_index_in_bounds (week_view->events, event_num))
4192 return;
4193
4194 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4195
4196 if (!is_comp_data_valid (event))
4197 return;
4198
4199 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4200 return;
4201
4202 span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num);
4203
4204 /* Reset the text to what was in the component */
4205
4206 summary = i_cal_component_get_summary (event->comp_data->icalcomp);
4207 g_object_set (span->text_item, "text", summary ? summary : "", NULL);
4208
4209 /* Stop editing */
4210 e_week_view_stop_editing_event (week_view);
4211 }
4212
4213 static gboolean
e_week_view_on_text_item_event(GnomeCanvasItem * item,GdkEvent * gdk_event,EWeekView * week_view)4214 e_week_view_on_text_item_event (GnomeCanvasItem *item,
4215 GdkEvent *gdk_event,
4216 EWeekView *week_view)
4217 {
4218 EWeekViewEvent *event;
4219 gint event_num, span_num;
4220 gint nevent;
4221 EWeekViewEvent *pevent;
4222 guint event_button = 0;
4223 guint event_keyval = 0;
4224 gdouble event_x_root = 0;
4225 gdouble event_y_root = 0;
4226
4227 e_week_view_check_layout (week_view);
4228
4229 nevent = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "event-num"));
4230 pevent = tooltip_get_view_event (week_view, -1, nevent);
4231
4232 switch (gdk_event->type) {
4233 case GDK_KEY_PRESS:
4234 tooltip_destroy (week_view, item);
4235 gdk_event_get_keyval (gdk_event, &event_keyval);
4236
4237 if (!E_TEXT (item)->preedit_len && (event_keyval == GDK_KEY_Return || event_keyval == GDK_KEY_KP_Enter)) {
4238 /* We set the keyboard focus to the EDayView, so the
4239 * EText item loses it and stops the edit. */
4240 gtk_widget_grab_focus (GTK_WIDGET (week_view));
4241
4242 /* Stop the signal last or we will also stop any
4243 * other events getting to the EText item. */
4244 g_signal_stop_emission_by_name (item, "event");
4245 return TRUE;
4246 } else if (event_keyval == GDK_KEY_Escape) {
4247 cancel_editing (week_view);
4248 g_signal_stop_emission_by_name (item, "event");
4249 /* focus should go to week view when stop editing */
4250 gtk_widget_grab_focus (GTK_WIDGET (week_view));
4251 return TRUE;
4252 }
4253 break;
4254 case GDK_2BUTTON_PRESS:
4255 if (!e_week_view_find_event_from_item (week_view, item,
4256 &event_num, &span_num))
4257 return FALSE;
4258
4259 if (!is_array_index_in_bounds (week_view->events, event_num))
4260 return FALSE;
4261
4262 event = &g_array_index (week_view->events, EWeekViewEvent,
4263 event_num);
4264
4265 if (!is_comp_data_valid (event))
4266 return FALSE;
4267
4268 /* if we started to editing new item on the canvas, then do not open editing dialog until it's saved,
4269 * because the save of the event recalculates event numbers and you can edit different one */
4270 if (event->comp_data->is_new_component)
4271 return TRUE;
4272
4273 e_calendar_view_edit_appointment (
4274 E_CALENDAR_VIEW (week_view),
4275 event->comp_data->client,
4276 event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT);
4277
4278 g_signal_stop_emission_by_name (item, "event");
4279 return TRUE;
4280 case GDK_BUTTON_PRESS:
4281 tooltip_destroy (week_view, item);
4282 if (!e_week_view_find_event_from_item (week_view, item,
4283 &event_num, &span_num))
4284 return FALSE;
4285
4286 gdk_event_get_button (gdk_event, &event_button);
4287 if (event_button == 3) {
4288 EWeekViewEvent *e;
4289
4290 if (E_TEXT (item)->editing) {
4291 e_week_view_stop_editing_event (week_view);
4292 gtk_widget_grab_focus (GTK_WIDGET (week_view));
4293 return FALSE;
4294 }
4295
4296 if (!is_array_index_in_bounds (week_view->events, event_num))
4297 return FALSE;
4298
4299 e = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4300
4301 if (!gtk_widget_has_focus (GTK_WIDGET (week_view)))
4302 gtk_widget_grab_focus (GTK_WIDGET (week_view));
4303
4304 e_week_view_set_selected_time_range_visible (week_view, e->start, e->end);
4305
4306 e_week_view_show_popup_menu (
4307 week_view, gdk_event, event_num);
4308
4309 g_signal_stop_emission_by_name (
4310 item->canvas, "button_press_event");
4311 return TRUE;
4312 }
4313
4314 if (event_button != 3) {
4315 week_view->pressed_event_num = event_num;
4316 week_view->pressed_span_num = span_num;
4317 e_week_view_set_popup_event (week_view, week_view->pressed_event_num);
4318 }
4319
4320 /* Only let the EText handle the event while editing. */
4321 if (!E_TEXT (item)->editing) {
4322 gdouble event_x_win = 0;
4323 gdouble event_y_win = 0;
4324
4325 g_signal_stop_emission_by_name (item, "event");
4326
4327 gdk_event_get_coords (
4328 gdk_event, &event_x_win, &event_y_win);
4329
4330 week_view->drag_event_x = (gint) event_x_win;
4331 week_view->drag_event_y = (gint) event_y_win;
4332
4333 /* FIXME: Remember the day offset from the start of
4334 * the event, for DnD. */
4335
4336 return TRUE;
4337 }
4338 break;
4339 case GDK_BUTTON_RELEASE:
4340 if (!E_TEXT (item)->editing) {
4341 /* This shouldn't ever happen. */
4342 if (!e_week_view_find_event_from_item (week_view,
4343 item,
4344 &event_num,
4345 &span_num))
4346 return FALSE;
4347
4348 if (week_view->pressed_event_num != -1
4349 && week_view->pressed_event_num == event_num
4350 && week_view->pressed_span_num == span_num) {
4351 e_week_view_start_editing_event (
4352 week_view,
4353 event_num,
4354 span_num,
4355 NULL);
4356 week_view->pressed_event_num = -1;
4357 }
4358
4359 /* Stop the signal last or we will also stop any
4360 * other events getting to the EText item. */
4361 g_signal_stop_emission_by_name (item, "event");
4362 return TRUE;
4363 }
4364 week_view->pressed_event_num = -1;
4365 break;
4366 case GDK_ENTER_NOTIFY:
4367 {
4368 ECalendarViewEventData *data;
4369 gint nspan;
4370
4371 if (week_view->editing_event_num != -1
4372 || !e_week_view_find_event_from_item (week_view, item, &nevent, &nspan))
4373 return FALSE;
4374
4375 g_object_set_data ((GObject *) item, "event-num", GINT_TO_POINTER (nevent));
4376
4377 pevent = tooltip_get_view_event (week_view, -1, nevent);
4378 g_return_val_if_fail (pevent != NULL, FALSE);
4379
4380 data = g_malloc (sizeof (ECalendarViewEventData));
4381
4382 gdk_event_get_root_coords (
4383 gdk_event, &event_x_root, &event_y_root);
4384
4385 pevent->x = (gint) event_x_root;
4386 pevent->y = (gint) event_y_root;
4387 pevent->tooltip = NULL;
4388
4389 data->cal_view = E_CALENDAR_VIEW (g_object_ref (week_view));
4390 data->day = -1;
4391 data->event_num = nevent;
4392 data->get_view_event = (ECalendarViewEvent * (*)(ECalendarView *, int, gint)) tooltip_get_view_event;
4393 pevent->timeout = e_named_timeout_add_full (
4394 G_PRIORITY_DEFAULT, 500,
4395 e_week_view_handle_tooltip_timeout,
4396 data, e_week_view_destroy_tooltip_timeout_data);
4397 g_object_set_data ((GObject *) week_view, "tooltip-timeout", GUINT_TO_POINTER (pevent->timeout));
4398
4399 return TRUE;
4400 }
4401 case GDK_LEAVE_NOTIFY:
4402 tooltip_destroy (week_view, item);
4403
4404 return FALSE;
4405 case GDK_MOTION_NOTIFY:
4406 g_return_val_if_fail (pevent != NULL, FALSE);
4407
4408 gdk_event_get_root_coords (
4409 gdk_event, &event_x_root, &event_y_root);
4410
4411 pevent->x = (gint) event_x_root;
4412 pevent->y = (gint) event_y_root;
4413 pevent->tooltip = (GtkWidget *) g_object_get_data (G_OBJECT (week_view), "tooltip-window");
4414
4415 if (pevent->tooltip) {
4416 e_calendar_view_move_tip (pevent->tooltip, pevent->x, pevent->y);
4417 }
4418 return TRUE;
4419 case GDK_FOCUS_CHANGE:
4420 if (gdk_event->focus_change.in) {
4421 e_week_view_on_editing_started (week_view, item);
4422 } else {
4423 e_week_view_on_editing_stopped (week_view, item);
4424 }
4425
4426 return FALSE;
4427 default:
4428 break;
4429 }
4430
4431 return FALSE;
4432 }
4433
4434 static gboolean
e_week_view_event_move(ECalendarView * cal_view,ECalViewMoveDirection direction)4435 e_week_view_event_move (ECalendarView *cal_view,
4436 ECalViewMoveDirection direction)
4437 {
4438 EWeekViewEvent *event;
4439 gint event_num, adjust_days, current_start_day, current_end_day;
4440 time_t start_dt, end_dt;
4441 ICalTime *start_time, *end_time;
4442 EWeekView *week_view = E_WEEK_VIEW (cal_view);
4443 gboolean is_all_day = FALSE;
4444
4445 event_num = week_view->editing_event_num;
4446 adjust_days = 0;
4447
4448 /* If no item is being edited, just return. */
4449 if (event_num == -1)
4450 return FALSE;
4451
4452 if (!is_array_index_in_bounds (week_view->events, event_num))
4453 return FALSE;
4454
4455 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4456
4457 if (!is_comp_data_valid (event))
4458 return FALSE;
4459
4460 end_dt = event->end;
4461 start_time = i_cal_component_get_dtstart (event->comp_data->icalcomp);
4462 end_time = i_cal_component_get_dtend (event->comp_data->icalcomp);
4463
4464 if (i_cal_time_is_date (start_time) && i_cal_time_is_date (end_time))
4465 is_all_day = TRUE;
4466
4467 current_end_day = e_week_view_get_day_offset_of_event (week_view,end_dt);
4468
4469 switch (direction) {
4470 case E_CAL_VIEW_MOVE_UP:
4471 adjust_days = e_week_view_get_adjust_days_for_move_up (week_view,current_end_day);
4472 break;
4473 case E_CAL_VIEW_MOVE_DOWN:
4474 adjust_days = e_week_view_get_adjust_days_for_move_down (week_view,current_end_day);
4475 break;
4476 case E_CAL_VIEW_MOVE_LEFT:
4477 adjust_days = e_week_view_get_adjust_days_for_move_left (week_view,current_end_day);
4478 break;
4479 case E_CAL_VIEW_MOVE_RIGHT:
4480 adjust_days = e_week_view_get_adjust_days_for_move_right (week_view,current_end_day);
4481 break;
4482 default:
4483 break;
4484 }
4485
4486 i_cal_time_adjust (start_time, adjust_days, 0, 0, 0);
4487 i_cal_time_adjust (end_time, adjust_days, 0, 0, 0);
4488 start_dt = i_cal_time_as_timet_with_zone (
4489 start_time,
4490 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
4491 end_dt = i_cal_time_as_timet_with_zone (
4492 end_time,
4493 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
4494
4495 g_clear_object (&start_time);
4496 g_clear_object (&end_time);
4497
4498 current_start_day = e_week_view_get_day_offset_of_event (week_view,start_dt);
4499 current_end_day = e_week_view_get_day_offset_of_event (week_view,end_dt);
4500 if (is_all_day)
4501 current_end_day--;
4502
4503 if (current_start_day < 0)
4504 return TRUE;
4505
4506 if (current_end_day >= e_week_view_get_weeks_shown (week_view) * 7)
4507 return TRUE;
4508
4509 e_week_view_change_event_time (week_view, start_dt, end_dt, is_all_day);
4510
4511 return TRUE;
4512 }
4513
4514 static gint
e_week_view_get_day_offset_of_event(EWeekView * week_view,time_t event_time)4515 e_week_view_get_day_offset_of_event (EWeekView *week_view,
4516 time_t event_time)
4517 {
4518 time_t first_day = week_view->day_starts[0];
4519
4520 if (event_time - first_day < 0)
4521 return -1;
4522 else
4523 return (event_time - first_day) / (24 * 60 * 60);
4524 }
4525
4526 void
e_week_view_scroll_a_step(EWeekView * week_view,ECalViewMoveDirection direction)4527 e_week_view_scroll_a_step (EWeekView *week_view,
4528 ECalViewMoveDirection direction)
4529 {
4530 GtkAdjustment *adjustment;
4531 GtkRange *range;
4532 gdouble step_increment;
4533 gdouble page_size;
4534 gdouble new_value;
4535 gdouble lower;
4536 gdouble upper;
4537 gdouble value;
4538
4539 range = GTK_RANGE (week_view->vscrollbar);
4540 adjustment = gtk_range_get_adjustment (range);
4541
4542 step_increment = gtk_adjustment_get_step_increment (adjustment);
4543 page_size = gtk_adjustment_get_page_size (adjustment);
4544 lower = gtk_adjustment_get_lower (adjustment);
4545 upper = gtk_adjustment_get_upper (adjustment);
4546 value = gtk_adjustment_get_value (adjustment);
4547
4548 switch (direction) {
4549 case E_CAL_VIEW_MOVE_UP:
4550 new_value = value - step_increment;
4551 break;
4552 case E_CAL_VIEW_MOVE_DOWN:
4553 new_value = value + step_increment;
4554 break;
4555 case E_CAL_VIEW_MOVE_PAGE_UP:
4556 new_value = value - page_size;
4557 break;
4558 case E_CAL_VIEW_MOVE_PAGE_DOWN:
4559 new_value = value + page_size;
4560 break;
4561 default:
4562 return;
4563 }
4564
4565 new_value = CLAMP (new_value, lower, upper - page_size);
4566 gtk_adjustment_set_value (adjustment, new_value);
4567 }
4568
4569 static void
e_week_view_change_event_time(EWeekView * week_view,time_t start_dt,time_t end_dt,gboolean is_all_day)4570 e_week_view_change_event_time (EWeekView *week_view,
4571 time_t start_dt,
4572 time_t end_dt,
4573 gboolean is_all_day)
4574 {
4575 EWeekViewEvent *event;
4576 gint event_num;
4577 ECalComponent *comp;
4578 ECalComponentDateTime *date;
4579 ECalClient *client;
4580 ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
4581 ICalTimezone *zone;
4582
4583 event_num = week_view->editing_event_num;
4584
4585 /* If no item is being edited, just return. */
4586 if (event_num == -1)
4587 return;
4588
4589 if (!is_array_index_in_bounds (week_view->events, event_num))
4590 return;
4591
4592 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4593
4594 if (!is_comp_data_valid (event))
4595 return;
4596
4597 client = event->comp_data->client;
4598
4599 /* We use a temporary shallow copy of the ico since we don't want to
4600 * change the original ico here. Otherwise we would not detect that
4601 * the event's time had changed in the "update_event" callback. */
4602 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
4603
4604 zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view));
4605
4606 date = e_cal_component_datetime_new_take (i_cal_time_new_from_timet_with_zone (start_dt, is_all_day, zone),
4607 zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
4608 cal_comp_set_dtstart_with_oldzone (client, comp, date);
4609 e_cal_component_datetime_free (date);
4610
4611 date = e_cal_component_datetime_new_take (i_cal_time_new_from_timet_with_zone (end_dt, is_all_day, zone),
4612 zone ? g_strdup (i_cal_timezone_get_tzid (zone)) : NULL);
4613 cal_comp_set_dtend_with_oldzone (client, comp, date);
4614 e_cal_component_datetime_free (date);
4615
4616 e_cal_component_commit_sequence (comp);
4617
4618 g_clear_pointer (&week_view->last_edited_comp_string, g_free);
4619
4620 week_view->last_edited_comp_string = e_cal_component_get_as_string (comp);
4621
4622 if (e_cal_component_has_recurrences (comp)) {
4623 if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
4624 gtk_widget_queue_draw (week_view->main_canvas);
4625 goto out;
4626 }
4627
4628 if (mod == E_CAL_OBJ_MOD_THIS) {
4629 e_cal_component_set_rdates (comp, NULL);
4630 e_cal_component_set_rrules (comp, NULL);
4631 e_cal_component_set_exdates (comp, NULL);
4632 e_cal_component_set_exrules (comp, NULL);
4633 }
4634 } else if (e_cal_component_is_instance (comp))
4635 mod = E_CAL_OBJ_MOD_THIS;
4636
4637 e_cal_component_commit_sequence (comp);
4638
4639 e_cal_ops_modify_component (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)),
4640 client, e_cal_component_get_icalcomponent (comp),
4641 mod, E_CAL_OPS_SEND_FLAG_ASK | E_CAL_OPS_SEND_FLAG_IS_NEW_COMPONENT);
4642
4643 out:
4644 g_object_unref (comp);
4645 }
4646
4647 static void
e_week_view_on_editing_started(EWeekView * week_view,GnomeCanvasItem * item)4648 e_week_view_on_editing_started (EWeekView *week_view,
4649 GnomeCanvasItem *item)
4650 {
4651 gint event_num = -1, span_num = -1;
4652
4653 if (!e_week_view_find_event_from_item (week_view, item,
4654 &event_num, &span_num))
4655 return;
4656
4657 week_view->editing_event_num = event_num;
4658 week_view->editing_span_num = span_num;
4659
4660 /* We need to reshape long events so the whole width is used while
4661 * editing. */
4662 if (!e_week_view_is_one_day_event (week_view, event_num)) {
4663 e_week_view_reshape_event_span (
4664 week_view, event_num, span_num);
4665 }
4666
4667 if (event_num != -1) {
4668 EWeekViewEvent *event;
4669 EWeekViewEventSpan *span;
4670
4671 if (is_array_index_in_bounds (week_view->events, event_num)) {
4672 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4673
4674 if (is_comp_data_valid (event) &&
4675 is_array_index_in_bounds (week_view->spans, event->spans_index + span_num)) {
4676 const gchar *summary;
4677
4678 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
4679 event->spans_index + span_num);
4680
4681 summary = i_cal_component_get_summary (event->comp_data->icalcomp);
4682
4683 gnome_canvas_item_set (
4684 span->text_item,
4685 "text", summary ? summary : "",
4686 NULL);
4687 }
4688 }
4689 }
4690
4691 g_signal_emit_by_name (week_view, "selection_changed");
4692
4693 g_object_notify (G_OBJECT (week_view), "is-editing");
4694 }
4695
4696 static void
e_week_view_on_editing_stopped(EWeekView * week_view,GnomeCanvasItem * item)4697 e_week_view_on_editing_stopped (EWeekView *week_view,
4698 GnomeCanvasItem *item)
4699 {
4700 gint event_num, span_num;
4701 EWeekViewEvent *event;
4702 EWeekViewEventSpan *span;
4703 gchar *text = NULL;
4704 ECalComponent *comp;
4705 ECalComponentText *summary = NULL;
4706 ECalClient *client;
4707 const gchar *uid;
4708 gboolean on_server;
4709
4710 /* Note: the item we are passed here isn't reliable, so we just stop
4711 * the edit of whatever item was being edited. We also receive this
4712 * event twice for some reason. */
4713 event_num = week_view->editing_event_num;
4714 span_num = week_view->editing_span_num;
4715
4716 /* If no item is being edited, just return. */
4717 if (event_num == -1)
4718 return;
4719
4720 if (!is_array_index_in_bounds (week_view->events, event_num))
4721 return;
4722
4723 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4724
4725 if (!is_comp_data_valid (event))
4726 return;
4727
4728 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4729 return;
4730
4731 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
4732 event->spans_index + span_num);
4733
4734 /* Reset the edit fields. */
4735 week_view->editing_event_num = -1;
4736
4737 /* Check that the event is still valid. */
4738 uid = i_cal_component_get_uid (event->comp_data->icalcomp);
4739 if (!uid) {
4740 g_object_notify (G_OBJECT (week_view), "is-editing");
4741 return;
4742 }
4743
4744 text = NULL;
4745 g_object_set (span->text_item, "handle_popup", FALSE, NULL);
4746 g_object_get (span->text_item, "text", &text, NULL);
4747
4748 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (event->comp_data->icalcomp));
4749 if (!comp) {
4750 g_free (text);
4751 g_object_notify (G_OBJECT (week_view), "is-editing");
4752 return;
4753 }
4754
4755 client = event->comp_data->client;
4756 on_server = !event->comp_data->is_new_component;
4757
4758 if (string_is_empty (text) && !on_server) {
4759 uid = e_cal_component_get_uid (comp);
4760 g_signal_handlers_disconnect_by_func (item, e_week_view_on_text_item_event, week_view);
4761 e_week_view_foreach_event_with_uid (week_view, uid,
4762 e_week_view_remove_event_cb, NULL);
4763 week_view->event_destroyed = TRUE;
4764 gtk_widget_queue_draw (week_view->main_canvas);
4765 e_week_view_check_layout (week_view);
4766 goto out;
4767 }
4768
4769 /* Only update the summary if necessary. */
4770 summary = e_cal_component_get_summary (comp);
4771 if (summary && !g_strcmp0 (e_cal_component_text_get_value (summary), text)) {
4772 gchar *summary_txt;
4773
4774 summary_txt = dup_comp_summary (event->comp_data->client, event->comp_data->icalcomp);
4775 g_object_set (span->text_item, "text", summary_txt ? summary_txt : "", NULL);
4776
4777 g_free (summary_txt);
4778
4779 if (!e_week_view_is_one_day_event (week_view, event_num))
4780 e_week_view_reshape_event_span (week_view, event_num, span_num);
4781 } else if ((summary && e_cal_component_text_get_value (summary)) || !string_is_empty (text)) {
4782 ICalComponent *icomp = e_cal_component_get_icalcomponent (comp);
4783
4784 e_cal_component_text_free (summary);
4785 summary = e_cal_component_text_new (text, NULL);
4786
4787 e_cal_component_set_summary (comp, summary);
4788 e_cal_component_commit_sequence (comp);
4789
4790 e_cal_component_text_free (summary);
4791 summary = NULL;
4792
4793 if (!on_server) {
4794 e_cal_ops_create_component (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)), client, icomp,
4795 e_calendar_view_component_created_cb, g_object_ref (week_view), g_object_unref);
4796
4797 /* we remove the object since we either got the update from the server or failed */
4798 e_week_view_remove_event_cb (week_view, event_num, NULL);
4799 } else {
4800 ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
4801
4802 if (e_cal_component_has_recurrences (comp)) {
4803 if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
4804 goto out;
4805 }
4806
4807 if (mod == E_CAL_OBJ_MOD_THIS) {
4808 ECalComponentDateTime *dt;
4809 ICalTime *tt = NULL;
4810 gchar *tzid;
4811
4812 dt = e_cal_component_get_dtstart (comp);
4813 if (dt)
4814 tt = e_cal_component_datetime_get_value (dt);
4815 if (dt && tt && i_cal_time_get_timezone (tt)) {
4816 tt = i_cal_time_new_from_timet_with_zone (
4817 event->comp_data->instance_start,
4818 i_cal_time_is_date (tt),
4819 i_cal_time_get_timezone (tt));
4820 } else {
4821 tt = i_cal_time_new_from_timet_with_zone (
4822 event->comp_data->instance_start,
4823 (dt && tt) ? i_cal_time_is_date (tt) : FALSE,
4824 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
4825 }
4826 tzid = dt ? g_strdup (e_cal_component_datetime_get_tzid (dt)) : NULL;
4827 e_cal_component_datetime_free (dt);
4828
4829 dt = e_cal_component_datetime_new_take (tt, tzid);
4830 e_cal_component_set_dtstart (comp, dt);
4831 e_cal_component_datetime_free (dt);
4832
4833 dt = e_cal_component_get_dtend (comp);
4834 if (dt)
4835 tt = e_cal_component_datetime_get_value (dt);
4836 if (dt && tt && i_cal_time_get_timezone (tt)) {
4837 tt = i_cal_time_new_from_timet_with_zone (
4838 event->comp_data->instance_end,
4839 i_cal_time_is_date (tt),
4840 i_cal_time_get_timezone (tt));
4841 } else {
4842 tt = i_cal_time_new_from_timet_with_zone (
4843 event->comp_data->instance_end,
4844 (dt && tt) ? i_cal_time_is_date (tt) : FALSE,
4845 e_calendar_view_get_timezone (E_CALENDAR_VIEW (week_view)));
4846 }
4847 tzid = dt ? g_strdup (e_cal_component_datetime_get_tzid (dt)) : NULL;
4848 e_cal_component_datetime_free (dt);
4849
4850 dt = e_cal_component_datetime_new_take (tt, tzid);
4851 e_cal_component_set_dtend (comp, dt);
4852 e_cal_component_datetime_free (dt);
4853
4854 e_cal_component_set_rdates (comp, NULL);
4855 e_cal_component_set_rrules (comp, NULL);
4856 e_cal_component_set_exdates (comp, NULL);
4857 e_cal_component_set_exrules (comp, NULL);
4858 }
4859 } else if (e_cal_component_is_instance (comp))
4860 mod = E_CAL_OBJ_MOD_THIS;
4861
4862 e_cal_component_commit_sequence (comp);
4863 e_cal_ops_modify_component (e_calendar_view_get_model (E_CALENDAR_VIEW (week_view)),
4864 client, e_cal_component_get_icalcomponent (comp), mod, E_CAL_OPS_SEND_FLAG_ASK);
4865 }
4866 }
4867
4868 out:
4869
4870 e_cal_component_text_free (summary);
4871 g_free (text);
4872 g_object_unref (comp);
4873
4874 g_signal_emit_by_name (week_view, "selection_changed");
4875
4876 g_object_notify (G_OBJECT (week_view), "is-editing");
4877 }
4878
4879 gboolean
e_week_view_find_event_from_item(EWeekView * week_view,GnomeCanvasItem * item,gint * event_num_return,gint * span_num_return)4880 e_week_view_find_event_from_item (EWeekView *week_view,
4881 GnomeCanvasItem *item,
4882 gint *event_num_return,
4883 gint *span_num_return)
4884 {
4885 EWeekViewEvent *event;
4886 EWeekViewEventSpan *span;
4887 gint event_num, span_num, num_events;
4888
4889 num_events = week_view->events->len;
4890 for (event_num = 0; event_num < num_events; event_num++) {
4891 event = &g_array_index (week_view->events, EWeekViewEvent,
4892 event_num);
4893 for (span_num = 0; span_num < event->num_spans; span_num++) {
4894 if (!is_array_index_in_bounds (week_view->spans, event->spans_index + span_num))
4895 continue;
4896
4897 span = &g_array_index (week_view->spans,
4898 EWeekViewEventSpan,
4899 event->spans_index + span_num);
4900 if (span->text_item == item) {
4901 *event_num_return = event_num;
4902 *span_num_return = span_num;
4903 return TRUE;
4904 }
4905 }
4906 }
4907
4908 return FALSE;
4909 }
4910
4911 /* Finds the index of the event with the given uid.
4912 * Returns TRUE if an event with the uid was found.
4913 * Note that for recurring events there may be several EWeekViewEvents, one
4914 * for each instance, all with the same iCalObject and uid. So only use this
4915 * function if you know the event doesn't recur or you are just checking to
4916 * see if any events with the uid exist. */
4917 static gboolean
e_week_view_find_event_from_uid(EWeekView * week_view,ECalClient * client,const gchar * uid,const gchar * rid,gint * event_num_return)4918 e_week_view_find_event_from_uid (EWeekView *week_view,
4919 ECalClient *client,
4920 const gchar *uid,
4921 const gchar *rid,
4922 gint *event_num_return)
4923 {
4924 EWeekViewEvent *event;
4925 gint event_num, num_events;
4926
4927 *event_num_return = -1;
4928 if (!uid)
4929 return FALSE;
4930
4931 num_events = week_view->events->len;
4932 for (event_num = 0; event_num < num_events; event_num++) {
4933 const gchar *u;
4934 gchar *r = NULL;
4935
4936 event = &g_array_index (week_view->events, EWeekViewEvent,
4937 event_num);
4938
4939 if (!is_comp_data_valid (event))
4940 continue;
4941
4942 if (event->comp_data->client != client)
4943 continue;
4944
4945 u = i_cal_component_get_uid (event->comp_data->icalcomp);
4946 if (u && !strcmp (uid, u)) {
4947 if (rid && *rid) {
4948 r = e_cal_util_component_get_recurid_as_string (event->comp_data->icalcomp);
4949 if (!r || !*r || strcmp (rid, r) != 0) {
4950 g_free (r);
4951 continue;
4952 }
4953
4954 g_free (r);
4955 }
4956
4957 *event_num_return = event_num;
4958 return TRUE;
4959 }
4960 }
4961
4962 return FALSE;
4963 }
4964
4965 gboolean
e_week_view_is_one_day_event(EWeekView * week_view,gint event_num)4966 e_week_view_is_one_day_event (EWeekView *week_view,
4967 gint event_num)
4968 {
4969 EWeekViewEvent *event;
4970 EWeekViewEventSpan *span;
4971
4972 if (!is_array_index_in_bounds (week_view->events, event_num))
4973 return FALSE;
4974
4975 event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
4976 if (event->num_spans != 1)
4977 return FALSE;
4978
4979 if (!is_array_index_in_bounds (week_view->spans, event->spans_index))
4980 return FALSE;
4981
4982 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
4983 event->spans_index);
4984
4985 if (event->start == week_view->day_starts[span->start_day]
4986 && event->end == week_view->day_starts[span->start_day + 1])
4987 return FALSE;
4988
4989 if (span->num_days == 1
4990 && event->start >= week_view->day_starts[span->start_day]
4991 && event->end <= week_view->day_starts[span->start_day + 1])
4992 return TRUE;
4993
4994 return FALSE;
4995 }
4996
4997 static void
e_week_view_cursor_key_up(EWeekView * week_view)4998 e_week_view_cursor_key_up (EWeekView *week_view)
4999 {
5000 EWeekViewClass *week_view_class;
5001
5002 week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
5003 g_return_if_fail (week_view_class->cursor_key_up != NULL);
5004
5005 week_view_class->cursor_key_up (week_view);
5006 }
5007
5008 static void
e_week_view_cursor_key_down(EWeekView * week_view)5009 e_week_view_cursor_key_down (EWeekView *week_view)
5010 {
5011 EWeekViewClass *week_view_class;
5012
5013 week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
5014 g_return_if_fail (week_view_class->cursor_key_down != NULL);
5015
5016 week_view_class->cursor_key_down (week_view);
5017 }
5018
5019 static void
e_week_view_cursor_key_left(EWeekView * week_view)5020 e_week_view_cursor_key_left (EWeekView *week_view)
5021 {
5022 EWeekViewClass *week_view_class;
5023
5024 week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
5025 g_return_if_fail (week_view_class->cursor_key_left != NULL);
5026
5027 week_view_class->cursor_key_left (week_view);
5028 }
5029
5030 static void
e_week_view_cursor_key_right(EWeekView * week_view)5031 e_week_view_cursor_key_right (EWeekView *week_view)
5032 {
5033 EWeekViewClass *week_view_class;
5034
5035 week_view_class = E_WEEK_VIEW_GET_CLASS (week_view);
5036 g_return_if_fail (week_view_class->cursor_key_right != NULL);
5037
5038 week_view_class->cursor_key_right (week_view);
5039 }
5040
5041 static gboolean
e_week_view_do_key_press(GtkWidget * widget,GdkEventKey * event)5042 e_week_view_do_key_press (GtkWidget *widget,
5043 GdkEventKey *event)
5044 {
5045 EWeekView *week_view;
5046 gchar *initial_text;
5047 guint keyval;
5048 gboolean stop_emission;
5049
5050 g_return_val_if_fail (widget != NULL, FALSE);
5051 g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
5052 g_return_val_if_fail (event != NULL, FALSE);
5053
5054 week_view = E_WEEK_VIEW (widget);
5055 keyval = event->keyval;
5056
5057 /* The Escape key aborts a resize operation. */
5058 #if 0
5059 if (week_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) {
5060 if (event->keyval == GDK_KEY_Escape) {
5061 e_week_view_abort_resize (week_view, event->time);
5062 }
5063 return FALSE;
5064 }
5065 #endif
5066
5067 /* Handle the cursor keys for moving the selection */
5068 stop_emission = FALSE;
5069 if (!(event->state & GDK_SHIFT_MASK)
5070 && !(event->state & GDK_MOD1_MASK)) {
5071 stop_emission = TRUE;
5072 switch (keyval) {
5073 case GDK_KEY_Page_Up:
5074 if (!e_week_view_get_multi_week_view (week_view))
5075 e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_UP);
5076 else
5077 e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_PAGE_UP);
5078 break;
5079 case GDK_KEY_Page_Down:
5080 if (!e_week_view_get_multi_week_view (week_view))
5081 e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_DOWN);
5082 else
5083 e_week_view_scroll_a_step (week_view, E_CAL_VIEW_MOVE_PAGE_DOWN);
5084 break;
5085 case GDK_KEY_Up:
5086 e_week_view_cursor_key_up (week_view);
5087 break;
5088 case GDK_KEY_Down:
5089 e_week_view_cursor_key_down (week_view);
5090 break;
5091 case GDK_KEY_Left:
5092 e_week_view_cursor_key_left (week_view);
5093 break;
5094 case GDK_KEY_Right:
5095 e_week_view_cursor_key_right (week_view);
5096 break;
5097 default:
5098 stop_emission = FALSE;
5099 break;
5100 }
5101 }
5102 if (stop_emission)
5103 return TRUE;
5104
5105 /*Navigation through days with arrow keys*/
5106 if (((event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK)
5107 &&((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
5108 &&((event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK)) {
5109 if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up)
5110 return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_UP);
5111 else if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
5112 return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_DOWN);
5113 else if (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left)
5114 return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_LEFT);
5115 else if (keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right)
5116 return e_week_view_event_move ((ECalendarView *) week_view, E_CAL_VIEW_MOVE_RIGHT);
5117 }
5118
5119 if (week_view->selection_start_day == -1)
5120 return FALSE;
5121
5122 /* We only want to start an edit with a return key or a simple
5123 * character. */
5124 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
5125 initial_text = NULL;
5126 } else if (((event->keyval >= 0x20) && (event->keyval <= 0xFF)
5127 && (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
5128 || (event->length == 0)
5129 || (event->keyval == GDK_KEY_Tab)
5130 || (event->keyval == GDK_KEY_Escape)
5131 || (event->keyval == GDK_KEY_Delete)
5132 || (event->keyval == GDK_KEY_KP_Delete)) {
5133 return FALSE;
5134 } else
5135 initial_text = e_utf8_from_gtk_event_key (widget, event->keyval, event->string);
5136
5137 e_week_view_add_new_event_in_selected_range (week_view, initial_text, FALSE);
5138
5139 g_free (initial_text);
5140
5141 return TRUE;
5142 }
5143
5144 static gint
e_week_view_get_adjust_days_for_move_up(EWeekView * week_view,gint current_day)5145 e_week_view_get_adjust_days_for_move_up (EWeekView *week_view,
5146 gint current_day)
5147 {
5148 return e_week_view_get_multi_week_view (week_view) ? -7 : 0;
5149 }
5150
5151 static gint
e_week_view_get_adjust_days_for_move_down(EWeekView * week_view,gint current_day)5152 e_week_view_get_adjust_days_for_move_down (EWeekView *week_view,
5153 gint current_day)
5154 {
5155 return e_week_view_get_multi_week_view (week_view) ? 7 : 0;
5156 }
5157
5158 static gint
e_week_view_get_adjust_days_for_move_left(EWeekView * week_view,gint current_day)5159 e_week_view_get_adjust_days_for_move_left (EWeekView *week_view,
5160 gint current_day)
5161 {
5162 return -1;
5163 }
5164
5165 static gint
e_week_view_get_adjust_days_for_move_right(EWeekView * week_view,gint current_day)5166 e_week_view_get_adjust_days_for_move_right (EWeekView *week_view,
5167 gint current_day)
5168 {
5169 return 1;
5170 }
5171
5172 void
e_week_view_show_popup_menu(EWeekView * week_view,GdkEvent * button_event,gint event_num)5173 e_week_view_show_popup_menu (EWeekView *week_view,
5174 GdkEvent *button_event,
5175 gint event_num)
5176 {
5177 guint timeout;
5178
5179 timeout = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (week_view), "tooltip-timeout"));
5180 if (timeout) {
5181 g_source_remove (timeout);
5182 g_object_set_data (G_OBJECT (week_view), "tooltip-timeout", NULL);
5183 }
5184
5185 e_week_view_set_popup_event (week_view, event_num);
5186
5187 e_calendar_view_popup_event (E_CALENDAR_VIEW (week_view), button_event);
5188 }
5189
5190 void
e_week_view_jump_to_button_item(EWeekView * week_view,GnomeCanvasItem * item)5191 e_week_view_jump_to_button_item (EWeekView *week_view,
5192 GnomeCanvasItem *item)
5193 {
5194 gint day;
5195
5196 for (day = 0; day < E_WEEK_VIEW_MAX_WEEKS * 7; ++day) {
5197 if (item == week_view->jump_buttons[day]) {
5198 e_calendar_view_move_view_range (E_CALENDAR_VIEW (week_view), E_CALENDAR_VIEW_MOVE_TO_EXACT_DAY, week_view->day_starts[day]);
5199 return;
5200 }
5201 }
5202 }
5203
5204 static gboolean
e_week_view_on_jump_button_event(GnomeCanvasItem * item,GdkEvent * event,EWeekView * week_view)5205 e_week_view_on_jump_button_event (GnomeCanvasItem *item,
5206 GdkEvent *event,
5207 EWeekView *week_view)
5208 {
5209 gint day;
5210
5211 if (event->type == GDK_BUTTON_PRESS) {
5212 e_week_view_jump_to_button_item (week_view, item);
5213 return TRUE;
5214 }
5215 else if (event->type == GDK_KEY_PRESS) {
5216 /* return, if Tab, Control or Alt is pressed */
5217 if ((event->key.keyval == GDK_KEY_Tab) ||
5218 (event->key.state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
5219 return FALSE;
5220 /* with a return key or a simple character (from 0x20 to 0xff),
5221 * jump to the day
5222 */
5223 if ((event->key.keyval == GDK_KEY_Return || event->key.keyval == GDK_KEY_KP_Enter) ||
5224 ((event->key.keyval >= 0x20) &&
5225 (event->key.keyval <= 0xFF))) {
5226 e_week_view_jump_to_button_item (week_view, item);
5227 return TRUE;
5228 }
5229 }
5230 else if (event->type == GDK_FOCUS_CHANGE) {
5231 GdkEventFocus *focus_event = (GdkEventFocus *) event;
5232 GdkPixbuf *pixbuf = NULL;
5233
5234 for (day = 0; day < E_WEEK_VIEW_MAX_WEEKS * 7; day++) {
5235 if (item == week_view->jump_buttons[day])
5236 break;
5237 }
5238
5239 if (day >= E_WEEK_VIEW_MAX_WEEKS * 7) {
5240 g_warn_if_reached ();
5241 return FALSE;
5242 }
5243
5244 if (focus_event->in) {
5245 week_view->focused_jump_button = day;
5246 pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) jump_xpm_focused);
5247 gnome_canvas_item_set (
5248 week_view->jump_buttons[day],
5249 "GnomeCanvasPixbuf::pixbuf",
5250 pixbuf, NULL);
5251 }
5252 else {
5253 week_view->focused_jump_button = E_WEEK_VIEW_JUMP_BUTTON_NO_FOCUS;
5254 pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) jump_xpm);
5255 gnome_canvas_item_set (
5256 week_view->jump_buttons[day],
5257 "GnomeCanvasPixbuf::pixbuf",
5258 pixbuf, NULL);
5259 }
5260 if (pixbuf)
5261 g_object_unref (pixbuf);
5262 }
5263
5264 return FALSE;
5265 }
5266
5267 /* Converts an hour from 0-23 to the preferred time format, and returns the
5268 * suffix to add and the width of it in the normal font. */
5269 void
e_week_view_convert_time_to_display(EWeekView * week_view,gint hour,gint * display_hour,const gchar ** suffix,gint * suffix_width)5270 e_week_view_convert_time_to_display (EWeekView *week_view,
5271 gint hour,
5272 gint *display_hour,
5273 const gchar **suffix,
5274 gint *suffix_width)
5275 {
5276 ECalModel *model;
5277
5278 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
5279
5280 /* Calculate the actual hour number to display. For 12-hour
5281 * format we convert 0-23 to 12-11am/12-11pm. */
5282 *display_hour = hour;
5283 if (e_cal_model_get_use_24_hour_format (model)) {
5284 *suffix = "";
5285 *suffix_width = 0;
5286 } else {
5287 if (hour < 12) {
5288 *suffix = week_view->am_string;
5289 *suffix_width = week_view->am_string_width;
5290 } else {
5291 *display_hour -= 12;
5292 *suffix = week_view->pm_string;
5293 *suffix_width = week_view->pm_string_width;
5294 }
5295
5296 /* 12-hour uses 12:00 rather than 0:00. */
5297 if (*display_hour == 0)
5298 *display_hour = 12;
5299 }
5300 }
5301
5302 gint
e_week_view_get_time_string_width(EWeekView * week_view)5303 e_week_view_get_time_string_width (EWeekView *week_view)
5304 {
5305 ECalModel *model;
5306 gint time_width;
5307
5308 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
5309
5310 if (week_view->use_small_font && week_view->small_font_desc)
5311 time_width = week_view->digit_width * 2
5312 + week_view->small_digit_width * 2;
5313 else
5314 time_width = week_view->digit_width * 4
5315 + week_view->colon_width;
5316
5317 if (!e_cal_model_get_use_24_hour_format (model))
5318 time_width += MAX (week_view->am_string_width,
5319 week_view->pm_string_width);
5320
5321 return time_width;
5322 }
5323
5324 /* Queues a layout, unless one is already queued. */
5325 static void
e_week_view_queue_layout(EWeekView * week_view)5326 e_week_view_queue_layout (EWeekView *week_view)
5327 {
5328 if (week_view->layout_timeout_id == 0) {
5329 week_view->layout_timeout_id = e_named_timeout_add (
5330 E_WEEK_VIEW_LAYOUT_TIMEOUT,
5331 e_week_view_layout_timeout_cb, week_view);
5332 }
5333 }
5334
5335 /* Removes any queued layout. */
5336 static void
e_week_view_cancel_layout(EWeekView * week_view)5337 e_week_view_cancel_layout (EWeekView *week_view)
5338 {
5339 if (week_view->layout_timeout_id != 0) {
5340 g_source_remove (week_view->layout_timeout_id);
5341 week_view->layout_timeout_id = 0;
5342 }
5343 }
5344
5345 static gboolean
e_week_view_layout_timeout_cb(gpointer data)5346 e_week_view_layout_timeout_cb (gpointer data)
5347 {
5348 EWeekView *week_view = E_WEEK_VIEW (data);
5349
5350 gtk_widget_queue_draw (week_view->main_canvas);
5351 e_week_view_check_layout (week_view);
5352
5353 week_view->layout_timeout_id = 0;
5354 return FALSE;
5355 }
5356
5357 /* Returns the number of selected events (0 or 1 at present). */
5358 gint
e_week_view_get_num_events_selected(EWeekView * week_view)5359 e_week_view_get_num_events_selected (EWeekView *week_view)
5360 {
5361 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), 0);
5362
5363 return (week_view->editing_event_num != -1) ? 1 : 0;
5364 }
5365
5366 gboolean
e_week_view_is_jump_button_visible(EWeekView * week_view,gint day)5367 e_week_view_is_jump_button_visible (EWeekView *week_view,
5368 gint day)
5369 {
5370 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
5371
5372 if ((day >= 0) && (day < E_WEEK_VIEW_MAX_WEEKS * 7))
5373 return week_view->jump_buttons[day]->flags & GNOME_CANVAS_ITEM_VISIBLE;
5374 return FALSE;
5375 }
5376
5377 gboolean
e_week_view_is_editing(EWeekView * week_view)5378 e_week_view_is_editing (EWeekView *week_view)
5379 {
5380 g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
5381
5382 return week_view->editing_event_num != -1;
5383 }
5384