Bug 793125 - Crash due to popup menus left attached too long
[evolution.git] / src / calendar / gui / e-week-view-event-item.c
blobcab9b5394cd93135fe7846c8eec6cc7388221bc6
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.
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.
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/>.
15 * Authors:
16 * Damon Chaplin <damon@ximian.com>
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
23 * EWeekViewEventItem - displays the background, times and icons for an event
24 * in the week/month views. A separate EText canvas item is used to display &
25 * edit the text.
28 #include "evolution-config.h"
30 #include "e-week-view-event-item.h"
32 #include <gtk/gtk.h>
34 #include "e-calendar-view.h"
35 #include "comp-util.h"
37 #define E_WEEK_VIEW_EVENT_ITEM_GET_PRIVATE(obj) \
38 (G_TYPE_INSTANCE_GET_PRIVATE \
39 ((obj), E_TYPE_WEEK_VIEW_EVENT_ITEM, EWeekViewEventItemPrivate))
41 struct _EWeekViewEventItemPrivate {
42 /* The event index in the EWeekView events array. */
43 gint event_num;
45 /* The span index within the event. */
46 gint span_num;
49 enum {
50 PROP_0,
51 PROP_EVENT_NUM,
52 PROP_SPAN_NUM
55 G_DEFINE_TYPE (
56 EWeekViewEventItem,
57 e_week_view_event_item,
58 GNOME_TYPE_CANVAS_ITEM)
60 static gboolean
61 can_draw_in_region (cairo_region_t *draw_region,
62 gint x,
63 gint y,
64 gint width,
65 gint height)
67 GdkRectangle rect;
69 g_return_val_if_fail (draw_region != NULL, FALSE);
71 rect.x = x;
72 rect.y = y;
73 rect.width = width;
74 rect.height = height;
76 return cairo_region_contains_rectangle (draw_region, &rect) !=
77 CAIRO_REGION_OVERLAP_OUT;
80 static ECalendarViewPosition
81 week_view_event_item_get_position (EWeekViewEventItem *event_item,
82 gdouble x,
83 gdouble y)
85 EWeekView *week_view;
86 GnomeCanvasItem *item;
87 GtkWidget *parent;
89 item = GNOME_CANVAS_ITEM (event_item);
91 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
92 g_return_val_if_fail (E_IS_WEEK_VIEW (parent), E_CALENDAR_VIEW_POS_NONE);
94 week_view = E_WEEK_VIEW (parent);
96 if (x < item->x1 + E_WEEK_VIEW_EVENT_L_PAD
97 || x >= item->x2 - E_WEEK_VIEW_EVENT_R_PAD)
98 return E_CALENDAR_VIEW_POS_NONE;
100 /* Support left/right edge for long events only. */
101 if (!e_week_view_is_one_day_event (week_view, event_item->priv->event_num)) {
102 if (x < item->x1 + E_WEEK_VIEW_EVENT_L_PAD
103 + E_WEEK_VIEW_EVENT_BORDER_WIDTH
104 + E_WEEK_VIEW_EVENT_EDGE_X_PAD)
105 return E_CALENDAR_VIEW_POS_LEFT_EDGE;
107 if (x >= item->x2 + 1 - E_WEEK_VIEW_EVENT_R_PAD
108 - E_WEEK_VIEW_EVENT_BORDER_WIDTH
109 - E_WEEK_VIEW_EVENT_EDGE_X_PAD)
110 return E_CALENDAR_VIEW_POS_RIGHT_EDGE;
113 return E_CALENDAR_VIEW_POS_EVENT;
116 static gboolean
117 week_view_event_item_double_click (EWeekViewEventItem *event_item,
118 GdkEvent *button_event)
120 EWeekView *week_view;
121 EWeekViewEvent *event;
122 GnomeCanvasItem *item;
123 GtkWidget *parent;
125 item = GNOME_CANVAS_ITEM (event_item);
127 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
128 g_return_val_if_fail (E_IS_WEEK_VIEW (parent), FALSE);
130 week_view = E_WEEK_VIEW (parent);
132 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
133 return TRUE;
135 event = &g_array_index (
136 week_view->events, EWeekViewEvent,
137 event_item->priv->event_num);
139 if (!is_comp_data_valid (event))
140 return TRUE;
142 if (week_view->editing_event_num >= 0) {
143 EWeekViewEvent *editing;
145 if (!is_array_index_in_bounds (
146 week_view->events, week_view->editing_event_num))
147 return TRUE;
149 editing = &g_array_index (
150 week_view->events, EWeekViewEvent,
151 week_view->editing_event_num);
153 /* Do not call edit of the component, if double clicked
154 * on the component, which is not on the server. */
155 if (editing && event &&
156 editing->comp_data == event->comp_data &&
157 is_comp_data_valid (editing) &&
158 (!event->comp_data || event->comp_data->is_new_component))
159 return TRUE;
162 e_week_view_stop_editing_event (week_view);
164 e_calendar_view_edit_appointment (
165 E_CALENDAR_VIEW (week_view),
166 event->comp_data->client,
167 event->comp_data->icalcomp, EDIT_EVENT_AUTODETECT);
169 return TRUE;
172 static gboolean
173 week_view_event_item_button_press (EWeekViewEventItem *event_item,
174 GdkEvent *button_event)
176 EWeekView *week_view;
177 ECalendarViewPosition pos;
178 EWeekViewEvent *event;
179 EWeekViewEventSpan *span;
180 GnomeCanvasItem *item;
181 GtkWidget *parent;
182 guint event_button = 0;
183 gdouble event_x_win = 0;
184 gdouble event_y_win = 0;
186 gdk_event_get_button (button_event, &event_button);
187 gdk_event_get_coords (button_event, &event_x_win, &event_y_win);
189 item = GNOME_CANVAS_ITEM (event_item);
191 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
192 g_return_val_if_fail (E_IS_WEEK_VIEW (parent), FALSE);
194 week_view = E_WEEK_VIEW (parent);
196 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
197 return FALSE;
199 event = &g_array_index (
200 week_view->events, EWeekViewEvent,
201 event_item->priv->event_num);
203 if (!is_array_index_in_bounds (
204 week_view->spans, event->spans_index +
205 event_item->priv->span_num))
206 return FALSE;
208 span = &g_array_index (week_view->spans, EWeekViewEventSpan,
209 event->spans_index + event_item->priv->span_num);
211 pos = week_view_event_item_get_position (
212 event_item, event_x_win, event_y_win);
213 if (pos == E_CALENDAR_VIEW_POS_NONE)
214 return FALSE;
216 if (event_button == 1) {
217 week_view->pressed_event_num = event_item->priv->event_num;
218 week_view->pressed_span_num = event_item->priv->span_num;
220 /* Ignore clicks on the event while editing. */
221 if (E_TEXT (span->text_item)->editing)
222 return FALSE;
224 /* Remember the item clicked and the mouse position,
225 * so we can start a drag if the mouse moves. */
226 week_view->drag_event_x = event_x_win;
227 week_view->drag_event_y = event_y_win;
229 /* FIXME: Remember the day offset from the start of the event.
232 return TRUE;
234 } else if (event_button == 3) {
235 if (!gtk_widget_has_focus (GTK_WIDGET (week_view))) {
236 gtk_widget_grab_focus (GTK_WIDGET (week_view));
237 if (week_view->event_destroyed) {
238 week_view->event_destroyed = FALSE;
239 return FALSE;
244 e_week_view_set_selected_time_range_visible (
245 week_view, event->start, event->end);
247 e_week_view_show_popup_menu (
248 week_view, button_event,
249 event_item->priv->event_num);
250 g_signal_stop_emission_by_name (
251 item->canvas, "button_press_event");
253 return TRUE;
256 return FALSE;
259 static gboolean
260 week_view_event_item_button_release (EWeekViewEventItem *event_item,
261 GdkEvent *event)
263 EWeekView *week_view;
264 GnomeCanvasItem *item;
265 GtkWidget *parent;
267 item = GNOME_CANVAS_ITEM (event_item);
269 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
270 g_return_val_if_fail (E_IS_WEEK_VIEW (parent), FALSE);
272 week_view = E_WEEK_VIEW (parent);
274 if (week_view->pressed_event_num != -1
275 && week_view->pressed_event_num == event_item->priv->event_num
276 && week_view->pressed_span_num == event_item->priv->span_num) {
277 e_week_view_start_editing_event (
278 week_view,
279 event_item->priv->event_num,
280 event_item->priv->span_num,
281 NULL);
282 week_view->pressed_event_num = -1;
283 return TRUE;
286 week_view->pressed_event_num = -1;
288 return FALSE;
291 static void
292 week_view_draw_time (EWeekView *week_view,
293 GdkRGBA bg_rgba,
294 cairo_t *cr,
295 gint time_x,
296 gint time_y,
297 gint hour,
298 gint minute)
300 ECalModel *model;
301 gint hour_to_display, suffix_width;
302 gint time_y_normal_font, time_y_small_font;
303 const gchar *suffix;
304 gchar buffer[128];
305 PangoLayout *layout;
306 PangoFontDescription *small_font_desc;
307 PangoContext *pango_context;
308 GdkColor color;
310 color.pixel = 0;
312 if ((bg_rgba.red > 0.7) || (bg_rgba.green > 0.7) || (bg_rgba.blue > 0.7)) {
313 color.red = 0.0;
314 color.green = 0.0;
315 color.blue = 0.0;
316 } else {
317 color.red = 65535.0f;
318 color.green = 65535.0f;
319 color.blue = 65535.0f;
322 cairo_save (cr);
324 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
326 small_font_desc = week_view->small_font_desc;
328 gdk_cairo_set_source_color (cr, &color);
330 layout = gtk_widget_create_pango_layout (GTK_WIDGET (week_view), NULL);
331 pango_context = gtk_widget_create_pango_context (GTK_WIDGET (week_view));
333 time_y_normal_font = time_y_small_font = time_y;
334 if (small_font_desc)
335 time_y_small_font = time_y;
337 e_week_view_convert_time_to_display (
338 week_view, hour, &hour_to_display,
339 &suffix, &suffix_width);
341 if (week_view->use_small_font && week_view->small_font_desc) {
342 PangoFontDescription *font_desc;
344 font_desc = pango_font_description_copy (pango_context_get_font_description (pango_context));
346 g_snprintf (
347 buffer, sizeof (buffer), "%2i:%02i",
348 hour_to_display, minute);
350 /* Draw the hour. */
351 if (hour_to_display < 10) {
352 pango_layout_set_text (layout, buffer + 1, 1);
353 cairo_move_to (
355 time_x + week_view->digit_width,
356 time_y_normal_font);
357 pango_cairo_show_layout (cr, layout);
358 } else {
359 pango_layout_set_text (layout, buffer, 2);
360 cairo_move_to (
362 time_x,
363 time_y_normal_font);
364 pango_cairo_show_layout (cr, layout);
367 time_x += week_view->digit_width * 2;
369 /* Draw the start minute, in the small font. */
370 pango_layout_set_font_description (layout, week_view->small_font_desc);
371 pango_layout_set_text (layout, buffer + 3, 2);
372 cairo_move_to (
374 time_x,
375 time_y_small_font);
376 pango_cairo_show_layout (cr, layout);
378 pango_layout_set_font_description (layout, font_desc);
380 time_x += week_view->small_digit_width * 2;
382 /* Draw the 'am'/'pm' suffix, if 12-hour format. */
383 if (!e_cal_model_get_use_24_hour_format (model)) {
384 pango_layout_set_text (layout, suffix, -1);
386 cairo_move_to (
388 time_x,
389 time_y_normal_font);
390 pango_cairo_show_layout (cr, layout);
393 pango_font_description_free (font_desc);
394 } else {
395 /* Draw the start time in one go. */
396 g_snprintf (
397 buffer, sizeof (buffer), "%2i:%02i%s",
398 hour_to_display, minute, suffix);
399 if (hour_to_display < 10) {
400 pango_layout_set_text (layout, buffer + 1, -1);
401 cairo_move_to (
403 time_x + week_view->digit_width,
404 time_y_normal_font);
405 pango_cairo_show_layout (cr, layout);
406 } else {
407 pango_layout_set_text (layout, buffer, -1);
408 cairo_move_to (
410 time_x,
411 time_y_normal_font);
412 pango_cairo_show_layout (cr, layout);
416 g_object_unref (pango_context);
417 g_object_unref (layout);
419 cairo_restore (cr);
422 static void
423 week_view_event_item_draw_icons (EWeekViewEventItem *event_item,
424 cairo_t *cr,
425 gint icon_x,
426 gint icon_y,
427 gint x2,
428 gboolean right_align,
429 cairo_region_t *draw_region)
431 EWeekView *week_view;
432 EWeekViewEvent *event;
433 ECalComponent *comp;
434 GnomeCanvas *canvas;
435 GtkWidget *parent;
436 gint num_icons = 0, icon_x_inc;
437 gboolean draw_reminder_icon = FALSE, draw_recurrence_icon = FALSE;
438 gboolean draw_timezone_icon = FALSE, draw_attach_icon = FALSE;
439 gboolean draw_meeting_icon = FALSE;
440 GSList *categories_pixbufs = NULL, *pixbufs;
442 canvas = GNOME_CANVAS_ITEM (event_item)->canvas;
443 parent = gtk_widget_get_parent (GTK_WIDGET (canvas));
444 week_view = E_WEEK_VIEW (parent);
446 if (e_week_view_get_multi_week_view (week_view) &&
447 !e_week_view_get_show_icons_month_view (week_view))
448 return;
450 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
451 return;
453 event = &g_array_index (week_view->events, EWeekViewEvent,
454 event_item->priv->event_num);
456 if (!is_comp_data_valid (event))
457 return;
459 comp = e_cal_component_new ();
460 e_cal_component_set_icalcomponent (
461 comp, icalcomponent_new_clone (event->comp_data->icalcomp));
463 if (e_cal_component_has_alarms (comp)) {
464 draw_reminder_icon = TRUE;
465 num_icons++;
468 if (e_cal_component_has_recurrences (comp) ||
469 e_cal_component_is_instance (comp)) {
470 draw_recurrence_icon = TRUE;
471 num_icons++;
474 if (e_cal_component_has_attachments (comp)) {
475 draw_attach_icon = TRUE;
476 num_icons++;
479 if (e_cal_component_has_attendees (comp)) {
480 draw_meeting_icon = TRUE;
481 num_icons++;
484 if (event->different_timezone) {
485 draw_timezone_icon = TRUE;
486 num_icons++;
489 num_icons += cal_comp_util_get_n_icons (comp, &categories_pixbufs);
491 icon_x_inc = E_WEEK_VIEW_ICON_WIDTH + E_WEEK_VIEW_ICON_X_PAD;
493 if (right_align)
494 icon_x -= icon_x_inc * num_icons;
496 #define draw_pixbuf(pf) \
497 if (can_draw_in_region (draw_region, icon_x, icon_y, \
498 E_WEEK_VIEW_ICON_WIDTH, E_WEEK_VIEW_ICON_HEIGHT)) { \
499 cairo_save (cr); \
500 gdk_cairo_set_source_pixbuf (cr, pf, icon_x, icon_y); \
501 cairo_paint (cr); \
502 cairo_restore (cr); \
505 icon_x += icon_x_inc;
507 if (draw_reminder_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
508 draw_pixbuf (week_view->reminder_icon);
511 if (draw_attach_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
512 draw_pixbuf (week_view->attach_icon);
515 if (draw_recurrence_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
516 draw_pixbuf (week_view->recurrence_icon);
519 if (draw_timezone_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
520 draw_pixbuf (week_view->timezone_icon);
523 if (draw_meeting_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
524 draw_pixbuf (week_view->meeting_icon);
527 /* draw categories icons */
528 for (pixbufs = categories_pixbufs;
529 pixbufs;
530 pixbufs = pixbufs->next) {
531 GdkPixbuf *pixbuf = pixbufs->data;
533 draw_pixbuf (pixbuf);
536 #undef draw_pixbuf
538 g_slist_foreach (categories_pixbufs, (GFunc) g_object_unref, NULL);
539 g_slist_free (categories_pixbufs);
541 g_object_unref (comp);
544 /* This draws a little triangle to indicate that an event extends past
545 * the days visible on screen. */
546 static void
547 week_view_event_item_draw_triangle (EWeekViewEventItem *event_item,
548 cairo_t *cr,
549 GdkRGBA bg_rgba,
550 gint x,
551 gint y,
552 gint w,
553 gint h,
554 cairo_region_t *draw_region)
556 EWeekView *week_view;
557 EWeekViewEvent *event;
558 GnomeCanvas *canvas;
559 GtkWidget *parent;
560 GdkPoint points[3];
561 gint c1, c2;
563 if (!can_draw_in_region (draw_region, x, y, w, h))
564 return;
566 canvas = GNOME_CANVAS_ITEM (event_item)->canvas;
567 parent = gtk_widget_get_parent (GTK_WIDGET (canvas));
568 week_view = E_WEEK_VIEW (parent);
570 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
571 return;
573 event = &g_array_index (week_view->events, EWeekViewEvent,
574 event_item->priv->event_num);
576 if (!is_comp_data_valid (event))
577 return;
579 points[0].x = x;
580 points[0].y = y;
581 points[1].x = x + w;
582 points[1].y = y + (h / 2);
583 points[2].x = x;
584 points[2].y = y + h - 1;
586 gdk_cairo_set_source_rgba (cr, &bg_rgba);
588 cairo_save (cr);
589 cairo_set_line_width (cr, 0.7);
590 cairo_move_to (cr, points[0].x, points[0].y);
591 cairo_line_to (cr, points[1].x, points[1].y);
592 cairo_line_to (cr, points[2].x, points[2].y);
593 cairo_line_to (cr, points[0].x, points[0].y);
594 cairo_fill (cr);
595 cairo_restore (cr);
597 cairo_save (cr);
598 gdk_cairo_set_source_color (
599 cr, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER]);
601 /* If the height is odd we can use the same central point for both
602 * lines. If it is even we use different end-points. */
603 c1 = c2 = y + (h / 2);
604 if (h % 2 == 0)
605 c1--;
607 cairo_set_line_width (cr, 0.7);
608 cairo_move_to (cr, x, y);
609 cairo_line_to (cr, x + w, c1);
610 cairo_move_to (cr, x, y + h - 1);
611 cairo_line_to (cr, x + w, c2);
612 cairo_restore (cr);
615 static void
616 week_view_event_item_set_property (GObject *object,
617 guint property_id,
618 const GValue *value,
619 GParamSpec *pspec)
621 switch (property_id) {
622 case PROP_EVENT_NUM:
623 e_week_view_event_item_set_event_num (
624 E_WEEK_VIEW_EVENT_ITEM (object),
625 g_value_get_int (value));
626 return;
628 case PROP_SPAN_NUM:
629 e_week_view_event_item_set_span_num (
630 E_WEEK_VIEW_EVENT_ITEM (object),
631 g_value_get_int (value));
632 return;
635 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
638 static void
639 week_view_event_item_get_property (GObject *object,
640 guint property_id,
641 GValue *value,
642 GParamSpec *pspec)
644 switch (property_id) {
645 case PROP_EVENT_NUM:
646 g_value_set_int (
647 value,
648 e_week_view_event_item_get_event_num (
649 E_WEEK_VIEW_EVENT_ITEM (object)));
650 return;
652 case PROP_SPAN_NUM:
653 g_value_set_int (
654 value,
655 e_week_view_event_item_get_span_num (
656 E_WEEK_VIEW_EVENT_ITEM (object)));
657 return;
660 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
663 static void
664 week_view_event_item_update (GnomeCanvasItem *item,
665 const cairo_matrix_t *i2c,
666 gint flags)
668 GnomeCanvasItemClass *canvas_item_class;
669 EWeekViewEventItem *event_item;
670 EWeekView *week_view;
671 GtkWidget *parent;
672 gint event_num, span_num;
673 gint span_x, span_y, span_w;
675 event_item = E_WEEK_VIEW_EVENT_ITEM (item);
676 parent = gtk_widget_get_parent (GTK_WIDGET (item->canvas));
677 g_return_if_fail (E_IS_WEEK_VIEW (parent));
679 week_view = E_WEEK_VIEW (parent);
681 /* Chain up to parent's update() method. */
682 canvas_item_class =
683 GNOME_CANVAS_ITEM_CLASS (e_week_view_event_item_parent_class);
684 canvas_item_class->update (item, i2c, flags);
686 item->x1 = 0;
687 item->y1 = 0;
688 item->x2 = 0;
689 item->y2 = 0;
691 event_num = e_week_view_event_item_get_event_num (event_item);
692 span_num = e_week_view_event_item_get_span_num (event_item);
694 if (event_num != -1 && span_num != -1) {
695 if (e_week_view_get_span_position (
696 week_view, event_num, span_num,
697 &span_x, &span_y, &span_w)) {
698 item->x1 = span_x;
699 item->y1 = span_y;
700 item->x2 = span_x + span_w - 1;
701 item->y2 = span_y + week_view->row_height - 1;
706 static void
707 week_view_event_item_draw (GnomeCanvasItem *canvas_item,
708 cairo_t *cr,
709 gint x,
710 gint y,
711 gint width,
712 gint height)
714 EWeekViewEventItem *event_item;
715 EWeekView *week_view;
716 EWeekViewEvent *event;
717 EWeekViewEventSpan *span;
718 ECalModel *model;
719 GtkWidget *parent;
720 gint x1, y1, x2, y2, time_x, time_y;
721 gint icon_x, icon_y, time_width, min_end_time_x, max_icon_x;
722 gint rect_x, rect_w, rect_x2 = 0;
723 gboolean one_day_event, editing_span = FALSE;
724 gint start_hour, start_minute, end_hour, end_minute;
725 gboolean draw_start, draw_end;
726 gboolean draw_start_triangle = FALSE, draw_end_triangle = FALSE;
727 GdkRGBA bg_rgba;
728 cairo_pattern_t *pat;
729 gdouble radius, cx0, cy0, rect_height, rect_width;
730 cairo_region_t *draw_region;
731 GdkRectangle rect;
732 gboolean draw_flat_events;
734 event_item = E_WEEK_VIEW_EVENT_ITEM (canvas_item);
735 parent = gtk_widget_get_parent (GTK_WIDGET (canvas_item->canvas));
736 g_return_if_fail (E_IS_WEEK_VIEW (parent));
738 week_view = E_WEEK_VIEW (parent);
740 if (event_item->priv->event_num == -1 || event_item->priv->span_num == -1)
741 return;
743 g_return_if_fail (event_item->priv->event_num < week_view->events->len);
745 if (!is_array_index_in_bounds (week_view->events, event_item->priv->event_num))
746 return;
748 event = &g_array_index (week_view->events, EWeekViewEvent,
749 event_item->priv->event_num);
751 if (!is_comp_data_valid (event))
752 return;
754 g_return_if_fail (
755 event->spans_index + event_item->priv->span_num <
756 week_view->spans->len);
758 if (!is_array_index_in_bounds (
759 week_view->spans, event->spans_index +
760 event_item->priv->span_num))
761 return;
763 span = &g_array_index (
764 week_view->spans, EWeekViewEventSpan,
765 event->spans_index + event_item->priv->span_num);
767 x1 = canvas_item->x1 - x;
768 y1 = canvas_item->y1 - y;
769 x2 = canvas_item->x2 - x;
770 y2 = canvas_item->y2 - y;
772 if (x1 == x2 || y1 == y2)
773 return;
775 rect.x = 0;
776 rect.y = 0;
777 rect.width = width;
778 rect.height = height;
779 if (rect.width > 0 && rect.height > 0)
780 draw_region = cairo_region_create_rectangle (&rect);
781 else
782 draw_region = cairo_region_create ();
784 if (!can_draw_in_region (draw_region, x1, y1, x2 - x1, y2 - y1)) {
785 cairo_region_destroy (draw_region);
786 return;
789 draw_flat_events = e_week_view_get_draw_flat_events (week_view);
791 icon_y = y1 + E_WEEK_VIEW_EVENT_BORDER_HEIGHT + E_WEEK_VIEW_ICON_Y_PAD;
793 /* Get the start & end times in 24-hour format. */
794 start_hour = event->start_minute / 60;
795 start_minute = event->start_minute % 60;
797 /* Modulo 24 because a midnight end time will be '24' */
798 end_hour = (event->end_minute / 60) % 24;
799 end_minute = event->end_minute % 60;
801 time_y = y1 + E_WEEK_VIEW_EVENT_BORDER_HEIGHT
802 + E_WEEK_VIEW_EVENT_TEXT_Y_PAD;
804 time_width = e_week_view_get_time_string_width (week_view);
806 one_day_event = e_week_view_is_one_day_event (
807 week_view, event_item->priv->event_num);
809 model = e_calendar_view_get_model (E_CALENDAR_VIEW (week_view));
811 if (!e_cal_model_get_rgba_for_component (model, event->comp_data, &bg_rgba)) {
812 gdouble cc = 65535.0;
814 bg_rgba.red = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].red / cc;
815 bg_rgba.green = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].green / cc;
816 bg_rgba.blue = week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].blue / cc;
817 bg_rgba.alpha = 1.0;
820 if (one_day_event) {
821 time_x = x1 + E_WEEK_VIEW_EVENT_L_PAD + 1;
822 rect_x = x1 + E_WEEK_VIEW_EVENT_L_PAD;
823 rect_w = x2 - x1 - E_WEEK_VIEW_EVENT_L_PAD - E_WEEK_VIEW_EVENT_R_PAD + 1;
825 cx0 = rect_x;
826 cy0 = y1 + 1;
827 rect_width = rect_w;
828 rect_height = y2 - y1 - 1;
830 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
831 if (draw_flat_events) {
832 cairo_save (cr);
833 cairo_rectangle (cr, cx0, cy0, rect_width, rect_height);
834 gdk_cairo_set_source_rgba (cr, &bg_rgba);
835 cairo_fill (cr);
836 cairo_restore (cr);
837 } else {
838 /* Here we draw the border around the event*/
840 radius = 12;
842 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
843 cairo_save (cr);
844 draw_curved_rectangle (cr, cx0, cy0, rect_width, rect_height, radius);
845 cairo_set_line_width (cr, 2.0);
846 gdk_cairo_set_source_rgba (cr, &bg_rgba);
847 cairo_stroke (cr);
848 cairo_restore (cr);
851 /* Fill it in the Event */
853 cx0 = rect_x + 1.5;
854 cy0 = y1 + 2.75;
855 rect_width = rect_w - 3.;
856 rect_height = y2 - y1 - 4.5;
858 radius = 8;
860 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
861 cairo_save (cr);
862 draw_curved_rectangle (
863 cr, cx0, cy0, rect_width, rect_height, radius);
865 pat = cairo_pattern_create_linear (
866 rect_x + 2, y1 + 1, rect_x + 2, y2 - 7.25);
867 cairo_pattern_add_color_stop_rgba (pat, 1, bg_rgba.red, bg_rgba.green, bg_rgba.blue, 0.8 * bg_rgba.alpha);
868 cairo_pattern_add_color_stop_rgba (pat, 0, bg_rgba.red, bg_rgba.green, bg_rgba.blue, 0.4 * bg_rgba.alpha);
869 cairo_set_source (cr, pat);
870 cairo_fill_preserve (cr);
871 cairo_pattern_destroy (pat);
872 cairo_set_source_rgba (cr, bg_rgba.red, bg_rgba.green, bg_rgba.blue, 0.2 * bg_rgba.alpha);
873 cairo_set_line_width (cr, 0.5);
874 cairo_stroke (cr);
875 cairo_restore (cr);
880 /* Draw the start and end times, as required. */
881 switch (week_view->time_format) {
882 case E_WEEK_VIEW_TIME_BOTH_SMALL_MIN:
883 case E_WEEK_VIEW_TIME_BOTH:
884 draw_start = TRUE;
885 draw_end = TRUE;
886 break;
888 case E_WEEK_VIEW_TIME_START_SMALL_MIN:
889 case E_WEEK_VIEW_TIME_START:
890 draw_start = TRUE;
891 draw_end = FALSE;
892 break;
894 case E_WEEK_VIEW_TIME_NONE:
895 draw_start = FALSE;
896 draw_end = FALSE;
897 break;
898 default:
899 g_warn_if_reached ();
900 draw_start = FALSE;
901 draw_end = FALSE;
902 break;
905 if (draw_start) {
906 week_view_draw_time (
907 week_view, bg_rgba, cr, time_x,
908 time_y, start_hour, start_minute);
909 time_x += time_width;
912 if (draw_end && (!draw_start || event->start_minute != event->end_minute)) {
913 time_x += E_WEEK_VIEW_EVENT_TIME_SPACING;
914 week_view_draw_time (
915 week_view, bg_rgba, cr, time_x,
916 time_y, end_hour, end_minute);
917 time_x += time_width;
920 icon_x = time_x;
921 if (draw_start)
922 icon_x += E_WEEK_VIEW_EVENT_TIME_X_PAD;
924 /* Draw the icons. */
925 week_view_event_item_draw_icons (
926 event_item, cr, icon_x,
927 icon_y, x2, FALSE, draw_region);
929 } else {
930 rect_x = x1 + E_WEEK_VIEW_EVENT_L_PAD;
931 rect_w = x2 - x1 - E_WEEK_VIEW_EVENT_L_PAD
932 - E_WEEK_VIEW_EVENT_R_PAD + 1;
934 /* Draw the triangles at the start & end, if needed.
935 * They also use the first few pixels at the edge of the
936 * event so we update rect_x & rect_w so we don't draw over
937 * them. */
938 if (event->start < week_view->day_starts[span->start_day]) {
939 draw_start_triangle = TRUE;
940 rect_x += 2;
941 rect_w -= 2;
944 if (event->end > week_view->day_starts[span->start_day
945 + span->num_days]) {
946 draw_end_triangle = TRUE;
947 rect_w -= 2;
950 cx0 = rect_x;
951 cy0 = y1 + 1;
952 rect_width = rect_w;
953 rect_height = y2 - y1 - 1;
955 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
956 if (draw_flat_events) {
957 cairo_save (cr);
958 gdk_cairo_set_source_rgba (cr, &bg_rgba);
959 cairo_rectangle (cr, cx0, cy0, rect_width, rect_height);
960 cairo_fill (cr);
961 cairo_restore (cr);
962 } else {
964 /* Here we draw the border around the event */
966 radius = 12;
968 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
969 cairo_save (cr);
970 draw_curved_rectangle (cr, cx0, cy0, rect_width, rect_height, radius);
971 cairo_set_line_width (cr, 2.0);
972 gdk_cairo_set_source_rgba (cr, &bg_rgba);
973 cairo_stroke (cr);
974 cairo_restore (cr);
977 /* Here we fill it in the event*/
979 cx0 = rect_x + 1.5;
980 cy0 = y1 + 2.75;
981 rect_width = rect_w - 3.;
982 rect_height = y2 - y1 - 4.5;
984 radius = 8;
986 if (can_draw_in_region (draw_region, cx0, cy0, rect_width, rect_height)) {
987 cairo_save (cr);
988 draw_curved_rectangle (
989 cr, cx0, cy0, rect_width, rect_height, radius);
991 pat = cairo_pattern_create_linear (rect_x + 2, y1 + 1, rect_x + 2, y2 - 7.25);
992 cairo_pattern_add_color_stop_rgba (pat, 1, bg_rgba.red, bg_rgba.green, bg_rgba.blue, 0.8 * bg_rgba.alpha);
993 cairo_pattern_add_color_stop_rgba (pat, 0, bg_rgba.red, bg_rgba.green, bg_rgba.blue, 0.4 * bg_rgba.alpha);
994 cairo_set_source (cr, pat);
995 cairo_fill_preserve (cr);
996 cairo_pattern_destroy (pat);
997 cairo_set_source_rgba (cr, bg_rgba.red, bg_rgba.green, bg_rgba.blue, 0.2 * bg_rgba.alpha);
998 cairo_set_line_width (cr, 0.5);
999 cairo_stroke (cr);
1000 cairo_restore (cr);
1005 if (draw_start_triangle) {
1006 week_view_event_item_draw_triangle (
1007 event_item, cr, bg_rgba,
1008 x1 + E_WEEK_VIEW_EVENT_L_PAD + 2,
1009 y1, -3, y2 - y1 + 1, draw_region);
1010 } else if (can_draw_in_region (draw_region, rect_x, y1, 1, y2 - y1)) {
1011 EWeekViewColors wvc;
1012 GdkColor *color;
1014 wvc = E_WEEK_VIEW_COLOR_EVENT_BORDER;
1015 color = &week_view->colors[wvc];
1017 cairo_save (cr);
1018 gdk_cairo_set_source_color (cr, color);
1019 cairo_set_line_width (cr, 0.7);
1020 cairo_move_to (cr, rect_x, y1);
1021 cairo_line_to (cr, rect_x, y2);
1022 cairo_stroke (cr);
1023 cairo_restore (cr);
1026 if (draw_end_triangle) {
1027 week_view_event_item_draw_triangle (
1028 event_item, cr, bg_rgba,
1029 x2 - E_WEEK_VIEW_EVENT_R_PAD - 2,
1030 y1, 3, y2 - y1 + 1, draw_region);
1031 } else if (can_draw_in_region (draw_region, rect_x2, y2, 1, 1)) {
1032 EWeekViewColors wvc;
1033 GdkColor *color;
1035 wvc = E_WEEK_VIEW_COLOR_EVENT_BORDER;
1036 color = &week_view->colors[wvc];
1038 cairo_save (cr);
1039 gdk_cairo_set_source_color (cr, color);
1040 cairo_set_line_width (cr, 0.7);
1041 /* rect_x2 is used uninitialized here */
1042 cairo_move_to (cr, rect_x2, y1);
1043 cairo_line_to (cr, rect_x2, y2);
1044 cairo_stroke (cr);
1045 cairo_restore (cr);
1048 if (span->text_item && E_TEXT (span->text_item)->editing)
1049 editing_span = TRUE;
1051 /* Draw the start & end times, if they are not on day
1052 * boundaries. The start time would always be shown if it was
1053 * needed, though it may be clipped as the window shrinks.
1054 * The end time is only displayed if there is enough room.
1055 * We calculate the minimum position for the end time, which
1056 * depends on whether the start time is displayed. If the end
1057 * time doesn't fit, then we don't draw it. */
1058 min_end_time_x = x1 + E_WEEK_VIEW_EVENT_L_PAD
1059 + E_WEEK_VIEW_EVENT_BORDER_WIDTH
1060 + E_WEEK_VIEW_EVENT_EDGE_X_PAD;
1061 if (!editing_span
1062 && event->start > week_view->day_starts[span->start_day]) {
1063 time_x = x1 + E_WEEK_VIEW_EVENT_L_PAD
1064 + E_WEEK_VIEW_EVENT_BORDER_WIDTH
1065 + E_WEEK_VIEW_EVENT_EDGE_X_PAD;
1067 cairo_save (cr);
1069 cairo_rectangle (
1071 x1, y1,
1072 x2 - x1 - E_WEEK_VIEW_EVENT_R_PAD
1073 - E_WEEK_VIEW_EVENT_BORDER_WIDTH + 1,
1074 y2 - y1 + 1);
1075 cairo_clip (cr);
1077 week_view_draw_time (
1078 week_view, bg_rgba, cr, time_x,
1079 time_y, start_hour, start_minute);
1081 cairo_restore (cr);
1083 /* We don't want the end time to be drawn over the
1084 * start time, so we increase the minimum position. */
1085 min_end_time_x += time_width
1086 + E_WEEK_VIEW_EVENT_TIME_X_PAD;
1089 max_icon_x = x2 + 1 - E_WEEK_VIEW_EVENT_R_PAD
1090 - E_WEEK_VIEW_EVENT_BORDER_WIDTH
1091 - E_WEEK_VIEW_EVENT_EDGE_X_PAD;
1093 if (!editing_span
1094 && event->end < week_view->day_starts[span->start_day
1095 + span->num_days]) {
1096 /* Calculate where the end time should be displayed. */
1097 time_x = x2 + 1 - E_WEEK_VIEW_EVENT_R_PAD
1098 - E_WEEK_VIEW_EVENT_BORDER_WIDTH
1099 - E_WEEK_VIEW_EVENT_EDGE_X_PAD
1100 - time_width;
1102 /* Draw the end time, if the position is greater than
1103 * the minimum calculated above. */
1104 if (time_x >= min_end_time_x) {
1105 week_view_draw_time (
1106 week_view, bg_rgba, cr, time_x,
1107 time_y, end_hour, end_minute);
1108 max_icon_x -= time_width
1109 + E_WEEK_VIEW_EVENT_TIME_X_PAD;
1113 /* Draw the icons. */
1114 if (span->text_item
1115 && (week_view->editing_event_num != event_item->priv->event_num
1116 || week_view->editing_span_num != event_item->priv->span_num)) {
1117 icon_x = span->text_item->x1 - E_WEEK_VIEW_ICON_R_PAD - x;
1118 week_view_event_item_draw_icons (
1119 event_item, cr, icon_x,
1120 icon_y, max_icon_x, TRUE, draw_region);
1124 cairo_region_destroy (draw_region);
1127 static GnomeCanvasItem *
1128 week_view_event_item_point (GnomeCanvasItem *item,
1129 gdouble x,
1130 gdouble y,
1131 gint cx,
1132 gint cy)
1134 return item;
1137 static gint
1138 week_view_event_item_event (GnomeCanvasItem *item,
1139 GdkEvent *event)
1141 EWeekViewEventItem *event_item;
1143 event_item = E_WEEK_VIEW_EVENT_ITEM (item);
1145 switch (event->type) {
1146 case GDK_2BUTTON_PRESS:
1147 return week_view_event_item_double_click (event_item, event);
1148 case GDK_BUTTON_PRESS:
1149 return week_view_event_item_button_press (event_item, event);
1150 case GDK_BUTTON_RELEASE:
1151 return week_view_event_item_button_release (event_item, event);
1152 case GDK_MOTION_NOTIFY:
1153 break;
1154 default:
1155 break;
1158 return FALSE;
1161 static void
1162 e_week_view_event_item_class_init (EWeekViewEventItemClass *class)
1164 GObjectClass *object_class;
1165 GnomeCanvasItemClass *item_class;
1167 g_type_class_add_private (class, sizeof (EWeekViewEventItemPrivate));
1169 object_class = G_OBJECT_CLASS (class);
1170 object_class->set_property = week_view_event_item_set_property;
1171 object_class->get_property = week_view_event_item_get_property;
1173 item_class = GNOME_CANVAS_ITEM_CLASS (class);
1174 item_class->update = week_view_event_item_update;
1175 item_class->draw = week_view_event_item_draw;
1176 item_class->point = week_view_event_item_point;
1177 item_class->event = week_view_event_item_event;
1179 g_object_class_install_property (
1180 object_class,
1181 PROP_EVENT_NUM,
1182 g_param_spec_int (
1183 "event-num",
1184 "Event Num",
1185 NULL,
1186 G_MININT,
1187 G_MAXINT,
1189 G_PARAM_READWRITE));
1191 g_object_class_install_property (
1192 object_class,
1193 PROP_SPAN_NUM,
1194 g_param_spec_int (
1195 "span-num",
1196 "Span Num",
1197 NULL,
1198 G_MININT,
1199 G_MAXINT,
1201 G_PARAM_READWRITE));
1204 static void
1205 e_week_view_event_item_init (EWeekViewEventItem *event_item)
1207 event_item->priv = E_WEEK_VIEW_EVENT_ITEM_GET_PRIVATE (event_item);
1209 event_item->priv->event_num = -1;
1210 event_item->priv->span_num = -1;
1213 gint
1214 e_week_view_event_item_get_event_num (EWeekViewEventItem *event_item)
1216 g_return_val_if_fail (E_IS_WEEK_VIEW_EVENT_ITEM (event_item), -1);
1218 return event_item->priv->event_num;
1221 void
1222 e_week_view_event_item_set_event_num (EWeekViewEventItem *event_item,
1223 gint event_num)
1225 g_return_if_fail (E_IS_WEEK_VIEW_EVENT_ITEM (event_item));
1227 if (event_item->priv->event_num == event_num)
1228 return;
1230 event_item->priv->event_num = event_num;
1231 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (event_item));
1233 g_object_notify (G_OBJECT (event_item), "event-num");
1236 gint
1237 e_week_view_event_item_get_span_num (EWeekViewEventItem *event_item)
1239 g_return_val_if_fail (E_IS_WEEK_VIEW_EVENT_ITEM (event_item), -1);
1241 return event_item->priv->span_num;
1244 void
1245 e_week_view_event_item_set_span_num (EWeekViewEventItem *event_item,
1246 gint span_num)
1248 g_return_if_fail (E_IS_WEEK_VIEW_EVENT_ITEM (event_item));
1250 if (event_item->priv->span_num == span_num)
1251 return;
1253 event_item->priv->span_num = span_num;
1254 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (event_item));
1256 g_object_notify (G_OBJECT (event_item), "span-num");