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