/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * Damon Chaplin * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ /* * EDayViewMainItem - canvas item which displays most of the appointment * data in the main Day/Work Week display. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "e-util/e-categories-config.h" #include "e-util/e-util.h" #include "e-day-view-layout.h" #include "e-day-view-main-item.h" #include "ea-calendar.h" #include "e-calendar-view.h" #include "comp-util.h" struct _EDayViewMainItemPrivate { EDayView *day_view; }; enum { PROP_0, PROP_DAY_VIEW }; static gpointer parent_class; static gboolean can_draw_in_region (cairo_region_t *draw_region, gint x, gint y, gint width, gint height) { GdkRectangle rect; g_return_val_if_fail (draw_region != NULL, FALSE); rect.x = x; rect.y = y; rect.width = width; rect.height = height; return cairo_region_contains_rectangle (draw_region, &rect) != CAIRO_REGION_OVERLAP_OUT; } static gboolean icalcomp_is_transparent (icalcomponent *icalcomp) { icalproperty *transp_prop; icalproperty_transp ical_transp = ICAL_TRANSP_NONE; g_return_val_if_fail (icalcomp != NULL, TRUE); transp_prop = icalcomponent_get_first_property (icalcomp, ICAL_TRANSP_PROPERTY); if (transp_prop) ical_transp = icalproperty_get_transp (transp_prop); return transp_prop && (ical_transp == ICAL_TRANSP_TRANSPARENT || ical_transp == ICAL_TRANSP_TRANSPARENTNOCONFLICT); } static void day_view_main_item_draw_long_events_in_vbars (EDayViewMainItem *main_item, cairo_t *cr, gint x, gint y, gint width, gint height, cairo_region_t *draw_region) { EDayView *day_view; EDayViewEvent *event; ECalendarView *cal_view; gint time_divisions; gint event_num, start_day, end_day, day, bar_y1, bar_y2, grid_x; GdkColor bg_color; day_view = e_day_view_main_item_get_day_view (main_item); cal_view = E_CALENDAR_VIEW (day_view); time_divisions = e_calendar_view_get_time_divisions (cal_view); for (event_num = 0; event_num < day_view->long_events->len; event_num++) { gboolean first = TRUE; event = &g_array_index (day_view->long_events, EDayViewEvent, event_num); if (!is_comp_data_valid (event)) continue; /* If the event is TRANSPARENT, skip it. */ if (icalcomp_is_transparent (event->comp_data->icalcomp)) { continue; } if (!e_day_view_find_long_event_days (event, day_view->days_shown, day_view->day_starts, &start_day, &end_day)) { continue; } for (day = start_day; day <= end_day; day++) { grid_x = day_view->day_offsets[day] + 1 - x; /* Skip if it isn't visible. */ if (grid_x >= width || grid_x + E_DAY_VIEW_BAR_WIDTH <= 0) continue; if (event->start <= day_view->day_starts[day]) { bar_y1 = 0; } else { bar_y1 = event->start_minute * day_view->row_height / time_divisions - y; } if (event->end >= day_view->day_starts[day + 1]) { bar_y2 = height; } else { bar_y2 = event->end_minute * day_view->row_height / time_divisions - y; } if (bar_y1 < height && bar_y2 > 0 && bar_y2 > bar_y1 && can_draw_in_region (draw_region, grid_x, bar_y1, E_DAY_VIEW_BAR_WIDTH - 2, bar_y2 - bar_y1)) { cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND]); if (first) { first = FALSE; if (gdk_color_parse (e_cal_model_get_color_for_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), event->comp_data), &bg_color)) { gdk_cairo_set_source_color (cr, &bg_color); } } cairo_rectangle (cr, grid_x, bar_y1, E_DAY_VIEW_BAR_WIDTH - 2, bar_y2 - bar_y1); cairo_fill (cr); cairo_restore (cr); } } } } static void day_view_main_item_draw_day_event (EDayViewMainItem *main_item, cairo_t *cr, gint x, gint y, gint width, gint height, gint day, gint event_num, cairo_region_t *draw_region) { EDayView *day_view; EDayViewEvent *event; ECalModel *model; ECalendarView *cal_view; gint time_divisions; gint item_x, item_y, item_w, item_h, bar_y1, bar_y2; GdkColor bg_color; ECalComponent *comp; gint num_icons, icon_x, icon_y, icon_x_inc = 0, icon_y_inc = 0; gint max_icon_w, max_icon_h; gboolean draw_reminder_icon, draw_recurrence_icon, draw_timezone_icon, draw_meeting_icon; gboolean draw_attach_icon; ECalComponentTransparency transparency; cairo_pattern_t *pat; guint16 red, green, blue; gint i; gdouble radius, x0, y0, rect_height, rect_width, text_x_offset = 0.0; gdouble cc = 65535.0; gdouble date_fraction; gboolean short_event = FALSE, resize_flag = FALSE, is_editing; const gchar *end_resize_suffix; gchar *end_regsizeime; gint start_hour, start_display_hour, start_minute, start_suffix_width; gint end_hour, end_display_hour, end_minute, end_suffix_width; gboolean show_span = FALSE, format_time; gint offset, interval; const gchar *start_suffix; const gchar *end_suffix; gchar *text = NULL; gint scroll_flag = 0; gint row_y; day_view = e_day_view_main_item_get_day_view (main_item); cal_view = E_CALENDAR_VIEW (day_view); model = e_calendar_view_get_model (cal_view); time_divisions = e_calendar_view_get_time_divisions (cal_view); /* If the event is currently being dragged, don't draw it. It will be drawn in the special drag items. */ if (day_view->drag_event_day == day && day_view->drag_event_num == event_num) return; /* Get the position of the event. If it is not shown skip it.*/ if (!e_day_view_get_event_position (day_view, day, event_num, &item_x, &item_y, &item_w, &item_h)) return; item_x -= x; item_y -= y; if (!can_draw_in_region (draw_region, item_x, item_y, item_w, item_h)) return; gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR]); if (!is_array_index_in_bounds (day_view->events[day], event_num)) return; event = &g_array_index (day_view->events[day], EDayViewEvent, event_num); if (!is_comp_data_valid (event)) return; /* Fill in the event background. Note that for events in the first column of the day, we might not want to paint over the vertical bar, since that is used for multiple events. But then you can't see where the event in the first column finishes. The border is drawn along with the event using cairo*/ red = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].red; green = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].green; blue = day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].blue; if (gdk_color_parse (e_cal_model_get_color_for_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), event->comp_data), &bg_color)) { red = bg_color.red; green = bg_color.green; blue = bg_color.blue; } is_editing = day_view->editing_event_day == day && day_view->editing_event_num == event_num; if (event->canvas_item) g_object_get (G_OBJECT (event->canvas_item), "x_offset", &text_x_offset, NULL); /* Draw shadow around the event when selected */ if (is_editing && (gtk_widget_has_focus (day_view->main_canvas))) { /* For embossing Item selection */ item_x -= 1; item_y -= 2; if (MAX (0, item_w - 31.5) != 0) { /* Vertical Line */ cairo_save (cr); pat = cairo_pattern_create_linear (item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 6.5, item_y + 13.75, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 13.75, item_y + 13.75); cairo_pattern_add_color_stop_rgba (pat, 0, 0, 0, 0, 1); cairo_pattern_add_color_stop_rgba (pat, 0.7, 0, 0, 0, 0.2); cairo_pattern_add_color_stop_rgba (pat, 1, 1, 1, 1, 0.3); cairo_set_source (cr, pat); cairo_rectangle (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 6.5, item_y + 14.75, 7.0, item_h - 22.0); cairo_fill (cr); cairo_pattern_destroy (pat); /* Arc at the right */ pat = cairo_pattern_create_radial (item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 3, item_y + 13.5, 5.0, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 5, item_y + 13.5, 12.0); cairo_pattern_add_color_stop_rgba (pat, 1, 1, 1, 1, 0.3); cairo_pattern_add_color_stop_rgba (pat, 0.25, 0, 0, 0, 0.2); cairo_pattern_add_color_stop_rgba (pat, 0, 0, 0, 0, 1); cairo_set_source (cr, pat); cairo_arc (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 5, item_y + 13.5, 8.0, 11 * G_PI / 8, G_PI / 8); cairo_fill (cr); cairo_pattern_destroy (pat); cairo_set_source_rgb (cr, 0, 0, 0); cairo_set_line_width (cr, 1.25); cairo_move_to (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 5, item_y + 9.5); cairo_line_to (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 9.5, item_y + 15); cairo_stroke (cr); /* Horizontal line */ pat = cairo_pattern_create_linear (item_x + E_DAY_VIEW_BAR_WIDTH + 15, item_y + item_h, item_x + E_DAY_VIEW_BAR_WIDTH + 15, item_y + item_h + 7); cairo_pattern_add_color_stop_rgba (pat, 0, 0, 0, 0, 1); cairo_pattern_add_color_stop_rgba (pat, 0.7, 0, 0, 0, 0.2); cairo_pattern_add_color_stop_rgba (pat, 1, 1, 1, 1, 0.3); cairo_set_source (cr, pat); cairo_rectangle (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 16.5, item_y + item_h, item_w - 31.5, 7.0); cairo_fill (cr); cairo_pattern_destroy (pat); /* Bottom arc */ pat = cairo_pattern_create_radial (item_x + E_DAY_VIEW_BAR_WIDTH + 12.5, item_y + item_h - 5, 5.0, item_x + E_DAY_VIEW_BAR_WIDTH + 12.5, item_y + item_h - 5, 12.0); cairo_pattern_add_color_stop_rgba (pat, 1, 1, 1, 1, 0.3); cairo_pattern_add_color_stop_rgba (pat, 0.7, 0, 0, 0, 0.2); cairo_pattern_add_color_stop_rgba (pat, 0, 0, 0, 0, 1); cairo_set_source (cr, pat); cairo_arc (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 13, item_y + item_h - 5, 12.0, 3 * G_PI / 8, 9 * G_PI / 8); cairo_fill (cr); cairo_pattern_destroy (pat); cairo_set_source_rgba (cr, 0, 0, 0, 0.5); cairo_set_line_width (cr, 2); cairo_move_to (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 14, item_y + item_h + 2); cairo_line_to (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 15.5, item_y + item_h + 3); cairo_stroke (cr); cairo_set_source_rgba (cr, 0, 0, 0, 0.27); cairo_move_to (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 15, item_y + item_h + 3.5); cairo_line_to (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 17, item_y + item_h + 3.5); cairo_stroke (cr); /* Arc in middle */ pat = cairo_pattern_create_radial (item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 1, item_y + item_h - 4.5, 1.0, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 1, item_y + item_h - 4.5, 12.0); cairo_pattern_add_color_stop_rgba (pat, 1, 1, 1, 1, 0.3); cairo_pattern_add_color_stop_rgba (pat, 0.8, 0, 0, 0, 0.2); cairo_pattern_add_color_stop_rgba (pat, 0, 0, 0, 0, 1); cairo_set_source (cr, pat); cairo_arc (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 1, item_y + item_h - 4.5, 12.0, 15 * G_PI / 8, 5 * G_PI / 8); cairo_fill (cr); cairo_pattern_destroy (pat); cairo_set_source_rgba (cr, 0, 0, 0, 0.27); cairo_move_to (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH - 1, item_y + item_h + 3); cairo_line_to (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH , item_y + item_h + 3); cairo_stroke (cr); cairo_set_source_rgba (cr, 0, 0, 0, 0.27); cairo_move_to (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 9, item_y + item_h - 6); cairo_line_to (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH + 10, item_y + item_h - 6); cairo_stroke (cr); cairo_restore (cr); /* Black border */ cairo_save (cr); x0 = item_x + E_DAY_VIEW_BAR_WIDTH + 9; y0 = item_y + 10; rect_width = MAX (item_w - E_DAY_VIEW_BAR_WIDTH - 7, 0); rect_height = item_h - 7; radius = 20; draw_curved_rectangle (cr, x0, y0, rect_width, rect_height, radius); cairo_set_source_rgb (cr, 0, 0, 0); cairo_fill (cr); cairo_restore (cr); /* Extra Grid lines when clicked */ cairo_save (cr); x0 = item_x + E_DAY_VIEW_BAR_WIDTH + 1; y0 = item_y + 2; rect_width = MAX (item_w - E_DAY_VIEW_BAR_WIDTH - 3, 0); rect_height = item_h - 4.; radius = 16; draw_curved_rectangle (cr, x0, y0, rect_width, rect_height, radius); cairo_set_source_rgb (cr, 1, 1, 1); cairo_fill (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_BG_GRID]); for (row_y = y0; row_y < rect_height + y0; row_y += day_view->row_height) { if (row_y >= 0 && row_y < rect_height + y0) { cairo_set_line_width (cr, 0.7); cairo_move_to (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 1 , row_y); cairo_line_to (cr, item_x + item_w -2, row_y); cairo_stroke (cr); } } cairo_restore (cr); } item_x += 1; item_y += 2; } /* Draw the background of the event with white to play with transparency */ cairo_save (cr); x0 = item_x + E_DAY_VIEW_BAR_WIDTH + 1; y0 = item_y + 2; rect_width = MAX (item_w - E_DAY_VIEW_BAR_WIDTH - 3, 0); rect_height = item_h - 4.; radius = 16; draw_curved_rectangle (cr, x0, y0, rect_width, rect_height, radius); cairo_set_source_rgba (cr, 1, 1, 1, 1.0); cairo_fill (cr); cairo_restore (cr); /* Here we draw the border in event color */ cairo_save (cr); x0 = item_x + E_DAY_VIEW_BAR_WIDTH; y0 = item_y + 1.; rect_width = MAX (item_w - E_DAY_VIEW_BAR_WIDTH - 1., 0); rect_height = item_h - 2.; radius = 16; draw_curved_rectangle (cr, x0, y0, rect_width,rect_height, radius); cairo_set_line_width (cr, 2.); cairo_set_source_rgb (cr, red/cc, green/cc, blue/cc); cairo_stroke (cr); cairo_restore (cr); /* Fill in the Event */ cairo_save (cr); x0 = item_x + E_DAY_VIEW_BAR_WIDTH + 1.75; y0 = item_y + 2.75; rect_width = item_w - E_DAY_VIEW_BAR_WIDTH - 4.5; rect_height = item_h - 5.5; radius = 14; draw_curved_rectangle (cr, x0, y0, rect_width, rect_height, radius); date_fraction = rect_height / day_view->row_height; interval = event->end_minute - event->start_minute; if ((interval / time_divisions) >= 2) short_event = FALSE; else if ((interval % time_divisions) == 0) { if (((event->end_minute % time_divisions) == 0) || ((event->start_minute % time_divisions) == 0)) short_event = TRUE; } else short_event = FALSE; if (is_editing) short_event = TRUE; pat = cairo_pattern_create_linear (item_x + E_DAY_VIEW_BAR_WIDTH + 1.75, item_y + 7.75, item_x + E_DAY_VIEW_BAR_WIDTH + 1.75, item_y + item_h - 7.75); if (!short_event) { cairo_pattern_add_color_stop_rgba (pat, 1, red/cc, green/cc, blue/cc, 0.8); cairo_pattern_add_color_stop_rgba (pat, 1/(date_fraction + (rect_height/18)), red/cc, green/cc, blue/cc, 0.8); cairo_pattern_add_color_stop_rgba (pat, 1/(date_fraction + (rect_height/18)), red/cc, green/cc, blue/cc, 0.4); cairo_pattern_add_color_stop_rgba (pat, 1, red/cc, green/cc, blue/cc, 0.8); } else { cairo_pattern_add_color_stop_rgba (pat, 1, red/cc, green/cc, blue/cc, 0.8); cairo_pattern_add_color_stop_rgba (pat, 0, red/cc, green/cc, blue/cc, 0.4); } cairo_set_source (cr, pat); cairo_fill_preserve (cr); cairo_pattern_destroy (pat); cairo_set_source_rgba (cr, red/cc, green/cc, blue/cc, 0.2); cairo_set_line_width (cr, 0.5); cairo_stroke (cr); cairo_restore (cr); /* Draw the right edge of the vertical bar. */ cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_BG_GRID]); cairo_set_line_width (cr, 0.7); cairo_move_to (cr, item_x + E_DAY_VIEW_BAR_WIDTH - 1, item_y + 1); cairo_line_to (cr, item_x + E_DAY_VIEW_BAR_WIDTH - 1, item_y + item_h - 2); cairo_stroke (cr); cairo_restore (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR]); /* Draw the vertical colored bar showing when the appointment begins & ends. */ bar_y1 = event->start_minute * day_view->row_height / time_divisions - y; bar_y2 = event->end_minute * day_view->row_height / time_divisions - y; scroll_flag = bar_y2; /* When an item is being resized, we fill the bar up to the new row. */ if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE && day_view->resize_event_day == day && day_view->resize_event_num == event_num) { resize_flag = TRUE; if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_TOP_EDGE) bar_y1 = item_y + 1; else if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_BOTTOM_EDGE) { bar_y2 = item_y + item_h - 1; end_minute = event->end_minute; end_hour = end_minute / 60; end_minute = end_minute % 60; e_day_view_convert_time_to_display (day_view, end_hour, &end_display_hour, &end_resize_suffix, &end_suffix_width); cairo_save (cr); cairo_rectangle (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 1.75, item_y + 2.75, item_w - E_DAY_VIEW_BAR_WIDTH - 4.5, item_h - 5.5); cairo_clip (cr); cairo_new_path (cr); if (e_cal_model_get_use_24_hour_format (model)) { cairo_translate (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH - 32, item_y + item_h - 8); end_regsizeime = g_strdup_printf ("%2i:%02i", end_display_hour, end_minute); } else { cairo_translate (cr, item_x + item_w - E_DAY_VIEW_BAR_WIDTH - 48, item_y + item_h - 8); end_regsizeime = g_strdup_printf ("%2i:%02i%s", end_display_hour, end_minute, end_resize_suffix); } cairo_set_font_size (cr, 14); if ((red/cc > 0.7) || (green/cc > 0.7) || (blue/cc > 0.7 )) cairo_set_source_rgb (cr, 0, 0, 0); else cairo_set_source_rgb (cr, 1, 1, 1); cairo_show_text (cr, end_regsizeime); cairo_close_path (cr); cairo_restore (cr); } } if (bar_y2 > scroll_flag) event->end_minute += time_divisions; else if (bar_y2 < scroll_flag) event->end_minute -= time_divisions; comp = e_cal_component_new (); e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); /* Only fill it in if the event isn't TRANSPARENT. */ e_cal_component_get_transparency (comp, &transparency); if (transparency != E_CAL_COMPONENT_TRANSP_TRANSPARENT) { cairo_save (cr); pat = cairo_pattern_create_linear (item_x + E_DAY_VIEW_BAR_WIDTH, item_y + 1, item_x + E_DAY_VIEW_BAR_WIDTH, item_y + item_h - 1); cairo_pattern_add_color_stop_rgba (pat, 1, red/cc, green/cc, blue/cc, 0.7); cairo_pattern_add_color_stop_rgba (pat, 0.5, red/cc, green/cc, blue/cc, 0.7); cairo_pattern_add_color_stop_rgba (pat, 0, red/cc, green/cc, blue/cc, 0.2); cairo_rectangle (cr, item_x + 1, bar_y1, E_DAY_VIEW_BAR_WIDTH - 2, bar_y2 - bar_y1); cairo_set_source (cr, pat); cairo_fill (cr); cairo_pattern_destroy (pat); cairo_restore (cr); /* This is for achieving the white stripes in vbar across event color */ for (i = 0; i <= (bar_y2 - bar_y1); i+=4) { cairo_set_source_rgb (cr, 1, 1, 1); cairo_set_line_width (cr, 0.3); cairo_move_to (cr, item_x + 1, bar_y1 + i); cairo_line_to (cr, item_x + E_DAY_VIEW_BAR_WIDTH - 1, bar_y1 + i); cairo_stroke (cr); } } gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR]); /* Draw the reminder & recurrence icons, if needed. */ if (!resize_flag && (!is_editing || text_x_offset > E_DAY_VIEW_ICON_X_PAD)) { GSList *categories_pixbufs = NULL, *pixbufs; num_icons = 0; draw_reminder_icon = FALSE; draw_recurrence_icon = FALSE; draw_timezone_icon = FALSE; draw_meeting_icon = FALSE; draw_attach_icon = FALSE; icon_x = item_x + E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_ICON_X_PAD; icon_y = item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_ICON_Y_PAD; if (e_cal_component_has_alarms (comp)) { draw_reminder_icon = TRUE; num_icons++; } if (e_cal_component_has_recurrences (comp) || e_cal_component_is_instance (comp)) { draw_recurrence_icon = TRUE; num_icons++; } if (e_cal_component_has_attachments (comp)) { draw_attach_icon = TRUE; num_icons++; } /* If the DTSTART or DTEND are in a different timezone to our current timezone, we display the timezone icon. */ if (event->different_timezone) { draw_timezone_icon = TRUE; num_icons++; } if (e_cal_component_has_attendees (comp)) { draw_meeting_icon = TRUE; num_icons++; } num_icons += cal_comp_util_get_n_icons (comp, &categories_pixbufs); if (num_icons != 0) { if (item_h >= (E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD) * num_icons) { icon_x_inc = 0; icon_y_inc = E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD; } else { icon_x_inc = E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD; icon_y_inc = 0; } #define fit_in_event() (icon_x + icon_x_inc < item_x + item_w && icon_y + icon_y_inc < item_y + item_h) #define draw_pixbuf(pf) \ max_icon_w = item_x + item_w - icon_x - E_DAY_VIEW_EVENT_BORDER_WIDTH; \ max_icon_h = item_y + item_h - icon_y - E_DAY_VIEW_EVENT_BORDER_HEIGHT; \ \ if (can_draw_in_region (draw_region, icon_x, icon_y, max_icon_w, max_icon_h)) { \ cairo_save (cr); \ cairo_rectangle (cr, icon_x, icon_y, max_icon_w, max_icon_h); \ cairo_clip (cr); \ cairo_new_path (cr); \ gdk_cairo_set_source_pixbuf (cr, pf, icon_x, icon_y); \ cairo_paint (cr); \ cairo_close_path (cr); \ cairo_restore (cr); \ } \ \ icon_x += icon_x_inc; \ icon_y += icon_y_inc; if (draw_reminder_icon && fit_in_event ()) { draw_pixbuf (day_view->reminder_icon); } if (draw_recurrence_icon && fit_in_event ()) { draw_pixbuf (day_view->recurrence_icon); } if (draw_attach_icon && fit_in_event ()) { draw_pixbuf (day_view->attach_icon); } if (draw_timezone_icon && fit_in_event ()) { draw_pixbuf (day_view->timezone_icon); } if (draw_meeting_icon && fit_in_event ()) { draw_pixbuf (day_view->meeting_icon); } /* draw categories icons */ for (pixbufs = categories_pixbufs; pixbufs && fit_in_event (); pixbufs = pixbufs->next) { GdkPixbuf *pixbuf = pixbufs->data; draw_pixbuf (pixbuf); } #undef draw_pixbuf #undef fit_in_event } /* free memory */ g_slist_foreach (categories_pixbufs, (GFunc) g_object_unref, NULL); g_slist_free (categories_pixbufs); } if (!short_event) { if (event->start_minute % time_divisions != 0 || (day_view->show_event_end_times && event->end_minute % time_divisions != 0)) { offset = day_view->first_hour_shown * 60 + day_view->first_minute_shown; show_span = TRUE; } else { offset = 0; } start_minute = offset + event->start_minute; end_minute = offset + event->end_minute; format_time = (((end_minute - start_minute) / time_divisions) >= 2) ? TRUE : FALSE; start_hour = start_minute / 60; start_minute = start_minute % 60; end_hour = end_minute / 60; end_minute = end_minute % 60; e_day_view_convert_time_to_display (day_view, start_hour, &start_display_hour, &start_suffix, &start_suffix_width); e_day_view_convert_time_to_display (day_view, end_hour, &end_display_hour, &end_suffix, &end_suffix_width); if (e_cal_model_get_use_24_hour_format (model)) { if (day_view->show_event_end_times && show_span) { /* 24 hour format with end time. */ text = g_strdup_printf ("%2i:%02i-%2i:%02i", start_display_hour, start_minute, end_display_hour, end_minute); } else { if (format_time) { /* 24 hour format without end time. */ text = g_strdup_printf ("%2i:%02i", start_display_hour, start_minute); } } } else { if (day_view->show_event_end_times && show_span) { /* 12 hour format with end time. */ text = g_strdup_printf ("%2i:%02i%s-%2i:%02i%s", start_display_hour, start_minute, start_suffix, end_display_hour, end_minute, end_suffix); } else { /* 12 hour format without end time. */ text = g_strdup_printf ("%2i:%02i%s", start_display_hour, start_minute, start_suffix); } } cairo_save (cr); cairo_rectangle (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 1.75, item_y + 2.75, item_w - E_DAY_VIEW_BAR_WIDTH - 4.5, 14); cairo_clip (cr); cairo_new_path (cr); if (icon_x_inc == 0) icon_x += 14; if (resize_flag) cairo_move_to (cr, item_x + E_DAY_VIEW_BAR_WIDTH + 10, item_y + 13); else cairo_move_to (cr, icon_x, item_y + 13); if ((red/cc > 0.7) || (green/cc > 0.7) || (blue/cc > 0.7 )) cairo_set_source_rgb (cr, 0, 0, 0); else cairo_set_source_rgb (cr, 1, 1, 1); cairo_set_font_size (cr, 14.0); cairo_show_text (cr, text); cairo_close_path (cr); cairo_restore (cr); } g_free (text); g_object_unref (comp); } static void day_view_main_item_draw_day_events (EDayViewMainItem *main_item, cairo_t *cr, gint x, gint y, gint width, gint height, gint day, cairo_region_t *draw_region) { EDayView *day_view; gint event_num; day_view = e_day_view_main_item_get_day_view (main_item); for (event_num = 0; event_num < day_view->events[day]->len; event_num++) { day_view_main_item_draw_day_event ( main_item, cr, x, y, width, height, day, event_num, draw_region); } } static void day_view_main_item_draw_events_in_vbars (EDayViewMainItem *main_item, cairo_t *cr, gint x, gint y, gint width, gint height, gint day, cairo_region_t *draw_region) { EDayView *day_view; EDayViewEvent *event; ECalendarView *cal_view; gint time_divisions; gint grid_x, event_num, bar_y, bar_h; GdkColor bg_color; day_view = e_day_view_main_item_get_day_view (main_item); cal_view = E_CALENDAR_VIEW (day_view); time_divisions = e_calendar_view_get_time_divisions (cal_view); grid_x = day_view->day_offsets[day] + 1 - x; /* Draw the busy times corresponding to the events in the day. */ for (event_num = 0; event_num < day_view->events[day]->len; event_num++) { event = &g_array_index (day_view->events[day], EDayViewEvent, event_num); if (!is_comp_data_valid (event)) continue; /* We can skip the events in the first column since they will draw over this anyway. */ if (event->num_columns > 0 && event->start_row_or_col == 0) { continue; } bar_y = event->start_minute * day_view->row_height / time_divisions; bar_h = event->end_minute * day_view->row_height / time_divisions - bar_y; bar_y -= y; /* Skip it if it isn't visible. */ if (bar_y >= height || bar_y + bar_h <= 0 || !can_draw_in_region (draw_region, grid_x, bar_y, E_DAY_VIEW_BAR_WIDTH - 2, bar_h)) { continue; } /* If the event is TRANSPARENT, skip it. */ if (icalcomp_is_transparent (event->comp_data->icalcomp)) { continue; } cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND]); if (gdk_color_parse (e_cal_model_get_color_for_component (e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)), event->comp_data), &bg_color)) { gdk_cairo_set_source_color (cr, &bg_color); } cairo_rectangle (cr, grid_x, bar_y, E_DAY_VIEW_BAR_WIDTH - 2, bar_h); cairo_fill (cr); cairo_restore (cr); } } static void day_view_main_item_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_DAY_VIEW: e_day_view_main_item_set_day_view ( E_DAY_VIEW_MAIN_ITEM (object), g_value_get_object (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void day_view_main_item_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_DAY_VIEW: g_value_set_object ( value, e_day_view_main_item_get_day_view ( E_DAY_VIEW_MAIN_ITEM (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void day_view_main_item_dispose (GObject *object) { EDayViewMainItemPrivate *priv; priv = E_DAY_VIEW_MAIN_ITEM (object)->priv; if (priv->day_view != NULL) { g_object_unref (priv->day_view); priv->day_view = NULL; } /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } static void day_view_main_item_update (GnomeCanvasItem *item, const cairo_matrix_t *i2c, gint flags) { GnomeCanvasItemClass *canvas_item_class; /* Chain up to parent's update() method. */ canvas_item_class = GNOME_CANVAS_ITEM_CLASS (parent_class); canvas_item_class->update (item, i2c, flags); /* The item covers the entire canvas area. */ item->x1 = 0; item->y1 = 0; item->x2 = INT_MAX; item->y2 = INT_MAX; } static void day_view_main_item_draw (GnomeCanvasItem *canvas_item, cairo_t *cr, gint x, gint y, gint width, gint height) { EDayViewMainItem *main_item; EDayView *day_view; ECalendarView *cal_view; ECalModel *model; gint time_divisions; gint work_day_start_hour; gint work_day_start_minute; gint work_day_end_hour; gint work_day_end_minute; gint row, row_y, grid_x1, grid_x2; gint day, grid_y1, grid_y2; gint work_day_start_y, work_day_end_y; gint day_x, day_w, work_day; gint start_row, end_row, rect_x, rect_y, rect_width, rect_height; struct icaltimetype day_start_tt, today_tt; gint weekday; gboolean today = FALSE; cairo_region_t *draw_region; GdkRectangle rect; main_item = E_DAY_VIEW_MAIN_ITEM (canvas_item); day_view = e_day_view_main_item_get_day_view (main_item); g_return_if_fail (day_view != NULL); cal_view = E_CALENDAR_VIEW (day_view); time_divisions = e_calendar_view_get_time_divisions (cal_view); model = e_calendar_view_get_model (cal_view); work_day_start_hour = e_cal_model_get_work_day_start_hour (model); work_day_start_minute = e_cal_model_get_work_day_start_minute (model); work_day_end_hour = e_cal_model_get_work_day_end_hour (model); work_day_end_minute = e_cal_model_get_work_day_end_minute (model); rect.x = 0; rect.y = 0; rect.width = width; rect.height = height; if (rect.width > 0 && rect.height > 0) draw_region = cairo_region_create_rectangle (&rect); else draw_region = cairo_region_create (); /* Paint the background colors. */ work_day_start_y = e_day_view_convert_time_to_position ( day_view, work_day_start_hour, work_day_start_minute) - y; work_day_end_y = e_day_view_convert_time_to_position ( day_view, work_day_end_hour, work_day_end_minute) - y; today_tt = icaltime_from_timet_with_zone (time (NULL), FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); for (day = 0; day < day_view->days_shown; day++) { day_start_tt = icaltime_from_timet_with_zone (day_view->day_starts[day], FALSE, e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view))); weekday = icaltime_day_of_week (day_start_tt) - 1; work_day = day_view->working_days & (1 << weekday); day_x = day_view->day_offsets[day] - x; day_w = day_view->day_widths[day]; if (work_day) { if (can_draw_in_region (draw_region, day_x, 0 - y, day_w, work_day_start_y - (0 - y))) { cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING]); cairo_rectangle (cr, day_x, 0 - y, day_w, work_day_start_y - (0 - y)); cairo_fill (cr); cairo_restore (cr); } if (day_view->days_shown > 1) { /* Check if we are drawing today */ today = day_start_tt.year == today_tt.year && day_start_tt.month == today_tt.month && day_start_tt.day == today_tt.day; } else { today = FALSE; } if (can_draw_in_region (draw_region, day_x, work_day_start_y, day_w, work_day_end_y - work_day_start_y)) { cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[today ? E_DAY_VIEW_COLOR_BG_MULTIDAY_TODAY : E_DAY_VIEW_COLOR_BG_WORKING]); cairo_rectangle (cr, day_x, work_day_start_y, day_w, work_day_end_y - work_day_start_y); cairo_fill (cr); cairo_restore (cr); } if (can_draw_in_region (draw_region, day_x, work_day_end_y, day_w, height - work_day_end_y)) { cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING]); cairo_rectangle (cr, day_x, work_day_end_y, day_w, height - work_day_end_y); cairo_fill (cr); cairo_restore (cr); } } else if (can_draw_in_region (draw_region, day_x, 0, day_w, height)) { cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING]); cairo_rectangle (cr, day_x, 0, day_w, height); cairo_fill (cr); cairo_restore (cr); } } /* Paint the selection background. */ if (day_view->selection_start_day != -1 && !day_view->selection_in_top_canvas) { for (day = day_view->selection_start_day; day <= day_view->selection_end_day; day++) { if (day == day_view->selection_start_day && day_view->selection_start_row != -1) start_row = day_view->selection_start_row; else start_row = 0; if (day == day_view->selection_end_day && day_view->selection_end_row != -1) end_row = day_view->selection_end_row; else end_row = day_view->rows - 1; rect_x = day_view->day_offsets[day] - x; rect_width = day_view->day_widths[day]; rect_y = start_row * day_view->row_height - y; rect_height = (end_row - start_row + 1) * day_view->row_height; if (can_draw_in_region (draw_region, rect_x, rect_y, rect_width, rect_height)) { cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[gtk_widget_has_focus (GTK_WIDGET (day_view)) ? E_DAY_VIEW_COLOR_BG_SELECTED : E_DAY_VIEW_COLOR_BG_SELECTED_UNFOCUSSED]); cairo_rectangle (cr, rect_x, rect_y, rect_width, rect_height); cairo_fill (cr); cairo_restore (cr); } } } /* Drawing the horizontal grid lines. */ grid_x1 = day_view->day_offsets[0] - x; grid_x2 = day_view->day_offsets[day_view->days_shown] - x; cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_BG_GRID]); for (row = 0, row_y = 0 - y; row < day_view->rows && row_y < height; row++, row_y += day_view->row_height) { if (row_y >= 0 && row_y < height) { cairo_set_line_width (cr, 0.7); cairo_move_to (cr, grid_x1, row_y); cairo_line_to (cr, grid_x2, row_y); cairo_stroke (cr); } } cairo_restore (cr); /* Draw the vertical bars down the left of each column. */ grid_y1 = 0; grid_y2 = height; for (day = 0; day < day_view->days_shown; day++) { grid_x1 = day_view->day_offsets[day] - x; /* Skip if it isn't visible. */ if (grid_x1 >= width || grid_x1 + E_DAY_VIEW_BAR_WIDTH <= 0) continue; cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_BG_GRID]); cairo_move_to (cr, grid_x1, grid_y1); cairo_line_to (cr, grid_x1, grid_y2); cairo_stroke (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_BG_GRID]); cairo_move_to (cr, grid_x1 + E_DAY_VIEW_BAR_WIDTH - 1, grid_y1); cairo_line_to (cr, grid_x1 + E_DAY_VIEW_BAR_WIDTH - 1, grid_y2); cairo_stroke (cr); cairo_set_source_rgb (cr, 1, 1, 1); cairo_rectangle (cr, grid_x1 + 1, grid_y1, E_DAY_VIEW_BAR_WIDTH - 2, grid_y2 - grid_y1); cairo_fill (cr); cairo_restore (cr); /* Fill in the bars when the user is busy. */ day_view_main_item_draw_events_in_vbars ( main_item, cr, x, y, width, height, day, draw_region); } /* Fill in the vertical bars corresponding to the busy times from the long events. */ day_view_main_item_draw_long_events_in_vbars ( main_item, cr, x, y, width, height, draw_region); /* Draw the event borders and backgrounds, and the vertical bars down the left edges. */ for (day = 0; day < day_view->days_shown; day++) day_view_main_item_draw_day_events ( main_item, cr, x, y, width, height, day, draw_region); if (e_day_view_marcus_bains_get_show_line (day_view)) { icaltimezone *zone; struct icaltimetype time_now, day_start; gint marcus_bains_y; GdkColor mb_color; cairo_save (cr); gdk_cairo_set_source_color (cr, &day_view->colors[E_DAY_VIEW_COLOR_MARCUS_BAINS_LINE]); if (day_view->marcus_bains_day_view_color && gdk_color_parse (day_view->marcus_bains_day_view_color, &mb_color)) { gdk_cairo_set_source_color (cr, &mb_color); } zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (day_view)); time_now = icaltime_current_time_with_zone (zone); for (day = 0; day < day_view->days_shown; day++) { day_start = icaltime_from_timet_with_zone (day_view->day_starts[day], FALSE, zone); if ((day_start.year == time_now.year) && (day_start.month == time_now.month) && (day_start.day == time_now.day)) { grid_x1 = day_view->day_offsets[day] - x + E_DAY_VIEW_BAR_WIDTH; grid_x2 = day_view->day_offsets[day + 1] - x - 1; marcus_bains_y = (time_now.hour * 60 + time_now.minute) * day_view->row_height / time_divisions - y; cairo_set_line_width (cr, 1.5); cairo_move_to (cr, grid_x1, marcus_bains_y); cairo_line_to (cr, grid_x2, marcus_bains_y); cairo_stroke (cr); } } cairo_restore (cr); } cairo_region_destroy (draw_region); } static GnomeCanvasItem * day_view_main_item_point (GnomeCanvasItem *item, gdouble x, gdouble y, gint cx, gint cy) { return item; } static void day_view_main_item_class_init (EDayViewMainItemClass *class) { GObjectClass *object_class; GnomeCanvasItemClass *item_class; parent_class = g_type_class_peek_parent (class); g_type_class_add_private (class, sizeof (EDayViewMainItemPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = day_view_main_item_set_property; object_class->get_property = day_view_main_item_get_property; object_class->dispose = day_view_main_item_dispose; item_class = GNOME_CANVAS_ITEM_CLASS (class); item_class->update = day_view_main_item_update; item_class->draw = day_view_main_item_draw; item_class->point = day_view_main_item_point; g_object_class_install_property ( object_class, PROP_DAY_VIEW, g_param_spec_object ( "day-view", "Day View", NULL, E_TYPE_DAY_VIEW, G_PARAM_READWRITE)); /* init the accessibility support for e_day_view */ e_day_view_main_item_a11y_init (); } static void day_view_main_item_init (EDayViewMainItem *main_item) { main_item->priv = G_TYPE_INSTANCE_GET_PRIVATE ( main_item, E_TYPE_DAY_VIEW_MAIN_ITEM, EDayViewMainItemPrivate); } GType e_day_view_main_item_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { const GTypeInfo type_info = { sizeof (EDayViewMainItemClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) day_view_main_item_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (EDayViewMainItem), 0, /* n_preallocs */ (GInstanceInitFunc) day_view_main_item_init, NULL /* value_table */ }; type = g_type_register_static ( GNOME_TYPE_CANVAS_ITEM, "EDayViewMainItem", &type_info, 0); } return type; } EDayView * e_day_view_main_item_get_day_view (EDayViewMainItem *main_item) { g_return_val_if_fail (E_IS_DAY_VIEW_MAIN_ITEM (main_item), NULL); return main_item->priv->day_view; } void e_day_view_main_item_set_day_view (EDayViewMainItem *main_item, EDayView *day_view) { g_return_if_fail (E_IS_DAY_VIEW_MAIN_ITEM (main_item)); g_return_if_fail (E_IS_DAY_VIEW (day_view)); if (main_item->priv->day_view != NULL) g_object_unref (main_item->priv->day_view); main_item->priv->day_view = g_object_ref (day_view); g_object_notify (G_OBJECT (main_item), "day-view"); }