No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Damon Chaplin <damon@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 /*
24 * EWeekViewEventItem - displays the background, times and icons for an event
25 * in the week/month views. A separate EText canvas item is used to display &
26 * edit the text.
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33 #include "e-util/e-categories-config.h"
34 #include "e-week-view-event-item.h"
35
36 #include <gtk/gtk.h>
37 #include "e-calendar-view.h"
38 #include "comp-util.h"
39
40 #include <text/e-text.h>
41
42 #define E_WEEK_VIEW_EVENT_ITEM_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE \
44 ((obj), E_TYPE_WEEK_VIEW_EVENT_ITEM, EWeekViewEventItemPrivate))
45
46 struct _EWeekViewEventItemPrivate {
47 /* The event index in the EWeekView events array. */
48 gint event_num;
49
50 /* The span index within the event. */
51 gint span_num;
52 };
53
54 enum {
55 PROP_0,
56 PROP_EVENT_NUM,
57 PROP_SPAN_NUM
58 };
59
60 G_DEFINE_TYPE (
61 EWeekViewEventItem,
62 e_week_view_event_item,
63 GNOME_TYPE_CANVAS_ITEM)
64
65 static gboolean
66 can_draw_in_region (cairo_region_t *draw_region,
67 gint x,
68 gint y,
69 gint width,
70 gint height)
71 {
72 GdkRectangle rect;
73
74 g_return_val_if_fail (draw_region != NULL, FALSE);
75
76 rect.x = x;
77 rect.y = y;
78 rect.width = width;
79 rect.height = height;
80
81 return cairo_region_contains_rectangle (draw_region, &rect) !=
82 CAIRO_REGION_OVERLAP_OUT;
83 }
84
85 static ECalendarViewPosition
86 week_view_event_item_get_position (EWeekViewEventItem *event_item,
87 gdouble x,
88 gdouble y)
89 {
90 EWeekView *week_view;
91 GnomeCanvasItem *item;
92 GtkWidget *parent;
93
94 item = GNOME_CANVAS_ITEM (event_item);
95
96 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
97 g_return_val_if_fail (E_IS_WEEK_VIEW (parent), E_CALENDAR_VIEW_POS_NONE);
98
99 week_view = E_WEEK_VIEW (parent);
100
101 if (x < item->x1 + E_WEEK_VIEW_EVENT_L_PAD
102 || x >= item->x2 - E_WEEK_VIEW_EVENT_R_PAD)
103 return E_CALENDAR_VIEW_POS_NONE;
104
105 /* Support left/right edge for long events only. */
106 if (!e_week_view_is_one_day_event (week_view, event_item->priv->event_num)) {
107 if (x < item->x1 + E_WEEK_VIEW_EVENT_L_PAD
108 + E_WEEK_VIEW_EVENT_BORDER_WIDTH
109 + E_WEEK_VIEW_EVENT_EDGE_X_PAD)
110 return E_CALENDAR_VIEW_POS_LEFT_EDGE;
111
112 if (x >= item->x2 + 1 - E_WEEK_VIEW_EVENT_R_PAD
113 - E_WEEK_VIEW_EVENT_BORDER_WIDTH
114 - E_WEEK_VIEW_EVENT_EDGE_X_PAD)
115 return E_CALENDAR_VIEW_POS_RIGHT_EDGE;
116 }
117
118 return E_CALENDAR_VIEW_POS_EVENT;
119 }
120
121 static gboolean
122 week_view_event_item_double_click (EWeekViewEventItem *event_item,
123 GdkEvent *bevent)
124 {
125 EWeekView *week_view;
126 EWeekViewEvent *event;
127 GnomeCanvasItem *item;
128 GtkWidget *parent;
129
130 item = GNOME_CANVAS_ITEM (event_item);
131
132 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
133 g_return_val_if_fail (E_IS_WEEK_VIEW (parent), FALSE);
134
135 week_view = E_WEEK_VIEW (parent);
136
137 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
138 return TRUE;
139
140 event = &g_array_index (
141 week_view->events, EWeekViewEvent,
142 event_item->priv->event_num);
143
144 if (!is_comp_data_valid (event))
145 return TRUE;
146
147 if (week_view->editing_event_num >= 0) {
148 EWeekViewEvent *editing;
149
150 if (!is_array_index_in_bounds (
151 week_view->events, week_view->editing_event_num))
152 return TRUE;
153
154 editing = &g_array_index (
155 week_view->events, EWeekViewEvent,
156 week_view->editing_event_num);
157
158 /* Do not call edit of the component, if double clicked
159 * on the component, which is not on the server. */
160 if (editing && event &&
161 editing->comp_data == event->comp_data &&
162 is_comp_data_valid (editing) &&
163 (!event->comp_data ||
164 !is_icalcomp_on_the_server (
165 event->comp_data->icalcomp,
166 event->comp_data->client)))
167 return TRUE;
168 }
169
170 e_week_view_stop_editing_event (week_view);
171
172 e_calendar_view_edit_appointment (
173 E_CALENDAR_VIEW (week_view),
174 event->comp_data->client,
175 event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT);
176
177 return TRUE;
178 }
179
180 static gboolean
181 week_view_event_item_button_press (EWeekViewEventItem *event_item,
182 GdkEvent *bevent)
183 {
184 EWeekView *week_view;
185 ECalendarViewPosition pos;
186 EWeekViewEvent *event;
187 EWeekViewEventSpan *span;
188 GnomeCanvasItem *item;
189 GtkWidget *parent;
190
191 item = GNOME_CANVAS_ITEM (event_item);
192
193 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
194 g_return_val_if_fail (E_IS_WEEK_VIEW (parent), FALSE);
195
196 week_view = E_WEEK_VIEW (parent);
197
198 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
199 return FALSE;
200
201 event = &g_array_index (
202 week_view->events, EWeekViewEvent,
203 event_item->priv->event_num);
204
205 if (!is_array_index_in_bounds (
206 week_view->spans, event->spans_index +
207 event_item->priv->span_num))
208 return FALSE;
209
210 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
211 event->spans_index + event_item->priv->span_num);
212
213 pos = week_view_event_item_get_position (
214 event_item, bevent->button.x,
215 bevent->button.y);
216 if (pos == E_CALENDAR_VIEW_POS_NONE)
217 return FALSE;
218
219 if (bevent->button.button == 1) {
220 week_view->pressed_event_num = event_item->priv->event_num;
221 week_view->pressed_span_num = event_item->priv->span_num;
222
223 /* Ignore clicks on the event while editing. */
224 if (E_TEXT (span->text_item)->editing)
225 return FALSE;
226
227 /* Remember the item clicked and the mouse position,
228 * so we can start a drag if the mouse moves. */
229 week_view->drag_event_x = bevent->button.x;
230 week_view->drag_event_y = bevent->button.y;
231
232 /* FIXME: Remember the day offset from the start of the event.
233 */
234
235 return TRUE;
236 } else if (bevent->button.button == 3) {
237 if (!gtk_widget_has_focus (GTK_WIDGET (week_view))) {
238 gtk_widget_grab_focus (GTK_WIDGET (week_view));
239 if (week_view->event_destroyed) {
240 week_view->event_destroyed = FALSE;
241 return FALSE;
242 }
243
244 }
245
246 e_week_view_set_selected_time_range_visible (
247 week_view, event->start, event->end);
248
249 e_week_view_show_popup_menu (
250 week_view, (GdkEventButton *) bevent,
251 event_item->priv->event_num);
252 g_signal_stop_emission_by_name (
253 item->canvas, "button_press_event");
254
255 return TRUE;
256 }
257
258 return FALSE;
259 }
260
261 static gboolean
262 week_view_event_item_button_release (EWeekViewEventItem *event_item,
263 GdkEvent *event)
264 {
265 EWeekView *week_view;
266 GnomeCanvasItem *item;
267 GtkWidget *parent;
268
269 item = GNOME_CANVAS_ITEM (event_item);
270
271 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
272 g_return_val_if_fail (E_IS_WEEK_VIEW (parent), FALSE);
273
274 week_view = E_WEEK_VIEW (parent);
275
276 if (week_view->pressed_event_num != -1
277 && week_view->pressed_event_num == event_item->priv->event_num
278 && week_view->pressed_span_num == event_item->priv->span_num) {
279 e_week_view_start_editing_event (
280 week_view,
281 event_item->priv->event_num,
282 event_item->priv->span_num,
283 NULL);
284 week_view->pressed_event_num = -1;
285 return TRUE;
286 }
287
288 week_view->pressed_event_num = -1;
289
290 return FALSE;
291 }
292
293 static void
294 week_view_draw_time (EWeekView *week_view,
295 cairo_t *cr,
296 gint time_x,
297 gint time_y,
298 gint hour,
299 gint minute)
300 {
301 ECalModel *model;
302 GtkStyle *style;
303 gint hour_to_display, suffix_width;
304 gint time_y_normal_font, time_y_small_font;
305 const gchar *suffix;
306 gchar buffer[128];
307 PangoLayout *layout;
308 PangoFontDescription *small_font_desc;
309
310 cairo_save (cr);
311
312 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
313
314 style = gtk_widget_get_style (GTK_WIDGET (week_view));
315 small_font_desc = week_view->small_font_desc;
316
317 gdk_cairo_set_source_color (cr, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_TEXT]);
318
319 layout = gtk_widget_create_pango_layout (GTK_WIDGET (week_view), NULL);
320
321 time_y_normal_font = time_y_small_font = time_y;
322 if (small_font_desc)
323 time_y_small_font = time_y;
324
325 e_week_view_convert_time_to_display (
326 week_view, hour, &hour_to_display,
327 &suffix, &suffix_width);
328
329 if (week_view->use_small_font && week_view->small_font_desc) {
330 g_snprintf (
331 buffer, sizeof (buffer), "%2i:%02i",
332 hour_to_display, minute);
333
334 /* Draw the hour. */
335 if (hour_to_display < 10) {
336 pango_layout_set_text (layout, buffer + 1, 1);
337 cairo_move_to (
338 cr,
339 time_x + week_view->digit_width,
340 time_y_normal_font);
341 pango_cairo_show_layout (cr, layout);
342 } else {
343 pango_layout_set_text (layout, buffer, 2);
344 cairo_move_to (
345 cr,
346 time_x,
347 time_y_normal_font);
348 pango_cairo_show_layout (cr, layout);
349 }
350
351 time_x += week_view->digit_width * 2;
352
353 /* Draw the start minute, in the small font. */
354 pango_layout_set_font_description (layout, week_view->small_font_desc);
355 pango_layout_set_text (layout, buffer + 3, 2);
356 cairo_move_to (
357 cr,
358 time_x,
359 time_y_small_font);
360 pango_cairo_show_layout (cr, layout);
361
362 pango_layout_set_font_description (layout, style->font_desc);
363
364 time_x += week_view->small_digit_width * 2;
365
366 /* Draw the 'am'/'pm' suffix, if 12-hour format. */
367 if (!e_cal_model_get_use_24_hour_format (model)) {
368 pango_layout_set_text (layout, suffix, -1);
369
370 cairo_move_to (
371 cr,
372 time_x,
373 time_y_normal_font);
374 pango_cairo_show_layout (cr, layout);
375 }
376 } else {
377 /* Draw the start time in one go. */
378 g_snprintf (
379 buffer, sizeof (buffer), "%2i:%02i%s",
380 hour_to_display, minute, suffix);
381 if (hour_to_display < 10) {
382 pango_layout_set_text (layout, buffer + 1, -1);
383 cairo_move_to (
384 cr,
385 time_x + week_view->digit_width,
386 time_y_normal_font);
387 pango_cairo_show_layout (cr, layout);
388 } else {
389 pango_layout_set_text (layout, buffer, -1);
390 cairo_move_to (
391 cr,
392 time_x,
393 time_y_normal_font);
394 pango_cairo_show_layout (cr, layout);
395 }
396
397 }
398 g_object_unref (layout);
399
400 cairo_restore (cr);
401 }
402
403 static void
404 week_view_event_item_draw_icons (EWeekViewEventItem *event_item,
405 cairo_t *cr,
406 gint icon_x,
407 gint icon_y,
408 gint x2,
409 gboolean right_align,
410 cairo_region_t *draw_region)
411 {
412 EWeekView *week_view;
413 EWeekViewEvent *event;
414 ECalComponent *comp;
415 GnomeCanvas *canvas;
416 GtkWidget *parent;
417 gint num_icons = 0, icon_x_inc;
418 gboolean draw_reminder_icon = FALSE, draw_recurrence_icon = FALSE;
419 gboolean draw_timezone_icon = FALSE, draw_attach_icon = FALSE;
420 gboolean draw_meeting_icon = FALSE;
421 GSList *categories_pixbufs = NULL, *pixbufs;
422
423 canvas = GNOME_CANVAS_ITEM (event_item)->canvas;
424 parent = gtk_widget_get_parent (GTK_WIDGET (canvas));
425 week_view = E_WEEK_VIEW (parent);
426
427 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
428 return;
429
430 event = &g_array_index (week_view->events, EWeekViewEvent,
431 event_item->priv->event_num);
432
433 if (!is_comp_data_valid (event))
434 return;
435
436 comp = e_cal_component_new ();
437 e_cal_component_set_icalcomponent (
438 comp, icalcomponent_new_clone (event->comp_data->icalcomp));
439
440 if (e_cal_component_has_alarms (comp)) {
441 draw_reminder_icon = TRUE;
442 num_icons++;
443 }
444
445 if (e_cal_component_has_recurrences (comp) ||
446 e_cal_component_is_instance (comp)) {
447 draw_recurrence_icon = TRUE;
448 num_icons++;
449 }
450
451 if (e_cal_component_has_attachments (comp)) {
452 draw_attach_icon = TRUE;
453 num_icons++;
454 }
455
456 if (e_cal_component_has_attendees (comp)) {
457 draw_meeting_icon = TRUE;
458 num_icons++;
459 }
460
461 if (event->different_timezone) {
462 draw_timezone_icon = TRUE;
463 num_icons++;
464 }
465
466 num_icons += cal_comp_util_get_n_icons (comp, &categories_pixbufs);
467
468 icon_x_inc = E_WEEK_VIEW_ICON_WIDTH + E_WEEK_VIEW_ICON_X_PAD;
469
470 if (right_align)
471 icon_x -= icon_x_inc * num_icons;
472
473 #define draw_pixbuf(pf) \
474 if (can_draw_in_region (draw_region, icon_x, icon_y, \
475 E_WEEK_VIEW_ICON_WIDTH, E_WEEK_VIEW_ICON_HEIGHT)) { \
476 cairo_save (cr); \
477 gdk_cairo_set_source_pixbuf (cr, pf, icon_x, icon_y); \
478 cairo_paint (cr); \
479 cairo_restore (cr); \
480 } \
481 \
482 icon_x += icon_x_inc;
483
484 if (draw_reminder_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
485 draw_pixbuf (week_view->reminder_icon);
486 }
487
488 if (draw_attach_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
489 draw_pixbuf (week_view->attach_icon);
490 }
491
492 if (draw_recurrence_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
493 draw_pixbuf (week_view->recurrence_icon);
494 }
495
496 if (draw_timezone_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
497 draw_pixbuf (week_view->timezone_icon);
498 }
499
500 if (draw_meeting_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
501 draw_pixbuf (week_view->meeting_icon);
502 }
503
504 /* draw categories icons */
505 for (pixbufs = categories_pixbufs;
506 pixbufs;
507 pixbufs = pixbufs->next) {
508 GdkPixbuf *pixbuf = pixbufs->data;
509
510 draw_pixbuf (pixbuf);
511 }
512
513 #undef draw_pixbuf
514
515 g_slist_foreach (categories_pixbufs, (GFunc) g_object_unref, NULL);
516 g_slist_free (categories_pixbufs);
517
518 g_object_unref (comp);
519 }
520
521 /* This draws a little triangle to indicate that an event extends past
522 * the days visible on screen. */
523 static void
524 week_view_event_item_draw_triangle (EWeekViewEventItem *event_item,
525 cairo_t *cr,
526 GdkColor bg_color,
527 gint x,
528 gint y,
529 gint w,
530 gint h,
531 cairo_region_t *draw_region)
532 {
533 ECalModel *model;
534 EWeekView *week_view;
535 EWeekViewEvent *event;
536 GnomeCanvas *canvas;
537 GtkWidget *parent;
538 GdkPoint points[3];
539 const gchar *color_spec;
540 gint c1, c2;
541
542 if (!can_draw_in_region (draw_region, x, y, w, h))
543 return;
544
545 canvas = GNOME_CANVAS_ITEM (event_item)->canvas;
546 parent = gtk_widget_get_parent (GTK_WIDGET (canvas));
547 week_view = E_WEEK_VIEW (parent);
548
549 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
550 return;
551
552 event = &g_array_index (week_view->events, EWeekViewEvent,
553 event_item->priv->event_num);
554
555 if (!is_comp_data_valid (event))
556 return;
557
558 points[0].x = x;
559 points[0].y = y;
560 points[1].x = x + w;
561 points[1].y = y + (h / 2);
562 points[2].x = x;
563 points[2].y = y + h - 1;
564
565 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
566
567 color_spec =
568 e_cal_model_get_color_for_component (model, event->comp_data);
569
570 if (gdk_color_parse (color_spec, &bg_color)) {
571 gdk_cairo_set_source_color (cr, &bg_color);
572 } else {
573 EWeekViewColors wvc;
574 GdkColor *color;
575
576 wvc = E_WEEK_VIEW_COLOR_EVENT_BACKGROUND;
577 color = &week_view->colors[wvc];
578
579 gdk_cairo_set_source_color (cr, color);
580 }
581
582 cairo_save (cr);
583 cairo_set_line_width (cr, 0.7);
584 cairo_move_to (cr, points[0].x, points[0].y);
585 cairo_line_to (cr, points[1].x, points[1].y);
586 cairo_line_to (cr, points[2].x, points[2].y);
587 cairo_line_to (cr, points[0].x, points[0].y);
588 cairo_fill (cr);
589 cairo_restore (cr);
590
591 cairo_save (cr);
592 gdk_cairo_set_source_color (
593 cr, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER]);
594
595 /* If the height is odd we can use the same central point for both
596 * lines. If it is even we use different end-points. */
597 c1 = c2 = y + (h / 2);
598 if (h % 2 == 0)
599 c1--;
600
601 cairo_set_line_width (cr, 0.7);
602 cairo_move_to (cr, x, y);
603 cairo_line_to (cr, x + w, c1);
604 cairo_move_to (cr, x, y + h - 1);
605 cairo_line_to (cr, x + w, c2);
606 cairo_restore (cr);
607 }
608
609 static void
610 week_view_event_item_set_property (GObject *object,
611 guint property_id,
612 const GValue *value,
613 GParamSpec *pspec)
614 {
615 switch (property_id) {
616 case PROP_EVENT_NUM:
617 e_week_view_event_item_set_event_num (
618 E_WEEK_VIEW_EVENT_ITEM (object),
619 g_value_get_int (value));
620 return;
621
622 case PROP_SPAN_NUM:
623 e_week_view_event_item_set_span_num (
624 E_WEEK_VIEW_EVENT_ITEM (object),
625 g_value_get_int (value));
626 return;
627 }
628
629 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
630 }
631
632 static void
633 week_view_event_item_get_property (GObject *object,
634 guint property_id,
635 GValue *value,
636 GParamSpec *pspec)
637 {
638 switch (property_id) {
639 case PROP_EVENT_NUM:
640 g_value_set_int (
641 value,
642 e_week_view_event_item_get_event_num (
643 E_WEEK_VIEW_EVENT_ITEM (object)));
644 return;
645
646 case PROP_SPAN_NUM:
647 g_value_set_int (
648 value,
649 e_week_view_event_item_get_span_num (
650 E_WEEK_VIEW_EVENT_ITEM (object)));
651 return;
652 }
653
654 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
655 }
656
657 static void
658 week_view_event_item_update (GnomeCanvasItem *item,
659 const cairo_matrix_t *i2c,
660 gint flags)
661 {
662 GnomeCanvasItemClass *canvas_item_class;
663 EWeekViewEventItem *event_item;
664 EWeekView *week_view;
665 GtkWidget *parent;
666 gint event_num, span_num;
667 gint span_x, span_y, span_w;
668
669 event_item = E_WEEK_VIEW_EVENT_ITEM (item);
670 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
671 g_return_if_fail (E_IS_WEEK_VIEW (parent));
672
673 week_view = E_WEEK_VIEW (parent);
674
675 /* Chain up to parent's update() method. */
676 canvas_item_class =
677 GNOME_CANVAS_ITEM_CLASS (e_week_view_event_item_parent_class);
678 canvas_item_class->update (item, i2c, flags);
679
680 item->x1 = 0;
681 item->y1 = 0;
682 item->x2 = 0;
683 item->y2 = 0;
684
685 event_num = e_week_view_event_item_get_event_num (event_item);
686 span_num = e_week_view_event_item_get_span_num (event_item);
687
688 if (event_num != -1 && span_num != -1) {
689 if (e_week_view_get_span_position (
690 week_view, event_num, span_num,
691 &span_x, &span_y, &span_w)) {
692 item->x1 = span_x;
693 item->y1 = span_y;
694 item->x2 = span_x + span_w - 1;
695 item->y2 = span_y + week_view->row_height - 1;
696 }
697 }
698 }
699
700 static void
701 week_view_event_item_draw (GnomeCanvasItem *canvas_item,
702 cairo_t *cr,
703 gint x,
704 gint y,
705 gint width,
706 gint height)
707 {
708 EWeekViewEventItem *event_item;
709 EWeekView *week_view;
710 EWeekViewEvent *event;
711 EWeekViewEventSpan *span;
712 ECalModel *model;
713 GtkWidget *parent;
714 gint x1, y1, x2, y2, time_x, time_y;
715 gint icon_x, icon_y, time_width, min_end_time_x, max_icon_x;
716 gint rect_x, rect_w, rect_x2 = 0;
717 gboolean one_day_event, editing_span = FALSE;
718 gint start_hour, start_minute, end_hour, end_minute;
719 gboolean draw_start, draw_end;
720 gboolean draw_start_triangle = FALSE, draw_end_triangle = FALSE;
721 GdkColor bg_color;
722 cairo_pattern_t *pat;
723 guint16 red, green, blue;
724 gdouble radius, cx0, cy0, rect_height, rect_width;
725 gdouble cc = 65535.0;
726 cairo_region_t *draw_region;
727 GdkRectangle rect;
728 const gchar *color_spec;
729
730 event_item = E_WEEK_VIEW_EVENT_ITEM (canvas_item);
731 parent = gtk_widget_get_parent (GTK_WIDGET (canvas_item->canvas));
732 g_return_if_fail (E_IS_WEEK_VIEW (parent));
733
734 week_view = E_WEEK_VIEW (parent);
735
736 if (event_item->priv->event_num == -1 || event_item->priv->span_num == -1)
737 return;
738
739 g_return_if_fail (event_item->priv->event_num < week_view->events->len);
740
741 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
742 return;
743
744 event = &g_array_index (week_view->events, EWeekViewEvent,
745 event_item->priv->event_num);
746
747 if (!is_comp_data_valid (event))
748 return;
749
750 g_return_if_fail (
751 event->spans_index + event_item->priv->span_num <
752 week_view->spans->len);
753
754 if (!is_array_index_in_bounds (
755 week_view->spans, event->spans_index +
756 event_item->priv->span_num))
757 return;
758
759 span = &g_array_index (
760 week_view->spans, EWeekViewEventSpan,
761 event->spans_index + event_item->priv->span_num);
762
763 x1 = canvas_item->x1 - x;
764 y1 = canvas_item->y1 - y;
765 x2 = canvas_item->x2 - x;
766 y2 = canvas_item->y2 - y;
767
768 if (x1 == x2 || y1 == y2)
769 return;
770
771 rect.x = 0;
772 rect.y = 0;
773 rect.width = width;
774 rect.height = height;
775 if (rect.width > 0 && rect.height > 0)
776 draw_region = cairo_region_create_rectangle (&rect);
777 else
778 draw_region = cairo_region_create ();
779
780 if (!can_draw_in_region (draw_region, x1, y1, x2 - x1, y2 - y1)) {
781 cairo_region_destroy (draw_region);
782 return;
783 }
784
785 icon_y = y1 + E_WEEK_VIEW_EVENT_BORDER_HEIGHT + E_WEEK_VIEW_ICON_Y_PAD;
786
787 /* Get the start & end times in 24-hour format. */
788 start_hour = event->start_minute / 60;
789 start_minute = event->start_minute % 60;
790
791 /* Modulo 24 because a midnight end time will be '24' */
792 end_hour = (event->end_minute / 60) % 24;
793 end_minute = event->end_minute % 60;
794
795 time_y = y1 + E_WEEK_VIEW_EVENT_BORDER_HEIGHT
796 + E_WEEK_VIEW_EVENT_TEXT_Y_PAD;
797
798 time_width = e_week_view_get_time_string_width (week_view);
799
800 one_day_event = e_week_view_is_one_day_event (
801 week_view, event_item->priv->event_num);
802
803 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
804
805 color_spec =
806 e_cal_model_get_color_for_component (model, event->comp_data);
807
808 bg_color = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND];
809 if (!gdk_color_parse (color_spec, &bg_color)) {
810 bg_color = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND];
811 }
812
813 red = bg_color.red;
814 green = bg_color.green;
815 blue = bg_color.blue;
816
817 if (one_day_event) {
818 time_x = x1 + E_WEEK_VIEW_EVENT_L_PAD + 1;
819 rect_x = x1 + E_WEEK_VIEW_EVENT_L_PAD;
820 rect_w = x2 - x1 - E_WEEK_VIEW_EVENT_L_PAD - E_WEEK_VIEW_EVENT_R_PAD + 1;
821
822 /* Here we draw the border around the event*/
823 cx0 = rect_x;
824 cy0 = y1 + 1;
825 rect_width = rect_w;
826 rect_height = y2 - y1 - 1;
827
828 radius = 12;
829
830 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
831 cairo_save (cr);
832 draw_curved_rectangle (cr, cx0, cy0, rect_width, rect_height, radius);
833 cairo_set_line_width (cr, 2.0);
834 cairo_set_source_rgb (cr, red / cc, green / cc, blue / cc);
835 cairo_stroke (cr);
836 cairo_restore (cr);
837 }
838
839 /* Fill it in the Event */
840
841 cx0 = rect_x + 1.5;
842 cy0 = y1 + 2.75;
843 rect_width = rect_w - 3.;
844 rect_height = y2 - y1 - 4.5;
845
846 radius = 8;
847
848 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
849 cairo_save (cr);
850 draw_curved_rectangle (
851 cr, cx0, cy0, rect_width, rect_height, radius);
852
853 pat = cairo_pattern_create_linear (
854 rect_x + 2, y1 + 1, rect_x + 2, y2 - 7.25);
855 cairo_pattern_add_color_stop_rgba (
856 pat, 1, red / cc, green / cc, blue / cc, 0.8);
857 cairo_pattern_add_color_stop_rgba (
858 pat, 0, red / cc, green / cc, blue / cc, 0.4);
859 cairo_set_source (cr, pat);
860 cairo_fill_preserve (cr);
861 cairo_pattern_destroy (pat);
862 cairo_set_source_rgba (cr, red / cc, green / cc, blue / cc, 0.2);
863 cairo_set_line_width (cr, 0.5);
864 cairo_stroke (cr);
865 cairo_restore (cr);
866 }
867
868 /* Draw the start and end times, as required. */
869 switch (week_view->time_format) {
870 case E_WEEK_VIEW_TIME_BOTH_SMALL_MIN:
871 case E_WEEK_VIEW_TIME_BOTH:
872 draw_start = TRUE;
873 draw_end = TRUE;
874 break;
875
876 case E_WEEK_VIEW_TIME_START_SMALL_MIN:
877 case E_WEEK_VIEW_TIME_START:
878 draw_start = TRUE;
879 draw_end = FALSE;
880 break;
881
882 case E_WEEK_VIEW_TIME_NONE:
883 draw_start = FALSE;
884 draw_end = FALSE;
885 break;
886 default:
887 g_return_if_reached ();
888 draw_start = FALSE;
889 draw_end = FALSE;
890 break;
891 }
892
893 if (draw_start) {
894 week_view_draw_time (
895 week_view, cr, time_x,
896 time_y, start_hour, start_minute);
897 time_x += time_width;
898 }
899
900 if (draw_end) {
901 time_x += E_WEEK_VIEW_EVENT_TIME_SPACING;
902 week_view_draw_time (
903 week_view, cr, time_x,
904 time_y, end_hour, end_minute);
905 time_x += time_width;
906 }
907
908 icon_x = time_x;
909 if (draw_start)
910 icon_x += E_WEEK_VIEW_EVENT_TIME_X_PAD;
911
912 /* Draw the icons. */
913 week_view_event_item_draw_icons (
914 event_item, cr, icon_x,
915 icon_y, x2, FALSE, draw_region);
916
917 } else {
918 rect_x = x1 + E_WEEK_VIEW_EVENT_L_PAD;
919 rect_w = x2 - x1 - E_WEEK_VIEW_EVENT_L_PAD
920 - E_WEEK_VIEW_EVENT_R_PAD + 1;
921
922 /* Draw the triangles at the start & end, if needed.
923 * They also use the first few pixels at the edge of the
924 * event so we update rect_x & rect_w so we don't draw over
925 * them. */
926 if (event->start < week_view->day_starts[span->start_day]) {
927 draw_start_triangle = TRUE;
928 rect_x += 2;
929 rect_w -= 2;
930 }
931
932 if (event->end > week_view->day_starts[span->start_day
933 + span->num_days]) {
934 draw_end_triangle = TRUE;
935 rect_w -= 2;
936 }
937
938 /* Here we draw the border around the event */
939
940 cx0 = rect_x;
941 cy0 = y1 + 1;
942 rect_width = rect_w;
943 rect_height = y2 - y1 - 1;
944
945 radius = 12;
946
947 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
948 cairo_save (cr);
949 draw_curved_rectangle (cr, cx0, cy0, rect_width, rect_height, radius);
950 cairo_set_line_width (cr, 2.0);
951 cairo_set_source_rgb (cr, red / cc, green / cc, blue / cc);
952 cairo_stroke (cr);
953 cairo_restore (cr);
954 }
955
956 /* Here we fill it in the event*/
957
958 cx0 = rect_x + 1.5;
959 cy0 = y1 + 2.75;
960 rect_width = rect_w - 3.;
961 rect_height = y2 - y1 - 4.5;
962
963 radius = 8;
964
965 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
966 cairo_save (cr);
967 draw_curved_rectangle (
968 cr, cx0, cy0, rect_width, rect_height, radius);
969
970 pat = cairo_pattern_create_linear (
971 rect_x + 2, y1 + 1, rect_x + 2, y2 - 7.25);
972 cairo_pattern_add_color_stop_rgba (
973 pat, 1, red / cc, green / cc, blue / cc, 0.8);
974 cairo_pattern_add_color_stop_rgba (
975 pat, 0, red / cc, green / cc, blue / cc, 0.4);
976 cairo_set_source (cr, pat);
977 cairo_fill_preserve (cr);
978 cairo_pattern_destroy (pat);
979 cairo_set_source_rgba (cr, red / cc, green / cc, blue / cc, 0.2);
980 cairo_set_line_width (cr, 0.5);
981 cairo_stroke (cr);
982 cairo_restore (cr);
983 }
984
985 if (draw_start_triangle) {
986 week_view_event_item_draw_triangle (
987 event_item, cr, bg_color,
988 x1 + E_WEEK_VIEW_EVENT_L_PAD + 2,
989 y1, -3, y2 - y1 + 1, draw_region);
990 } else if (can_draw_in_region (draw_region, rect_x, y1, 1, y2 - y1)) {
991 EWeekViewColors wvc;
992 GdkColor *color;
993
994 wvc = E_WEEK_VIEW_COLOR_EVENT_BORDER;
995 color = &week_view->colors[wvc];
996
997 cairo_save (cr);
998 gdk_cairo_set_source_color (cr, color);
999 cairo_set_line_width (cr, 0.7);
1000 cairo_move_to (cr, rect_x, y1);
1001 cairo_line_to (cr, rect_x, y2);
1002 cairo_stroke (cr);
1003 cairo_restore (cr);
1004 }
1005
1006 if (draw_end_triangle) {
1007 week_view_event_item_draw_triangle (
1008 event_item, cr, bg_color,
1009 x2 - E_WEEK_VIEW_EVENT_R_PAD - 2,
1010 y1, 3, y2 - y1 + 1, draw_region);
1011 } else if (can_draw_in_region (draw_region, rect_x2, y2, 1, 1)) {
1012 EWeekViewColors wvc;
1013 GdkColor *color;
1014
1015 wvc = E_WEEK_VIEW_COLOR_EVENT_BORDER;
1016 color = &week_view->colors[wvc];
1017
1018 cairo_save (cr);
1019 gdk_cairo_set_source_color (cr, color);
1020 cairo_set_line_width (cr, 0.7);
1021 /* rect_x2 is used uninitialized here */
1022 cairo_move_to (cr, rect_x2, y2);
1023 cairo_line_to (cr, rect_x2, y2);
1024 cairo_stroke (cr);
1025 cairo_restore (cr);
1026 }
1027
1028 if (span->text_item && E_TEXT (span->text_item)->editing)
1029 editing_span = TRUE;
1030
1031 /* Draw the start & end times, if they are not on day
1032 * boundaries. The start time would always be shown if it was
1033 * needed, though it may be clipped as the window shrinks.
1034 * The end time is only displayed if there is enough room.
1035 * We calculate the minimum position for the end time, which
1036 * depends on whether the start time is displayed. If the end
1037 * time doesn't fit, then we don't draw it. */
1038 min_end_time_x = x1 + E_WEEK_VIEW_EVENT_L_PAD
1039 + E_WEEK_VIEW_EVENT_BORDER_WIDTH
1040 + E_WEEK_VIEW_EVENT_EDGE_X_PAD;
1041 if (!editing_span
1042 && event->start > week_view->day_starts[span->start_day]) {
1043 time_x = x1 + E_WEEK_VIEW_EVENT_L_PAD
1044 + E_WEEK_VIEW_EVENT_BORDER_WIDTH
1045 + E_WEEK_VIEW_EVENT_EDGE_X_PAD;
1046
1047 cairo_save (cr);
1048
1049 cairo_rectangle (
1050 cr,
1051 x1, y1,
1052 x2 - x1 - E_WEEK_VIEW_EVENT_R_PAD
1053 - E_WEEK_VIEW_EVENT_BORDER_WIDTH + 1,
1054 y2 - y1 + 1);
1055 cairo_clip (cr);
1056
1057 week_view_draw_time (
1058 week_view, cr, time_x,
1059 time_y, start_hour, start_minute);
1060
1061 cairo_restore (cr);
1062
1063 /* We don't want the end time to be drawn over the
1064 * start time, so we increase the minimum position. */
1065 min_end_time_x += time_width
1066 + E_WEEK_VIEW_EVENT_TIME_X_PAD;
1067 }
1068
1069 max_icon_x = x2 + 1 - E_WEEK_VIEW_EVENT_R_PAD
1070 - E_WEEK_VIEW_EVENT_BORDER_WIDTH
1071 - E_WEEK_VIEW_EVENT_EDGE_X_PAD;
1072
1073 if (!editing_span
1074 && event->end < week_view->day_starts[span->start_day
1075 + span->num_days]) {
1076 /* Calculate where the end time should be displayed. */
1077 time_x = x2 + 1 - E_WEEK_VIEW_EVENT_R_PAD
1078 - E_WEEK_VIEW_EVENT_BORDER_WIDTH
1079 - E_WEEK_VIEW_EVENT_EDGE_X_PAD
1080 - time_width;
1081
1082 /* Draw the end time, if the position is greater than
1083 * the minimum calculated above. */
1084 if (time_x >= min_end_time_x) {
1085 week_view_draw_time (
1086 week_view, cr, time_x,
1087 time_y, end_hour, end_minute);
1088 max_icon_x -= time_width
1089 + E_WEEK_VIEW_EVENT_TIME_X_PAD;
1090 }
1091 }
1092
1093 /* Draw the icons. */
1094 if (span->text_item
1095 && (week_view->editing_event_num != event_item->priv->event_num
1096 || week_view->editing_span_num != event_item->priv->span_num)) {
1097 icon_x = span->text_item->x1 - E_WEEK_VIEW_ICON_R_PAD - x;
1098 week_view_event_item_draw_icons (
1099 event_item, cr, icon_x,
1100 icon_y, max_icon_x, TRUE, draw_region);
1101 }
1102 }
1103
1104 cairo_region_destroy (draw_region);
1105 }
1106
1107 static GnomeCanvasItem *
1108 week_view_event_item_point (GnomeCanvasItem *item,
1109 gdouble x,
1110 gdouble y,
1111 gint cx,
1112 gint cy)
1113 {
1114 return item;
1115 }
1116
1117 static gint
1118 week_view_event_item_event (GnomeCanvasItem *item,
1119 GdkEvent *event)
1120 {
1121 EWeekViewEventItem *event_item;
1122
1123 event_item = E_WEEK_VIEW_EVENT_ITEM (item);
1124
1125 switch (event->type) {
1126 case GDK_2BUTTON_PRESS:
1127 return week_view_event_item_double_click (event_item, event);
1128 case GDK_BUTTON_PRESS:
1129 return week_view_event_item_button_press (event_item, event);
1130 case GDK_BUTTON_RELEASE:
1131 return week_view_event_item_button_release (event_item, event);
1132 case GDK_MOTION_NOTIFY:
1133 break;
1134 default:
1135 break;
1136 }
1137
1138 return FALSE;
1139 }
1140
1141 static void
1142 e_week_view_event_item_class_init (EWeekViewEventItemClass *class)
1143 {
1144 GObjectClass *object_class;
1145 GnomeCanvasItemClass *item_class;
1146
1147 g_type_class_add_private (class, sizeof (EWeekViewEventItemPrivate));
1148
1149 object_class = G_OBJECT_CLASS (class);
1150 object_class->set_property = week_view_event_item_set_property;
1151 object_class->get_property = week_view_event_item_get_property;
1152
1153 item_class = GNOME_CANVAS_ITEM_CLASS (class);
1154 item_class->update = week_view_event_item_update;
1155 item_class->draw = week_view_event_item_draw;
1156 item_class->point = week_view_event_item_point;
1157 item_class->event = week_view_event_item_event;
1158
1159 g_object_class_install_property (
1160 object_class,
1161 PROP_EVENT_NUM,
1162 g_param_spec_int (
1163 "event-num",
1164 "Event Num",
1165 NULL,
1166 G_MININT,
1167 G_MAXINT,
1168 -1,
1169 G_PARAM_READWRITE));
1170
1171 g_object_class_install_property (
1172 object_class,
1173 PROP_SPAN_NUM,
1174 g_param_spec_int (
1175 "span-num",
1176 "Span Num",
1177 NULL,
1178 G_MININT,
1179 G_MAXINT,
1180 -1,
1181 G_PARAM_READWRITE));
1182 }
1183
1184 static void
1185 e_week_view_event_item_init (EWeekViewEventItem *event_item)
1186 {
1187 event_item->priv = E_WEEK_VIEW_EVENT_ITEM_GET_PRIVATE (event_item);
1188
1189 event_item->priv->event_num = -1;
1190 event_item->priv->span_num = -1;
1191 }
1192
1193 gint
1194 e_week_view_event_item_get_event_num (EWeekViewEventItem *event_item)
1195 {
1196 g_return_val_if_fail (E_IS_WEEK_VIEW_EVENT_ITEM (event_item), -1);
1197
1198 return event_item->priv->event_num;
1199 }
1200
1201 void
1202 e_week_view_event_item_set_event_num (EWeekViewEventItem *event_item,
1203 gint event_num)
1204 {
1205 g_return_if_fail (E_IS_WEEK_VIEW_EVENT_ITEM (event_item));
1206
1207 if (event_item->priv->event_num == event_num)
1208 return;
1209
1210 event_item->priv->event_num = event_num;
1211 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (event_item));
1212
1213 g_object_notify (G_OBJECT (event_item), "event-num");
1214 }
1215
1216 gint
1217 e_week_view_event_item_get_span_num (EWeekViewEventItem *event_item)
1218 {
1219 g_return_val_if_fail (E_IS_WEEK_VIEW_EVENT_ITEM (event_item), -1);
1220
1221 return event_item->priv->span_num;
1222 }
1223
1224 void
1225 e_week_view_event_item_set_span_num (EWeekViewEventItem *event_item,
1226 gint span_num)
1227 {
1228 g_return_if_fail (E_IS_WEEK_VIEW_EVENT_ITEM (event_item));
1229
1230 if (event_item->priv->span_num == span_num)
1231 return;
1232
1233 event_item->priv->span_num = span_num;
1234 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (event_item));
1235
1236 g_object_notify (G_OBJECT (event_item), "span-num");
1237 }