evolution-3.6.4/calendar/gui/e-day-view-top-item.c

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  * EDayViewTopItem - displays the top part of the Day/Work Week calendar view.
 25  */
 26 
 27 #ifdef HAVE_CONFIG_H
 28 #include <config.h>
 29 #endif
 30 
 31 #include <glib/gi18n.h>
 32 #include "e-util/e-categories-config.h"
 33 
 34 #include "e-calendar-view.h"
 35 #include "e-day-view-top-item.h"
 36 
 37 #define E_DAY_VIEW_TOP_ITEM_GET_PRIVATE(obj) \
 38 	(G_TYPE_INSTANCE_GET_PRIVATE \
 39 	((obj), E_TYPE_DAY_VIEW_TOP_ITEM, EDayViewTopItemPrivate))
 40 
 41 struct _EDayViewTopItemPrivate {
 42 	/* The parent EDayView widget. */
 43 	EDayView *day_view;
 44 
 45 	/* Show dates or events. */
 46 	gboolean show_dates;
 47 };
 48 
 49 enum {
 50 	PROP_0,
 51 	PROP_DAY_VIEW,
 52 	PROP_SHOW_DATES
 53 };
 54 
 55 G_DEFINE_TYPE (
 56 	EDayViewTopItem,
 57 	e_day_view_top_item,
 58 	GNOME_TYPE_CANVAS_ITEM)
 59 
 60 /* This draws a little triangle to indicate that an event extends past
 61  * the days visible on screen. */
 62 static void
 63 day_view_top_item_draw_triangle (EDayViewTopItem *top_item,
 64                                  cairo_t *cr,
 65                                  gint x,
 66                                  gint y,
 67                                  gint w,
 68                                  gint h,
 69                                  gint event_num)
 70 {
 71 	EDayView *day_view;
 72 	EDayViewEvent *event;
 73 	GdkColor bg_color;
 74 	GdkPoint points[3];
 75 	gint c1, c2;
 76 
 77 	day_view = e_day_view_top_item_get_day_view (top_item);
 78 
 79 	points[0].x = x;
 80 	points[0].y = y;
 81 	points[1].x = x + w;
 82 	points[1].y = y + (h / 2);
 83 	points[2].x = x;
 84 	points[2].y = y + h - 1;
 85 
 86 	/* If the height is odd we can use the same central point for both
 87 	 * lines. If it is even we use different end-points. */
 88 	c1 = c2 = y + (h / 2);
 89 	if (h % 2 == 0)
 90 		c1--;
 91 
 92 	if (!is_array_index_in_bounds (day_view->long_events, event_num))
 93 		return;
 94 
 95 	event = &g_array_index (day_view->long_events, EDayViewEvent,
 96 				event_num);
 97 
 98 	if (!is_comp_data_valid (event))
 99 		return;
100 
101 	cairo_save (cr);
102 	/* Fill it in. */
103 	if (gdk_color_parse (
104 		e_cal_model_get_color_for_component (
105 		e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)),
106 		event->comp_data), &bg_color)) {
107 		gdk_cairo_set_source_color (cr, &bg_color);
108 	} else {
109 		gdk_cairo_set_source_color (
110 			cr, &day_view->colors
111 			[E_DAY_VIEW_COLOR_LONG_EVENT_BACKGROUND]);
112 	}
113 
114 	cairo_move_to (cr, points[0].x, points[0].y);
115 	cairo_line_to (cr, points[1].x, points[1].y);
116 	cairo_line_to (cr, points[2].x, points[2].y);
117 	cairo_line_to (cr, points[0].x, points[0].y);
118 	cairo_fill (cr);
119 	cairo_restore (cr);
120 
121 	cairo_save (cr);
122 	gdk_cairo_set_source_color (
123 		cr, &day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BORDER]);
124 	cairo_move_to (cr, x, y);
125 	cairo_line_to (cr, x + w, c1);
126 	cairo_move_to (cr, x, y + h - 1);
127 	cairo_line_to (cr, x + w, c2);
128 	cairo_stroke (cr);
129 	cairo_restore (cr);
130 }
131 
132 /* This draws one event in the top canvas. */
133 static void
134 day_view_top_item_draw_long_event (EDayViewTopItem *top_item,
135                                    gint event_num,
136                                    cairo_t *cr,
137                                    gint x,
138                                    gint y,
139                                    gint width,
140                                    gint height)
141 {
142 	EDayView *day_view;
143 	EDayViewEvent *event;
144 	GtkStyle *style;
145 	gint start_day, end_day;
146 	gint item_x, item_y, item_w, item_h;
147 	gint text_x, icon_x, icon_y, icon_x_inc;
148 	ECalModel *model;
149 	ECalComponent *comp;
150 	gchar buffer[16];
151 	gint hour, display_hour, minute, offset, time_width, time_x;
152 	gint min_end_time_x, suffix_width, max_icon_x;
153 	const gchar *suffix;
154 	gboolean draw_start_triangle, draw_end_triangle;
155 	GSList *categories_list, *elem;
156 	PangoLayout *layout;
157 	GdkColor bg_color;
158 	cairo_pattern_t *pat;
159 	guint16 red, green, blue;
160 	gdouble cc = 65535.0;
161 	gdouble x0, y0, rect_height, rect_width, radius;
162 
163 	day_view = e_day_view_top_item_get_day_view (top_item);
164 	model = e_calendar_view_get_model (E_CALENDAR_VIEW (day_view));
165 
166 	/* If the event is currently being dragged, don't draw it. It will
167 	 * be drawn in the special drag items. */
168 	if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT
169 	    && day_view->drag_event_num == event_num)
170 		return;
171 
172 	if (!e_day_view_get_long_event_position (day_view, event_num,
173 						 &start_day, &end_day,
174 						 &item_x, &item_y,
175 						 &item_w, &item_h))
176 		return;
177 
178 	if (!is_array_index_in_bounds (day_view->long_events, event_num))
179 		return;
180 
181 	event = &g_array_index (day_view->long_events, EDayViewEvent,
182 				event_num);
183 
184 	if (!is_comp_data_valid (event))
185 		return;
186 
187 	style = gtk_widget_get_style (GTK_WIDGET (day_view));
188 	comp = e_cal_component_new ();
189 	e_cal_component_set_icalcomponent (
190 		comp, icalcomponent_new_clone (event->comp_data->icalcomp));
191 
192 	if (gdk_color_parse (
193 		e_cal_model_get_color_for_component (
194 		e_calendar_view_get_model (E_CALENDAR_VIEW (day_view)),
195 		event->comp_data), &bg_color)) {
196 		red = bg_color.red;
197 		green = bg_color.green;
198 		blue = bg_color.blue;
199 	} else {
200 		red = day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BACKGROUND].red;
201 		green = day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BACKGROUND].green;
202 		blue = day_view->colors[E_DAY_VIEW_COLOR_LONG_EVENT_BACKGROUND].blue;
203 	}
204 
205 	/* Fill the background with white to play with transparency */
206 	cairo_save (cr);
207 	x0	   = item_x - x + 4;
208 	y0	   = item_y + 1 - y;
209 	rect_width  = item_w - 8;
210 	rect_height = item_h - 2;
211 
212 	radius = 12;
213 
214 	draw_curved_rectangle (cr, x0, y0, rect_width, rect_height, radius);
215 
216 	cairo_set_source_rgba (cr, 1, 1, 1, 1.0);
217 	cairo_fill_preserve (cr);
218 
219 	cairo_restore (cr);
220 
221 	/* Draw the border around the event */
222 
223 	cairo_save (cr);
224 	x0	   = item_x - x + 4;
225 	y0	   = item_y + 1 - y;
226 	rect_width  = item_w - 8;
227 	rect_height = item_h - 2;
228 
229 	radius = 12;
230 
231 	draw_curved_rectangle (cr, x0, y0, rect_width, rect_height, radius);
232 
233 	cairo_set_source_rgb (cr, red / cc, green / cc, blue / cc);
234 	cairo_set_line_width (cr, 1.5);
235 	cairo_stroke (cr);
236 	cairo_restore (cr);
237 
238 	/* Fill in with gradient */
239 
240 	cairo_save (cr);
241 
242 	x0	   = item_x - x + 5.5;
243 	y0	   = item_y + 2.5 - y;
244 	rect_width  = item_w - 11;
245 	rect_height = item_h - 5;
246 
247 	radius = 10;
248 
249 	draw_curved_rectangle (cr, x0, y0, rect_width, rect_height, radius);
250 
251 	pat = cairo_pattern_create_linear (
252 		item_x - x + 5.5, item_y + 2.5 - y,
253 		item_x - x + 5, item_y - y + item_h + 7.5);
254 	cairo_pattern_add_color_stop_rgba (pat, 1, red / cc, green / cc, blue / cc, 0.8);
255 	cairo_pattern_add_color_stop_rgba (pat, 0, red / cc, green / cc, blue / cc, 0.4);
256 	cairo_set_source (cr, pat);
257 	cairo_fill_preserve (cr);
258 	cairo_pattern_destroy (pat);
259 
260 	cairo_set_source_rgba (cr, red / cc, green / cc, blue / cc, 0);
261 	cairo_set_line_width (cr, 0.5);
262 	cairo_stroke (cr);
263 	cairo_restore (cr);
264 
265 	/* When resizing we don't draw the triangles.*/
266 	draw_start_triangle = TRUE;
267 	draw_end_triangle = TRUE;
268 	if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE
269 	    && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT
270 	    && day_view->resize_event_num == event_num) {
271 		if (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_LEFT_EDGE)
272 			draw_start_triangle = FALSE;
273 
274 		if  (day_view->resize_drag_pos == E_CALENDAR_VIEW_POS_RIGHT_EDGE)
275 			draw_end_triangle = FALSE;
276 	}
277 
278 	/* If the event starts before the first day shown, draw a triangle */
279 	if (draw_start_triangle
280 	    && event->start < day_view->day_starts[start_day]) {
281 		day_view_top_item_draw_triangle (
282 			top_item, cr, item_x - x + 4, item_y - y,
283 			-E_DAY_VIEW_BAR_WIDTH, item_h, event_num);
284 	}
285 
286 	/* Similar for the event end. */
287 	if (draw_end_triangle
288 	    && event->end > day_view->day_starts[end_day + 1]) {
289 		day_view_top_item_draw_triangle (
290 			top_item, cr, item_x + item_w - 4 - x,
291 			item_y - y, E_DAY_VIEW_BAR_WIDTH, item_h,
292 			event_num);
293 	}
294 
295 	/* If we are editing the event we don't show the icons or the start
296 	 * & end times. */
297 	if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT
298 	    && day_view->editing_event_num == event_num) {
299 		g_object_unref (comp);
300 		return;
301 	}
302 
303 	/* Determine the position of the label, so we know where to place the
304 	 * icons. Note that since the top canvas never scrolls we don't need
305 	 * to take the scroll offset into account. It will always be 0. */
306 	text_x = event->canvas_item->x1;
307 
308 	/* Draw the start & end times, if necessary. */
309 	min_end_time_x = item_x + E_DAY_VIEW_LONG_EVENT_X_PAD - x;
310 
311 	time_width = e_day_view_get_time_string_width (day_view);
312 
313 	gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_NORMAL]);
314 
315 	if (event->start > day_view->day_starts[start_day]) {
316 		offset = day_view->first_hour_shown * 60
317 			+ day_view->first_minute_shown + event->start_minute;
318 		hour = offset / 60;
319 		minute = offset % 60;
320 		/* Calculate the actual hour number to display. For 12-hour
321 		 * format we convert 0-23 to 12-11am/12-11pm. */
322 		e_day_view_convert_time_to_display (
323 			day_view, hour,
324 			&display_hour,
325 			&suffix, &suffix_width);
326 		if (e_cal_model_get_use_24_hour_format (model)) {
327 			g_snprintf (
328 				buffer, sizeof (buffer), "%i:%02i",
329 				display_hour, minute);
330 		} else {
331 			g_snprintf (
332 				buffer, sizeof (buffer), "%i:%02i%s",
333 				display_hour, minute, suffix);
334 		}
335 
336 		cairo_save (cr);
337 
338 		cairo_rectangle (
339 			cr,
340 			item_x - x, item_y - y,
341 			item_w - E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH, item_h);
342 		cairo_clip (cr);
343 
344 		time_x = item_x + E_DAY_VIEW_LONG_EVENT_X_PAD - x;
345 		if (display_hour < 10)
346 			time_x += day_view->digit_width;
347 
348 		layout = gtk_widget_create_pango_layout (GTK_WIDGET (day_view), buffer);
349 		cairo_move_to (
350 			cr,
351 			time_x,
352 			item_y + E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT +
353 			E_DAY_VIEW_LONG_EVENT_Y_PAD - y);
354 		pango_cairo_show_layout (cr, layout);
355 		g_object_unref (layout);
356 
357 		cairo_restore (cr);
358 
359 		min_end_time_x += time_width
360 			+ E_DAY_VIEW_LONG_EVENT_TIME_X_PAD;
361 	}
362 
363 	max_icon_x = item_x + item_w - E_DAY_VIEW_LONG_EVENT_X_PAD
364 		- E_DAY_VIEW_ICON_WIDTH;
365 
366 	if (event->end < day_view->day_starts[end_day + 1]) {
367 		offset = day_view->first_hour_shown * 60
368 			+ day_view->first_minute_shown
369 			+ event->end_minute;
370 		hour = offset / 60;
371 		minute = offset % 60;
372 		time_x =
373 			item_x + item_w - E_DAY_VIEW_LONG_EVENT_X_PAD -
374 			time_width - E_DAY_VIEW_LONG_EVENT_TIME_X_PAD - x;
375 
376 		if (time_x >= min_end_time_x) {
377 			/* Calculate the actual hour number to display. */
378 			e_day_view_convert_time_to_display (
379 				day_view, hour,
380 				&display_hour,
381 				&suffix,
382 				&suffix_width);
383 			if (e_cal_model_get_use_24_hour_format (model)) {
384 				g_snprintf (
385 					buffer, sizeof (buffer),
386 					"%i:%02i", display_hour, minute);
387 			} else {
388 				g_snprintf (
389 					buffer, sizeof (buffer),
390 					"%i:%02i%s", display_hour, minute,
391 					suffix);
392 			}
393 
394 			if (display_hour < 10)
395 				time_x += day_view->digit_width;
396 
397 			layout = gtk_widget_create_pango_layout (GTK_WIDGET (day_view), buffer);
398 			cairo_move_to (
399 				cr,
400 				time_x,
401 				item_y + E_DAY_VIEW_LONG_EVENT_Y_PAD + 1 - y);
402 			pango_cairo_show_layout (cr, layout);
403 			g_object_unref (layout);
404 
405 			max_icon_x -= time_width + E_DAY_VIEW_LONG_EVENT_TIME_X_PAD;
406 		}
407 	}
408 
409 	/* Draw the icons. */
410 	icon_x_inc = E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD;
411 	icon_x = text_x - E_DAY_VIEW_LONG_EVENT_ICON_R_PAD
412 		- icon_x_inc - x;
413 	icon_y = item_y + E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT
414 		+ E_DAY_VIEW_ICON_Y_PAD - y;
415 
416 	if (icon_x <= max_icon_x && (
417 		e_cal_component_has_recurrences (comp) ||
418 		e_cal_component_is_instance (comp))) {
419 		cairo_save (cr);
420 		gdk_cairo_set_source_pixbuf (cr, day_view->recurrence_icon, icon_x, icon_y);
421 		cairo_paint (cr);
422 		cairo_restore (cr);
423 
424 		icon_x -= icon_x_inc;
425 	}
426 
427 	if (icon_x <= max_icon_x && e_cal_component_has_attachments (comp)) {
428 		cairo_save (cr);
429 		gdk_cairo_set_source_pixbuf (cr, day_view->attach_icon, icon_x, icon_y);
430 		cairo_paint (cr);
431 		cairo_restore (cr);
432 
433 		icon_x -= icon_x_inc;
434 	}
435 	if (icon_x <= max_icon_x && e_cal_component_has_alarms (comp)) {
436 		cairo_save (cr);
437 		gdk_cairo_set_source_pixbuf (cr, day_view->reminder_icon, icon_x, icon_y);
438 		cairo_paint (cr);
439 		cairo_restore (cr);
440 
441 		icon_x -= icon_x_inc;
442 	}
443 
444 	if (icon_x <= max_icon_x && e_cal_component_has_attendees (comp)) {
445 		cairo_save (cr);
446 		gdk_cairo_set_source_pixbuf (cr, day_view->meeting_icon, icon_x, icon_y);
447 		cairo_paint (cr);
448 		cairo_restore (cr);
449 
450 		icon_x -= icon_x_inc;
451 	}
452 
453 	/* draw categories icons */
454 	e_cal_component_get_categories_list (comp, &categories_list);
455 	for (elem = categories_list; elem; elem = elem->next) {
456 		gchar *category;
457 		const gchar *file;
458 		GdkPixbuf *pixbuf;
459 
460 		category = (gchar *) elem->data;
461 		file = e_categories_get_icon_file_for (category);
462 		if (!file)
463 			continue;
464 
465 		pixbuf = gdk_pixbuf_new_from_file (file, NULL);
466 		if (pixbuf == NULL)
467 			continue;
468 
469 		if (icon_x <= max_icon_x) {
470 			gdk_cairo_set_source_pixbuf (
471 				cr, pixbuf,
472 				icon_x, icon_y);
473 			cairo_rectangle (
474 				cr,
475 				icon_x, icon_y,
476 				E_DAY_VIEW_ICON_WIDTH,
477 				E_DAY_VIEW_ICON_HEIGHT);
478 			cairo_fill (cr);
479 			icon_x -= icon_x_inc;
480 		}
481 	}
482 
483 	e_cal_component_free_categories_list (categories_list);
484 	g_object_unref (comp);
485 }
486 
487 static void
488 day_view_top_item_set_property (GObject *object,
489                                 guint property_id,
490                                 const GValue *value,
491                                 GParamSpec *pspec)
492 {
493 	switch (property_id) {
494 		case PROP_DAY_VIEW:
495 			e_day_view_top_item_set_day_view (
496 				E_DAY_VIEW_TOP_ITEM (object),
497 				g_value_get_object (value));
498 			return;
499 
500 		case PROP_SHOW_DATES:
501 			e_day_view_top_item_set_show_dates (
502 				E_DAY_VIEW_TOP_ITEM (object),
503 				g_value_get_boolean (value));
504 			return;
505 	}
506 
507 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
508 }
509 
510 static void
511 day_view_top_item_get_property (GObject *object,
512                                 guint property_id,
513                                 GValue *value,
514                                 GParamSpec *pspec)
515 {
516 	switch (property_id) {
517 		case PROP_DAY_VIEW:
518 			g_value_set_object (
519 				value, e_day_view_top_item_get_day_view (
520 				E_DAY_VIEW_TOP_ITEM (object)));
521 			return;
522 
523 		case PROP_SHOW_DATES:
524 			g_value_set_boolean (
525 				value, e_day_view_top_item_get_show_dates (
526 				E_DAY_VIEW_TOP_ITEM (object)));
527 			return;
528 	}
529 
530 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
531 }
532 
533 static void
534 day_view_top_item_dispose (GObject *object)
535 {
536 	EDayViewTopItemPrivate *priv;
537 
538 	priv = E_DAY_VIEW_TOP_ITEM_GET_PRIVATE (object);
539 
540 	if (priv->day_view != NULL) {
541 		g_object_unref (priv->day_view);
542 		priv->day_view = NULL;
543 	}
544 
545 	/* Chain up to parent's dispose() method. */
546 	G_OBJECT_CLASS (e_day_view_top_item_parent_class)->dispose (object);
547 }
548 
549 static void
550 day_view_top_item_update (GnomeCanvasItem *item,
551                           const cairo_matrix_t *i2c,
552                           gint flags)
553 {
554 	GnomeCanvasItemClass *canvas_item_class;
555 
556 	/* Chain up to parent's update() method. */
557 	canvas_item_class =
558 		GNOME_CANVAS_ITEM_CLASS (e_day_view_top_item_parent_class);
559 	canvas_item_class->update (item, i2c, flags);
560 
561 	/* The item covers the entire canvas area. */
562 	item->x1 = 0;
563 	item->y1 = 0;
564 	item->x2 = INT_MAX;
565 	item->y2 = INT_MAX;
566 }
567 
568 static void
569 day_view_top_item_draw (GnomeCanvasItem *canvas_item,
570                         cairo_t *cr,
571                         gint x,
572                         gint y,
573                         gint width,
574                         gint height)
575 {
576 	EDayViewTopItem *top_item;
577 	EDayView *day_view;
578 	GtkStyle *style;
579 	gchar buffer[128];
580 	GtkAllocation allocation;
581 	GdkRectangle clip_rect;
582 	gint canvas_width, canvas_height, left_edge, day, date_width, date_x;
583 	gint item_height, event_num;
584 	PangoLayout *layout;
585 	GdkColor bg, light, dark;
586 	gboolean show_dates;
587 
588 	top_item = E_DAY_VIEW_TOP_ITEM (canvas_item);
589 	day_view = e_day_view_top_item_get_day_view (top_item);
590 	g_return_if_fail (day_view != NULL);
591 	show_dates = top_item->priv->show_dates;
592 
593 	style = gtk_widget_get_style (GTK_WIDGET (day_view));
594 	gtk_widget_get_allocation (
595 		GTK_WIDGET (canvas_item->canvas), &allocation);
596 	canvas_width = allocation.width;
597 	canvas_height =
598 		(show_dates ? 1 :
599 		(MAX (1, day_view->rows_in_top_display) + 1)) *
600 		day_view->top_row_height;
601 	left_edge = 0;
602 	item_height = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP;
603 
604 	bg = style->bg[GTK_STATE_NORMAL];
605 	light = style->light[GTK_STATE_NORMAL];
606 	dark = style->dark[GTK_STATE_NORMAL];
607 
608 	if (show_dates) {
609 		/* Draw the shadow around the dates. */
610 		cairo_save (cr);
611 		gdk_cairo_set_source_color (cr, &light);
612 		cairo_move_to (cr, left_edge - x, 1 - y);
613 		cairo_line_to (cr, canvas_width - 2 - x, 1 - y);
614 		cairo_move_to (cr, left_edge - x, 2 - y);
615 		cairo_line_to (cr, left_edge - x, item_height - 2 - y);
616 		cairo_stroke (cr);
617 		cairo_restore (cr);
618 
619 		cairo_save (cr);
620 		gdk_cairo_set_source_color (cr, &dark);
621 		cairo_move_to (cr, left_edge - x, item_height - 1 - y);
622 		cairo_line_to (cr, canvas_width - 1 - x, item_height - 1 - y);
623 		cairo_move_to (cr, canvas_width - 1 - x, 1 - y);
624 		cairo_line_to (cr, canvas_width - 1 - x, item_height - 1 - y);
625 		cairo_stroke (cr);
626 		cairo_restore (cr);
627 
628 		/* Draw the background for the dates. */
629 		cairo_save (cr);
630 		gdk_cairo_set_source_color (cr, &bg);
631 		cairo_rectangle (
632 			cr, left_edge + 2 - x, 2 - y,
633 			canvas_width - left_edge - 3,
634 			item_height - 3);
635 		cairo_fill (cr);
636 		cairo_restore (cr);
637 	}
638 
639 	if (!show_dates) {
640 		/* Clear the main area background. */
641 		cairo_save (cr);
642 		gdk_cairo_set_source_color (
643 			cr, &day_view->colors[E_DAY_VIEW_COLOR_BG_TOP_CANVAS]);
644 		cairo_rectangle (
645 			cr, left_edge - x, - y,
646 			canvas_width - left_edge,
647 			canvas_height);
648 		cairo_fill (cr);
649 		cairo_restore (cr);
650 
651 		/* Draw the selection background. */
652 		if (gtk_widget_has_focus (GTK_WIDGET (day_view))
653 			&& day_view->selection_start_day != -1) {
654 			gint start_col, end_col, rect_x, rect_y, rect_w, rect_h;
655 
656 			start_col = day_view->selection_start_day;
657 			end_col = day_view->selection_end_day;
658 
659 			if (end_col > start_col
660 			    || day_view->selection_start_row == -1
661 			    || day_view->selection_end_row == -1) {
662 				rect_x = day_view->day_offsets[start_col];
663 				rect_y = 0;
664 				rect_w = day_view->day_offsets[end_col + 1] - rect_x;
665 				rect_h = canvas_height - 1 - rect_y;
666 
667 				cairo_save (cr);
668 				gdk_cairo_set_source_color (
669 					cr, &day_view->colors
670 					[E_DAY_VIEW_COLOR_BG_TOP_CANVAS_SELECTED]);
671 				cairo_rectangle (
672 					cr, rect_x - x, rect_y - y,
673 					rect_w, rect_h);
674 				cairo_fill (cr);
675 				cairo_restore (cr);
676 			}
677 		}
678 	}
679 
680 	if (show_dates) {
681 		/* Draw the date. Set a clipping rectangle
682 		 * so we don't draw over the next day. */
683 		for (day = 0; day < day_view->days_shown; day++) {
684 			e_day_view_top_item_get_day_label (
685 				day_view, day, buffer, sizeof (buffer));
686 			clip_rect.x = day_view->day_offsets[day] - x;
687 			clip_rect.y = 2 - y;
688 			if (day_view->days_shown == 1) {
689 				gtk_widget_get_allocation (
690 					day_view->top_canvas, &allocation);
691 				clip_rect.width =
692 					allocation.width -
693 					day_view->day_offsets[day];
694 			} else
695 				clip_rect.width = day_view->day_widths[day];
696 			clip_rect.height = item_height - 2;
697 
698 			cairo_save (cr);
699 
700 			gdk_cairo_rectangle (cr, &clip_rect);
701 			cairo_clip (cr);
702 
703 			layout = gtk_widget_create_pango_layout (
704 				GTK_WIDGET (day_view), buffer);
705 			pango_layout_get_pixel_size (layout, &date_width, NULL);
706 			date_x = day_view->day_offsets[day] +
707 				(clip_rect.width - date_width) / 2;
708 
709 			gdk_cairo_set_source_color (
710 				cr, &style->fg[GTK_STATE_NORMAL]);
711 			cairo_move_to (cr, date_x - x, 3 - y);
712 			pango_cairo_show_layout (cr, layout);
713 
714 			g_object_unref (layout);
715 			cairo_restore (cr);
716 
717 			/* Draw the lines down the left and right of the date cols. */
718 			if (day != 0) {
719 				cairo_save (cr);
720 				gdk_cairo_set_source_color (cr, &light);
721 				cairo_move_to (
722 					cr, day_view->day_offsets[day] - x,
723 					4 - y);
724 				cairo_line_to (
725 					cr, day_view->day_offsets[day] - x,
726 					item_height - 4 - y);
727 				cairo_stroke (cr);
728 				gdk_cairo_set_source_color (cr, &dark);
729 				cairo_move_to (
730 					cr, day_view->day_offsets[day] - 1 - x,
731 					4 - y);
732 				cairo_line_to (
733 					cr, day_view->day_offsets[day] - 1 - x,
734 					item_height - 4 - y);
735 				cairo_stroke (cr);
736 				cairo_restore (cr);
737 			}
738 
739 			/* Draw the lines between each column. */
740 			if (day != 0) {
741 				cairo_save (cr);
742 				gdk_cairo_set_source_color (
743 					cr, &day_view->colors
744 					[E_DAY_VIEW_COLOR_BG_TOP_CANVAS_GRID]);
745 				cairo_move_to (
746 					cr, day_view->day_offsets[day] - x,
747 					item_height - y);
748 				cairo_line_to (
749 					cr, day_view->day_offsets[day] - x,
750 					canvas_height - y);
751 				cairo_stroke (cr);
752 				cairo_restore (cr);
753 			}
754 		}
755 	}
756 
757 	if (!show_dates) {
758 		/* Draw the long events. */
759 		for (event_num = 0; event_num < day_view->long_events->len; event_num++) {
760 			day_view_top_item_draw_long_event (
761 				top_item, event_num, cr,
762 				x, y, width, height);
763 		}
764 	}
765 }
766 
767 static GnomeCanvasItem *
768 day_view_top_item_point (GnomeCanvasItem *item,
769                          gdouble x,
770                          gdouble y,
771                          gint cx,
772                          gint cy)
773 {
774 	return item;
775 }
776 
777 static void
778 e_day_view_top_item_class_init (EDayViewTopItemClass *class)
779 {
780 	GObjectClass *object_class;
781 	GnomeCanvasItemClass *item_class;
782 
783 	g_type_class_add_private (class, sizeof (EDayViewTopItemPrivate));
784 
785 	object_class = G_OBJECT_CLASS (class);
786 	object_class->set_property = day_view_top_item_set_property;
787 	object_class->get_property = day_view_top_item_get_property;
788 	object_class->dispose = day_view_top_item_dispose;
789 
790 	item_class = GNOME_CANVAS_ITEM_CLASS (class);
791 	item_class->update = day_view_top_item_update;
792 	item_class->draw = day_view_top_item_draw;
793 	item_class->point = day_view_top_item_point;
794 
795 	g_object_class_install_property (
796 		object_class,
797 		PROP_DAY_VIEW,
798 		g_param_spec_object (
799 			"day_view",
800 			"Day View",
801 			NULL,
802 			E_TYPE_DAY_VIEW,
803 			G_PARAM_READWRITE));
804 
805 	g_object_class_install_property (
806 		object_class,
807 		PROP_SHOW_DATES,
808 		g_param_spec_boolean (
809 			"show_dates",
810 			"Show Dates",
811 			NULL,
812 			TRUE,
813 			G_PARAM_READWRITE));
814 }
815 
816 static void
817 e_day_view_top_item_init (EDayViewTopItem *top_item)
818 {
819 	top_item->priv = E_DAY_VIEW_TOP_ITEM_GET_PRIVATE (top_item);
820 }
821 
822 void
823 e_day_view_top_item_get_day_label (EDayView *day_view,
824                                    gint day,
825                                    gchar *buffer,
826                                    gint buffer_len)
827 {
828 	ECalendarView *view;
829 	struct icaltimetype day_start_tt;
830 	const icaltimezone *zone;
831 	struct tm day_start = { 0 };
832 	const gchar *format;
833 
834 	view = E_CALENDAR_VIEW (day_view);
835 	zone = e_calendar_view_get_timezone (view);
836 
837 	day_start_tt = icaltime_from_timet_with_zone (
838 		day_view->day_starts[day], FALSE, zone);
839 	day_start.tm_year = day_start_tt.year - 1900;
840 	day_start.tm_mon = day_start_tt.month - 1;
841 	day_start.tm_mday = day_start_tt.day;
842 	day_start.tm_isdst = -1;
843 
844 	day_start.tm_wday = time_day_of_week (
845 		day_start_tt.day,
846 		day_start_tt.month - 1,
847 		day_start_tt.year);
848 
849 	if (day_view->date_format == E_DAY_VIEW_DATE_FULL)
850 		/* strftime format %A = full weekday name, %d = day of month,
851 		 * %B = full month name. Don't use any other specifiers. */
852 		format = _("%A %d %B");
853 	else if (day_view->date_format == E_DAY_VIEW_DATE_ABBREVIATED)
854 		/* strftime format %a = abbreviated weekday name, %d = day of month,
855 		 * %b = abbreviated month name. Don't use any other specifiers. */
856 		format = _("%a %d %b");
857 	else if (day_view->date_format == E_DAY_VIEW_DATE_NO_WEEKDAY)
858 		/* strftime format %d = day of month, %b = abbreviated month name.
859 		 * Don't use any other specifiers. */
860 		format = _("%d %b");
861 	else
862 		format = "%d";
863 
864 	e_utf8_strftime (buffer, buffer_len, format, &day_start);
865 }
866 
867 EDayView *
868 e_day_view_top_item_get_day_view (EDayViewTopItem *top_item)
869 {
870 	g_return_val_if_fail (E_IS_DAY_VIEW_TOP_ITEM (top_item), NULL);
871 
872 	return top_item->priv->day_view;
873 }
874 
875 void
876 e_day_view_top_item_set_day_view (EDayViewTopItem *top_item,
877                                   EDayView *day_view)
878 {
879 	g_return_if_fail (E_IS_DAY_VIEW_TOP_ITEM (top_item));
880 	g_return_if_fail (E_IS_DAY_VIEW (day_view));
881 
882 	if (top_item->priv->day_view == day_view)
883 		return;
884 
885 	if (top_item->priv->day_view != NULL)
886 		g_object_unref (top_item->priv->day_view);
887 
888 	top_item->priv->day_view = g_object_ref (day_view);
889 
890 	g_object_notify (G_OBJECT (top_item), "day-view");
891 }
892 
893 gboolean
894 e_day_view_top_item_get_show_dates (EDayViewTopItem *top_item)
895 {
896 	g_return_val_if_fail (E_IS_DAY_VIEW_TOP_ITEM (top_item), FALSE);
897 
898 	return top_item->priv->show_dates;
899 }
900 
901 void
902 e_day_view_top_item_set_show_dates (EDayViewTopItem *top_item,
903                                     gboolean show_dates)
904 {
905 	g_return_if_fail (E_IS_DAY_VIEW_TOP_ITEM (top_item));
906 
907 	if (top_item->priv->show_dates == show_dates)
908 		return;
909 
910 	top_item->priv->show_dates = show_dates;
911 
912 	g_object_notify (G_OBJECT (top_item), "show-dates");
913 }