evolution-3.6.4/calendar/gui/print.c

No issues found

   1 /*
   2  * Evolution calendar - Print support
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  *
  18  * Authors:
  19  *		Michael Zucchi <notzed@ximian.com>
  20  *      Federico Mena-Quintero <federico@ximian.com>
  21  *	    Damon Chaplin <damon@ximian.com>
  22  *
  23  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  24  *
  25  */
  26 
  27 #ifdef HAVE_CONFIG_H
  28 #include <config.h>
  29 #endif
  30 
  31 #include <sys/stat.h>
  32 #include <sys/time.h>
  33 #include <math.h>
  34 #include <string.h>
  35 #include <time.h>
  36 #include <gtk/gtk.h>
  37 #include <glib/gi18n.h>
  38 
  39 #include <e-util/e-util.h>
  40 #include <e-util/e-print.h>
  41 #include "e-cal-model.h"
  42 #include "e-day-view.h"
  43 #include "e-day-view-layout.h"
  44 #include "e-week-view.h"
  45 #include "e-week-view-layout.h"
  46 #include "e-task-table.h"
  47 #include "gnome-cal.h"
  48 #include "print.h"
  49 
  50 #include "art/jump.xpm"
  51 
  52 typedef struct PrintCompItem PrintCompItem;
  53 typedef struct PrintCalItem PrintCalItem;
  54 
  55 struct PrintCompItem {
  56 	ECalClient *client;
  57 	ECalComponent *comp;
  58 	icaltimezone *zone;
  59 	gboolean use_24_hour_format;
  60 };
  61 
  62 struct PrintCalItem {
  63 	GnomeCalendar *gcal;
  64 	time_t start;
  65 };
  66 
  67 static gdouble
  68 evo_calendar_print_renderer_get_width (GtkPrintContext *context,
  69                                        PangoFontDescription *font,
  70                                        const gchar *text)
  71 {
  72 	PangoLayout *layout;
  73 	gint layout_width, layout_height;
  74 
  75 	layout = gtk_print_context_create_pango_layout (context);
  76 
  77 	pango_layout_set_font_description (layout, font);
  78 	pango_layout_set_text (layout, text, -1);
  79 	pango_layout_set_indent (layout, 0);
  80 	pango_layout_get_size (layout, &layout_width, &layout_height);
  81 
  82 	g_object_unref (layout);
  83 
  84 	return pango_units_to_double (layout_width);
  85 }
  86 
  87 static gdouble
  88 evo_calendar_print_renderer_get_height (GtkPrintContext *context,
  89                                         PangoFontDescription *font,
  90                                         const gchar *text)
  91 {
  92 	PangoLayout *layout;
  93 	gint layout_width, layout_height;
  94 
  95 	layout = gtk_print_context_create_pango_layout (context);
  96 
  97 	pango_layout_set_font_description (layout, font);
  98 	pango_layout_set_text (layout, text, -1);
  99 	pango_layout_set_indent (layout, 0);
 100 	pango_layout_get_size (layout, &layout_width, &layout_height);
 101 
 102 	g_object_unref (layout);
 103 
 104 	return pango_units_to_double (layout_height);
 105 }
 106 
 107 static gdouble
 108 get_font_size (PangoFontDescription *font)
 109 {
 110 	g_return_val_if_fail (font, 0.0);
 111 
 112 	return pango_units_to_double (pango_font_description_get_size (font));
 113 }
 114 
 115 static gint
 116 get_day_view_time_divisions (void)
 117 {
 118 	GSettings *settings;
 119 	gint time_divisions;
 120 
 121 	settings = g_settings_new ("org.gnome.evolution.calendar");
 122 
 123 	time_divisions = g_settings_get_int (settings, "time-divisions");
 124 	if (time_divisions < 5 || time_divisions > 30)
 125 		time_divisions = 30;
 126 
 127 	g_object_unref (settings);
 128 
 129 	return time_divisions;
 130 }
 131 
 132 /*
 133  * Note that most dimensions are in points (1/72 of an inch) since that is
 134  * what gnome-print uses.
 135  */
 136 
 137 /* GtkHTML prints using a fixed margin. It has code to get the margins from
 138  * gnome-print keys, but it's commented out. The corresponding code here
 139  * doesn't seem to work either (getting zero margins), so we adopt
 140  * gtkhtml's cheat. */
 141 
 142 #define TEMP_MARGIN .05
 143 
 144 /* The fonts to use */
 145 #define FONT_FAMILY "Sans"
 146 
 147 /* The font size to use for normal text. */
 148 #define DAY_NORMAL_FONT_SIZE	12
 149 #define WEEK_NORMAL_FONT_SIZE	12
 150 #define MONTH_NORMAL_FONT_SIZE	8
 151 #define WEEK_EVENT_FONT_SIZE	9
 152 #define WEEK_SMALL_FONT_SIZE	8
 153 
 154 /* The height of the header bar across the top of the Day, Week & Month views,
 155  * which contains the dates shown and the 2 small calendar months. */
 156 #define HEADER_HEIGHT		80
 157 
 158 /* The width of the small calendar months, the space from the right edge of
 159  * the header rectangle, and the space between the months. */
 160 #define MIN_SMALL_MONTH_WIDTH	120
 161 #define SMALL_MONTH_PAD		5
 162 #define SMALL_MONTH_SPACING	20
 163 
 164 /* The minimum number of rows we leave space for for the long events in the
 165  * day view. */
 166 #define DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY	2
 167 
 168 /* The row height for long events in the day view. */
 169 #define DAY_VIEW_ROW_HEIGHT		14
 170 
 171 #define CALC_DAY_VIEW_ROWS(divis)		((60 / divis) * 24)
 172 
 173 /* The width of the column with all the times in it. */
 174 #define DAY_VIEW_TIME_COLUMN_WIDTH	36
 175 
 176 /* The space on the right of each event. */
 177 #define DAY_VIEW_EVENT_X_PAD		8
 178 
 179 /* Allowance for small errors in floating point comparisons. */
 180 #define EPSILON			0.01
 181 
 182 /* The weird month of September 1752, where 3 Sep through 13 Sep were
 183  * eliminated due to the Gregorian reformation. */
 184 static const gint sept_1752[42] = {
 185 	 0,  0,  1,  2, 14, 15, 16,
 186 	17, 18, 19, 20, 21, 22, 23,
 187 	24, 25, 26, 27, 28, 29, 30,
 188 	 0,  0,  0,  0,  0,  0,  0,
 189 	 0,  0,  0,  0,  0,  0,  0,
 190 	 0,  0,  0,  0,  0,  0,  0
 191 };
 192 #define SEPT_1752_START 2		/* Start day within month */
 193 #define SEPT_1752_END 20		/* End day within month */
 194 
 195 struct pdinfo
 196 {
 197 	gint days_shown;
 198 	time_t day_starts[E_DAY_VIEW_MAX_DAYS + 1];
 199 
 200 	GArray *long_events;
 201 	GArray *events[E_DAY_VIEW_MAX_DAYS];
 202 
 203 	gint start_hour;
 204 	gint end_hour;
 205 	gint start_minute_offset;
 206 	gint end_minute_offset;
 207 	gint rows;
 208 	gint mins_per_row;
 209 	guint8 cols_per_row[CALC_DAY_VIEW_ROWS (1)];
 210 	gboolean use_24_hour_format;
 211 	icaltimezone *zone;
 212 };
 213 
 214 struct psinfo
 215 {
 216 	gint days_shown;
 217 	time_t day_starts[E_WEEK_VIEW_MAX_WEEKS * 7 + 1];
 218 
 219 	GArray *events;
 220 
 221 	gint rows_per_cell;
 222 	gint rows_per_compressed_cell;
 223 	gint display_start_weekday;
 224 	gboolean multi_week_view;
 225 	gint weeks_shown;
 226 	gint month;
 227 	gboolean compress_weekend;
 228 	gboolean use_24_hour_format;
 229 	gdouble row_height;
 230 	gdouble header_row_height;
 231 	icaltimezone *zone;
 232 };
 233 
 234 /* Convenience function to help the transition to timezone functions.
 235  * It converts a time_t to a struct tm. */
 236 static void
 237 convert_timet_to_struct_tm (time_t time,
 238                             icaltimezone *zone,
 239                             struct tm *tm)
 240 {
 241 	struct icaltimetype tt;
 242 
 243 	/* Convert it to an icaltimetype. */
 244 	tt = icaltime_from_timet_with_zone (time, FALSE, zone);
 245 
 246 	/* Fill in the struct tm. */
 247 	tm->tm_year = tt.year - 1900;
 248 	tm->tm_mon = tt.month - 1;
 249 	tm->tm_mday = tt.day;
 250 	tm->tm_hour = tt.hour;
 251 	tm->tm_min = tt.minute;
 252 	tm->tm_sec = tt.second;
 253 	tm->tm_isdst = tt.is_daylight;
 254 
 255 	tm->tm_wday = time_day_of_week (tt.day, tt.month - 1, tt.year);
 256 }
 257 
 258 /* Fills the 42-element days array with the day numbers for the specified
 259  * month.  Slots outside the bounds of the month are filled with zeros.
 260  * The starting and ending indexes of the days are returned in the start
 261  * and end arguments. */
 262 static void
 263 build_month (ECalModel *model,
 264              gint month,
 265              gint year,
 266              gint *days,
 267              gint *start,
 268              gint *end)
 269 {
 270 	gint i;
 271 	gint d_month, d_week, week_start_day;
 272 
 273 	/* Note that months are zero-based, so September is month 8 */
 274 
 275 	if ((year == 1752) && (month == 8)) {
 276 		memcpy (days, sept_1752, 42 * sizeof (gint));
 277 
 278 		if (start)
 279 			*start = SEPT_1752_START;
 280 
 281 		if (end)
 282 			*end = SEPT_1752_END;
 283 
 284 		return;
 285 	}
 286 
 287 	for (i = 0; i < 42; i++)
 288 		days[i] = 0;
 289 
 290 	d_month = time_days_in_month (year, month);
 291 	/* Get the start weekday in the month, 0=Sun to 6=Sat. */
 292 	d_week = time_day_of_week (1, month, year);
 293 
 294 	/* Get the configuration setting specifying which weekday we put on
 295 	 * the left column, 0=Sun to 6=Sat. */
 296 	week_start_day = e_cal_model_get_week_start_day (model);
 297 
 298 	/* Figure out which square we want to put the 1 in. */
 299 	d_week = (d_week + 7 - week_start_day) % 7;
 300 
 301 	for (i = 0; i < d_month; i++)
 302 		days[d_week + i] = i + 1;
 303 
 304 	if (start)
 305 		*start = d_week;
 306 
 307 	if (end)
 308 		*end = d_week + d_month - 1;
 309 }
 310 
 311 static PangoFontDescription *
 312 get_font_for_size (gdouble height,
 313                    PangoWeight weight)
 314 {
 315 	PangoFontDescription *desc;
 316 	gint size;
 317 
 318 	#define MAGIC_SCALE_FACTOR (0.86)
 319 	size = pango_units_from_double (height * MAGIC_SCALE_FACTOR);
 320 
 321 	desc = pango_font_description_new ();
 322 	pango_font_description_set_size (desc, size);
 323 	pango_font_description_set_weight (desc, weight);
 324 	pango_font_description_set_family_static (desc, FONT_FAMILY);
 325 
 326 	return desc;
 327 }
 328 
 329 /* Prints a rectangle, with or without a border, filled or outline, and
 330  * possibly with triangular arrows at one or both horizontal edges.
 331  * width      = width of border, -ve means no border.
 332  * red,green,blue = bgcolor to fill,   -ve means no fill.
 333  * left_triangle_width, right_triangle_width = width from edge of rectangle to
 334  *        point of triangle, or -ve for no triangle. */
 335 static void
 336 print_border_with_triangles (GtkPrintContext *pc,
 337                              gdouble x1,
 338                              gdouble x2,
 339                              gdouble y1,
 340                              gdouble y2,
 341                              gdouble line_width,
 342                              gdouble red,
 343                              gdouble green,
 344                              gdouble blue,
 345                              gdouble left_triangle_width,
 346                              gdouble right_triangle_width)
 347 {
 348 	cairo_t *cr = gtk_print_context_get_cairo_context (pc);
 349 
 350 	cairo_save (cr);
 351 
 352 	/* Fill in the interior of the rectangle, if desired. */
 353 	if (red >= -EPSILON && green >= -EPSILON && blue >= -EPSILON) {
 354 
 355 		cairo_move_to (cr, x1, y1);
 356 
 357 		if (left_triangle_width > 0.0)
 358 			cairo_line_to (
 359 				cr, x1 - left_triangle_width,
 360 				(y1 + y2) / 2);
 361 
 362 		cairo_line_to (cr, x1, y2);
 363 		cairo_line_to (cr, x2, y2);
 364 
 365 		if (right_triangle_width > 0.0)
 366 			cairo_line_to (cr, x2 + right_triangle_width, (y1 + y2) / 2);
 367 
 368 		cairo_line_to (cr, x2, y1);
 369 		cairo_close_path (cr);
 370 		cairo_set_source_rgb (cr, red, green, blue);
 371 		cairo_fill (cr);
 372 		cairo_restore (cr);
 373 		cairo_save (cr);
 374 	}
 375 
 376 	/* Draw the outline, if desired. */
 377 	if (line_width >= EPSILON) {
 378 
 379 		cr = gtk_print_context_get_cairo_context (pc);
 380 		cairo_move_to (cr, x1, y1);
 381 
 382 		if (left_triangle_width > 0.0)
 383 			cairo_line_to (
 384 				cr, x1 - left_triangle_width,
 385 				(y1 + y2) / 2);
 386 
 387 		cairo_line_to (cr, x1, y2);
 388 		cairo_line_to (cr, x2, y2);
 389 
 390 		if (right_triangle_width > 0.0)
 391 			cairo_line_to (
 392 				cr, x2 + right_triangle_width,
 393 				(y1 + y2) / 2);
 394 
 395 		cairo_line_to (cr, x2, y1);
 396 		cairo_close_path (cr);
 397 		cairo_set_source_rgb (cr, 0, 0, 0);
 398 		cairo_set_line_width (cr, line_width);
 399 		cairo_stroke (cr);
 400 	}
 401 
 402 	cairo_restore (cr);
 403 }
 404 
 405 /* Prints a rectangle, with or without a border, and filled or outline.
 406  * width      = width of border, -ve means no border.
 407  * fillcolor = shade of fill,   -ve means no fill. */
 408 static void
 409 print_border_rgb (GtkPrintContext *pc,
 410                   gdouble x1,
 411                   gdouble x2,
 412                   gdouble y1,
 413                   gdouble y2,
 414                   gdouble line_width,
 415                   gdouble red,
 416                   gdouble green,
 417                   gdouble blue)
 418 {
 419 	print_border_with_triangles (
 420 		pc, x1, x2, y1, y2, line_width,
 421 		red, green, blue, -1.0, -1.0);
 422 }
 423 
 424 static void
 425 print_border (GtkPrintContext *pc,
 426               gdouble x1,
 427               gdouble x2,
 428               gdouble y1,
 429               gdouble y2,
 430               gdouble line_width,
 431               gdouble fillcolor)
 432 {
 433 	print_border_rgb (
 434 		pc, x1, x2, y1, y2, line_width,
 435 		fillcolor, fillcolor, fillcolor);
 436 }
 437 
 438 static void
 439 print_rectangle (GtkPrintContext *context,
 440                  gdouble x,
 441                  gdouble y,
 442                  gdouble width,
 443                  gdouble height,
 444                  gdouble red,
 445                  gdouble green,
 446                  gdouble blue)
 447 {
 448 	cairo_t *cr = gtk_print_context_get_cairo_context (context);
 449 
 450 	cairo_save (cr);
 451 
 452 	cairo_rectangle (cr, x, y, width, height);
 453 	cairo_set_source_rgb (cr, red, green, blue);
 454 	cairo_fill (cr);
 455 
 456 	cairo_restore (cr);
 457 }
 458 
 459 /* Recreate the layout by shrinking the text string if we have a line that's
 460  * too high.
 461  */
 462 static PangoLayout *
 463 shrink_text_to_line (PangoLayout *layout,
 464                      gint layout_width,
 465                      gint layout_height,
 466                      GtkPrintContext *context,
 467                      PangoFontDescription *desc,
 468                      const gchar *text,
 469                      PangoAlignment alignment,
 470                      gdouble x1,
 471                      gdouble x2,
 472                      gdouble y1,
 473                      gdouble y2)
 474 {
 475 	gint new_length;
 476 
 477 	if (layout_width == 0 || x2 - x1 < EPSILON)
 478 		return layout; /* Do nothing */
 479 
 480 	new_length = (gint) floor (pango_units_from_double (x2 - x1) /
 481 			(gdouble) layout_width * (gdouble) strlen (text));
 482 
 483 	if (new_length < strlen (text)) {
 484 		g_object_unref (layout); /* Destroy old layout */
 485 		layout = gtk_print_context_create_pango_layout (context);
 486 
 487 		pango_layout_set_font_description (layout, desc);
 488 		pango_layout_set_alignment (layout, alignment);
 489 		pango_layout_set_text (layout, text, new_length);
 490 	}
 491 
 492 	return layout;
 493 }
 494 
 495 /* Prints 1 line of aligned text in a box. It is centered vertically, and
 496  * the horizontal alignment can be either PANGO_ALIGN_LEFT, PANGO_ALIGN_RIGHT,
 497  * or PANGO_ALIGN_CENTER. Text is truncated if too long for cell. */
 498 static gdouble
 499 print_text_line (GtkPrintContext *context,
 500                  PangoFontDescription *desc,
 501                  const gchar *text,
 502                  PangoAlignment alignment,
 503                  gdouble x1,
 504                  gdouble x2,
 505                  gdouble y1,
 506                  gdouble y2,
 507                  gboolean shrink)
 508 {
 509 	PangoLayout *layout;
 510 	gint layout_width, layout_height;
 511 	cairo_t *cr;
 512 
 513 	cr = gtk_print_context_get_cairo_context (context);
 514 	layout = gtk_print_context_create_pango_layout (context);
 515 
 516 	pango_layout_set_font_description (layout, desc);
 517 	pango_layout_set_alignment (layout, alignment);
 518 	pango_layout_set_text (layout, text, -1);
 519 
 520 	/* Grab the width before expanding the layout. */
 521 	pango_layout_get_size (layout, &layout_width, &layout_height);
 522 
 523 	if (shrink && layout_width > pango_units_from_double (x2 - x1)) /* Too wide */
 524 		layout = shrink_text_to_line (
 525 			layout, layout_width, layout_height,
 526 			context, desc, text, alignment,
 527 			x1, x2, y1, y2);
 528 
 529 	pango_layout_set_width (layout, pango_units_from_double (x2 - x1));
 530 
 531 	cairo_save (cr);
 532 
 533 	/* Set a clipping rectangle. */
 534 	cairo_move_to (cr, x1, y1);
 535 	cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
 536 	cairo_clip (cr);
 537 
 538 	cairo_new_path (cr);
 539 	cairo_set_source_rgb (cr, 0, 0, 0);
 540 
 541 	cairo_move_to (cr, x1, y1);
 542 	pango_cairo_show_layout (cr, layout);
 543 
 544 	cairo_stroke (cr);
 545 
 546 	cairo_restore (cr);
 547 
 548 	g_object_unref (layout);
 549 
 550 	return pango_units_to_double (layout_width);
 551 }
 552 
 553 /* Prints 1 or more lines of aligned text in a box. It is centered vertically, and
 554  * the horizontal alignment can be either PANGO_ALIGN_LEFT, PANGO_ALIGN_RIGHT,
 555  * or PANGO_ALIGN_CENTER. */
 556 static double
 557 print_text (GtkPrintContext *context,
 558             PangoFontDescription *desc,
 559             const gchar *text,
 560             PangoAlignment alignment,
 561             gdouble x1,
 562             gdouble x2,
 563             gdouble y1,
 564             gdouble y2)
 565 {
 566 	return print_text_line (
 567 		context, desc,
 568 		text, alignment,
 569 		x1, x2, y1, y2, FALSE);
 570 }
 571 
 572 /* gets/frees the font for you, as a bold font */
 573 static gdouble
 574 print_text_size_bold (GtkPrintContext *context,
 575                       const gchar *text,
 576                       PangoAlignment alignment,
 577                       gdouble x1,
 578                       gdouble x2,
 579                       gdouble y1,
 580                       gdouble y2)
 581 {
 582 	PangoFontDescription *font;
 583 	gdouble w;
 584 
 585 	font = get_font_for_size (ABS (y2 - y1) * 0.5, PANGO_WEIGHT_BOLD);
 586 	w = print_text (context, font, text, alignment, x1, x2, y1, y2);
 587 	pango_font_description_free (font);
 588 
 589 	return w;
 590 }
 591 
 592 /* gets/frees the font for you, as a bold font - absolute size parameter */
 593 static double
 594 print_text_abs_bold (GtkPrintContext *context,
 595                      const gchar *text,
 596                      gdouble font_size,
 597                      PangoAlignment alignment,
 598                      gdouble x1,
 599                      gdouble x2,
 600                      gdouble y1,
 601                      gdouble y2)
 602 {
 603 	PangoFontDescription *font;
 604 	gdouble w;
 605 
 606 	font = get_font_for_size (font_size, PANGO_WEIGHT_BOLD);
 607 	w = print_text_line (context, font, text, alignment, x1, x2, y1, y2, TRUE);
 608 	pango_font_description_free (font);
 609 
 610 	return w;
 611 }
 612 
 613 static void
 614 titled_box (GtkPrintContext *context,
 615             const gchar *text,
 616             PangoFontDescription *font,
 617             PangoAlignment alignment,
 618             gdouble *x1,
 619             gdouble *y1,
 620             gdouble *x2,
 621             gdouble *y2,
 622             gdouble linewidth)
 623 {
 624 	gdouble size;
 625 
 626 	size = evo_calendar_print_renderer_get_height (context, font, text);
 627 	print_border (context, *x1, *x2, *y1, *y1 + size  + 2, linewidth, 0.9);
 628 	print_border (context, *x1, *x2, *y1 + size + 2, *y2, linewidth, -1.0);
 629 	*x1 += 2;
 630 	*x2 -= 2;
 631 	*y2 += 2;
 632 	print_text (context, font, text, alignment, *x1, *x2, *y1 + 1.0, *y1 + size);
 633 	*y1 += size + 2;
 634 }
 635 
 636 static gboolean
 637 get_show_week_numbers (void)
 638 {
 639 	EShell *shell;
 640 	EShellSettings *shell_settings;
 641 
 642 	shell = e_shell_get_default ();
 643 	shell_settings = e_shell_get_shell_settings (shell);
 644 
 645 	return e_shell_settings_get_boolean (
 646 		shell_settings, "cal-show-week-numbers");
 647 }
 648 
 649 enum datefmt {
 650 	DATE_MONTH	= 1 << 0,
 651 	DATE_DAY	= 1 << 1,
 652 	DATE_DAYNAME	= 1 << 2,
 653 	DATE_YEAR	= 1 << 3
 654 };
 655 
 656 static const gchar *days[] = {
 657 	N_("1st"), N_("2nd"), N_("3rd"), N_("4th"), N_("5th"),
 658 	N_("6th"), N_("7th"), N_("8th"), N_("9th"), N_("10th"),
 659 	N_("11th"), N_("12th"), N_("13th"), N_("14th"), N_("15th"),
 660 	N_("16th"), N_("17th"), N_("18th"), N_("19th"), N_("20th"),
 661 	N_("21st"), N_("22nd"), N_("23rd"), N_("24th"), N_("25th"),
 662 	N_("26th"), N_("27th"), N_("28th"), N_("29th"),	N_("30th"),
 663 	N_("31st")
 664 };
 665 
 666 /*
 667   format the date 'nicely' and consistently for various headers
 668 */
 669 static gchar *
 670 format_date (struct tm *tm,
 671              gint flags,
 672              gchar *buffer,
 673              gint bufflen)
 674 {
 675 	gchar fmt[64];
 676 
 677 	fmt[0] = 0;
 678 	if (flags & DATE_DAYNAME) {
 679 		strcat (fmt, "%A");
 680 	}
 681 	if (flags & DATE_DAY) {
 682 		if (flags & DATE_DAYNAME)
 683 			strcat (fmt, " ");
 684 		strcat (fmt, gettext (days[tm->tm_mday - 1]));
 685 	}
 686 	if (flags & DATE_MONTH) {
 687 		if (flags & (DATE_DAY | DATE_DAYNAME))
 688 			strcat (fmt, " ");
 689 		strcat (fmt, "%B");
 690 		if ((flags & (DATE_DAY | DATE_YEAR)) == (DATE_DAY | DATE_YEAR))
 691 			strcat (fmt, ",");
 692 	}
 693 	if (flags & DATE_YEAR) {
 694 		if (flags & (DATE_DAY | DATE_DAYNAME | DATE_MONTH))
 695 			strcat (fmt, " ");
 696 		strcat (fmt, "%Y");
 697 	}
 698 	e_utf8_strftime (buffer, bufflen, fmt, tm);
 699 	buffer[bufflen - 1] = '\0';
 700 
 701 	return buffer;
 702 }
 703 
 704 static gboolean
 705 instance_cb (ECalComponent *comp,
 706              time_t instance_start,
 707              time_t instance_end,
 708              gpointer data)
 709 {
 710 
 711 	gboolean *found = ((ECalModelGenerateInstancesData *) data)->cb_data;
 712 
 713 	*found = TRUE;
 714 
 715 	return FALSE;
 716 }
 717 
 718 const gchar *daynames[] =
 719 /* Translators: These are workday abbreviations, e.g. Su=Sunday and Th=thursday */
 720 	{ N_("Su"), N_("Mo"), N_("Tu"), N_("We"),
 721 	  N_("Th"), N_("Fr"), N_("Sa") };
 722 
 723 static gdouble
 724 calc_small_month_width (GtkPrintContext *context,
 725                         gdouble for_height)
 726 {
 727 
 728 	PangoFontDescription *font_bold;
 729 	gdouble res = 0.0;
 730 	gint ii;
 731 
 732 	font_bold = get_font_for_size (for_height / 7.4, PANGO_WEIGHT_BOLD);
 733 	res = MAX (evo_calendar_print_renderer_get_width (
 734 		context, font_bold, "23"), res);
 735 	for (ii = 0; ii < 7; ii++) {
 736 		res = MAX (evo_calendar_print_renderer_get_width (
 737 			context, font_bold, _(daynames[ii])), res);
 738 	}
 739 
 740 	pango_font_description_free (font_bold);
 741 
 742 	/* res is max cell width, so multiply it with column
 743 	 * count plus some space between columns. */
 744 	res = (res + 1.0) * (7 + (get_show_week_numbers () ? 1 : 0)) - 1.0;
 745 
 746 	if (res < MIN_SMALL_MONTH_WIDTH)
 747 		res = MIN_SMALL_MONTH_WIDTH;
 748 
 749 	return res;
 750 }
 751 
 752 /*
 753   print out the month small, embolden any days with events.
 754 */
 755 static void
 756 print_month_small (GtkPrintContext *context,
 757                    GnomeCalendar *gcal,
 758                    time_t month,
 759                    gdouble x1,
 760                    gdouble y1,
 761                    gdouble x2,
 762                    gdouble y2,
 763                    gint titleflags,
 764                    time_t greystart,
 765                    time_t greyend,
 766                    gint bordertitle)
 767 {
 768 	icaltimezone *zone;
 769 	PangoFontDescription *font, *font_bold, *font_normal;
 770 	ECalModel *model;
 771 	time_t now, next;
 772 	gint x, y;
 773 	gint days[42];
 774 	gint day, weekday, week_start_day;
 775 	gchar buf[100];
 776 	struct tm tm;
 777 	gdouble font_size;
 778 	gdouble header_size, col_width, row_height, text_xpad, w;
 779 	gdouble cell_top, cell_bottom, cell_left, cell_right, text_right;
 780 	gboolean week_numbers;
 781 	cairo_t *cr;
 782 
 783 	model = gnome_calendar_get_model (gcal);
 784 	zone = e_cal_model_get_timezone (model);
 785 
 786 	week_numbers = get_show_week_numbers ();
 787 
 788 	/* Print the title, e.g. 'June 2001', in the top 16% of the area. */
 789 	convert_timet_to_struct_tm (month, zone, &tm);
 790 	format_date (&tm, titleflags, buf, 100);
 791 
 792 	header_size = ABS (y2 - y1) * 0.16;
 793 
 794 	font = get_font_for_size (header_size, PANGO_WEIGHT_BOLD);
 795 	if (bordertitle)
 796 		print_border (context, x1, x2, y1, y1 + header_size, 1.0, 0.9);
 797 	print_text (
 798 		context, font, buf, PANGO_ALIGN_CENTER, x1, x2,
 799 		y1, y1 + header_size);
 800 	pango_font_description_free (font);
 801 
 802 	y1 += header_size;
 803 	col_width = (x2 - x1) / (7 + (week_numbers ? 1 : 0));
 804 
 805 	/* The top row with the day abbreviations gets an extra bit of
 806 	 * vertical space around it. */
 807 	row_height = ABS (y2 - y1) / 7.4;
 808 
 809 	/* First we need to calculate a reasonable font size. We start with a
 810 	 * rough guess of just under the height of each row. */
 811 	font_size = row_height;
 812 
 813 	/* get month days */
 814 	convert_timet_to_struct_tm (month, zone, &tm);
 815 	build_month (model, tm.tm_mon, tm.tm_year + 1900, days, NULL, NULL);
 816 
 817 	font_normal = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
 818 	font_bold = get_font_for_size (font_size, PANGO_WEIGHT_BOLD);
 819 
 820 	/* Get a reasonable estimate of the largest number we will need,
 821 	 * and use it to calculate the offset from the right edge of the
 822 	 * cell that we should put the numbers. */
 823 	w = evo_calendar_print_renderer_get_width (context, font_bold, "23");
 824 	text_xpad = (col_width - w) / 2;
 825 
 826 	cr = gtk_print_context_get_cairo_context (context);
 827 	cairo_set_source_rgb (cr, 0, 0, 0);
 828 
 829 	/* Print the abbreviated day names across the top in bold. */
 830 	week_start_day = e_cal_model_get_week_start_day (model);
 831 	weekday = week_start_day;
 832 	for (x = 0; x < 7; x++) {
 833 		print_text (
 834 			context, font_bold,
 835 			_(daynames[weekday]), PANGO_ALIGN_RIGHT,
 836 			x1 + (x + (week_numbers ? 1 : 0)) * col_width,
 837 			x1 + (x + 1 + (week_numbers ? 1 : 0)) * col_width,
 838 			y1, y1 + row_height * 1.4);
 839 		weekday = (weekday + 1) % 7;
 840 	}
 841 
 842 	y1 += row_height * 1.4;
 843 
 844 	now = time_month_begin_with_zone (month, zone);
 845 	for (y = 0; y < 6; y++) {
 846 
 847 		cell_top = y1 + y * row_height;
 848 		cell_bottom = cell_top + row_height;
 849 
 850 		if (week_numbers) {
 851 			cell_left = x1;
 852 			/* We add a 0.05 to make sure the cells meet up with
 853 			 * each other. Otherwise you sometimes get lines
 854 			 * between them which looks bad. Maybe I'm not using
 855 			 * coords in the way gnome-print expects. */
 856 			cell_right = cell_left + col_width + 0.05;
 857 			text_right = cell_right - text_xpad;
 858 
 859 			/* last week can be empty */
 860 			for (x = 0; x < 7; x++) {
 861 				day = days[y * 7 + x];
 862 				if (day != 0)
 863 					break;
 864 			}
 865 
 866 			if (day != 0) {
 867 				time_t week_begin;
 868 
 869 				week_begin = time_week_begin_with_zone (
 870 					now, week_start_day, zone);
 871 
 872 				convert_timet_to_struct_tm (
 873 					week_begin, zone, &tm);
 874 
 875 				/* Month in e_calendar_item_get_week_number
 876 				 * is also zero-based. */
 877 				sprintf (
 878 					buf, "%d",
 879 					e_calendar_item_get_week_number (
 880 					NULL, tm.tm_mday, tm.tm_mon,
 881 					tm.tm_year + 1900));
 882 
 883 				print_text (
 884 					context, font_normal,
 885 					buf, PANGO_ALIGN_RIGHT,
 886 					cell_left, text_right,
 887 					cell_top, cell_bottom);
 888 			}
 889 		}
 890 
 891 		for (x = 0; x < 7; x++) {
 892 
 893 			cell_left = x1 + (x + (week_numbers ? 1 : 0)) * col_width;
 894 			/* We add a 0.05 to make sure the cells meet up with
 895 			 * each other. Otherwise you sometimes get lines
 896 			 * between them which looks bad. Maybe I'm not using
 897 			 * coords in the way gnome-print expects. */
 898 			cell_right = cell_left + col_width + 0.05;
 899 			text_right = cell_right - text_xpad;
 900 
 901 			day = days[y * 7 + x];
 902 			if (day != 0) {
 903 				gboolean found = FALSE;
 904 				sprintf (buf, "%d", day);
 905 
 906 				/* this is a slow messy way to do this ... but easy ... */
 907 				e_cal_model_generate_instances_sync (
 908 					gnome_calendar_get_model (gcal), now,
 909 					time_day_end_with_zone (now, zone),
 910 					instance_cb, &found);
 911 
 912 				font = found ? font_bold : font_normal;
 913 
 914 				next = time_add_day_with_zone (now, 1, zone);
 915 				if ((now >= greystart && now < greyend)
 916 				    || (greystart >= now && greystart < next)) {
 917 					print_border (
 918 						context,
 919 						cell_left, cell_right,
 920 						cell_top, cell_bottom,
 921 						-1.0, 0.75);
 922 				}
 923 				print_text (
 924 					context, font, buf, PANGO_ALIGN_RIGHT,
 925 					cell_left, text_right,
 926 					cell_top, cell_bottom);
 927 
 928 				now = next;
 929 			}
 930 		}
 931 	}
 932 	pango_font_description_free (font_normal);
 933 	pango_font_description_free (font_bold);
 934 }
 935 
 936 /* wraps text into the print context, not taking up more than its allowed space */
 937 static gdouble
 938 bound_text (GtkPrintContext *context,
 939             PangoFontDescription *font,
 940             const gchar *text,
 941             gint len,
 942             gdouble x1,
 943             gdouble y1,
 944             gdouble x2,
 945             gdouble y2,
 946             gboolean can_wrap,
 947             gdouble *last_page_start,
 948             gint *pages)
 949 {
 950 	PangoLayout *layout;
 951 	gint layout_width, layout_height;
 952 	cairo_t *cr;
 953 
 954 	cr = gtk_print_context_get_cairo_context (context);
 955 	layout = gtk_print_context_create_pango_layout (context);
 956 
 957 	pango_layout_set_font_description (layout, font);
 958 	pango_layout_set_text (layout, text, len);
 959 	pango_layout_set_width (layout, pango_units_from_double (x2 - x1));
 960 
 961 	if (can_wrap)
 962 		pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
 963 
 964 	pango_layout_get_size (layout, &layout_width, &layout_height);
 965 
 966 	if (last_page_start &&
 967 		y1 + pango_units_to_double (layout_height) >
 968 		y2 + (*last_page_start)) {
 969 		/* draw this on new page */
 970 		if (pages)
 971 			*pages = *pages + 1;
 972 
 973 		*last_page_start = *last_page_start + y2;
 974 		y1 = *last_page_start + 10.0;
 975 	}
 976 
 977 	if (!last_page_start || (y1 >= 0.0 && y1 < y2)) {
 978 		cairo_save (cr);
 979 
 980 		/* Set a clipping rectangle. */
 981 		cairo_move_to (cr, x1, y1);
 982 		cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
 983 		cairo_clip (cr);
 984 		cairo_new_path (cr);
 985 
 986 		cairo_move_to (cr, x1, y1);
 987 		pango_cairo_show_layout (cr, layout);
 988 		cairo_stroke (cr);
 989 
 990 		cairo_restore (cr);
 991 	}
 992 
 993 	g_object_unref (layout);
 994 
 995 	return y1 + pango_units_to_double (layout_height);
 996 }
 997 
 998 /* Draw the borders, lines, and times down the left of the day view. */
 999 static void
