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