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