1000 print_day_background (GtkPrintContext *context,
1001                       GnomeCalendar *gcal,
1002                       time_t whence,
1003                       struct pdinfo *pdi,
1004                       gdouble left,
1005                       gdouble right,
1006                       gdouble top,
1007                       gdouble bottom)
1008 {
1009 	ECalModel *model;
1010 	PangoFontDescription *font_hour, *font_minute;
1011 	gdouble yinc, y;
1012 	gdouble width = DAY_VIEW_TIME_COLUMN_WIDTH;
1013 	gdouble font_size, max_font_size, hour_font_size, minute_font_size;
1014 	gchar buf[20];
1015 	const gchar *minute;
1016 	gboolean use_24_hour;
1017 	gint i, hour, row;
1018 	gdouble hour_minute_x, hour_minute_width;
1019 	cairo_t *cr;
1020 
1021 	model = gnome_calendar_get_model (gcal);
1022 	use_24_hour = e_cal_model_get_use_24_hour_format (model);
1023 
1024 	/* Fill the time column in light-gray. */
1025 	print_border (context, left, left + width, top, bottom, -1.0, 0.9);
1026 
1027 	/* Draw the border around the entire view. */
1028 	cr = gtk_print_context_get_cairo_context (context);
1029 
1030 	cairo_set_source_rgb (cr, 0, 0, 0);
1031 	print_border (context, left, right, top, bottom, 1.0, -1.0);
1032 
1033 	/* Draw the vertical line on the right of the time column. */
1034 	cr = gtk_print_context_get_cairo_context (context);
1035 	cairo_set_line_width (cr, 0.0);
1036 	cairo_move_to (cr, left + width, bottom);
1037 	cairo_line_to (cr, left + width, top);
1038 	cairo_stroke (cr);
1039 
1040 	/* Calculate the row height. */
1041 	if (top > bottom)
1042 		yinc = (top - bottom) / (pdi->end_hour - pdi->start_hour);
1043 	else
1044 		yinc = (bottom - top) / (pdi->end_hour - pdi->start_hour);
1045 
1046         /* Get the 2 fonts we need. */
1047 	font_size = yinc * 0.6;
1048 	max_font_size = width * 0.45;
1049 	hour_font_size = MIN (font_size, max_font_size);
1050 	font_hour = get_font_for_size (hour_font_size, PANGO_WEIGHT_BOLD);
1051 
1052 	font_size = yinc * 0.33;
1053 	max_font_size = width * 0.2;
1054 	minute_font_size = MIN (font_size, max_font_size);
1055 	font_minute = get_font_for_size (minute_font_size, PANGO_WEIGHT_BOLD);
1056 	hour_minute_width = evo_calendar_print_renderer_get_width (
1057 		context, font_minute, use_24_hour ? "00" : _("am"));
1058 	if (!use_24_hour)
1059 		hour_minute_width = MAX (
1060 			hour_minute_width,
1061 			evo_calendar_print_renderer_get_width (
1062 				context, font_minute, _("pm")));
1063 
1064 	row = 0;
1065 	hour_minute_x = left + width - hour_minute_width - 3;
1066 	for (i = pdi->start_hour; i < pdi->end_hour; i++) {
1067 		y = top + yinc * (row + 1);
1068 		cr = gtk_print_context_get_cairo_context (context);
1069 		cairo_set_source_rgb (cr, 0, 0, 0);
1070 
1071 		if (use_24_hour) {
1072 			hour = i;
1073 			minute = "00";
1074 		} else {
1075 			if (i < 12)
1076 				minute = _("am");
1077 			else
1078 				minute = _("pm");
1079 
1080 			hour = i % 12;
1081 			if (hour == 0)
1082 				hour = 12;
1083 		}
1084 
1085 		/* the hour label/minute */
1086 		sprintf (buf, "%d", hour);
1087 		print_text (
1088 			context, font_hour, buf, PANGO_ALIGN_RIGHT,
1089 			left, hour_minute_x,
1090 			y - yinc, y - yinc + hour_font_size);
1091 		print_text (
1092 			context, font_minute, minute, PANGO_ALIGN_LEFT,
1093 			hour_minute_x, left + width - 3,
1094 			y - yinc, y - yinc + minute_font_size);
1095 
1096                 /* Draw the horizontal line between hours, across the entire
1097 		   width of the day view. */
1098 		cr = gtk_print_context_get_cairo_context (context);
1099 		cairo_move_to (cr, left, y);
1100 		cairo_line_to (cr, right, y);
1101 		cairo_set_line_width (cr, 1);
1102 		cairo_stroke (cr);
1103 
1104 		/* Draw the horizontal line for the 1/2-hours, across the
1105 		 * entire width except for part of the time column. */
1106 		cairo_move_to (cr, left + width * 0.6, y - yinc / 2);
1107 		cairo_line_to (cr, right, y - yinc / 2);
1108 		cairo_set_line_width (cr, 1);
1109 		cairo_stroke (cr);
1110 		row++;
1111 	}
1112 
1113 	pango_font_description_free (font_hour);
1114 	pango_font_description_free (font_minute);
1115 }
1116 
1117 /* This adds one event to the view, adding it to the appropriate array. */
1118 static gint
1119 print_day_add_event (ECalModelComponent *comp_data,
1120                      time_t start,
1121                      time_t end,
1122                      icaltimezone *zone,
1123                      gint days_shown,
1124                      time_t *day_starts,
1125                      GArray *long_events,
1126                      GArray **events)
1127 
1128 {
1129 	EDayViewEvent event;
1130 	gint day, offset;
1131 	struct icaltimetype start_tt, end_tt;
1132 
1133 #if 0
1134 	g_print ("Day view lower: %s", ctime (&day_starts[0]));
1135 	g_print ("Day view upper: %s", ctime (&day_starts[days_shown]));
1136 	g_print ("Event start: %s", ctime (&start));
1137 	g_print ("Event end  : %s\n", ctime (&end));
1138 #endif
1139 
1140 	/* Check that the event times are valid. */
1141 	g_return_val_if_fail (start <= end, -1);
1142 	g_return_val_if_fail (start < day_starts[days_shown], -1);
1143 	g_return_val_if_fail (end > day_starts[0], -1);
1144 
1145 	start_tt = icaltime_from_timet_with_zone (start, FALSE, zone);
1146 	end_tt = icaltime_from_timet_with_zone (end, FALSE, zone);
1147 
1148 	event.comp_data = comp_data;
1149 	event.start = start;
1150 	event.end = end;
1151 	event.canvas_item = NULL;
1152 
1153 	/* Calculate the start & end minute, relative to the top of the
1154 	 * display. */
1155 	/*offset = day_view->first_hour_shown * 60
1156 	  + day_view->first_minute_shown;*/
1157 	offset = 0;
1158 	event.start_minute = start_tt.hour * 60 + start_tt.minute - offset;
1159 	event.end_minute = end_tt.hour * 60 + end_tt.minute - offset;
1160 
1161 	event.start_row_or_col = 0;
1162 	event.num_columns = 0;
1163 
1164 	/* Find out which array to add the event to. */
1165 	for (day = 0; day < days_shown; day++) {
1166 		if (start >= day_starts[day] && end <= day_starts[day + 1]) {
1167 
1168 			/* Special case for when the appointment ends at
1169 			 * midnight, i.e. the start of the next day. */
1170 			if (end == day_starts[day + 1]) {
1171 
1172 				/* If the event last the entire day, then we
1173 				 * skip it here so it gets added to the top
1174 				 * canvas. */
1175 				if (start == day_starts[day])
1176 				    break;
1177 
1178 				event.end_minute = 24 * 60;
1179 			}
1180 			g_array_append_val (events[day], event);
1181 			return day;
1182 		}
1183 	}
1184 
1185 	/* The event wasn't within one day so it must be a long event,
1186 	 * i.e. shown in the top canvas. */
1187 	g_array_append_val (long_events, event);
1188 	return E_DAY_VIEW_LONG_EVENT;
1189 }
1190 
1191 static gboolean
1192 print_day_details_cb (ECalComponent *comp,
1193                       time_t istart,
1194                       time_t iend,
1195                       gpointer data)
1196 {
1197 	ECalModelGenerateInstancesData *mdata = (ECalModelGenerateInstancesData *) data;
1198 	struct pdinfo *pdi = (struct pdinfo *) mdata->cb_data;
1199 
1200 	print_day_add_event (
1201 		mdata->comp_data, istart, iend,
1202 		pdi->zone, pdi->days_shown, pdi->day_starts,
1203 		pdi->long_events, pdi->events);
1204 
1205 	return TRUE;
1206 }
1207 
1208 static void
1209 free_event_array (GArray *array)
1210 {
1211 	EDayViewEvent *event;
1212 	gint event_num;
1213 
1214 	for (event_num = 0; event_num < array->len; event_num++) {
1215 		event = &g_array_index (array, EDayViewEvent, event_num);
1216 		if (event->canvas_item)
1217 			g_object_run_dispose (G_OBJECT (event->canvas_item));
1218 	}
1219 
1220 	g_array_set_size (array, 0);
1221 }
1222 
1223 static const gchar *
1224 get_type_as_string (icalparameter_cutype cutype)
1225 {
1226 	const gchar *res;
1227 
1228 	switch (cutype) {
1229 		case ICAL_CUTYPE_NONE:       res = NULL;            break;
1230 		case ICAL_CUTYPE_INDIVIDUAL: res = _("Individual"); break;
1231 		case ICAL_CUTYPE_GROUP:      res = _("Group");      break;
1232 		case ICAL_CUTYPE_RESOURCE:   res = _("Resource");   break;
1233 		case ICAL_CUTYPE_ROOM:       res = _("Room");       break;
1234 		default:                     res = _("Unknown");    break;
1235 	}
1236 
1237 	return res;
1238 }
1239 
1240 static const gchar *
1241 get_role_as_string (icalparameter_role role)
1242 {
1243 	const gchar *res;
1244 
1245 	switch (role) {
1246 		case ICAL_ROLE_NONE:           res = NULL;                      break;
1247 		case ICAL_ROLE_CHAIR:          res = _("Chair");                break;
1248 		case ICAL_ROLE_REQPARTICIPANT: res = _("Required Participant"); break;
1249 		case ICAL_ROLE_OPTPARTICIPANT: res = _("Optional Participant"); break;
1250 		case ICAL_ROLE_NONPARTICIPANT: res = _("Non-Participant");      break;
1251 		default:                       res = _("Unknown");              break;
1252 	}
1253 
1254 	return res;
1255 }
1256 
1257 static gdouble
1258 print_attendees (GtkPrintContext *context,
1259                  PangoFontDescription *font,
1260                  cairo_t *cr,
1261                  gdouble left,
1262                  gdouble right,
1263                  gdouble top,
1264                  gdouble bottom,
1265                  ECalComponent *comp,
1266                  gint page_nr,
1267                  gint *pages)
1268 {
1269 	GSList *attendees = NULL, *l;
1270 
1271 	g_return_val_if_fail (context != NULL, top);
1272 	g_return_val_if_fail (font != NULL, top);
1273 	g_return_val_if_fail (cr != NULL, top);
1274 
1275 	e_cal_component_get_attendee_list (comp, &attendees);
1276 
1277 	for (l = attendees; l; l = l->next) {
1278 		ECalComponentAttendee *attendee = l->data;
1279 
1280 		if (attendee && attendee->value && *attendee->value) {
1281 			GString *text;
1282 			const gchar *tmp;
1283 
1284 			tmp = get_type_as_string (attendee->cutype);
1285 			text = g_string_new (tmp ? tmp : "");
1286 
1287 			if (tmp)
1288 				g_string_append (text, " ");
1289 
1290 			if (attendee->cn && *attendee->cn)
1291 				g_string_append (text, attendee->cn);
1292 			else {
1293 				/* it's usually in form of "mailto:email@domain" */
1294 				tmp = strchr (attendee->value, ':');
1295 				g_string_append (text, tmp ? tmp + 1 : attendee->value);
1296 			}
1297 
1298 			tmp = get_role_as_string (attendee->role);
1299 			if (tmp) {
1300 				g_string_append (text, " (");
1301 				g_string_append (text, tmp);
1302 				g_string_append (text, ")");
1303 			}
1304 
1305 			if (top > bottom) {
1306 				top = 10.0;
1307 				cairo_show_page (cr);
1308 			}
1309 
1310 			top = bound_text (
1311 				context, font, text->str, -1, left + 40.0,
1312 				top, right, bottom, FALSE, NULL, pages);
1313 
1314 			g_string_free (text, TRUE);
1315 		}
1316 	}
1317 
1318 	e_cal_component_free_attendee_list (attendees);
1319 
1320 	return top;
1321 }
1322 
1323 static gchar *
1324 get_summary_with_location (icalcomponent *icalcomp)
1325 {
1326 	const gchar *summary, *location;
1327 	gchar *text;
1328 
1329 	g_return_val_if_fail (icalcomp != NULL, NULL);
1330 
1331 	summary = icalcomponent_get_summary (icalcomp);
1332 	if (summary == NULL)
1333 		summary = "";
1334 
1335 	location = icalcomponent_get_location (icalcomp);
1336 	if (location && *location) {
1337 		text = g_strdup_printf ("%s (%s)", summary, location);
1338 	} else {
1339 		text = g_strdup (summary);
1340 	}
1341 
1342 	return text;
1343 }
1344 
1345 static void
1346 print_day_long_event (GtkPrintContext *context,
1347                       PangoFontDescription *font,
1348                       gdouble left,
1349                       gdouble right,
1350                       gdouble top,
1351                       gdouble bottom,
1352                       gdouble row_height,
1353                       EDayViewEvent *event,
1354                       struct pdinfo *pdi,
1355                       ECalModel *model)
1356 {
1357 	gdouble x1, x2, y1, y2;
1358 	gdouble left_triangle_width = -1.0, right_triangle_width = -1.0;
1359 	gchar *text;
1360 	gchar buffer[32];
1361 	struct tm date_tm;
1362 	gdouble red, green, blue;
1363 
1364 	if (!is_comp_data_valid (event))
1365 		return;
1366 
1367 	/* If the event starts before the first day being printed, draw a
1368 	 * triangle. (Note that I am assuming we are just showing 1 day at
1369 	 * the moment.) */
1370 	if (event->start < pdi->day_starts[0])
1371 		left_triangle_width = 4;
1372 
1373 	/* If the event ends after the last day being printed, draw a
1374 	 * triangle. */
1375 	if (event->end > pdi->day_starts[1])
1376 		right_triangle_width = 4;
1377 
1378 	x1 = left + 10;
1379 	x2 = right - 10;
1380 	y1 = top + event->start_row_or_col * row_height + 1;
1381 	y2 = y1 + row_height - 1;
1382 	red = green = blue = 0.95;
1383 	e_cal_model_get_rgb_color_for_component (
1384 		model, event->comp_data, &red, &green, &blue);
1385 	print_border_with_triangles (
1386 		context, x1, x2, y1, y2, 0.5, red, green, blue,
1387 		left_triangle_width,
1388 		right_triangle_width);
1389 
1390 	/* If the event starts after the first day being printed, we need to
1391 	 * print the start time. */
1392 	if (event->start > pdi->day_starts[0]) {
1393 		date_tm.tm_year = 2001;
1394 		date_tm.tm_mon = 0;
1395 		date_tm.tm_mday = 1;
1396 		date_tm.tm_hour = event->start_minute / 60;
1397 		date_tm.tm_min = event->start_minute % 60;
1398 		date_tm.tm_sec = 0;
1399 		date_tm.tm_isdst = -1;
1400 
1401 		e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
1402 				    buffer, sizeof (buffer));
1403 
1404 		x1 += 4;
1405 		x1 += print_text (context, font, buffer, PANGO_ALIGN_LEFT, x1, x2, y1, y2);
1406 	}
1407 
1408 	/* If the event ends before the end of the last day being printed,
1409 	 * we need to print the end time. */
1410 	if (event->end < pdi->day_starts[1]) {
1411 		date_tm.tm_year = 2001;
1412 		date_tm.tm_mon = 0;
1413 		date_tm.tm_mday = 1;
1414 		date_tm.tm_hour = event->end_minute / 60;
1415 		date_tm.tm_min = event->end_minute % 60;
1416 		date_tm.tm_sec = 0;
1417 		date_tm.tm_isdst = -1;
1418 
1419 		e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
1420 				    buffer, sizeof (buffer));
1421 
1422 		x2 -= 4;
1423 		x2 -= print_text (context, font, buffer, PANGO_ALIGN_RIGHT, x1, x2, y1, y2);
1424 	}
1425 
1426 	/* Print the text. */
1427 	text = get_summary_with_location (event->comp_data->icalcomp);
1428 
1429 	x1 += 4;
1430 	x2 -= 4;
1431 	print_text_line (context, font, text, PANGO_ALIGN_CENTER, x1, x2, y1, y2, TRUE);
1432 
1433 	g_free (text);
1434 }
1435 
1436 static void
1437 print_day_event (GtkPrintContext *context,
1438                  PangoFontDescription *font,
1439                  gdouble left,
1440                  gdouble right,
1441                  gdouble top,
1442                  gdouble bottom,
1443                  EDayViewEvent *event,
1444                  struct pdinfo *pdi,
1445                  ECalModel *model)
1446 {
1447 	gdouble x1, x2, y1, y2, col_width, row_height;
1448 	gint start_offset, end_offset, start_row, end_row;
1449 	gchar *text, start_buffer[32], end_buffer[32];
1450 	gboolean display_times = FALSE;
1451 	struct tm date_tm;
1452 	gdouble red, green, blue;
1453 
1454 	if (!is_comp_data_valid (event))
1455 		return;
1456 
1457 	if ((event->start_minute >= pdi->end_minute_offset)
1458 	    || (event->end_minute <= pdi->start_minute_offset))
1459 		return;
1460 
1461 	start_offset = event->start_minute - pdi->start_minute_offset;
1462 	end_offset = event->end_minute - pdi->start_minute_offset;
1463 
1464 	start_row = start_offset / pdi->mins_per_row;
1465 	start_row = MAX (0, start_row);
1466 	end_row = (end_offset - 1) / pdi->mins_per_row;
1467 	end_row = MIN (pdi->rows - 1, end_row);
1468 	col_width = (right - left) /
1469 		pdi->cols_per_row[event->start_minute / pdi->mins_per_row];
1470 
1471 	if (start_offset != start_row * pdi->mins_per_row
1472 	    || end_offset != (end_row + 1) * pdi->mins_per_row)
1473 		display_times = TRUE;
1474 
1475 	x1 = left + event->start_row_or_col * col_width;
1476 	x2 = x1 + event->num_columns * col_width - DAY_VIEW_EVENT_X_PAD;
1477 
1478 	row_height = (bottom - top) / pdi->rows;
1479 	y1 = top + start_row * row_height;
1480 	y2 = top + (end_row + 1) * row_height;
1481 #if 0
1482 	g_print (
1483 		"Event: %g,%g %g,%g\n  row_height: %g start_row: %i top: %g rows: %i\n",
1484 		x1, y1, x2, y2, row_height, start_row, top, pdi->rows);
1485 #endif
1486 
1487 	red = green = blue = 0.95;
1488 	e_cal_model_get_rgb_color_for_component (
1489 		model, event->comp_data, &red, &green, &blue);
1490 	print_border_rgb (context, x1, x2, y1, y2, 1.0, red, green, blue);
1491 
1492 	text = get_summary_with_location (event->comp_data->icalcomp);
1493 
1494 	if (display_times) {
1495 		gchar *t = NULL;
1496 
1497 		date_tm.tm_year = 2001;
1498 		date_tm.tm_mon = 0;
1499 		date_tm.tm_mday = 1;
1500 		date_tm.tm_hour = event->start_minute / 60;
1501 		date_tm.tm_min = event->start_minute % 60;
1502 		date_tm.tm_sec = 0;
1503 		date_tm.tm_isdst = -1;
1504 
1505 		e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
1506 				    start_buffer, sizeof (start_buffer));
1507 
1508 		date_tm.tm_hour = event->end_minute / 60;
1509 		date_tm.tm_min = event->end_minute % 60;
1510 
1511 		e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
1512 				    end_buffer, sizeof (end_buffer));
1513 
1514 		t = text;
1515 		text = g_strdup_printf (
1516 			"%s - %s %s ",
1517 			start_buffer, end_buffer, text);
1518 
1519 		g_free (t);
1520 	}
1521 
1522 	bound_text (context, font, text, -1, x1 + 2, y1, x2 - 2, y2, FALSE, NULL, NULL);
1523 
1524 	g_free (text);
1525 }
1526 
1527 static void
1528 print_day_details (GtkPrintContext *context,
1529                    GnomeCalendar *gcal,
1530                    time_t whence,
1531                    gdouble left,
1532                    gdouble right,
1533                    gdouble top,
1534                    gdouble bottom)
1535 {
1536 	ECalModel *model;
1537 	icaltimezone *zone;
1538 	EDayViewEvent *event;
1539 	PangoFontDescription *font;
1540 	time_t start, end;
1541 	struct pdinfo pdi = { 0 };
1542 	gint rows_in_top_display, i, rows_with_30_mins;
1543 	gdouble font_size, max_font_size;
1544 	cairo_t *cr;
1545 	GdkPixbuf *pixbuf = NULL;
1546 #define LONG_DAY_EVENTS_TOP_SPACING 4
1547 #define LONG_DAY_EVENTS_BOTTOM_SPACING 2
1548 
1549 	model = gnome_calendar_get_model (gcal);
1550 	zone = e_cal_model_get_timezone (model);
1551 
1552 	start = time_day_begin_with_zone (whence, zone);
1553 	end = time_day_end_with_zone (start, zone);
1554 
1555 	pdi.days_shown = 1;
1556 	pdi.day_starts[0] = start;
1557 	pdi.day_starts[1] = end;
1558 	pdi.long_events = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
1559 	pdi.events[0] = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
1560 	pdi.start_hour = e_cal_model_get_work_day_start_hour (model);
1561 	pdi.end_hour = e_cal_model_get_work_day_end_hour (model);
1562 	if (e_cal_model_get_work_day_end_minute (model) != 0)
1563 		pdi.end_hour++;
1564 	pdi.mins_per_row = get_day_view_time_divisions ();
1565 	pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
1566 	pdi.start_minute_offset = pdi.start_hour * 60;
1567 	pdi.end_minute_offset = pdi.end_hour * 60;
1568 	pdi.use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
1569 	pdi.zone = e_cal_model_get_timezone (model);
1570 
1571 	/* Get the events from the server. */
1572 	e_cal_model_generate_instances_sync (model, start, end, print_day_details_cb, &pdi);
1573 	qsort (
1574 		pdi.long_events->data, pdi.long_events->len,
1575 		sizeof (EDayViewEvent), e_day_view_event_sort_func);
1576 	qsort (
1577 		pdi.events[0]->data, pdi.events[0]->len,
1578 		sizeof (EDayViewEvent), e_day_view_event_sort_func);
1579 
1580 	/* Also print events outside of work hours */
1581 	if (pdi.events[0]->len > 0) {
1582 		struct icaltimetype tt;
1583 
1584 		event = &g_array_index (pdi.events[0], EDayViewEvent, 0);
1585 		tt = icaltime_from_timet_with_zone (event->start, FALSE, zone);
1586 		if (tt.hour < pdi.start_hour)
1587 			pdi.start_hour = tt.hour;
1588 		pdi.start_minute_offset = pdi.start_hour * 60;
1589 
1590 		event = &g_array_index (pdi.events[0], EDayViewEvent, pdi.events[0]->len - 1);
1591 		tt = icaltime_from_timet_with_zone (event->end, FALSE, zone);
1592 		if (tt.hour > pdi.end_hour || tt.hour == 0) {
1593 			pdi.end_hour = tt.hour ? tt.hour : 24;
1594 			if (tt.minute > 0)
1595 				pdi.end_hour++;
1596 		}
1597 		pdi.end_minute_offset = pdi.end_hour * 60;
1598 
1599 		pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
1600 	}
1601 
1602 	/* Lay them out the long events, across the top of the page. */
1603 	e_day_view_layout_long_events (
1604 		pdi.long_events, pdi.days_shown,
1605 		pdi.day_starts, &rows_in_top_display);
1606 
1607 	 /*Print the long events. */
1608 	font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
1609 
1610 	/* We always leave space for DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY in the
1611 	 * top display, but we may have more rows than that, in which case
1612 	 * the main display area will be compressed. */
1613 	/* Limit long day event to half the height of the panel */
1614 	rows_in_top_display = MIN (
1615 		MAX (rows_in_top_display,
1616 		DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY),
1617 		(bottom - top) * 0.5 / DAY_VIEW_ROW_HEIGHT);
1618 
1619 	if (rows_in_top_display > pdi.long_events->len)
1620 		rows_in_top_display = pdi.long_events->len;
1621 
1622 	for (i = 0; i < rows_in_top_display && i < pdi.long_events->len; i++) {
1623 		event = &g_array_index (pdi.long_events, EDayViewEvent, i);
1624 		print_day_long_event (
1625 			context, font, left, right,
1626 			top + LONG_DAY_EVENTS_TOP_SPACING, bottom,
1627 			DAY_VIEW_ROW_HEIGHT, event, &pdi, model);
1628 	}
1629 
1630 	if (rows_in_top_display < pdi.long_events->len) {
1631 		/* too many events */
1632 		cairo_t *cr = gtk_print_context_get_cairo_context (context);
1633 		gint x, y;
1634 
1635 		if (!pixbuf) {
1636 			const gchar **xpm = (const gchar **) jump_xpm;
1637 
1638 			/* this ugly thing is here only to get rid of compiler warning
1639 			 * about unused 'jump_xpm_focused' */
1640 			if (pixbuf)
1641 				xpm = (const gchar **) jump_xpm_focused;
1642 
1643 			pixbuf = gdk_pixbuf_new_from_xpm_data (xpm);
1644 		}
1645 
1646 		/* Right align - 10 comes from print_day_long_event  too */
1647 		x = right - gdk_pixbuf_get_width (pixbuf) * 0.5 - 10;
1648 		/* Placing '...' over the last all day event entry printed. '-1 -1' comes
1649 			from print_long_day_event (top / bottom spacing in each cell) */
1650 		y = top + LONG_DAY_EVENTS_TOP_SPACING
1651 			+ DAY_VIEW_ROW_HEIGHT * (i - 1)
1652 			+ (DAY_VIEW_ROW_HEIGHT - 1 - 1) * 0.5;
1653 
1654 		cairo_save (cr);
1655 		cairo_scale (cr, 0.5, 0.5);
1656 		gdk_cairo_set_source_pixbuf (cr, pixbuf, x * 2.0, y * 2.0);
1657 		cairo_paint (cr);
1658 		cairo_restore (cr);
1659 	}
1660 
1661 	if (!rows_in_top_display)
1662 		rows_in_top_display++;
1663 
1664 	/* Draw the border around the long events. */
1665 	cr = gtk_print_context_get_cairo_context (context);
1666 
1667 	cairo_set_source_rgb (cr, 0, 0, 0);
1668 	print_border (
1669 		context, left, right,
1670 		top, top + rows_in_top_display * DAY_VIEW_ROW_HEIGHT +
1671 		LONG_DAY_EVENTS_TOP_SPACING + LONG_DAY_EVENTS_BOTTOM_SPACING,
1672 		1.0, -1.0);
1673 
1674 	/* Adjust the area containing the main display. */
1675 	top += rows_in_top_display * DAY_VIEW_ROW_HEIGHT
1676 		+ LONG_DAY_EVENTS_TOP_SPACING
1677 		+ LONG_DAY_EVENTS_BOTTOM_SPACING;
1678 
1679 	/* Draw the borders, lines, and times down the left. */
1680 	print_day_background (
1681 		context, gcal, whence, &pdi,
1682 		left, right, top, bottom);
1683 	/* Now adjust to get rid of the time column. */
1684 	left += DAY_VIEW_TIME_COLUMN_WIDTH;
1685 
1686 	/* lay out the short events, within the day. */
1687 	e_day_view_layout_day_events (
1688 		pdi.events[0], CALC_DAY_VIEW_ROWS (pdi.mins_per_row),
1689 		pdi.mins_per_row, pdi.cols_per_row, -1);
1690 
1691 	/* use font like with 30 minutes time division */
1692 	rows_with_30_mins = (pdi.end_hour - pdi.start_hour) * (60 / 30);
1693 
1694 	/* print the short events. */
1695 	if (top > bottom)
1696 		max_font_size = ((top - bottom) / rows_with_30_mins) - 4;
1697 	else
1698 		max_font_size = ((bottom - top) / rows_with_30_mins) - 4;
1699 	font_size = MIN (DAY_NORMAL_FONT_SIZE, max_font_size);
1700 	font = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
1701 
1702 	for (i = 0; i < pdi.events[0]->len; i++) {
1703 		event = &g_array_index (pdi.events[0], EDayViewEvent, i);
1704 		print_day_event (
1705 			context, font, left, right, top, bottom,
1706 			event, &pdi, model);
1707 	}
1708 
1709 	/* Free everything. */
1710 	if (pixbuf)
1711 		g_object_unref (pixbuf);
1712 	free_event_array (pdi.long_events);
1713 	pango_font_description_free (font);
1714 	g_array_free (pdi.long_events, TRUE);
1715 	free_event_array (pdi.events[0]);
1716 	g_array_free (pdi.events[0], TRUE);
1717 }
1718 
1719 /* Returns TRUE if the event is a one-day event (i.e. not a long event). */
1720 static gboolean
1721 print_is_one_day_week_event (EWeekViewEvent *event,
1722                              EWeekViewEventSpan *span,
1723                              time_t *day_starts)
1724 {
1725 	if (event->start == day_starts[span->start_day]
1726 	    && event->end == day_starts[span->start_day + 1])
1727 		return FALSE;
1728 
1729 	if (span->num_days == 1
1730 	    && event->start >= day_starts[span->start_day]
1731 	    && event->end <= day_starts[span->start_day + 1])
1732 		return TRUE;
1733 
1734 	return FALSE;
1735 }
1736 
1737 static void
1738 print_week_long_event (GtkPrintContext *context,
1739                        PangoFontDescription *font,
1740                        struct psinfo *psi,
1741                        gdouble x1,
1742                        gdouble x2,
1743                        gdouble y1,
1744                        gdouble row_height,
1745                        EWeekViewEvent *event,
1746                        EWeekViewEventSpan *span,
1747                        gchar *text,
1748                        gdouble red,
1749                        gdouble green,
1750                        gdouble blue)
1751 {
1752 	gdouble left_triangle_width = -1.0, right_triangle_width = -1.0;
1753 	struct tm date_tm;
1754 	gchar buffer[32];
1755 
1756 	/* If the event starts before the first day of the span, draw a
1757 	 * triangle to indicate it continues. */
1758 	if (event->start < psi->day_starts[span->start_day])
1759 		left_triangle_width = 4;
1760 
1761 	/* If the event ends after the last day of the span, draw a
1762 	 * triangle. */
1763 	if (event->end > psi->day_starts[span->start_day + span->num_days])
1764 		right_triangle_width = 4;
1765 
1766 	print_border_with_triangles (
1767 		context, x1 + 6, x2 - 6, y1, y1 + row_height, 0.0, red, green, blue,
1768 		left_triangle_width, right_triangle_width);
1769 
1770 	x1 += 6;
1771 	x2 -= 6;
1772 
1773 	/* If the event starts after the first day being printed, we need to
1774 	 * print the start time. */
1775 	if (event->start > psi->day_starts[span->start_day]) {
1776 		date_tm.tm_year = 2001;
1777 		date_tm.tm_mon = 0;
1778 		date_tm.tm_mday = 1;
1779 		date_tm.tm_hour = event->start_minute / 60;
1780 		date_tm.tm_min = event->start_minute % 60;
1781 		date_tm.tm_sec = 0;
1782 		date_tm.tm_isdst = -1;
1783 
1784 		e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
1785 				    buffer, sizeof (buffer));
1786 
1787 		x1 += 2;
1788 		x1 += print_text_line (
1789 			context, font, buffer, PANGO_ALIGN_LEFT,
1790 			x1, x2 - 2, y1, y1 + row_height, TRUE);
1791 	}
1792 
1793 	/* If the event ends before the end of the last day being printed,
1794 	 * we need to print the end time. */
1795 	if (event->end < psi->day_starts[span->start_day + span->num_days]) {
1796 		date_tm.tm_year = 2001;
1797 		date_tm.tm_mon = 0;
1798 		date_tm.tm_mday = 1;
1799 		date_tm.tm_hour = event->end_minute / 60;
1800 		date_tm.tm_min = event->end_minute % 60;
1801 		date_tm.tm_sec = 0;
1802 		date_tm.tm_isdst = -1;
1803 
1804 		e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
1805 				    buffer, sizeof (buffer));
1806 
1807 		x2 -= 2;
1808 		x2 -= print_text_line (
1809 			context, font, buffer, PANGO_ALIGN_RIGHT,
1810 			x1 + 2, x2, y1, y1 + row_height, TRUE);
1811 	}
1812 
1813 	x1 += 2;
1814 	x2 -= 2;
1815 	print_text_line (context, font, text, PANGO_ALIGN_CENTER, x1, x2, y1, y1 + row_height, TRUE);
1816 }
1817 
1818 static void
1819 print_week_day_event (GtkPrintContext *context,
1820                       PangoFontDescription *font,
1821                       struct psinfo *psi,
1822                       gdouble x1,
1823                       gdouble x2,
1824                       gdouble y1,
1825                       gdouble row_height,
1826                       EWeekViewEvent *event,
1827                       EWeekViewEventSpan *span,
1828                       gchar *text,
1829                       gdouble red,
1830                       gdouble green,
1831                       gdouble blue)
1832 {
1833 	struct tm date_tm;
1834 	gchar buffer[32];
1835 
1836 	date_tm.tm_year = 2001;
1837 	date_tm.tm_mon = 0;
1838 	date_tm.tm_mday = 1;
1839 	date_tm.tm_hour = event->start_minute / 60;
1840 	date_tm.tm_min = event->start_minute % 60;
1841 	date_tm.tm_sec = 0;
1842 	date_tm.tm_isdst = -1;
1843 
1844 	e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
1845 			    buffer, sizeof (buffer));
1846 	print_rectangle (context, x1 + 1, y1, x2 - x1 - 2, row_height, red, green, blue);
1847 	x1 += print_text_line (
1848 		context, font, buffer, PANGO_ALIGN_LEFT,
1849 		x1 + 2, x2 - 3, y1, y1 + row_height, TRUE) + 4;
1850 
1851 	if (psi->weeks_shown <= 2) {
1852 		date_tm.tm_hour = event->end_minute / 60;
1853 		date_tm.tm_min = event->end_minute % 60;
1854 
1855 		e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
1856 				    buffer, sizeof (buffer));
1857 
1858 		x1 += print_text_line (
1859 			context, font, buffer, PANGO_ALIGN_LEFT,
1860 			x1, x2 - 3, y1, y1 + row_height, TRUE) + 4;
1861 	}
1862 
1863 	print_text_line (
1864 		context, font, text, PANGO_ALIGN_LEFT,
1865 		x1, x2 - 3, y1, y1 + row_height, TRUE);
1866 }
1867 
1868 static void
1869 print_week_event (GtkPrintContext *context,
1870                   PangoFontDescription *font,
1871                   struct psinfo *psi,
1872                   gdouble left,
1873                   gdouble top,
1874                   gdouble cell_width,
1875                   gdouble cell_height,
1876                   ECalModel *model,
1877                   EWeekViewEvent *event,
1878                   GArray *spans)
1879 {
1880 	EWeekViewEventSpan *span;
1881 	gint span_num;
1882 	gchar *text;
1883 	gint num_days, start_x, start_y, start_h, end_x, end_y, end_h;
1884 	gdouble x1, x2, y1;
1885 	gdouble red, green, blue;
1886 	GdkPixbuf *pixbuf = NULL;
1887 
1888 	if (!is_comp_data_valid (event))
1889 		return;
1890 
1891 	text = get_summary_with_location (event->comp_data->icalcomp);
1892 
1893 	for (span_num = 0; span_num < event->num_spans; span_num++) {
1894 		span = &g_array_index (spans, EWeekViewEventSpan,
1895 				       event->spans_index + span_num);
1896 
1897 		if (e_week_view_layout_get_span_position
1898 		    (event, span,
1899 		     psi->rows_per_cell,
1900 		     psi->rows_per_compressed_cell,
1901 		     psi->display_start_weekday,
1902 		     psi->multi_week_view,
1903 		     psi->compress_weekend,
1904 		     &num_days)) {
1905 
1906 			e_week_view_layout_get_day_position
1907 				(span->start_day,
1908 				 psi->multi_week_view,
1909 				 psi->weeks_shown,
1910 				 psi->display_start_weekday,
1911 				 psi->compress_weekend,
1912 				 &start_x, &start_y, &start_h);
1913 
1914 			if (num_days == 1) {
1915 				end_x = start_x;
1916 				end_y = start_y;
1917 				end_h = start_h;
1918 			} else {
1919 				e_week_view_layout_get_day_position
1920 					(span->start_day + num_days - 1,
1921 					 psi->multi_week_view,
1922 					 psi->weeks_shown,
1923 					 psi->display_start_weekday,
1924 					 psi->compress_weekend,
1925 					 &end_x, &end_y, &end_h);
1926 			}
1927 
1928 			x1 = left + start_x * cell_width;
1929 			x2 = left + (end_x + 1) * cell_width;
1930 			y1 = top + start_y * cell_height
1931 				 + psi->header_row_height
1932 				 + span->row * (psi->row_height + 2);
1933 
1934 			red = .9;
1935 			green = .9;
1936 			blue = .9;
1937 			e_cal_model_get_rgb_color_for_component (
1938 				model, event->comp_data, &red, &green, &blue);
1939 			if (print_is_one_day_week_event (event, span,
1940 							 psi->day_starts)) {
1941 				print_week_day_event (
1942 					context, font, psi,
1943 					x1, x2, y1, psi->row_height,
1944 					event, span, text, red, green, blue);
1945 			} else {
1946 				print_week_long_event (
1947 					context, font, psi,
1948 					x1, x2, y1, psi->row_height,
1949 					event, span, text, red, green, blue);
1950 			}
1951 		} else {
1952 			cairo_t *cr = gtk_print_context_get_cairo_context (context);
1953 
1954 			e_week_view_layout_get_day_position
1955 				(span->start_day,
1956 				 psi->multi_week_view,
1957 				 psi->weeks_shown,
1958 				 psi->display_start_weekday,
1959 				 psi->compress_weekend,
1960 				 &start_x, &start_y, &start_h);
1961 
1962 			y1 = top + start_y * cell_height
1963 				 + psi->header_row_height
1964 				 + psi->rows_per_cell * (psi->row_height + 2);
1965 
1966 			if (span->row >= psi->rows_per_compressed_cell && psi->compress_weekend) {
1967 				gint end_day_of_week =
1968 					(psi->display_start_weekday +
1969 					 span->start_day) % 7;
1970 
1971 				if (end_day_of_week == 5 || end_day_of_week == 6) {
1972 					/* Sat or Sun */
1973 					y1 = top + start_y * cell_height
1974 						 + psi->header_row_height
1975 						 + psi->rows_per_compressed_cell * (psi->row_height + 2);
1976 
1977 				}
1978 			}
1979 
1980 			if (!pixbuf) {
1981 				const gchar **xpm = (const gchar **) jump_xpm;
1982 
1983 				/* this ugly thing is here only to get rid of compiler warning
1984 				 * about unused 'jump_xpm_focused' */
1985 				if (pixbuf)
1986 					xpm = (const gchar **) jump_xpm_focused;
1987 
1988 				pixbuf = gdk_pixbuf_new_from_xpm_data (xpm);
1989 			}
1990 
1991 			x1 = left + (start_x + 1) * cell_width - 6 -
1992 				gdk_pixbuf_get_width (pixbuf) * 0.5;
1993 
1994 			cairo_save (cr);
1995 			cairo_scale (cr, 0.5, 0.5);
1996 			gdk_cairo_set_source_pixbuf (cr, pixbuf, x1 * 2.0, y1 * 2.0);
1997 			cairo_paint (cr);
1998 			cairo_restore (cr);
1999 		}
2000 	}
2001 
2002 	if (pixbuf)
2003 		g_object_unref (pixbuf);
2004 
2005 	g_free (text);
2006 }
2007 
2008 static void
2009 print_week_view_background (GtkPrintContext *context,
2010                             PangoFontDescription *font,
2011                             struct psinfo *psi,
2012                             gdouble left,
2013                             gdouble top,
2014                             gdouble cell_width,
2015                             gdouble cell_height)
2016 {
2017 	struct tm tm;
2018 	gint day, day_x, day_y, day_h;
2019 	gdouble x1, x2, y1, y2, font_size, fillcolor;
2020 	const gchar *format_string;
2021 	gchar buffer[128];
2022 	cairo_t *cr;
2023 
2024 	font_size = get_font_size (font);
2025 
2026 	for (day = 0; day < psi->days_shown; day++) {
2027 		e_week_view_layout_get_day_position
2028 			(day, psi->multi_week_view, psi->weeks_shown,
2029 			 psi->display_start_weekday, psi->compress_weekend,
2030 			 &day_x, &day_y, &day_h);
2031 
2032 		x1 = left + day_x * cell_width;
2033 		x2 = left + (day_x + 1) * cell_width;
2034 		y1 = top + day_y * cell_height;
2035 		y2 = y1 + day_h * cell_height;
2036 
2037 		convert_timet_to_struct_tm (psi->day_starts[day], psi->zone, &tm);
2038 
2039 		/* In the month view we draw a grey background for the end
2040 		 * of the previous month and the start of the following. */
2041 		fillcolor = -1.0;
2042 		if (psi->multi_week_view && (tm.tm_mon != psi->month))
2043 			fillcolor = 0.9;
2044 
2045 		print_border (context, x1, x2, y1, y2, 1.0, fillcolor);
2046 
2047 		if (psi->multi_week_view) {
2048 			if (tm.tm_mday == 1)
2049 				format_string = _("%d %B");
2050 			else
2051 				format_string = "%d";
2052 		} else {
2053 			cr = gtk_print_context_get_cairo_context (context);
2054 
2055 			cairo_move_to (
2056 				cr, x1 + 0.1 * cell_width,
2057 				y1 + psi->header_row_height - 4);
2058 			cairo_line_to (
2059 				cr, x2,
2060 				y1 + psi->header_row_height - 4);
2061 
2062 			cairo_set_source_rgb (cr, 0, 0, 0);
2063 			cairo_set_line_width (cr, 0.5);
2064 			cairo_stroke (cr);
2065 
2066 			/* strftime format %A = full weekday name, %d = day of
2067 			 * month, %B = full month name. You can change the
2068 			 * order but don't change the specifiers or add
2069 			 * anything. */
2070 			format_string = _("%A %d %B");
2071 
2072 		}
2073 
2074 		e_utf8_strftime (buffer, sizeof (buffer), format_string, &tm);
2075 
2076 		print_text_line (
2077 			context, font, buffer, PANGO_ALIGN_RIGHT,
2078 			x1, x2 - 4, y1 + 2, y1 + 2 + font_size, TRUE);
2079 	}
2080 }
2081 
2082 /* This adds one event to the view, adding it to the appropriate array. */
2083 static gboolean
2084 print_week_summary_cb (ECalComponent *comp,
2085                        time_t start,
2086                        time_t end,
2087                        gpointer data)
2088 
2089 {
2090 	EWeekViewEvent event;
2091 	struct icaltimetype start_tt, end_tt;
2092 	ECalModelGenerateInstancesData *mdata = (ECalModelGenerateInstancesData *) data;
2093 	struct psinfo *psi = (struct psinfo *) mdata->cb_data;
2094 
2095 	/* Check that the event times are valid. */
2096 
2097 #if 0
2098 	g_print (
2099 		"View start:%li end:%li  Event start:%li end:%li\n",
2100 		psi->day_starts[0], psi->day_starts[psi->days_shown],
2101 		start, end);
2102 #endif
2103 
2104 	g_return_val_if_fail (start <= end, TRUE);
2105 	g_return_val_if_fail (start < psi->day_starts[psi->days_shown], TRUE);
2106 	g_return_val_if_fail (end > psi->day_starts[0], TRUE);
2107 
2108 	start_tt = icaltime_from_timet_with_zone (start, FALSE, psi->zone);
2109 	end_tt = icaltime_from_timet_with_zone (end, FALSE, psi->zone);
2110 
2111 	event.comp_data = g_object_ref (mdata->comp_data);
2112 
2113 	event.start = start;
2114 	event.end = end;
2115 	event.spans_index = 0;
2116 	event.num_spans = 0;
2117 
2118 	event.start_minute = start_tt.hour * 60 + start_tt.minute;
2119 	event.end_minute = end_tt.hour * 60 + end_tt.minute;
2120 	if (event.end_minute == 0 && start != end)
2121 		event.end_minute = 24 * 60;
2122 
2123 	g_array_append_val (psi->events, event);
2124 
2125 	return TRUE;
2126 }
2127 
2128 static void
2129 print_week_summary (GtkPrintContext *context,
2130                     GnomeCalendar *gcal,
2131                     time_t whence,
2132                     gboolean multi_week_view,
2133                     gint weeks_shown,
2134                     gint month,
2135                     gdouble font_size,
2136                     gdouble font_size_background,
2137                     gdouble left,
2138                     gdouble right,
2139                     gdouble top,
2140                     gdouble bottom)
2141 {
2142 	icaltimezone *zone;
2143 	EWeekViewEvent *event;
2144 	struct psinfo psi = { 0 };
2145 	time_t day_start;
2146 	gint rows_per_day[E_WEEK_VIEW_MAX_WEEKS * 7], day, event_num;
2147 	GArray *spans;
2148 	PangoFontDescription *font, *font_background;
2149 	gdouble cell_width, cell_height;
2150 	ECalModel *model;
2151 
2152 	model = gnome_calendar_get_model (gcal);
2153 	zone = e_cal_model_get_timezone (model);
2154 
2155 	psi.days_shown = weeks_shown * 7;
2156 	psi.events = g_array_new (FALSE, FALSE, sizeof (EWeekViewEvent));
2157 	psi.multi_week_view = multi_week_view;
2158 	psi.weeks_shown = weeks_shown;
2159 	psi.month = month;
2160 	psi.zone = zone;
2161 
2162 	/* Get a few config settings. */
2163 	if (multi_week_view)
2164 		psi.compress_weekend = e_cal_model_get_compress_weekend (model);
2165 	else
2166 		psi.compress_weekend = TRUE;
2167 	psi.use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
2168 
2169 	/* We convert this from (0 = Sun, 6 = Sat) to (0 = Mon, 6 = Sun). */
2170 	psi.display_start_weekday = e_cal_model_get_week_start_day (model);
2171 	psi.display_start_weekday = (psi.display_start_weekday + 6) % 7;
2172 
2173 	/* If weekends are compressed then we can't start on a Sunday. */
2174 	if (psi.compress_weekend && psi.display_start_weekday == 6)
2175 		psi.display_start_weekday = 5;
2176 
2177 	day_start = time_day_begin_with_zone (whence, zone);
2178 	for (day = 0; day <= psi.days_shown; day++) {
2179 		psi.day_starts[day] = day_start;
2180 		day_start = time_add_day_with_zone (day_start, 1, zone);
2181 	}
2182 
2183 	/* Get the events from the server. */
2184 	e_cal_model_generate_instances_sync (
2185 		model,
2186 					psi.day_starts[0], psi.day_starts[psi.days_shown],
2187 					print_week_summary_cb, &psi);
2188 	qsort (
2189 		psi.events->data, psi.events->len,
2190 		sizeof (EWeekViewEvent), e_week_view_event_sort_func);
2191 
2192 	/* Layout the events. */
2193 	spans = e_week_view_layout_events (
2194 		psi.events, NULL,
2195 		psi.multi_week_view,
2196 		psi.weeks_shown,
2197 		psi.compress_weekend,
2198 		psi.display_start_weekday,
2199 		psi.day_starts, rows_per_day);
2200 
2201 	/* Calculate the size of the cells. */
2202 	if (multi_week_view) {
2203 		cell_width = (right - left) / (psi.compress_weekend ? 6 : 7);
2204 		cell_height = (bottom - top) / (weeks_shown * 2);
2205 	} else {
2206 		cell_width = (right - left) / 2;
2207 		cell_height = (bottom - top) / 6;
2208 	}
2209 
2210 	/* Calculate the row height, using the normal font and with room for
2211 	 * space or a rectangle around it. */
2212 	psi.row_height = font_size * 1.2;
2213 	psi.header_row_height = font_size * 1.5;
2214 
2215 	/* Calculate how many rows we can fit into each type of cell. */
2216 	psi.rows_per_cell = ((cell_height * 2) - psi.header_row_height)
2217 		/ (psi.row_height + 2);
2218 	psi.rows_per_compressed_cell = (cell_height - psi.header_row_height)
2219 		/ (psi.row_height + 2);
2220 
2221 	/* Draw the grid and the day names/numbers. */
2222 	font_background = get_font_for_size (font_size_background, PANGO_WEIGHT_NORMAL);
2223 	print_week_view_background (
2224 		context, font_background, &psi, left, top,
2225 		cell_width, cell_height);
2226 	pango_font_description_free (font_background);
2227 
2228 	/* Print the events. */
2229 	font = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
2230 	for (event_num = 0; event_num < psi.events->len; event_num++) {
2231 		event = &g_array_index (psi.events, EWeekViewEvent, event_num);
2232 		print_week_event (
2233 			context, font, &psi, left, top,
2234 			cell_width, cell_height, model, event, spans);
2235 	}
2236 
2237 	pango_font_description_free (font);
2238 
2239 	/* Free everything. */
2240 	for (event_num = 0; event_num < psi.events->len; event_num++) {
2241 		event = &g_array_index (psi.events, EWeekViewEvent, event_num);
2242 		g_object_unref (event->comp_data);
2243 	}
2244 	g_array_free (psi.events, TRUE);
2245 	g_array_free (spans, TRUE);
2246 }
2247 
2248 static void
2249 print_month_summary (GtkPrintContext *context,
2250                      GnomeCalendar *gcal,
2251                      time_t whence,
2252                      gdouble left,
2253                      gdouble right,
2254                      gdouble top,
2255                      gdouble bottom)
2256 {
2257 	icaltimezone *zone;
2258 	time_t date;
2259 	struct tm tm;
2260 	struct icaltimetype tt;
2261 	gchar buffer[100];
2262 	ECalModel *model;
2263 	PangoFontDescription *font;
2264 	gboolean compress_weekend;
2265 	gint columns, col, weekday, month, weeks;
2266 	gdouble font_size, cell_width, x1, x2, y1, y2;
2267 
2268 	model = gnome_calendar_get_model (gcal);
2269 	zone = e_cal_model_get_timezone (model);
2270 	weekday = e_cal_model_get_week_start_day (model);
2271 	compress_weekend = e_cal_model_get_compress_weekend (model);
2272 
2273 	date = 0;
2274 	weeks = 6;
2275 	if (gnome_calendar_get_view (gcal) == GNOME_CAL_MONTH_VIEW) {
2276 		GnomeCalendarViewType view_type;
2277 		ECalendarView *calendar_view;
2278 		EWeekView *week_view;
2279 
2280 		view_type = gnome_calendar_get_view (gcal);
2281 		calendar_view = gnome_calendar_get_calendar_view (gcal, view_type);
2282 		week_view = E_WEEK_VIEW (calendar_view);
2283 
2284 		if (week_view && week_view->multi_week_view
2285 		    && !(week_view->weeks_shown >= 4 &&
2286 		    g_date_valid (&week_view->first_day_shown))) {
2287 			weeks = week_view->weeks_shown;
2288 			date = whence;
2289 		}
2290 	}
2291 
2292 	/* Remember which month we want. */
2293 	tt = icaltime_from_timet_with_zone (whence, FALSE, zone);
2294 	month = tt.month - 1;
2295 
2296 	/* Find the start of the month, and then the start of the week on
2297 	 * or before that day. */
2298 	if (!date)
2299 		date = time_month_begin_with_zone (whence, zone);
2300 	date = time_week_begin_with_zone (date, weekday, zone);
2301 
2302 	/* If weekends are compressed then we can't start on a Sunday. */
2303 	if (compress_weekend && weekday == 0)
2304 		date = time_add_day_with_zone (date, -1, zone);
2305 
2306 	/* do day names ... */
2307 
2308 	/* We are only interested in outputting the weekday here, but we want
2309 	 * to be able to step through the week without worrying about
2310 	 * overflows making strftime choke, so we move near to the start of
2311 	 * the month. */
2312 	convert_timet_to_struct_tm (date, zone, &tm);
2313 	tm.tm_mday = (tm.tm_mday % 7) + 7;
2314 
2315 	font = get_font_for_size (MONTH_NORMAL_FONT_SIZE, PANGO_WEIGHT_BOLD);
2316 	font_size = get_font_size (font);
2317 
2318 	columns = compress_weekend ? 6 : 7;
2319 	cell_width = (right - left) / columns;
2320 	y1 = top;
2321 	y2 = top + font_size * 1.5;
2322 
2323 	for (col = 0; col < columns; col++) {
2324 		if (tm.tm_wday == 6 && compress_weekend)
2325 			g_snprintf (
2326 				buffer, sizeof (buffer), "%s/%s",
2327 				e_get_weekday_name (G_DATE_SATURDAY, TRUE),
2328 				e_get_weekday_name (G_DATE_SUNDAY, TRUE));
2329 		else
2330 			g_snprintf (
2331 				buffer, sizeof (buffer), "%s",
2332 				e_get_weekday_name (
2333 				tm.tm_wday ? tm.tm_wday : 7, FALSE));
2334 
2335 		x1 = left + cell_width * col;
2336 		x2 = x1 + cell_width;
2337 
2338 		print_border (context, x1, x2, y1, y2, 1.0, -1.0);
2339 		print_text_line (context, font, buffer, PANGO_ALIGN_CENTER, x1, x2, y1, y2, TRUE);
2340 
2341 		tm.tm_mday++;
2342 		tm.tm_wday = (tm.tm_wday + 1) % 7;
2343 	}
2344 	pango_font_description_free (font);
2345 
2346 	top = y2;
2347 	print_week_summary (
2348 		context, gcal, date, TRUE, weeks, month,
2349 		MONTH_NORMAL_FONT_SIZE, MONTH_NORMAL_FONT_SIZE,
2350 		left, right, top, bottom);
2351 }
2352 
2353 static void
2354 print_todo_details (GtkPrintContext *context,
2355                     GnomeCalendar *gcal,
2356                     time_t start,
2357                     time_t end,
2358                     gdouble left,
2359                     gdouble right,
2360                     gdouble top,
2361                     gdouble bottom)
2362 {
2363 	PangoFontDescription *font_summary;
2364 	gdouble y, yend, x, xend;
2365 	struct icaltimetype *tt;
2366 	GtkWidget *task_table;
2367 	ETable *table;
2368 	ECalModel *model;
2369 	gint rows, row;
2370 	cairo_t *cr;
2371 
2372 	/* We get the tasks directly from the TaskPad ETable. This means we
2373 	 * get them filtered & sorted for free. */
2374 	task_table = gnome_calendar_get_task_table (gcal);
2375 	table = E_TABLE (task_table);
2376 	g_return_if_fail (table != NULL);
2377 	model = e_task_table_get_model (E_TASK_TABLE (task_table));
2378 
2379 	font_summary = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
2380 
2381 	cr = gtk_print_context_get_cairo_context (context);
2382 
2383 	cairo_set_source_rgb (cr, 0, 0, 0);
2384 	cairo_set_line_width (cr, 0.0);
2385 	top +=2;
2386 
2387 	titled_box (
2388 		context, _("Tasks"), font_summary, PANGO_ALIGN_CENTER,
2389 		&left, &top, &right, &bottom, 1.0);
2390 
2391 	y = top;
2392 	yend = bottom - 2;
2393 
2394 	rows = e_table_model_row_count (E_TABLE_MODEL (model));
2395 	for (row = 0; row < rows; row++) {
2396 		ECalModelComponent *comp_data;
2397 		ECalComponent *comp;
2398 		ECalComponentText summary;
2399 		gint model_row;
2400 
2401 		model_row = e_table_view_to_model_row (table, row);
2402 		comp_data = e_cal_model_get_component_at (model, model_row);
2403 		if (!comp_data)
2404 			continue;
2405 
2406 		comp = e_cal_component_new ();
2407 		e_cal_component_set_icalcomponent (
2408 			comp, icalcomponent_new_clone (comp_data->icalcomp));
2409 
2410 		e_cal_component_get_summary (comp, &summary);
2411 		if (!summary.value) {
2412 			g_object_unref (comp);
2413 			continue;
2414 		}
2415 
2416 		x = left;
2417 		xend = right - 2;
2418 		if (y > bottom) {
2419 			g_object_unref (comp);
2420 			break;
2421 		}
2422 
2423 		/* Print the box to put the tick in. */
2424 		print_border (context, x + 2, x + 8, y + 6, y + 15, 0.1, -1.0);
2425 
2426 		/* If the task is complete, print a tick in the box. */
2427 		e_cal_component_get_completed (comp, &tt);
2428 		if (tt) {
2429 			e_cal_component_free_icaltimetype (tt);
2430 
2431 			cr = gtk_print_context_get_cairo_context (context);
2432 			cairo_set_source_rgb (cr, 0, 0, 0);
2433 			cairo_move_to (cr, x + 3, y + 11);
2434 			cairo_line_to (cr, x + 5, y + 14);
2435 			cairo_line_to (cr, x + 7, y + 5.5);
2436 			cairo_set_line_width (cr, 1);
2437 			cairo_stroke (cr);
2438 		}
2439 
2440 		y = bound_text (
2441 			context, font_summary, summary.value, -1,
2442 			x + 14, y + 4, xend, yend, FALSE, NULL, NULL);
2443 
2444 		y += get_font_size (font_summary) - 5;
2445 		cr = gtk_print_context_get_cairo_context (context);
2446 		cairo_move_to (cr, x, y);
2447 		cairo_line_to (cr, xend, y);
2448 		cairo_set_line_width (cr, 1);
2449 		cairo_stroke (cr);
2450 
2451 		g_object_unref (comp);
2452 	}
2453 
2454 	pango_font_description_free (font_summary);
2455 }
2456 
2457 static void
2458 print_day_view (GtkPrintContext *context,
2459                 GnomeCalendar *gcal,
2460                 time_t date)
2461 {
2462 	ECalModel *model;
2463 	GtkPageSetup *setup;
2464 	icaltimezone *zone;
2465 	gint i, days = 1;
2466 	gdouble todo, l, week_numbers_inc, small_month_width;
2467 	gchar buf[100];
2468 	gdouble width, height;
2469 	struct tm tm;
2470 
2471 	model = gnome_calendar_get_model (gcal);
2472 	zone = e_cal_model_get_timezone (model);
2473 
2474 	setup = gtk_print_context_get_page_setup (context);
2475 
2476 	width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
2477 	height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
2478 	small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
2479 	week_numbers_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;
2480 
2481 	for (i = 0; i < days; i++) {
2482 		todo = width * 0.75;
2483 
2484 		/* Print the main view with all the events in. */
2485 		print_day_details (
2486 			context, gcal, date,
2487 			0.0, todo - 2.0, HEADER_HEIGHT + 4,
2488 			height);
2489 
2490 		 /* Print the TaskPad down the right. */
2491 		print_todo_details (
2492 			context, gcal, 0, INT_MAX,
2493 			todo, width, HEADER_HEIGHT + 4,
2494 			height);
2495 
2496 		/* Print the filled border around the header. */
2497 		print_border (
2498 			context, 0.0, width,
2499 			0.0, HEADER_HEIGHT + 4, 1.0, 0.9);
2500 
2501 		/* Print the 2 mini calendar-months. */
2502 		l = width - SMALL_MONTH_PAD -
2503 			(small_month_width + week_numbers_inc) * 2 -
2504 			SMALL_MONTH_SPACING;
2505 
2506 		print_month_small (
2507 			context, gcal, date,
2508 			l, 2, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 2,
2509 			DATE_MONTH | DATE_YEAR, date, date, FALSE);
2510 
2511 		l += SMALL_MONTH_SPACING + small_month_width + week_numbers_inc;
2512 		print_month_small (
2513 			context, gcal,
2514 			time_add_month_with_zone (date, 1, zone),
2515 			l, 2, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 2,
2516 			DATE_MONTH | DATE_YEAR, 0, 0, FALSE);
2517 
2518 		/* Print the date, e.g. '8th May, 2001'. */
2519 		convert_timet_to_struct_tm (date, zone, &tm);
2520 		format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR,
2521 			     buf, 100);
2522 
2523 		print_text_size_bold (
2524 			context, buf, PANGO_ALIGN_LEFT,
2525 			4, todo, 4,
2526 			4 + 24);
2527 
2528 		/* Print the day, e.g. 'Tuesday'. */
2529 		format_date (&tm, DATE_DAYNAME, buf, 100);
2530 
2531 		print_text_size_bold (
2532 			context, buf, PANGO_ALIGN_LEFT,
2533 			4, todo,
2534 			HEADER_HEIGHT + 9,
2535 			HEADER_HEIGHT + 9 + 18);
2536 
2537 		date = time_add_day_with_zone (date, 1, zone);
2538 	 }
2539 }
2540 
2541 static void
2542 print_work_week_background (GtkPrintContext *context,
2543                             GnomeCalendar *gcal,
2544                             time_t whence,
2545                             struct pdinfo *pdi,
2546                             gdouble left,
2547                             gdouble right,
2548                             gdouble top,
2549                             gdouble bottom)
2550 {
2551 	ECalModel *model;
2552 	PangoFontDescription *font_hour, *font_minute;
2553 	gdouble yinc, y;
2554 	gdouble width = DAY_VIEW_TIME_COLUMN_WIDTH;
2555 	gdouble day_width;
2556 	gdouble font_size, max_font_size, hour_font_size, minute_font_size;
2557 	gchar buf[20];
2558 	const gchar *minute;
2559 	const gint LONG_EVENT_OFFSET = 6;
2560 	gboolean use_24_hour;
2561 	gint i, hour, row;
2562 	gdouble hour_minute_xl, hour_minute_xr;
2563 	cairo_t *cr;
2564 
2565 	model = gnome_calendar_get_model (gcal);
2566 	use_24_hour = e_cal_model_get_use_24_hour_format (model);
2567 
2568 	/* Fill the left time column in light-gray. */
2569 	print_border (context, left, left + width, top, bottom, -1.0, 0.9);
2570 	/* Fill the right time column in light-gray */
2571 	print_border (context, right - width, right, top, bottom, -1.0, 0.9);
2572 
2573 	/* Draw the border around the entire view. */
2574 	cr = gtk_print_context_get_cairo_context (context);
2575 
2576 	cairo_set_source_rgb (cr, 0, 0, 0);
2577 	print_border (context, left, right, top, bottom, 1.0, -1.0);
2578 
2579 	/* Draw the vertical line on the right of the time column. */
2580 	cr = gtk_print_context_get_cairo_context (context);
2581 	cairo_set_line_width (cr, 0.0);
2582 	cairo_move_to (cr, left + width, bottom);
2583 	cairo_line_to (cr, left + width, top);
2584 	cairo_stroke (cr);
2585 
2586 	cairo_move_to (cr, right - width, bottom);
2587 	cairo_line_to (cr, right - width, top);
2588 	cairo_stroke (cr);
2589 
2590 	/* Calculate the row height. */
2591 	if (top > bottom)
2592 		yinc = (top - bottom) / (pdi->end_hour - pdi->start_hour);
2593 	else
2594 		yinc = (bottom - top) / (pdi->end_hour - pdi->start_hour);
2595 
2596         /* Get the 2 fonts we need. */
2597 	font_size = yinc * 0.6;
2598 	max_font_size = width * 0.45;
2599 	hour_font_size = MIN (font_size, max_font_size);
2600 	font_hour = get_font_for_size (hour_font_size, PANGO_WEIGHT_BOLD);
2601 
2602 	font_size = yinc * 0.33;
2603 	max_font_size = width * 0.2;
2604 	minute_font_size = MIN (font_size, max_font_size);
2605 	font_minute = get_font_for_size (minute_font_size, PANGO_WEIGHT_BOLD);
2606 	hour_minute_xr = evo_calendar_print_renderer_get_width (
2607 		context, font_minute, use_24_hour ? "00" : _("am"));
2608 	if (!use_24_hour)
2609 		hour_minute_xr = MAX (
2610 			hour_minute_xr,
2611 			evo_calendar_print_renderer_get_width (
2612 				context, font_minute, _("pm")));
2613 
2614 	row = 0;
2615 	hour_minute_xl = left + width - hour_minute_xr - 3;
2616 	hour_minute_xr = right - hour_minute_xr - 3;
2617 	for (i = pdi->start_hour; i < pdi->end_hour; i++) {
2618 		y = top + yinc * (row + 1);
2619 		cr = gtk_print_context_get_cairo_context (context);
2620 		cairo_set_source_rgb (cr, 0, 0, 0);
2621 
2622 		if (use_24_hour) {
2623 			hour = i;
2624 			minute = "00";
2625 		} else {
2626 			if (i < 12)
2627 				minute = _("am");
2628 			else
2629 				minute = _("pm");
2630 
2631 			hour = i % 12;
2632 			if (hour == 0)
2633 				hour = 12;
2634 		}
2635 
2636 		/* the hour label/minute */
2637 		sprintf (buf, "%d", hour);
2638 		print_text (
2639 			context, font_hour, buf, PANGO_ALIGN_RIGHT,
2640 			left, hour_minute_xl,
2641 			y - yinc, y - yinc + hour_font_size);
2642 		print_text (
2643 			context, font_minute, minute, PANGO_ALIGN_LEFT,
2644 			hour_minute_xl, left + width - 3,
2645 			y - yinc, y - yinc + minute_font_size);
2646 
2647 		/* To the right */
2648 		print_text (
2649 			context, font_hour, buf, PANGO_ALIGN_RIGHT,
2650 			right - width, hour_minute_xr,
2651 			y - yinc, y - yinc + hour_font_size);
2652 		print_text (
2653 			context, font_minute, minute, PANGO_ALIGN_LEFT,
2654 			hour_minute_xr, right - 3,
2655 			y - yinc, y - yinc + minute_font_size);
2656 
2657                 /* Draw the horizontal line between hours, across the entire
2658 		   width of the day view. */
2659 		cr = gtk_print_context_get_cairo_context (context);
2660 		cairo_move_to (cr, left, y);
2661 		cairo_line_to (cr, right, y);
2662 		cairo_set_line_width (cr, 1);
2663 		cairo_stroke (cr);
2664 
2665 		/* Draw the horizontal line for the 1/2-hours, across the
2666 		 * entire width except for part of the time column. */
2667 		cairo_move_to (cr, left + width * 0.6, y - yinc / 2);
2668 		cairo_line_to (cr, right, y - yinc / 2);
2669 		cairo_set_line_width (cr, 1);
2670 		cairo_stroke (cr);
2671 		row++;
2672 	}
2673 
2674 	/* Draw the vertical lines for the days */
2675 	day_width = (right - left - 2 *width) / pdi->days_shown;
2676 	for (i = 0; i < pdi->days_shown - 1; ++i) {
2677 	  cr = gtk_print_context_get_cairo_context (context);
2678 	  cairo_move_to (cr, left + width + day_width * (i + 1), top);
2679 	  cairo_line_to (cr, left + width + day_width * (i + 1), bottom);
2680 	  cairo_set_line_width (cr, 1);
2681 	  cairo_stroke (cr);
2682 	}
2683 
2684 	/* And now the ones from the border to the hours, looks weird otherwise */
2685 	cr = gtk_print_context_get_cairo_context (context);
2686 	cairo_move_to (cr, left, HEADER_HEIGHT);
2687 	cairo_line_to (cr, left, HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET);
2688 
2689 	cairo_move_to (cr, right, HEADER_HEIGHT);
2690 	cairo_line_to (cr, right, HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET);
2691 	cairo_stroke (cr);
2692 
2693 	pango_font_description_free (font_hour);
2694 	pango_font_description_free (font_minute);
2695 }
2696 
2697 static void
2698 print_work_week_day_details (GtkPrintContext *context,
2699                              GnomeCalendar *gcal,
2700                              time_t whence,
2701                              gdouble left,
2702                              gdouble right,
2703                              gdouble top,
2704                              gdouble bottom,
2705                              struct pdinfo *_pdi)
2706 {
2707 	ECalModel *model;
2708 	icaltimezone *zone;
2709 	EDayViewEvent *event;
2710 	PangoFontDescription *font;
2711 	time_t start, end;
2712 	struct pdinfo pdi = { 0 };
2713 	gint rows_in_top_display, i, rows_with_30_mins;
2714 	gdouble font_size, max_font_size;
2715 	cairo_t *cr;
2716 	GdkPixbuf *pixbuf = NULL;
2717 #define LONG_DAY_EVENTS_TOP_SPACING 4
2718 #define LONG_DAY_EVENTS_BOTTOM_SPACING 2
2719 
2720 	model = gnome_calendar_get_model (gcal);
2721 	zone = e_cal_model_get_timezone (model);
2722 
2723 	start = time_day_begin_with_zone (whence, zone);
2724 	end = time_day_end_with_zone (start, zone);
2725 
2726 	pdi.days_shown = 1;
2727 	pdi.day_starts[0] = start;
2728 	pdi.day_starts[1] = end;
2729 	pdi.long_events = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
2730 	pdi.events[0] = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
2731 	pdi.start_hour = e_cal_model_get_work_day_start_hour (model);
2732 	pdi.end_hour = e_cal_model_get_work_day_end_hour (model);
2733 	if (e_cal_model_get_work_day_end_minute (model) != 0)
2734 		pdi.end_hour++;
2735 	pdi.mins_per_row = get_day_view_time_divisions ();
2736 	pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
2737 	pdi.start_minute_offset = pdi.start_hour * 60;
2738 	pdi.end_minute_offset = pdi.end_hour * 60;
2739 	pdi.use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
2740 	pdi.zone = e_cal_model_get_timezone (model);
2741 
2742 	/* Get the events from the server. */
2743 	e_cal_model_generate_instances_sync (model, start, end, print_day_details_cb, &pdi);
2744 	qsort (
2745 		pdi.long_events->data, pdi.long_events->len,
2746 		sizeof (EDayViewEvent), e_day_view_event_sort_func);
2747 	qsort (
2748 		pdi.events[0]->data, pdi.events[0]->len,
2749 		sizeof (EDayViewEvent), e_day_view_event_sort_func);
2750 
2751 	pdi.start_hour = MIN (pdi.start_hour, _pdi->start_hour);
2752 	pdi.end_hour = MAX (pdi.end_hour, _pdi->end_hour);
2753 
2754 	/* TODO: This should be redundant */
2755 	/* Also print events outside of work hours */
2756 	if (pdi.events[0]->len > 0) {
2757 		struct icaltimetype tt;
2758 
2759 		event = &g_array_index (pdi.events[0], EDayViewEvent, 0);
2760 		tt = icaltime_from_timet_with_zone (event->start, FALSE, zone);
2761 		if (tt.hour < pdi.start_hour)
2762 			pdi.start_hour = tt.hour;
2763 		pdi.start_minute_offset = pdi.start_hour * 60;
2764 
2765 		event = &g_array_index (pdi.events[0], EDayViewEvent, pdi.events[0]->len - 1);
2766 		tt = icaltime_from_timet_with_zone (event->end, FALSE, zone);
2767 		if (tt.hour > pdi.end_hour || tt.hour == 0) {
2768 			pdi.end_hour = tt.hour ? tt.hour : 24;
2769 			if (tt.minute > 0)
2770 				pdi.end_hour++;
2771 		}
2772 		pdi.end_minute_offset = pdi.end_hour * 60;
2773 
2774 		pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
2775 	}
2776 
2777 	/* Lay them out the long events, across the top of the page. */
2778 	e_day_view_layout_long_events (
2779 		pdi.long_events, pdi.days_shown,
2780 		pdi.day_starts, &rows_in_top_display);
2781 
2782 	 /*Print the long events. */
2783 	font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
2784 
2785 	/* We always leave space for DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY in the
2786 	 * top display, but we may have more rows than that, in which case
2787 	 * the main display area will be compressed. */
2788 	/* Limit long day event to half the height of the panel */
2789 	rows_in_top_display = MIN (
2790 		MAX (rows_in_top_display,
2791 		DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY),
2792 		(bottom - top) * 0.5 / DAY_VIEW_ROW_HEIGHT);
2793 
2794 	if (rows_in_top_display > pdi.long_events->len)
2795 		rows_in_top_display = pdi.long_events->len;
2796 
2797 	for (i = 0; i < rows_in_top_display && i < pdi.long_events->len; i++) {
2798 		event = &g_array_index (pdi.long_events, EDayViewEvent, i);
2799 		print_day_long_event (
2800 			context, font, left, right,
2801 			top + LONG_DAY_EVENTS_TOP_SPACING, bottom,
2802 			DAY_VIEW_ROW_HEIGHT, event, &pdi, model);
2803 	}
2804 
2805 	if (rows_in_top_display < pdi.long_events->len) {
2806 		/* too many events */
2807 		cairo_t *cr = gtk_print_context_get_cairo_context (context);
2808 		gint x, y;
2809 
2810 		if (!pixbuf) {
2811 			const gchar **xpm = (const gchar **) jump_xpm;
2812 
2813 			/* this ugly thing is here only to get rid of compiler warning
2814 			 * about unused 'jump_xpm_focused' */
2815 			if (pixbuf)
2816 				xpm = (const gchar **) jump_xpm_focused;
2817 
2818 			pixbuf = gdk_pixbuf_new_from_xpm_data (xpm);
2819 		}
2820 
2821 		/* Right align - 10 comes from print_day_long_event  too */
2822 		x = right - gdk_pixbuf_get_width (pixbuf) * 0.5 - 10;
2823 		/* Placing '...' over the last all day event entry printed. '-1 -1' comes
2824 			from print_long_day_event (top / bottom spacing in each cell) */
2825 		y = top + LONG_DAY_EVENTS_TOP_SPACING
2826 			+ DAY_VIEW_ROW_HEIGHT * (i - 1)
2827 			+ (DAY_VIEW_ROW_HEIGHT - 1 - 1) * 0.5;
2828 
2829 		cairo_save (cr);
2830 		cairo_scale (cr, 0.5, 0.5);
2831 		gdk_cairo_set_source_pixbuf (cr, pixbuf, x * 2.0, y * 2.0);
2832 		cairo_paint (cr);
2833 		cairo_restore (cr);
2834 	}
2835 
2836 	if (!rows_in_top_display)
2837 		rows_in_top_display++;
2838 
2839 	/* Draw the border around the long events. */
2840 	cr = gtk_print_context_get_cairo_context (context);
2841 
2842 	cairo_set_source_rgb (cr, 0, 0, 0);
2843 	print_border (
2844 		context, left, right,
2845 		top, top + rows_in_top_display * DAY_VIEW_ROW_HEIGHT +
2846 		LONG_DAY_EVENTS_TOP_SPACING + LONG_DAY_EVENTS_BOTTOM_SPACING,
2847 		1.0, -1.0);
2848 
2849 	/* Adjust the area containing the main display. */
2850 	top += rows_in_top_display * DAY_VIEW_ROW_HEIGHT
2851 		+ LONG_DAY_EVENTS_TOP_SPACING
2852 		+ LONG_DAY_EVENTS_BOTTOM_SPACING;
2853 
2854 	/* lay out the short events, within the day. */
2855 	e_day_view_layout_day_events (
2856 		pdi.events[0], CALC_DAY_VIEW_ROWS (pdi.mins_per_row),
2857 		pdi.mins_per_row, pdi.cols_per_row, -1);
2858 
2859 	/* use font like with 30 minutes time division */
2860 	rows_with_30_mins = (pdi.end_hour - pdi.start_hour) * (60 / 30);
2861 
2862 	/* print the short events. */
2863 	if (top > bottom)
2864 		max_font_size = ((top - bottom) / rows_with_30_mins) - 4;
2865 	else
2866 		max_font_size = ((bottom - top) / rows_with_30_mins) - 4;
2867 	font_size = MIN (DAY_NORMAL_FONT_SIZE, max_font_size);
2868 	font = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
2869 
2870 	for (i = 0; i < pdi.events[0]->len; i++) {
2871 		event = &g_array_index (pdi.events[0], EDayViewEvent, i);
2872 		print_day_event (
2873 			context, font, left,
2874 			right, top, bottom, event, &pdi, model);
2875 	}
2876 
2877 	/* Free everything. */
2878 	if (pixbuf)
2879 		g_object_unref (pixbuf);
2880 	free_event_array (pdi.long_events);
2881 	pango_font_description_free (font);
2882 	g_array_free (pdi.long_events, TRUE);
2883 	free_event_array (pdi.events[0]);
2884 	g_array_free (pdi.events[0], TRUE);
2885 }
2886 
2887 /* Figure out what the overal hour limits are */
2888 static gboolean
2889 print_work_week_view_cb (ECalComponent *comp,
2890                          time_t istart,
2891                          time_t iend,
2892                          gpointer data)
2893 {
2894 	ECalModelGenerateInstancesData *mdata = (ECalModelGenerateInstancesData *) data;
2895 	struct pdinfo *pdi = (struct pdinfo *) mdata->cb_data;
2896 	struct icaltimetype tt;
2897 
2898 	tt = icaltime_from_timet_with_zone (istart, FALSE, pdi->zone);
2899 	pdi->start_hour = MIN (pdi->start_hour, tt.hour);
2900 
2901 	tt = icaltime_from_timet_with_zone (iend, FALSE, pdi->zone);
2902 	/* If we're past the hour, use the next one */
2903 	pdi->end_hour = MAX (pdi->end_hour, tt.minute ? tt.hour + 1 : tt.hour);
2904 
2905 	return TRUE;
2906 }
2907 
2908 static void
2909 print_work_week_view (GtkPrintContext *context,
2910                       GnomeCalendar *gcal,
2911                       time_t date)
2912 {
2913 	GtkPageSetup *setup;
2914 	icaltimezone *zone;
2915 	time_t when, start, end;
2916 	gdouble width, height, l;
2917 	gdouble small_month_width;
2918 	gdouble weeknum_inc;
2919 	gint i, days = 5;
2920 	gchar buf[100];
2921 	const gint LONG_EVENT_OFFSET = 6;
2922 	struct pdinfo pdi = { 0 };
2923 	struct tm tm;
2924 	gdouble day_width, day_x;
2925 	ECalModel *model;
2926 
2927 	model = gnome_calendar_get_model (gcal);
2928 	zone = e_cal_model_get_timezone (model);
2929 
2930 	setup = gtk_print_context_get_page_setup (context);
2931 
2932 	width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
2933 	height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
2934 
2935 	small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
2936 	weeknum_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;
2937 
2938 	/* We always start on a Monday */
2939 	start = time_week_begin_with_zone (date, 1, zone);
2940 	end = time_add_day_with_zone (start, days, zone);
2941 
2942 	pdi.days_shown = days;
2943 	pdi.start_hour = e_cal_model_get_work_day_start_hour (model);
2944 	pdi.end_hour = e_cal_model_get_work_day_end_hour (model);
2945 	pdi.zone = zone;
2946 
2947 	e_cal_model_generate_instances_sync (model, start, end, print_work_week_view_cb, &pdi);
2948 
2949 	print_work_week_background (
2950 		context, gcal, date, &pdi, 0.0, width,
2951 		HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET,
2952 		height);
2953 
2954 	print_border (context, 0.0, width, 0.0, HEADER_HEIGHT, 1.0, 0.9);
2955 
2956 	/* Print the 2 mini calendar-months. */
2957 	l = width - SMALL_MONTH_PAD - (small_month_width + weeknum_inc) * 2 -
2958 		SMALL_MONTH_SPACING;
2959 
2960 	print_month_small (
2961 		context, gcal, start,
2962 		l, 4, l + small_month_width + weeknum_inc, HEADER_HEIGHT + 4,
2963 		DATE_MONTH | DATE_YEAR, start, end, FALSE);
2964 
2965 	l += SMALL_MONTH_SPACING + small_month_width + weeknum_inc;
2966 	print_month_small (
2967 		context, gcal,
2968 		time_add_month_with_zone (start, 1, zone),
2969 		l, 4, l + small_month_width + weeknum_inc, HEADER_HEIGHT + 4,
2970 		DATE_MONTH | DATE_YEAR, 0, 0, FALSE);
2971 
2972 	/* Print the start day of the week, e.g. '7th May 2001'. */
2973 	convert_timet_to_struct_tm (start, zone, &tm);
2974 	format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
2975 	print_text_size_bold (
2976 		context, buf, PANGO_ALIGN_LEFT,
2977 		3, width,
2978 		4, 4 + 24);
2979 
2980 	/* Print the end day of the week, e.g. '13th May 2001'. */
2981 	/* We need to substract one or the wrong day will be printed */
2982 	convert_timet_to_struct_tm (
2983 		time_add_day_with_zone (end, -1, zone), zone, &tm);
2984 	format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
2985 	print_text_size_bold (
2986 		context, buf, PANGO_ALIGN_LEFT,
2987 		3, width,
2988 		24 + 3, 24 + 3 + 24);
2989 
2990 	/* Now print each days' events */
2991 	day_width = (width - 2 *DAY_VIEW_TIME_COLUMN_WIDTH) / days;
2992 	when = start;
2993 	for (i = 0; i < days; ++i) {
2994 		day_x = DAY_VIEW_TIME_COLUMN_WIDTH + day_width * i;
2995 
2996 		/* Print the day, e.g. 'Tuesday'. */
2997 		convert_timet_to_struct_tm (when, zone, &tm);
2998 		format_date (&tm, DATE_DAYNAME, buf, 100);
2999 
3000 		print_text_size_bold (
3001 			context, buf, PANGO_ALIGN_LEFT,
3002 			day_x + 4, day_x + day_width,
3003 			HEADER_HEIGHT + 4, HEADER_HEIGHT + 4 + 18);
3004 
3005 		print_work_week_day_details (
3006 			context, gcal, when,
3007 			day_x, day_x + day_width,
3008 			HEADER_HEIGHT, height, &pdi);
3009 		when = time_add_day_with_zone (when, 1, zone);
3010 	}
3011 }
3012 
3013 static void
3014 print_week_view (GtkPrintContext *context,
3015                  GnomeCalendar *gcal,
3016                  time_t date)
3017 {
3018 	GtkPageSetup *setup;
3019 	ECalModel *model;
3020 	icaltimezone *zone;
3021 	gdouble l, week_numbers_inc, small_month_width;
3022 	gchar buf[100];
3023 	time_t when;
3024 	gint week_start_day;
3025 	struct tm tm;
3026 	gdouble width, height;
3027 
3028 	setup = gtk_print_context_get_page_setup (context);
3029 
3030 	width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
3031 	height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
3032 	small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
3033 	week_numbers_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;
3034 
3035 	model = gnome_calendar_get_model (gcal);
3036 	zone = e_cal_model_get_timezone (model);
3037 
3038 	convert_timet_to_struct_tm (date, zone, &tm);
3039 	week_start_day = e_cal_model_get_week_start_day (model);
3040 	when = time_week_begin_with_zone (date, week_start_day, zone);
3041 
3042 	/* If the week starts on a Sunday, we have to show the Saturday first,
3043 	 * since the weekend is compressed. */
3044 	if (week_start_day == 0) {
3045 		if (tm.tm_wday == 6)
3046 			when = time_add_day_with_zone (when, 6, zone);
3047 		else
3048 			when = time_add_day_with_zone (when, -1, zone);
3049 	}
3050 
3051 	/* Print the main week view. */
3052 	print_week_summary (
3053 		context, gcal, when, FALSE, 1, 0,
3054 		WEEK_EVENT_FONT_SIZE, WEEK_SMALL_FONT_SIZE,
3055 		0.0, width,
3056 		HEADER_HEIGHT + 20, height);
3057 
3058 	/* Print the border around the main view. */
3059 	print_border (
3060 		context, 0.0, width, HEADER_HEIGHT ,
3061 		height, 1.0, -1.0);
3062 
3063 	/* Print the border around the header area. */
3064 	print_border (
3065 		context, 0.0, width,
3066 		0.0, HEADER_HEIGHT + 2.0 + 20, 1.0, 0.9);
3067 
3068 	/* Print the 2 mini calendar-months. */
3069 	l = width - SMALL_MONTH_PAD - (small_month_width + week_numbers_inc) * 2
3070 		- SMALL_MONTH_SPACING;
3071 	print_month_small (
3072 		context, gcal, when,
3073 		l, 4, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 10,
3074 		DATE_MONTH | DATE_YEAR, when,
3075 		time_add_week_with_zone (when, 1, zone), FALSE);
3076 
3077 	l += SMALL_MONTH_SPACING + small_month_width + week_numbers_inc;
3078 	print_month_small (
3079 		context, gcal,
3080 		time_add_month_with_zone (when, 1, zone),
3081 		l, 4, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 10,
3082 		DATE_MONTH | DATE_YEAR, when,
3083 		time_add_week_with_zone (when, 1, zone), FALSE);
3084 
3085 	/* Print the start day of the week, e.g. '7th May 2001'. */
3086 	convert_timet_to_struct_tm (when, zone, &tm);
3087 	format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
3088 	print_text_abs_bold (
3089 		context, buf, WEEK_NORMAL_FONT_SIZE, PANGO_ALIGN_LEFT,
3090 		3, width, 4, 4 + 24);
3091 
3092 	/* Print the end day of the week, e.g. '13th May 2001'. */
3093 	when = time_add_day_with_zone (when, 6, zone);
3094 	convert_timet_to_struct_tm (when, zone, &tm);
3095 	format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
3096 	print_text_abs_bold (
3097 		context, buf, WEEK_NORMAL_FONT_SIZE, PANGO_ALIGN_LEFT,
3098 		3, width, 24 + 3, 24 + 3 + 24);
3099 }
3100 
3101 static void
3102 print_month_view (GtkPrintContext *context,
3103                   GnomeCalendar *gcal,
3104                   time_t date)
3105 {
3106 	ECalModel *model;
3107 	GtkPageSetup *setup;
3108 	icaltimezone *zone;
3109 	gchar buf[100];
3110 	gdouble width, height;
3111 	gdouble l, week_numbers_inc, small_month_width;
3112 	struct tm tm;
3113 
3114 	model = gnome_calendar_get_model (gcal);
3115 	zone = e_cal_model_get_timezone (model);
3116 
3117 	setup = gtk_print_context_get_page_setup (context);
3118 
3119 	width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
3120 	height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
3121 	small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
3122 	week_numbers_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;
3123 
3124 	/* Print the main month view. */
3125 	print_month_summary (context, gcal, date, 0.0, width, HEADER_HEIGHT, height);
3126 
3127 	/* Print the border around the header. */
3128 	print_border (context, 0.0, width, 0.0, HEADER_HEIGHT + 10, 1.0, 0.9);
3129 
3130 	l = width - SMALL_MONTH_PAD - small_month_width - week_numbers_inc;
3131 
3132 	/* Print the 2 mini calendar-months. */
3133 	print_month_small (
3134 		context, gcal,
3135 		time_add_month_with_zone (date, 1, zone),
3136 		l, 4, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 4,
3137 		DATE_MONTH | DATE_YEAR, 0, 0, FALSE);
3138 
3139 	print_month_small (
3140 		context, gcal,
3141 		time_add_month_with_zone (date, -1, zone),
3142 		SMALL_MONTH_PAD, 4, SMALL_MONTH_PAD + small_month_width + week_numbers_inc, HEADER_HEIGHT + 4,
3143 		DATE_MONTH | DATE_YEAR, 0, 0, FALSE);
3144 
3145 	/* Print the month, e.g. 'May 2001'. */
3146 	convert_timet_to_struct_tm (date, zone, &tm);
3147 	format_date (&tm, DATE_MONTH | DATE_YEAR, buf, 100);
3148 	print_text_size_bold (
3149 		context, buf, PANGO_ALIGN_CENTER,
3150 		3, width - 3,
3151 		3, 3 + 24);
3152 
3153 }
3154 
3155 static gboolean
3156 same_date (struct tm tm1,
3157            time_t t2,
3158            icaltimezone *zone)
3159 {
3160 	struct tm tm2;
3161 
3162 	convert_timet_to_struct_tm (t2, zone, &tm2);
3163 
3164 	return
3165 	    tm1.tm_mday == tm2.tm_mday &&
3166 	    tm1.tm_mon == tm2.tm_mon &&
3167 	    tm1.tm_year == tm2.tm_year;
3168 }
3169 
3170 static void
3171 write_label_piece (time_t t,
3172                    time_t *start_cmp,
3173                    icaltimezone *zone,
3174                    gboolean use_24_hour_format,
3175                    gchar *buffer,
3176                    gint size,
3177                    gchar *stext,
3178                    const gchar *etext)
3179 {
3180 	struct tm tmp_tm;
3181 	gint len;
3182 
3183 	convert_timet_to_struct_tm (t, zone, &tmp_tm);
3184 
3185 	if (stext != NULL)
3186 		strcat (buffer, stext);
3187 
3188 	len = strlen (buffer);
3189 	if (start_cmp && same_date (tmp_tm, *start_cmp, zone))
3190 		e_time_format_time (
3191 			&tmp_tm, use_24_hour_format,
3192 			FALSE, &buffer[len], size - len);
3193 	else
3194 		e_time_format_date_and_time (
3195 			&tmp_tm, use_24_hour_format, FALSE,
3196 			FALSE, &buffer[len], size - len);
3197 	if (etext != NULL)
3198 		strcat (buffer, etext);
3199 }
3200 
3201 static icaltimezone *
3202 get_zone_from_tzid (ECalClient *client,
3203                     const gchar *tzid)
3204 {
3205 	icaltimezone *zone;
3206 
3207 	/* Note that the timezones may not be on the server, so we try to get
3208 	 * the builtin timezone with the TZID first. */
3209 	zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
3210 	if (!zone && tzid) {
3211 		GError *error = NULL;
3212 
3213 		e_cal_client_get_timezone_sync (
3214 			client, tzid, &zone, NULL, &error);
3215 
3216 		if (error != NULL) {
3217 			g_warning (
3218 				"Couldn't get timezone '%s' from server: %s",
3219 				tzid ? tzid : "", error->message);
3220 			g_error_free (error);
3221 		}
3222 	}
3223 
3224 	return zone;
3225 }
3226 
3227 static void
3228 print_date_label (GtkPrintContext *context,
3229                   ECalComponent *comp,
3230                   ECalClient *client,
3231                   icaltimezone *zone,
3232                   gboolean use_24_hour_format,
3233                   gdouble left,
3234                   gdouble right,
3235                   gdouble top,
3236                   gdouble bottom)
3237 {
3238 	icaltimezone *start_zone, *end_zone, *due_zone, *completed_zone;
3239 	ECalComponentDateTime datetime;
3240 	time_t start = 0, end = 0, complete = 0, due = 0;
3241 	static gchar buffer[1024];
3242 
3243 	e_cal_component_get_dtstart (comp, &datetime);
3244 	if (datetime.value) {
3245 		start_zone = get_zone_from_tzid (client, datetime.tzid);
3246 		if (!start_zone || datetime.value->is_date)
3247 			start_zone = zone;
3248 		start = icaltime_as_timet_with_zone (
3249 			*datetime.value,
3250 			start_zone);
3251 	}
3252 	e_cal_component_free_datetime (&datetime);
3253 
3254 	e_cal_component_get_dtend (comp, &datetime);
3255 	if (datetime.value) {
3256 		end_zone = get_zone_from_tzid (client, datetime.tzid);
3257 		if (!end_zone || datetime.value->is_date)
3258 			end_zone = zone;
3259 		end = icaltime_as_timet_with_zone (
3260 			*datetime.value,
3261 			end_zone);
3262 	}
3263 	e_cal_component_free_datetime (&datetime);
3264 
3265 	e_cal_component_get_due (comp, &datetime);
3266 	if (datetime.value) {
3267 		due_zone = get_zone_from_tzid (client, datetime.tzid);
3268 		if (!due_zone || datetime.value->is_date)
3269 			due_zone = zone;
3270 		due = icaltime_as_timet_with_zone (
3271 			*datetime.value, due_zone);
3272 	}
3273 	e_cal_component_free_datetime (&datetime);
3274 
3275 	e_cal_component_get_completed (comp, &datetime.value);
3276 	if (datetime.value) {
3277 		completed_zone = icaltimezone_get_utc_timezone ();
3278 		complete = icaltime_as_timet_with_zone (
3279 			*datetime.value, completed_zone);
3280 		e_cal_component_free_icaltimetype (datetime.value);
3281 	}
3282 
3283 	buffer[0] = '\0';
3284 
3285 	if (start > 0)
3286 		write_label_piece (
3287 			start, NULL, zone, use_24_hour_format,
3288 			buffer, 1024, NULL, NULL);
3289 
3290 	if (end > 0 && start > 0) {
3291 		write_label_piece (
3292 			end, &start, zone, use_24_hour_format,
3293 			/* Translators: This is part of "START to END" text,
3294 			 * where START and END are date/times. */
3295 			buffer, 1024, _(" to "), NULL);
3296 	}
3297 
3298 	if (complete > 0) {
3299 		if (start > 0) {
3300 			write_label_piece (
3301 				complete, NULL, zone, use_24_hour_format,
3302 				/* Translators: This is part of "START to END
3303 				 * (Completed COMPLETED)", where COMPLETED is a
3304 				 * completed date/time. */
3305 				buffer, 1024, _(" (Completed "), ")");
3306 		} else {
3307 			write_label_piece (
3308 				complete, &start, zone, use_24_hour_format,
3309 				/* Translators: This is part of "Completed COMPLETED",
3310 				 * where COMPLETED is a completed date/time. */
3311 				buffer, 1024, _("Completed "), NULL);
3312 		}
3313 	}
3314 
3315 	if (due > 0 && complete == 0) {
3316 		if (start > 0) {
3317 			write_label_piece (
3318 				due, NULL, zone, use_24_hour_format,
3319 				/* Translators: This is part of "START (Due DUE)",
3320 				 * where START and DUE are dates/times. */
3321 				buffer, 1024, _(" (Due "), ")");
3322 		} else {
3323 			write_label_piece (
3324 				due, &start, zone, use_24_hour_format,
3325 				/* Translators: This is part of "Due DUE",
3326 				 * where DUE is a date/time due the event
3327 				 * should be finished. */
3328 				buffer, 1024, _("Due "), NULL);
3329 		}
3330 	}
3331 
3332 	print_text_size_bold (
3333 		context, buffer, PANGO_ALIGN_LEFT,
3334 		left, right, top, top + 24);
3335 }
3336 
3337 static void
3338 print_calendar_draw_page (GtkPrintOperation *operation,
3339                           GtkPrintContext *context,
3340                           gint page_nr,
3341                           PrintCalItem *pcali)
3342 {
3343 	switch (gnome_calendar_get_view (pcali->gcal)) {
3344 		case GNOME_CAL_DAY_VIEW:
3345 			print_day_view (context, pcali->gcal, pcali->start);
3346 			break;
3347 		case GNOME_CAL_WORK_WEEK_VIEW:
3348 			print_work_week_view (context, pcali->gcal, pcali->start);
3349 			break;
3350 		case GNOME_CAL_WEEK_VIEW:
3351 			print_week_view (context, pcali->gcal, pcali->start);
3352 			break;
3353 		case GNOME_CAL_MONTH_VIEW:
3354 			print_month_view (context, pcali->gcal, pcali->start);
3355 			break;
3356 		default:
3357 			g_return_if_reached ();
3358 	}
3359 }
3360 
3361 void
3362 print_calendar (GnomeCalendar *gcal,
3363                 GtkPrintOperationAction action,
3364                 time_t start)
3365 {
3366 	GtkPrintOperation *operation;
3367 	PrintCalItem pcali;
3368 
3369 	g_return_if_fail (gcal != NULL);
3370 	g_return_if_fail (GNOME_IS_CALENDAR (gcal));
3371 
3372 	if (gnome_calendar_get_view (gcal) == GNOME_CAL_MONTH_VIEW) {
3373 		GnomeCalendarViewType view_type;
3374 		ECalendarView *calendar_view;
3375 		EWeekView *week_view;
3376 
3377 		view_type = gnome_calendar_get_view (gcal);
3378 		calendar_view = gnome_calendar_get_calendar_view (gcal, view_type);
3379 		week_view = E_WEEK_VIEW (calendar_view);
3380 
3381 		if (week_view && week_view->multi_week_view &&
3382 			week_view->weeks_shown >= 4 &&
3383 			g_date_valid (&week_view->first_day_shown)) {
3384 
3385 			GDate date = week_view->first_day_shown;
3386 			struct icaltimetype start_tt;
3387 
3388 			g_date_add_days (&date, 7);
3389 
3390 			start_tt = icaltime_null_time ();
3391 			start_tt.is_date = TRUE;
3392 			start_tt.year = g_date_get_year (&date);
3393 			start_tt.month = g_date_get_month (&date);
3394 			start_tt.day = g_date_get_day (&date);
3395 
3396 			start = icaltime_as_timet (start_tt);
3397 		} else if (week_view && week_view->multi_week_view) {
3398 			start = week_view->day_starts[0];
3399 		}
3400 	}
3401 
3402 	pcali.gcal = (GnomeCalendar *) gcal;
3403 	pcali.start = start;
3404 
3405 	operation = e_print_operation_new ();
3406 	gtk_print_operation_set_n_pages (operation, 1);
3407 
3408 	g_signal_connect (
3409 		operation, "draw_page",
3410 		G_CALLBACK (print_calendar_draw_page), &pcali);
3411 
3412 	gtk_print_operation_run (operation, action, NULL, NULL);
3413 
3414 	g_object_unref (operation);
3415 }
3416 
3417 /* returns number of required pages, when page_nr is -1 */
3418 static gint
3419 print_comp_draw_real (GtkPrintOperation *operation,
3420                       GtkPrintContext *context,
3421                       gint page_nr,
3422                       PrintCompItem *pci)
3423 {
3424 	GtkPageSetup *setup;
3425 	PangoFontDescription *font;
3426 	ECalClient *client;
3427 	ECalComponent *comp;
3428 	ECalComponentVType vtype;
3429 	ECalComponentText text;
3430 	GSList *desc, *l;
3431 	GSList *contact_list, *elem;
3432 
3433 	const gchar *title, *categories, *location;
3434 	gchar *categories_string, *location_string, *summary_string;
3435 	gdouble header_size;
3436 	cairo_t *cr;
3437 	gdouble width, height, page_start;
3438 	gdouble top;
3439 	gint pages = 1;
3440 
3441 	setup = gtk_print_context_get_page_setup (context);
3442 
3443 	width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
3444 	height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
3445 
3446 	top = 0.0;
3447 
3448 	/* Either draw only the right page or do not draw
3449 	 * anything when calculating number of pages. */
3450 	if (page_nr != -1)
3451 		top = top - ((page_nr) * height);
3452 	else
3453 		top = height;
3454 
3455 	page_start = top;
3456 
3457         /* PrintCompItem structure contains elements to be used
3458          * with the Print Context , obtained in comp_draw_page
3459          */
3460 	client = pci->client;
3461 	comp = pci->comp;
3462 
3463 	vtype = e_cal_component_get_vtype (comp);
3464 
3465 	/* We should only be asked to print VEVENTs, VTODOs, or VJOURNALs. */
3466 	if (vtype == E_CAL_COMPONENT_EVENT)
3467 		title = _("Appointment");
3468 	else if (vtype == E_CAL_COMPONENT_TODO)
3469 		title = _("Task");
3470 	else if (vtype == E_CAL_COMPONENT_JOURNAL)
3471 		title = _("Memo");
3472 	else
3473 		return pages;
3474 
3475 	cr = gtk_print_context_get_cairo_context (context);
3476 
3477 	/* Print the title in a box at the top of the page. */
3478 	font = get_font_for_size (18, PANGO_WEIGHT_BOLD);
3479 	header_size = 40;
3480 
3481 	if (page_nr == 0) {
3482 		print_border (
3483 			context, 0.0, width, 0.0, header_size,
3484 			1.0, 0.9);
3485 		print_text (
3486 			context, font, title, PANGO_ALIGN_CENTER, 0.0, width,
3487 			0.1, header_size - 0.1);
3488 		pango_font_description_free (font);
3489 	}
3490 
3491 	top += header_size + 30;
3492 
3493 	/* Summary */
3494 	font = get_font_for_size (18, PANGO_WEIGHT_BOLD);
3495 	e_cal_component_get_summary (comp, &text);
3496 	summary_string = g_strdup_printf (_("Summary: %s"), text.value);
3497 	top = bound_text (
3498 		context, font, summary_string, -1, 0.0, top, width,
3499 		height, FALSE, &page_start, &pages);
3500 
3501 	g_free (summary_string);
3502 
3503 	/* Location */
3504 	e_cal_component_get_location (comp, &location);
3505 	if (location && location[0]) {
3506 		location_string = g_strdup_printf (
3507 			_("Location: %s"),
3508 			location);
3509 		top = bound_text (
3510 			context, font, location_string, -1, 0.0,
3511 			top + 3, width, height, FALSE, &page_start, &pages);
3512 		g_free (location_string);
3513 	}
3514 
3515 	/* Date information */
3516 	if (page_nr == 0)
3517 		print_date_label (
3518 			context, comp, client,
3519 			pci->zone, pci->use_24_hour_format,
3520 			0.0, width, top + 3, top + 15);
3521 	top += 20;
3522 
3523 	/* Attendees */
3524 	if ((page_nr == 0) && e_cal_component_has_attendees (comp)) {
3525 		top = bound_text (
3526 			context, font, _("Attendees: "), -1, 0.0,
3527 			top, width, height, FALSE, &page_start, &pages);
3528 		pango_font_description_free (font);
3529 		font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
3530 		top = print_attendees (
3531 			context, font, cr, 0.0, width,
3532 			top, height, comp, page_nr, &pages);
3533 		top += get_font_size (font) - 6;
3534 	}
3535 
3536 	pango_font_description_free (font);
3537 
3538 	font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
3539 
3540 	/* For a VTODO we print the Status, Priority, % Complete and URL. */
3541 	if (vtype == E_CAL_COMPONENT_TODO) {
3542 		icalproperty_status status;
3543 		const gchar *status_string = NULL;
3544 		gint *percent;
3545 		gint *priority;
3546 		const gchar *url;
3547 
3548 		/* Status */
3549 		e_cal_component_get_status (comp, &status);
3550 		if (status != ICAL_STATUS_NONE) {
3551 			switch (status) {
3552 			case ICAL_STATUS_NEEDSACTION:
3553 				status_string = _("Not Started");
3554 				break;
3555 			case ICAL_STATUS_INPROCESS:
3556 				status_string = _("In Progress");
3557 				break;
3558 			case ICAL_STATUS_COMPLETED:
3559 				status_string = _("Completed");
3560 				break;
3561 			case ICAL_STATUS_CANCELLED:
3562 				status_string = _("Canceled");
3563 				break;
3564 			default:
3565 				break;
3566 			}
3567 
3568 			if (status_string) {
3569 				gchar *status_text = g_strdup_printf (
3570 					_("Status: %s"),
3571 					status_string);
3572 				top = bound_text (
3573 					context, font, status_text, -1,
3574 					0.0, top, width, height, FALSE, &page_start, &pages);
3575 				top += get_font_size (font) - 6;
3576 				g_free (status_text);
3577 			}
3578 		}
3579 
3580 		/* Priority */
3581 		e_cal_component_get_priority (comp, &priority);
3582 		if (priority && *priority >= 0) {
3583 			gchar *pri_text;
3584 
3585 			pri_text = g_strdup_printf (
3586 				_("Priority: %s"),
3587 				e_cal_util_priority_to_string (*priority));
3588 			top = bound_text (
3589 				context, font, pri_text, -1,
3590 				0.0, top, width, height, FALSE,
3591 				&page_start, &pages);
3592 			top += get_font_size (font) - 6;
3593 			g_free (pri_text);
3594 		}
3595 
3596 		if (priority)
3597 			e_cal_component_free_priority (priority);
3598 
3599 		/* Percent Complete */
3600 		e_cal_component_get_percent (comp, &percent);
3601 		if (percent) {
3602 			gchar *percent_string;
3603 
3604 			percent_string = g_strdup_printf (_("Percent Complete: %i"), *percent);
3605 			e_cal_component_free_percent (percent);
3606 
3607 			top = bound_text (
3608 				context, font, percent_string, -1,
3609 				0.0, top, width, height, FALSE, &page_start, &pages);
3610 			top += get_font_size (font) - 6;
3611 		}
3612 
3613 		/* URL */
3614 		e_cal_component_get_url (comp, &url);
3615 		if (url && url[0]) {
3616 			gchar *url_string;
3617 
3618 			url_string = g_strdup_printf (_("URL: %s"), url);
3619 
3620 			top = bound_text (
3621 				context, font, url_string, -1,
3622 				0.0, top, width, height, TRUE, &page_start, &pages);
3623 			top += get_font_size (font) - 6;
3624 			g_free (url_string);
3625 		}
3626 	}
3627 
3628 	/* Categories */
3629 	e_cal_component_get_categories (comp, &categories);
3630 	if (categories && categories[0]) {
3631 		categories_string = g_strdup_printf (
3632 			_("Categories: %s"), categories);
3633 		top = bound_text (
3634 			context, font, categories_string, -1,
3635 			0.0, top, width, height, TRUE, &page_start, &pages);
3636 		top += get_font_size (font) - 6;
3637 		g_free (categories_string);
3638 	}
3639 
3640 	/* Contacts */
3641 	e_cal_component_get_contact_list (comp, &contact_list);
3642 	if (contact_list) {
3643 		GString *contacts = g_string_new (_("Contacts: "));
3644 		for (elem = contact_list; elem; elem = elem->next) {
3645 			ECalComponentText *t = elem->data;
3646 			/* Put a comma between contacts. */
3647 			if (elem != contact_list)
3648 				g_string_append (contacts, ", ");
3649 			g_string_append (contacts, t->value);
3650 		}
3651 		e_cal_component_free_text_list (contact_list);
3652 
3653 		top = bound_text (
3654 			context, font, contacts->str, -1,
3655 			0.0, top, width, height, TRUE, &page_start, &pages);
3656 		top += get_font_size (font) - 6;
3657 		g_string_free (contacts, TRUE);
3658 	}
3659 	top += 16;
3660 
3661 	/* Description */
3662 	e_cal_component_get_description_list (comp, &desc);
3663 	for (l = desc; l != NULL; l = l->next) {
3664 		ECalComponentText *ptext = l->data;
3665 		const gchar *line, *next_line;
3666 
3667 		for (line = ptext->value; line != NULL; line = next_line) {
3668 			next_line = strchr (line, '\n');
3669 
3670 			top = bound_text (
3671 				context, font, line,
3672 				next_line ? next_line - line : -1,
3673 				0.0, top + 3, width, height, TRUE,
3674 				&page_start, &pages);
3675 
3676 			if (next_line) {
3677 				next_line++;
3678 				if (!*next_line)
3679 					next_line = NULL;
3680 			}
3681 		}
3682 
3683 	}
3684 
3685 	e_cal_component_free_text_list (desc);
3686 	pango_font_description_free (font);
3687 
3688 	return pages;
3689 }
3690 
3691 static void
3692 print_comp_draw_page (GtkPrintOperation *operation,
3693                       GtkPrintContext *context,
3694                       gint page_nr,
3695                       PrintCompItem *pci)
3696 {
3697 	print_comp_draw_real (operation, context, page_nr, pci);
3698 }
3699 
3700 static void
3701 print_comp_begin_print (GtkPrintOperation *operation,
3702                         GtkPrintContext *context,
3703                         PrintCompItem *pci)
3704 {
3705 	gint pages;
3706 
3707 	pages = print_comp_draw_real (operation, context, -1, pci);
3708 
3709 	gtk_print_operation_set_n_pages (operation, pages);
3710 }
3711 
3712 void
3713 print_comp (ECalComponent *comp,
3714             ECalClient *cal_client,
3715             icaltimezone *zone,
3716             gboolean use_24_hour_format,
3717             GtkPrintOperationAction action)
3718 {
3719 	GtkPrintOperation *operation;
3720 	PrintCompItem pci;
3721 
3722 	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
3723 
3724 	pci.comp = comp;
3725 	pci.client = cal_client;
3726 	pci.zone = zone;
3727 	pci.use_24_hour_format = use_24_hour_format;
3728 
3729 	operation = e_print_operation_new ();
3730 	gtk_print_operation_set_n_pages (operation, 1);
3731 
3732 	g_signal_connect (
3733 		operation, "begin-print",
3734 		G_CALLBACK (print_comp_begin_print), &pci);
3735 
3736 	g_signal_connect (
3737 		operation, "draw-page",
3738 		G_CALLBACK (print_comp_draw_page), &pci);
3739 
3740 	gtk_print_operation_run (operation, action, NULL, NULL);
3741 
3742 	g_object_unref (operation);
3743 }
3744 
3745 static void
3746 print_title (GtkPrintContext *context,
3747              const gchar *text,
3748              gdouble page_width)
3749 {
3750 	PangoFontDescription *desc;
3751 	PangoLayout *layout;
3752 	cairo_t *cr;
3753 
3754 	cr = gtk_print_context_get_cairo_context (context);
3755 
3756 	desc = pango_font_description_from_string (FONT_FAMILY " Bold 18");
3757 
3758 	layout = gtk_print_context_create_pango_layout (context);
3759 	pango_layout_set_text (layout, text, -1);
3760 	pango_layout_set_font_description (layout, desc);
3761 	pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
3762 	pango_layout_set_width (layout, pango_units_from_double (page_width));
3763 
3764 	cairo_save (cr);
3765 
3766 	cairo_move_to (cr, 0.0, 0.0);
3767 	pango_cairo_show_layout (cr, layout);
3768 	cairo_translate (cr, 0.0, 18);
3769 	cairo_save (cr);
3770 	cairo_restore (cr);
3771 
3772 	g_object_unref (layout);
3773 
3774 	pango_font_description_free (desc);
3775 }
3776 
3777 struct print_opts {
3778   EPrintable *printable;
3779   const gchar *print_header;
3780 };
3781 
3782 static void
3783 print_table_draw_page (GtkPrintOperation *operation,
3784                        GtkPrintContext *context,
3785                        gint page_nr,
3786                        struct print_opts *opts)
3787 {
3788 	GtkPageSetup *setup;
3789 	gdouble width;
3790 
3791 	setup = gtk_print_context_get_page_setup (context);
3792 
3793 	width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
3794 
3795 	do {
3796 		/* TODO Allow the user to customize the title. */
3797 		print_title (context, opts->print_header, width);
3798 
3799 		if (e_printable_data_left (opts->printable))
3800 			e_printable_print_page (
3801 				opts->printable, context, width, 24, TRUE);
3802 
3803 	} while (e_printable_data_left (opts->printable));
3804 
3805 	g_free (opts);
3806 }
3807 
3808 void
3809 print_table (ETable *table,
3810              const gchar *dialog_title,
3811              const gchar *print_header,
3812              GtkPrintOperationAction action)
3813 {
3814 	GtkPrintOperation *operation;
3815 	EPrintable *printable;
3816 	struct print_opts *opts;
3817 
3818 	printable = e_table_get_printable (table);
3819 	g_object_ref_sink (printable);
3820 	e_printable_reset (printable);
3821 
3822 	operation = e_print_operation_new ();
3823 	gtk_print_operation_set_n_pages (operation, 1);
3824 
3825 	opts = g_malloc (sizeof (struct print_opts));
3826 	opts->printable = printable;
3827 	opts->print_header = print_header;
3828 
3829 	g_signal_connect (
3830 		operation, "draw_page",
3831 		G_CALLBACK (print_table_draw_page), opts);
3832 
3833 	gtk_print_operation_run (operation, action, NULL, NULL);
3834 
3835 	g_object_unref (operation);
3836 }