3749 lines
108 KiB
C
3749 lines
108 KiB
C
/*
|
|
* Evolution calendar - Print support
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with the program; if not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
*
|
|
* Authors:
|
|
* Michael Zucchi <notzed@ximian.com>
|
|
* Federico Mena-Quintero <federico@ximian.com>
|
|
* Damon Chaplin <damon@ximian.com>
|
|
*
|
|
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <gtk/gtk.h>
|
|
#include <glib/gi18n.h>
|
|
|
|
#include <e-util/e-util.h>
|
|
#include <e-util/e-print.h>
|
|
#include "e-cal-model.h"
|
|
#include "e-day-view.h"
|
|
#include "e-day-view-layout.h"
|
|
#include "e-week-view.h"
|
|
#include "e-week-view-layout.h"
|
|
#include "e-task-table.h"
|
|
#include "gnome-cal.h"
|
|
#include "print.h"
|
|
|
|
#include "art/jump.xpm"
|
|
|
|
typedef struct PrintCompItem PrintCompItem;
|
|
typedef struct PrintCalItem PrintCalItem;
|
|
|
|
struct PrintCompItem {
|
|
ECalClient *client;
|
|
ECalComponent *comp;
|
|
icaltimezone *zone;
|
|
gboolean use_24_hour_format;
|
|
};
|
|
|
|
struct PrintCalItem {
|
|
GnomeCalendar *gcal;
|
|
time_t start;
|
|
};
|
|
|
|
static gdouble
|
|
evo_calendar_print_renderer_get_width (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
const gchar *text)
|
|
{
|
|
PangoLayout *layout;
|
|
gint layout_width, layout_height;
|
|
|
|
layout = gtk_print_context_create_pango_layout (context);
|
|
|
|
pango_layout_set_font_description (layout, font);
|
|
pango_layout_set_text (layout, text, -1);
|
|
pango_layout_set_indent (layout, 0);
|
|
pango_layout_get_size (layout, &layout_width, &layout_height);
|
|
|
|
g_object_unref (layout);
|
|
|
|
return pango_units_to_double (layout_width);
|
|
}
|
|
|
|
static gdouble
|
|
evo_calendar_print_renderer_get_height (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
const gchar *text)
|
|
{
|
|
PangoLayout *layout;
|
|
gint layout_width, layout_height;
|
|
|
|
layout = gtk_print_context_create_pango_layout (context);
|
|
|
|
pango_layout_set_font_description (layout, font);
|
|
pango_layout_set_text (layout, text, -1);
|
|
pango_layout_set_indent (layout, 0);
|
|
pango_layout_get_size (layout, &layout_width, &layout_height);
|
|
|
|
g_object_unref (layout);
|
|
|
|
return pango_units_to_double (layout_height);
|
|
}
|
|
|
|
static gdouble
|
|
get_font_size (PangoFontDescription *font)
|
|
{
|
|
g_return_val_if_fail (font, 0.0);
|
|
|
|
return pango_units_to_double (pango_font_description_get_size (font));
|
|
}
|
|
|
|
static gint
|
|
get_day_view_time_divisions (void)
|
|
{
|
|
GSettings *settings;
|
|
gint time_divisions;
|
|
|
|
settings = g_settings_new ("org.gnome.evolution.calendar");
|
|
|
|
time_divisions = g_settings_get_int (settings, "time-divisions");
|
|
if (time_divisions < 5 || time_divisions > 30)
|
|
time_divisions = 30;
|
|
|
|
g_object_unref (settings);
|
|
|
|
return time_divisions;
|
|
}
|
|
|
|
/*
|
|
* Note that most dimensions are in points (1/72 of an inch) since that is
|
|
* what gnome-print uses.
|
|
*/
|
|
|
|
/* GtkHTML prints using a fixed margin. It has code to get the margins from
|
|
* gnome-print keys, but it's commented out. The corresponding code here
|
|
* doesn't seem to work either (getting zero margins), so we adopt
|
|
* gtkhtml's cheat. */
|
|
|
|
#define TEMP_MARGIN .05
|
|
|
|
/* The fonts to use */
|
|
#define FONT_FAMILY "Sans"
|
|
|
|
/* The font size to use for normal text. */
|
|
#define DAY_NORMAL_FONT_SIZE 12
|
|
#define WEEK_NORMAL_FONT_SIZE 12
|
|
#define MONTH_NORMAL_FONT_SIZE 8
|
|
#define WEEK_EVENT_FONT_SIZE 9
|
|
#define WEEK_SMALL_FONT_SIZE 8
|
|
|
|
/* The height of the header bar across the top of the Day, Week & Month views,
|
|
* which contains the dates shown and the 2 small calendar months. */
|
|
#define HEADER_HEIGHT 80
|
|
|
|
/* The width of the small calendar months, the space from the right edge of
|
|
* the header rectangle, and the space between the months. */
|
|
#define MIN_SMALL_MONTH_WIDTH 120
|
|
#define SMALL_MONTH_PAD 5
|
|
#define SMALL_MONTH_SPACING 20
|
|
|
|
/* The minimum number of rows we leave space for for the long events in the
|
|
* day view. */
|
|
#define DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY 2
|
|
|
|
/* The row height for long events in the day view. */
|
|
#define DAY_VIEW_ROW_HEIGHT 14
|
|
|
|
#define CALC_DAY_VIEW_ROWS(divis) ((60 / divis) * 24)
|
|
|
|
/* The width of the column with all the times in it. */
|
|
#define DAY_VIEW_TIME_COLUMN_WIDTH 36
|
|
|
|
/* The space on the right of each event. */
|
|
#define DAY_VIEW_EVENT_X_PAD 8
|
|
|
|
/* Allowance for small errors in floating point comparisons. */
|
|
#define EPSILON 0.01
|
|
|
|
/* The weird month of September 1752, where 3 Sep through 13 Sep were
|
|
* eliminated due to the Gregorian reformation. */
|
|
static const gint sept_1752[42] = {
|
|
0, 0, 1, 2, 14, 15, 16,
|
|
17, 18, 19, 20, 21, 22, 23,
|
|
24, 25, 26, 27, 28, 29, 30,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
#define SEPT_1752_START 2 /* Start day within month */
|
|
#define SEPT_1752_END 20 /* End day within month */
|
|
|
|
struct pdinfo
|
|
{
|
|
gint days_shown;
|
|
time_t day_starts[E_DAY_VIEW_MAX_DAYS + 1];
|
|
|
|
GArray *long_events;
|
|
GArray *events[E_DAY_VIEW_MAX_DAYS];
|
|
|
|
gint start_hour;
|
|
gint end_hour;
|
|
gint start_minute_offset;
|
|
gint end_minute_offset;
|
|
gint rows;
|
|
gint mins_per_row;
|
|
guint8 cols_per_row[CALC_DAY_VIEW_ROWS (1)];
|
|
gboolean use_24_hour_format;
|
|
icaltimezone *zone;
|
|
};
|
|
|
|
struct psinfo
|
|
{
|
|
gint days_shown;
|
|
time_t day_starts[E_WEEK_VIEW_MAX_WEEKS * 7 + 1];
|
|
|
|
GArray *events;
|
|
|
|
gint rows_per_cell;
|
|
gint rows_per_compressed_cell;
|
|
gint display_start_weekday;
|
|
gboolean multi_week_view;
|
|
gint weeks_shown;
|
|
gint month;
|
|
gboolean compress_weekend;
|
|
gboolean use_24_hour_format;
|
|
gdouble row_height;
|
|
gdouble header_row_height;
|
|
icaltimezone *zone;
|
|
};
|
|
|
|
/* Convenience function to help the transition to timezone functions.
|
|
* It converts a time_t to a struct tm. */
|
|
static void
|
|
convert_timet_to_struct_tm (time_t time,
|
|
icaltimezone *zone,
|
|
struct tm *tm)
|
|
{
|
|
struct icaltimetype tt;
|
|
|
|
/* Convert it to an icaltimetype. */
|
|
tt = icaltime_from_timet_with_zone (time, FALSE, zone);
|
|
|
|
/* Fill in the struct tm. */
|
|
tm->tm_year = tt.year - 1900;
|
|
tm->tm_mon = tt.month - 1;
|
|
tm->tm_mday = tt.day;
|
|
tm->tm_hour = tt.hour;
|
|
tm->tm_min = tt.minute;
|
|
tm->tm_sec = tt.second;
|
|
tm->tm_isdst = tt.is_daylight;
|
|
|
|
tm->tm_wday = time_day_of_week (tt.day, tt.month - 1, tt.year);
|
|
}
|
|
|
|
/* Fills the 42-element days array with the day numbers for the specified
|
|
* month. Slots outside the bounds of the month are filled with zeros.
|
|
* The starting and ending indexes of the days are returned in the start
|
|
* and end arguments. */
|
|
static void
|
|
build_month (ECalModel *model,
|
|
gint month,
|
|
gint year,
|
|
gint *days,
|
|
gint *start,
|
|
gint *end)
|
|
{
|
|
gint i;
|
|
gint d_month, d_week, week_start_day;
|
|
|
|
/* Note that months are zero-based, so September is month 8 */
|
|
|
|
if ((year == 1752) && (month == 8)) {
|
|
memcpy (days, sept_1752, 42 * sizeof (gint));
|
|
|
|
if (start)
|
|
*start = SEPT_1752_START;
|
|
|
|
if (end)
|
|
*end = SEPT_1752_END;
|
|
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < 42; i++)
|
|
days[i] = 0;
|
|
|
|
d_month = time_days_in_month (year, month);
|
|
/* Get the start weekday in the month, 0=Sun to 6=Sat. */
|
|
d_week = time_day_of_week (1, month, year);
|
|
|
|
/* Get the configuration setting specifying which weekday we put on
|
|
* the left column, 0=Sun to 6=Sat. */
|
|
week_start_day = e_cal_model_get_week_start_day (model);
|
|
|
|
/* Figure out which square we want to put the 1 in. */
|
|
d_week = (d_week + 7 - week_start_day) % 7;
|
|
|
|
for (i = 0; i < d_month; i++)
|
|
days[d_week + i] = i + 1;
|
|
|
|
if (start)
|
|
*start = d_week;
|
|
|
|
if (end)
|
|
*end = d_week + d_month - 1;
|
|
}
|
|
|
|
static PangoFontDescription *
|
|
get_font_for_size (gdouble height,
|
|
PangoWeight weight)
|
|
{
|
|
PangoFontDescription *desc;
|
|
gint size;
|
|
|
|
#define MAGIC_SCALE_FACTOR (0.86)
|
|
size = pango_units_from_double (height * MAGIC_SCALE_FACTOR);
|
|
|
|
desc = pango_font_description_new ();
|
|
pango_font_description_set_size (desc, size);
|
|
pango_font_description_set_weight (desc, weight);
|
|
pango_font_description_set_family_static (desc, FONT_FAMILY);
|
|
|
|
return desc;
|
|
}
|
|
|
|
/* Prints a rectangle, with or without a border, filled or outline, and
|
|
* possibly with triangular arrows at one or both horizontal edges.
|
|
* width = width of border, -ve means no border.
|
|
* red,green,blue = bgcolor to fill, -ve means no fill.
|
|
* left_triangle_width, right_triangle_width = width from edge of rectangle to
|
|
* point of triangle, or -ve for no triangle. */
|
|
static void
|
|
print_border_with_triangles (GtkPrintContext *pc,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble y2,
|
|
gdouble line_width,
|
|
gdouble red,
|
|
gdouble green,
|
|
gdouble blue,
|
|
gdouble left_triangle_width,
|
|
gdouble right_triangle_width)
|
|
{
|
|
cairo_t *cr = gtk_print_context_get_cairo_context (pc);
|
|
|
|
cairo_save (cr);
|
|
|
|
/* Fill in the interior of the rectangle, if desired. */
|
|
if (red >= -EPSILON && green >= -EPSILON && blue >= -EPSILON) {
|
|
|
|
cairo_move_to (cr, x1, y1);
|
|
|
|
if (left_triangle_width > 0.0)
|
|
cairo_line_to (cr, x1 - left_triangle_width,
|
|
(y1 + y2) / 2);
|
|
|
|
cairo_line_to (cr, x1, y2);
|
|
cairo_line_to (cr, x2, y2);
|
|
|
|
if (right_triangle_width > 0.0)
|
|
cairo_line_to (cr, x2 + right_triangle_width, (y1 + y2) / 2);
|
|
|
|
cairo_line_to (cr, x2, y1);
|
|
cairo_close_path (cr);
|
|
cairo_set_source_rgb (cr, red, green, blue);
|
|
cairo_fill (cr);
|
|
cairo_restore (cr);
|
|
cairo_save (cr);
|
|
}
|
|
|
|
/* Draw the outline, if desired. */
|
|
if (line_width >= EPSILON) {
|
|
|
|
cr = gtk_print_context_get_cairo_context (pc);
|
|
cairo_move_to (cr, x1, y1);
|
|
|
|
if (left_triangle_width > 0.0)
|
|
cairo_line_to (cr, x1 - left_triangle_width,
|
|
(y1 + y2) / 2);
|
|
|
|
cairo_line_to (cr, x1, y2);
|
|
cairo_line_to (cr, x2, y2);
|
|
|
|
if (right_triangle_width > 0.0)
|
|
cairo_line_to (cr, x2 + right_triangle_width,
|
|
(y1 + y2) / 2);
|
|
|
|
cairo_line_to (cr, x2, y1);
|
|
cairo_close_path (cr);
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
cairo_set_line_width (cr, line_width);
|
|
cairo_stroke (cr);
|
|
}
|
|
|
|
cairo_restore (cr);
|
|
}
|
|
|
|
/* Prints a rectangle, with or without a border, and filled or outline.
|
|
* width = width of border, -ve means no border.
|
|
* fillcolor = shade of fill, -ve means no fill. */
|
|
static void
|
|
print_border_rgb (GtkPrintContext *pc,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble y2,
|
|
gdouble line_width,
|
|
gdouble red,
|
|
gdouble green,
|
|
gdouble blue)
|
|
{
|
|
print_border_with_triangles (
|
|
pc, x1, x2, y1, y2, line_width,
|
|
red, green, blue, -1.0, -1.0);
|
|
}
|
|
|
|
static void
|
|
print_border (GtkPrintContext *pc,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble y2,
|
|
gdouble line_width,
|
|
gdouble fillcolor)
|
|
{
|
|
print_border_rgb (
|
|
pc, x1, x2, y1, y2, line_width,
|
|
fillcolor, fillcolor, fillcolor);
|
|
}
|
|
|
|
static void
|
|
print_rectangle (GtkPrintContext *context,
|
|
gdouble x,
|
|
gdouble y,
|
|
gdouble width,
|
|
gdouble height,
|
|
gdouble red,
|
|
gdouble green,
|
|
gdouble blue)
|
|
{
|
|
cairo_t *cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
cairo_save (cr);
|
|
|
|
cairo_rectangle (cr, x, y, width, height);
|
|
cairo_set_source_rgb (cr, red, green, blue);
|
|
cairo_fill (cr);
|
|
|
|
cairo_restore (cr);
|
|
}
|
|
|
|
/* Recreate the layout by shrinking the text string if we have a line that's
|
|
* too high.
|
|
*/
|
|
static PangoLayout *
|
|
shrink_text_to_line (PangoLayout *layout,
|
|
gint layout_width,
|
|
gint layout_height,
|
|
GtkPrintContext *context,
|
|
PangoFontDescription *desc,
|
|
const gchar *text,
|
|
PangoAlignment alignment,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble y2)
|
|
{
|
|
gint new_length;
|
|
|
|
if (layout_width == 0 || x2 - x1 < EPSILON)
|
|
return layout; /* Do nothing */
|
|
|
|
new_length = (gint) floor (pango_units_from_double (x2 - x1) /
|
|
(gdouble) layout_width * (gdouble) strlen (text));
|
|
|
|
if (new_length < strlen(text)) {
|
|
g_object_unref (layout); /* Destroy old layout */
|
|
layout = gtk_print_context_create_pango_layout (context);
|
|
|
|
pango_layout_set_font_description (layout, desc);
|
|
pango_layout_set_alignment (layout, alignment);
|
|
pango_layout_set_text (layout, text, new_length);
|
|
}
|
|
|
|
return layout;
|
|
}
|
|
|
|
/* Prints 1 line of aligned text in a box. It is centered vertically, and
|
|
* the horizontal alignment can be either PANGO_ALIGN_LEFT, PANGO_ALIGN_RIGHT,
|
|
* or PANGO_ALIGN_CENTER. Text is truncated if too long for cell. */
|
|
static gdouble
|
|
print_text_line (GtkPrintContext *context,
|
|
PangoFontDescription *desc,
|
|
const gchar *text,
|
|
PangoAlignment alignment,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble y2,
|
|
gboolean shrink)
|
|
{
|
|
PangoLayout *layout;
|
|
gint layout_width, layout_height;
|
|
cairo_t *cr;
|
|
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
layout = gtk_print_context_create_pango_layout (context);
|
|
|
|
pango_layout_set_font_description (layout, desc);
|
|
pango_layout_set_alignment (layout, alignment);
|
|
pango_layout_set_text (layout, text, -1);
|
|
|
|
/* Grab the width before expanding the layout. */
|
|
pango_layout_get_size (layout, &layout_width, &layout_height);
|
|
|
|
if (shrink && layout_width > pango_units_from_double (x2 - x1)) /* Too wide */
|
|
layout = shrink_text_to_line (layout, layout_width, layout_height,
|
|
context, desc, text, alignment,
|
|
x1, x2, y1, y2);
|
|
|
|
pango_layout_set_width (layout, pango_units_from_double (x2 - x1));
|
|
|
|
cairo_save (cr);
|
|
|
|
/* Set a clipping rectangle. */
|
|
cairo_move_to (cr, x1, y1);
|
|
cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
|
|
cairo_clip (cr);
|
|
|
|
cairo_new_path (cr);
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
|
|
cairo_move_to (cr, x1, y1);
|
|
pango_cairo_show_layout (cr, layout);
|
|
|
|
cairo_stroke (cr);
|
|
|
|
cairo_restore (cr);
|
|
|
|
g_object_unref (layout);
|
|
|
|
return pango_units_to_double (layout_width);
|
|
}
|
|
|
|
/* Prints 1 or more lines of aligned text in a box. It is centered vertically, and
|
|
the horizontal alignment can be either PANGO_ALIGN_LEFT, PANGO_ALIGN_RIGHT,
|
|
or PANGO_ALIGN_CENTER. */
|
|
static double
|
|
print_text (GtkPrintContext *context,
|
|
PangoFontDescription *desc,
|
|
const gchar *text,
|
|
PangoAlignment alignment,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble y2)
|
|
{
|
|
return print_text_line (context, desc,
|
|
text, alignment,
|
|
x1, x2, y1, y2, FALSE);
|
|
}
|
|
|
|
/* gets/frees the font for you, as a bold font */
|
|
static gdouble
|
|
print_text_size_bold (GtkPrintContext *context,
|
|
const gchar *text,
|
|
PangoAlignment alignment,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble y2)
|
|
{
|
|
PangoFontDescription *font;
|
|
gdouble w;
|
|
|
|
font = get_font_for_size (ABS (y2 - y1) * 0.5, PANGO_WEIGHT_BOLD);
|
|
w = print_text (context, font, text, alignment, x1, x2, y1, y2);
|
|
pango_font_description_free (font);
|
|
|
|
return w;
|
|
}
|
|
|
|
/* gets/frees the font for you, as a bold font - absolute size parameter */
|
|
static double
|
|
print_text_abs_bold (GtkPrintContext *context,
|
|
const gchar *text,
|
|
gdouble font_size,
|
|
PangoAlignment alignment,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble y2)
|
|
{
|
|
PangoFontDescription *font;
|
|
gdouble w;
|
|
|
|
font = get_font_for_size (font_size, PANGO_WEIGHT_BOLD);
|
|
w = print_text_line (context, font, text, alignment, x1, x2, y1, y2, TRUE);
|
|
pango_font_description_free (font);
|
|
|
|
return w;
|
|
}
|
|
|
|
static void
|
|
titled_box (GtkPrintContext *context,
|
|
const gchar *text,
|
|
PangoFontDescription *font,
|
|
PangoAlignment alignment,
|
|
gdouble *x1,
|
|
gdouble *y1,
|
|
gdouble *x2,
|
|
gdouble *y2,
|
|
gdouble linewidth)
|
|
{
|
|
gdouble size;
|
|
|
|
size = evo_calendar_print_renderer_get_height (context, font, text);
|
|
print_border (context, *x1, *x2, *y1, *y1 + size + 2, linewidth, 0.9);
|
|
print_border (context, *x1, *x2, *y1 + size + 2, *y2, linewidth, -1.0);
|
|
*x1 += 2;
|
|
*x2 -= 2;
|
|
*y2 += 2;
|
|
print_text (context, font, text, alignment, *x1, *x2, *y1 + 1.0, *y1 + size);
|
|
*y1 += size + 2;
|
|
}
|
|
|
|
static gboolean
|
|
get_show_week_numbers (void)
|
|
{
|
|
EShell *shell;
|
|
EShellSettings *shell_settings;
|
|
|
|
shell = e_shell_get_default ();
|
|
shell_settings = e_shell_get_shell_settings (shell);
|
|
|
|
return e_shell_settings_get_boolean (
|
|
shell_settings, "cal-show-week-numbers");
|
|
}
|
|
|
|
enum datefmt {
|
|
DATE_MONTH = 1 << 0,
|
|
DATE_DAY = 1 << 1,
|
|
DATE_DAYNAME = 1 << 2,
|
|
DATE_YEAR = 1 << 3
|
|
};
|
|
|
|
static const gchar *days[] = {
|
|
N_("1st"), N_("2nd"), N_("3rd"), N_("4th"), N_("5th"),
|
|
N_("6th"), N_("7th"), N_("8th"), N_("9th"), N_("10th"),
|
|
N_("11th"), N_("12th"), N_("13th"), N_("14th"), N_("15th"),
|
|
N_("16th"), N_("17th"), N_("18th"), N_("19th"), N_("20th"),
|
|
N_("21st"), N_("22nd"), N_("23rd"), N_("24th"), N_("25th"),
|
|
N_("26th"), N_("27th"), N_("28th"), N_("29th"), N_("30th"),
|
|
N_("31st")
|
|
};
|
|
|
|
/*
|
|
format the date 'nicely' and consistently for various headers
|
|
*/
|
|
static gchar *
|
|
format_date (struct tm *tm,
|
|
gint flags,
|
|
gchar *buffer,
|
|
gint bufflen)
|
|
{
|
|
gchar fmt[64];
|
|
|
|
fmt[0] = 0;
|
|
if (flags & DATE_DAYNAME) {
|
|
strcat (fmt, "%A");
|
|
}
|
|
if (flags & DATE_DAY) {
|
|
if (flags & DATE_DAYNAME)
|
|
strcat (fmt, " ");
|
|
strcat (fmt, gettext (days[tm->tm_mday - 1]));
|
|
}
|
|
if (flags & DATE_MONTH) {
|
|
if (flags & (DATE_DAY | DATE_DAYNAME))
|
|
strcat (fmt, " ");
|
|
strcat (fmt, "%B");
|
|
if ((flags & (DATE_DAY | DATE_YEAR)) == (DATE_DAY | DATE_YEAR))
|
|
strcat (fmt, ",");
|
|
}
|
|
if (flags & DATE_YEAR) {
|
|
if (flags & (DATE_DAY | DATE_DAYNAME | DATE_MONTH))
|
|
strcat (fmt, " ");
|
|
strcat (fmt, "%Y");
|
|
}
|
|
e_utf8_strftime (buffer, bufflen, fmt, tm);
|
|
buffer[bufflen - 1] = '\0';
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static gboolean
|
|
instance_cb (ECalComponent *comp,
|
|
time_t instance_start,
|
|
time_t instance_end,
|
|
gpointer data)
|
|
{
|
|
|
|
gboolean *found = ((ECalModelGenerateInstancesData *) data)->cb_data;
|
|
|
|
*found = TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
const gchar *daynames[] =
|
|
/* Translators: These are workday abbreviations, e.g. Su=Sunday and Th=thursday */
|
|
{ N_("Su"), N_("Mo"), N_("Tu"), N_("We"),
|
|
N_("Th"), N_("Fr"), N_("Sa") };
|
|
|
|
static gdouble
|
|
calc_small_month_width (GtkPrintContext *context,
|
|
gdouble for_height)
|
|
{
|
|
|
|
PangoFontDescription *font_bold;
|
|
gdouble res = 0.0;
|
|
gint ii;
|
|
|
|
font_bold = get_font_for_size (for_height / 7.4, PANGO_WEIGHT_BOLD);
|
|
res = MAX (evo_calendar_print_renderer_get_width (
|
|
context, font_bold, "23"), res);
|
|
for (ii = 0; ii < 7; ii++) {
|
|
res = MAX (evo_calendar_print_renderer_get_width (
|
|
context, font_bold, _(daynames[ii])), res);
|
|
}
|
|
|
|
pango_font_description_free (font_bold);
|
|
|
|
/* res is max cell width, so multiply it with column
|
|
* count plus some space between columns. */
|
|
res = (res + 1.0) * (7 + (get_show_week_numbers () ? 1 : 0)) - 1.0;
|
|
|
|
if (res < MIN_SMALL_MONTH_WIDTH)
|
|
res = MIN_SMALL_MONTH_WIDTH;
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
print out the month small, embolden any days with events.
|
|
*/
|
|
static void
|
|
print_month_small (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t month,
|
|
gdouble x1,
|
|
gdouble y1,
|
|
gdouble x2,
|
|
gdouble y2,
|
|
gint titleflags,
|
|
time_t greystart,
|
|
time_t greyend,
|
|
gint bordertitle)
|
|
{
|
|
icaltimezone *zone;
|
|
PangoFontDescription *font, *font_bold, *font_normal;
|
|
ECalModel *model;
|
|
time_t now, next;
|
|
gint x, y;
|
|
gint days[42];
|
|
gint day, weekday, week_start_day;
|
|
gchar buf[100];
|
|
struct tm tm;
|
|
gdouble font_size;
|
|
gdouble header_size, col_width, row_height, text_xpad, w;
|
|
gdouble cell_top, cell_bottom, cell_left, cell_right, text_right;
|
|
gboolean week_numbers;
|
|
cairo_t *cr;
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
zone = e_cal_model_get_timezone (model);
|
|
|
|
week_numbers = get_show_week_numbers ();
|
|
|
|
/* Print the title, e.g. 'June 2001', in the top 16% of the area. */
|
|
convert_timet_to_struct_tm (month, zone, &tm);
|
|
format_date (&tm, titleflags, buf, 100);
|
|
|
|
header_size = ABS (y2 - y1) * 0.16;
|
|
|
|
font = get_font_for_size (header_size, PANGO_WEIGHT_BOLD);
|
|
if (bordertitle)
|
|
print_border (context, x1, x2, y1, y1 + header_size, 1.0, 0.9);
|
|
print_text (context, font, buf, PANGO_ALIGN_CENTER, x1, x2,
|
|
y1, y1 + header_size);
|
|
pango_font_description_free (font);
|
|
|
|
y1 += header_size;
|
|
col_width = (x2 - x1) / (7 + (week_numbers ? 1 : 0));
|
|
|
|
/* The top row with the day abbreviations gets an extra bit of
|
|
* vertical space around it. */
|
|
row_height = ABS (y2 - y1) / 7.4;
|
|
|
|
/* First we need to calculate a reasonable font size. We start with a
|
|
* rough guess of just under the height of each row. */
|
|
font_size = row_height;
|
|
|
|
/* get month days */
|
|
convert_timet_to_struct_tm (month, zone, &tm);
|
|
build_month (model, tm.tm_mon, tm.tm_year + 1900, days, NULL, NULL);
|
|
|
|
font_normal = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
|
|
font_bold = get_font_for_size (font_size, PANGO_WEIGHT_BOLD);
|
|
|
|
/* Get a reasonable estimate of the largest number we will need,
|
|
* and use it to calculate the offset from the right edge of the
|
|
* cell that we should put the numbers. */
|
|
w = evo_calendar_print_renderer_get_width (context, font_bold, "23");
|
|
text_xpad = (col_width - w) / 2;
|
|
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
|
|
/* Print the abbreviated day names across the top in bold. */
|
|
week_start_day = e_cal_model_get_week_start_day (model);
|
|
weekday = week_start_day;
|
|
for (x = 0; x < 7; x++) {
|
|
print_text (
|
|
context, font_bold,
|
|
_(daynames[weekday]), PANGO_ALIGN_RIGHT,
|
|
x1 + (x + (week_numbers ? 1 : 0)) * col_width,
|
|
x1 + (x + 1 + (week_numbers ? 1 : 0)) * col_width,
|
|
y1, y1 + row_height * 1.4);
|
|
weekday = (weekday + 1) % 7;
|
|
}
|
|
|
|
y1 += row_height * 1.4;
|
|
|
|
now = time_month_begin_with_zone (month, zone);
|
|
for (y = 0; y < 6; y++) {
|
|
|
|
cell_top = y1 + y * row_height;
|
|
cell_bottom = cell_top + row_height;
|
|
|
|
if (week_numbers) {
|
|
cell_left = x1;
|
|
/* We add a 0.05 to make sure the cells meet up with
|
|
* each other. Otherwise you sometimes get lines
|
|
* between them which looks bad. Maybe I'm not using
|
|
* coords in the way gnome-print expects. */
|
|
cell_right = cell_left + col_width + 0.05;
|
|
text_right = cell_right - text_xpad;
|
|
|
|
/* last week can be empty */
|
|
for (x = 0; x < 7; x++) {
|
|
day = days[y * 7 + x];
|
|
if (day != 0)
|
|
break;
|
|
}
|
|
|
|
if (day != 0) {
|
|
time_t week_begin;
|
|
|
|
week_begin = time_week_begin_with_zone (
|
|
now, week_start_day, zone);
|
|
|
|
convert_timet_to_struct_tm (
|
|
week_begin, zone, &tm);
|
|
|
|
/* Month in e_calendar_item_get_week_number
|
|
* is also zero-based. */
|
|
sprintf (
|
|
buf, "%d",
|
|
e_calendar_item_get_week_number (
|
|
NULL, tm.tm_mday, tm.tm_mon,
|
|
tm.tm_year + 1900));
|
|
|
|
print_text (
|
|
context, font_normal,
|
|
buf, PANGO_ALIGN_RIGHT,
|
|
cell_left, text_right,
|
|
cell_top, cell_bottom);
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < 7; x++) {
|
|
|
|
cell_left = x1 + (x + (week_numbers ? 1 : 0)) * col_width;
|
|
/* We add a 0.05 to make sure the cells meet up with
|
|
* each other. Otherwise you sometimes get lines
|
|
* between them which looks bad. Maybe I'm not using
|
|
* coords in the way gnome-print expects. */
|
|
cell_right = cell_left + col_width + 0.05;
|
|
text_right = cell_right - text_xpad;
|
|
|
|
day = days[y * 7 + x];
|
|
if (day != 0) {
|
|
gboolean found = FALSE;
|
|
sprintf (buf, "%d", day);
|
|
|
|
/* this is a slow messy way to do this ... but easy ... */
|
|
e_cal_model_generate_instances_sync (gnome_calendar_get_model (gcal), now,
|
|
time_day_end_with_zone (now, zone),
|
|
instance_cb, &found);
|
|
|
|
font = found ? font_bold : font_normal;
|
|
|
|
next = time_add_day_with_zone (now, 1, zone);
|
|
if ((now >= greystart && now < greyend)
|
|
|| (greystart >= now && greystart < next)) {
|
|
print_border (context,
|
|
cell_left, cell_right,
|
|
cell_top, cell_bottom,
|
|
-1.0, 0.75);
|
|
}
|
|
print_text (context, font, buf, PANGO_ALIGN_RIGHT,
|
|
cell_left, text_right,
|
|
cell_top, cell_bottom);
|
|
|
|
now = next;
|
|
}
|
|
}
|
|
}
|
|
pango_font_description_free (font_normal);
|
|
pango_font_description_free (font_bold);
|
|
}
|
|
|
|
/* wraps text into the print context, not taking up more than its allowed space */
|
|
static gdouble
|
|
bound_text (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
const gchar *text,
|
|
gint len,
|
|
gdouble x1,
|
|
gdouble y1,
|
|
gdouble x2,
|
|
gdouble y2,
|
|
gboolean can_wrap,
|
|
gdouble *last_page_start,
|
|
gint *pages)
|
|
{
|
|
PangoLayout *layout;
|
|
gint layout_width, layout_height;
|
|
cairo_t *cr;
|
|
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
layout = gtk_print_context_create_pango_layout (context);
|
|
|
|
pango_layout_set_font_description (layout, font);
|
|
pango_layout_set_text (layout, text, len);
|
|
pango_layout_set_width (layout, pango_units_from_double (x2 - x1));
|
|
|
|
if (can_wrap)
|
|
pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
|
|
|
|
pango_layout_get_size (layout, &layout_width, &layout_height);
|
|
|
|
if (last_page_start &&
|
|
y1 + pango_units_to_double (layout_height) >
|
|
y2 + (*last_page_start)) {
|
|
/* draw this on new page */
|
|
if (pages)
|
|
*pages = *pages + 1;
|
|
|
|
*last_page_start = *last_page_start + y2;
|
|
y1 = *last_page_start + 10.0;
|
|
}
|
|
|
|
if (!last_page_start || (y1 >= 0.0 && y1 < y2)) {
|
|
cairo_save (cr);
|
|
|
|
/* Set a clipping rectangle. */
|
|
cairo_move_to (cr, x1, y1);
|
|
cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
|
|
cairo_clip (cr);
|
|
cairo_new_path (cr);
|
|
|
|
cairo_move_to (cr, x1, y1);
|
|
pango_cairo_show_layout (cr, layout);
|
|
cairo_stroke (cr);
|
|
|
|
cairo_restore (cr);
|
|
}
|
|
|
|
g_object_unref (layout);
|
|
|
|
return y1 + pango_units_to_double (layout_height);
|
|
}
|
|
|
|
/* Draw the borders, lines, and times down the left of the day view. */
|
|
static void
|
|
print_day_background (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t whence,
|
|
struct pdinfo *pdi,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom)
|
|
{
|
|
ECalModel *model;
|
|
PangoFontDescription *font_hour, *font_minute;
|
|
gdouble yinc, y;
|
|
gdouble width = DAY_VIEW_TIME_COLUMN_WIDTH;
|
|
gdouble font_size, max_font_size, hour_font_size, minute_font_size;
|
|
gchar buf[20];
|
|
const gchar *minute;
|
|
gboolean use_24_hour;
|
|
gint i, hour, row;
|
|
gdouble hour_minute_x, hour_minute_width;
|
|
cairo_t *cr;
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
use_24_hour = e_cal_model_get_use_24_hour_format (model);
|
|
|
|
/* Fill the time column in light-gray. */
|
|
print_border (context, left, left + width, top, bottom, -1.0, 0.9);
|
|
|
|
/* Draw the border around the entire view. */
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
print_border (context, left, right, top, bottom, 1.0, -1.0);
|
|
|
|
/* Draw the vertical line on the right of the time column. */
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_set_line_width (cr, 0.0);
|
|
cairo_move_to (cr, left + width, bottom);
|
|
cairo_line_to (cr, left + width, top);
|
|
cairo_stroke (cr);
|
|
|
|
/* Calculate the row height. */
|
|
if (top > bottom)
|
|
yinc = (top - bottom) / (pdi->end_hour - pdi->start_hour);
|
|
else
|
|
yinc = (bottom - top) / (pdi->end_hour - pdi->start_hour);
|
|
|
|
/* Get the 2 fonts we need. */
|
|
font_size = yinc * 0.6;
|
|
max_font_size = width * 0.45;
|
|
hour_font_size = MIN (font_size, max_font_size);
|
|
font_hour = get_font_for_size (hour_font_size, PANGO_WEIGHT_BOLD);
|
|
|
|
font_size = yinc * 0.33;
|
|
max_font_size = width * 0.2;
|
|
minute_font_size = MIN (font_size, max_font_size);
|
|
font_minute = get_font_for_size (minute_font_size, PANGO_WEIGHT_BOLD);
|
|
hour_minute_width = evo_calendar_print_renderer_get_width (
|
|
context, font_minute, use_24_hour ? "00" : _("am"));
|
|
if (!use_24_hour)
|
|
hour_minute_width =
|
|
MAX (hour_minute_width,
|
|
evo_calendar_print_renderer_get_width (
|
|
context, font_minute, _("pm")));
|
|
|
|
row = 0;
|
|
hour_minute_x = left + width - hour_minute_width - 3;
|
|
for (i = pdi->start_hour; i < pdi->end_hour; i++) {
|
|
y = top + yinc * (row + 1);
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
|
|
if (use_24_hour) {
|
|
hour = i;
|
|
minute = "00";
|
|
} else {
|
|
if (i < 12)
|
|
minute = _("am");
|
|
else
|
|
minute = _("pm");
|
|
|
|
hour = i % 12;
|
|
if (hour == 0)
|
|
hour = 12;
|
|
}
|
|
|
|
/* the hour label/minute */
|
|
sprintf (buf, "%d", hour);
|
|
print_text (context, font_hour, buf, PANGO_ALIGN_RIGHT,
|
|
left, hour_minute_x,
|
|
y - yinc, y - yinc + hour_font_size);
|
|
print_text (context, font_minute, minute, PANGO_ALIGN_LEFT,
|
|
hour_minute_x, left + width - 3,
|
|
y - yinc, y - yinc + minute_font_size);
|
|
|
|
/* Draw the horizontal line between hours, across the entire
|
|
width of the day view. */
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_move_to (cr, left, y);
|
|
cairo_line_to (cr, right, y);
|
|
cairo_set_line_width (cr, 1);
|
|
cairo_stroke (cr);
|
|
|
|
/* Draw the horizontal line for the 1/2-hours, across the
|
|
* entire width except for part of the time column. */
|
|
cairo_move_to (cr, left + width * 0.6, y - yinc / 2);
|
|
cairo_line_to (cr, right, y - yinc / 2);
|
|
cairo_set_line_width (cr, 1);
|
|
cairo_stroke (cr);
|
|
row++;
|
|
}
|
|
|
|
pango_font_description_free (font_hour);
|
|
pango_font_description_free (font_minute);
|
|
}
|
|
|
|
/* This adds one event to the view, adding it to the appropriate array. */
|
|
static gint
|
|
print_day_add_event (ECalModelComponent *comp_data,
|
|
time_t start,
|
|
time_t end,
|
|
icaltimezone *zone,
|
|
gint days_shown,
|
|
time_t *day_starts,
|
|
GArray *long_events,
|
|
GArray **events)
|
|
|
|
{
|
|
EDayViewEvent event;
|
|
gint day, offset;
|
|
struct icaltimetype start_tt, end_tt;
|
|
|
|
#if 0
|
|
g_print ("Day view lower: %s", ctime (&day_starts[0]));
|
|
g_print ("Day view upper: %s", ctime (&day_starts[days_shown]));
|
|
g_print ("Event start: %s", ctime (&start));
|
|
g_print ("Event end : %s\n", ctime (&end));
|
|
#endif
|
|
|
|
/* Check that the event times are valid. */
|
|
g_return_val_if_fail (start <= end, -1);
|
|
g_return_val_if_fail (start < day_starts[days_shown], -1);
|
|
g_return_val_if_fail (end > day_starts[0], -1);
|
|
|
|
start_tt = icaltime_from_timet_with_zone (start, FALSE, zone);
|
|
end_tt = icaltime_from_timet_with_zone (end, FALSE, zone);
|
|
|
|
event.comp_data = comp_data;
|
|
event.start = start;
|
|
event.end = end;
|
|
event.canvas_item = NULL;
|
|
|
|
/* Calculate the start & end minute, relative to the top of the
|
|
* display. */
|
|
/*offset = day_view->first_hour_shown * 60
|
|
+ day_view->first_minute_shown;*/
|
|
offset = 0;
|
|
event.start_minute = start_tt.hour * 60 + start_tt.minute - offset;
|
|
event.end_minute = end_tt.hour * 60 + end_tt.minute - offset;
|
|
|
|
event.start_row_or_col = 0;
|
|
event.num_columns = 0;
|
|
|
|
/* Find out which array to add the event to. */
|
|
for (day = 0; day < days_shown; day++) {
|
|
if (start >= day_starts[day] && end <= day_starts[day + 1]) {
|
|
|
|
/* Special case for when the appointment ends at
|
|
* midnight, i.e. the start of the next day. */
|
|
if (end == day_starts[day + 1]) {
|
|
|
|
/* If the event last the entire day, then we
|
|
* skip it here so it gets added to the top
|
|
* canvas. */
|
|
if (start == day_starts[day])
|
|
break;
|
|
|
|
event.end_minute = 24 * 60;
|
|
}
|
|
g_array_append_val (events[day], event);
|
|
return day;
|
|
}
|
|
}
|
|
|
|
/* The event wasn't within one day so it must be a long event,
|
|
* i.e. shown in the top canvas. */
|
|
g_array_append_val (long_events, event);
|
|
return E_DAY_VIEW_LONG_EVENT;
|
|
}
|
|
|
|
static gboolean
|
|
print_day_details_cb (ECalComponent *comp,
|
|
time_t istart,
|
|
time_t iend,
|
|
gpointer data)
|
|
{
|
|
ECalModelGenerateInstancesData *mdata = (ECalModelGenerateInstancesData *) data;
|
|
struct pdinfo *pdi = (struct pdinfo *) mdata->cb_data;
|
|
|
|
print_day_add_event (mdata->comp_data, istart, iend,
|
|
pdi->zone, pdi->days_shown, pdi->day_starts,
|
|
pdi->long_events, pdi->events);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
free_event_array (GArray *array)
|
|
{
|
|
EDayViewEvent *event;
|
|
gint event_num;
|
|
|
|
for (event_num = 0; event_num < array->len; event_num++) {
|
|
event = &g_array_index (array, EDayViewEvent, event_num);
|
|
if (event->canvas_item)
|
|
g_object_run_dispose (G_OBJECT (event->canvas_item));
|
|
}
|
|
|
|
g_array_set_size (array, 0);
|
|
}
|
|
|
|
static const gchar *
|
|
get_type_as_string (icalparameter_cutype cutype)
|
|
{
|
|
const gchar *res;
|
|
|
|
switch (cutype) {
|
|
case ICAL_CUTYPE_NONE: res = NULL; break;
|
|
case ICAL_CUTYPE_INDIVIDUAL: res = _("Individual"); break;
|
|
case ICAL_CUTYPE_GROUP: res = _("Group"); break;
|
|
case ICAL_CUTYPE_RESOURCE: res = _("Resource"); break;
|
|
case ICAL_CUTYPE_ROOM: res = _("Room"); break;
|
|
default: res = _("Unknown"); break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static const gchar *
|
|
get_role_as_string (icalparameter_role role)
|
|
{
|
|
const gchar *res;
|
|
|
|
switch (role) {
|
|
case ICAL_ROLE_NONE: res = NULL; break;
|
|
case ICAL_ROLE_CHAIR: res = _("Chair"); break;
|
|
case ICAL_ROLE_REQPARTICIPANT: res = _("Required Participant"); break;
|
|
case ICAL_ROLE_OPTPARTICIPANT: res = _("Optional Participant"); break;
|
|
case ICAL_ROLE_NONPARTICIPANT: res = _("Non-Participant"); break;
|
|
default: res = _("Unknown"); break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static gdouble
|
|
print_attendees (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
cairo_t *cr,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom,
|
|
ECalComponent *comp,
|
|
gint page_nr,
|
|
gint *pages)
|
|
{
|
|
GSList *attendees = NULL, *l;
|
|
|
|
g_return_val_if_fail (context != NULL, top);
|
|
g_return_val_if_fail (font != NULL, top);
|
|
g_return_val_if_fail (cr != NULL, top);
|
|
|
|
e_cal_component_get_attendee_list (comp, &attendees);
|
|
|
|
for (l = attendees; l; l = l->next) {
|
|
ECalComponentAttendee *attendee = l->data;
|
|
|
|
if (attendee && attendee->value && *attendee->value) {
|
|
GString *text;
|
|
const gchar *tmp;
|
|
|
|
tmp = get_type_as_string (attendee->cutype);
|
|
text = g_string_new (tmp ? tmp : "");
|
|
|
|
if (tmp)
|
|
g_string_append (text, " ");
|
|
|
|
if (attendee->cn && *attendee->cn)
|
|
g_string_append (text, attendee->cn);
|
|
else {
|
|
/* it's usually in form of "mailto:email@domain" */
|
|
tmp = strchr (attendee->value, ':');
|
|
g_string_append (text, tmp ? tmp + 1 : attendee->value);
|
|
}
|
|
|
|
tmp = get_role_as_string (attendee->role);
|
|
if (tmp) {
|
|
g_string_append (text, " (");
|
|
g_string_append (text, tmp);
|
|
g_string_append (text, ")");
|
|
}
|
|
|
|
if (top > bottom) {
|
|
top = 10.0;
|
|
cairo_show_page (cr);
|
|
}
|
|
|
|
top = bound_text (
|
|
context, font, text->str, -1, left + 40.0,
|
|
top, right, bottom, FALSE, NULL, pages);
|
|
|
|
g_string_free (text, TRUE);
|
|
}
|
|
}
|
|
|
|
e_cal_component_free_attendee_list (attendees);
|
|
|
|
return top;
|
|
}
|
|
|
|
static gchar *
|
|
get_summary_with_location (icalcomponent *icalcomp)
|
|
{
|
|
const gchar *summary, *location;
|
|
gchar *text;
|
|
|
|
g_return_val_if_fail (icalcomp != NULL, NULL);
|
|
|
|
summary = icalcomponent_get_summary (icalcomp);
|
|
if (summary == NULL)
|
|
summary = "";
|
|
|
|
location = icalcomponent_get_location (icalcomp);
|
|
if (location && *location) {
|
|
text = g_strdup_printf ("%s (%s)", summary, location);
|
|
} else {
|
|
text = g_strdup (summary);
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
static void
|
|
print_day_long_event (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom,
|
|
gdouble row_height,
|
|
EDayViewEvent *event,
|
|
struct pdinfo *pdi,
|
|
ECalModel *model)
|
|
{
|
|
gdouble x1, x2, y1, y2;
|
|
gdouble left_triangle_width = -1.0, right_triangle_width = -1.0;
|
|
gchar *text;
|
|
gchar buffer[32];
|
|
struct tm date_tm;
|
|
gdouble red, green, blue;
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return;
|
|
|
|
/* If the event starts before the first day being printed, draw a
|
|
* triangle. (Note that I am assuming we are just showing 1 day at
|
|
* the moment.) */
|
|
if (event->start < pdi->day_starts[0])
|
|
left_triangle_width = 4;
|
|
|
|
/* If the event ends after the last day being printed, draw a
|
|
* triangle. */
|
|
if (event->end > pdi->day_starts[1])
|
|
right_triangle_width = 4;
|
|
|
|
x1 = left + 10;
|
|
x2 = right - 10;
|
|
y1 = top + event->start_row_or_col * row_height + 1;
|
|
y2 = y1 + row_height - 1;
|
|
red = green = blue = 0.95;
|
|
e_cal_model_get_rgb_color_for_component (
|
|
model, event->comp_data, &red, &green, &blue);
|
|
print_border_with_triangles (context, x1, x2, y1, y2, 0.5, red, green, blue,
|
|
left_triangle_width,
|
|
right_triangle_width);
|
|
|
|
/* If the event starts after the first day being printed, we need to
|
|
* print the start time. */
|
|
if (event->start > pdi->day_starts[0]) {
|
|
date_tm.tm_year = 2001;
|
|
date_tm.tm_mon = 0;
|
|
date_tm.tm_mday = 1;
|
|
date_tm.tm_hour = event->start_minute / 60;
|
|
date_tm.tm_min = event->start_minute % 60;
|
|
date_tm.tm_sec = 0;
|
|
date_tm.tm_isdst = -1;
|
|
|
|
e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
|
|
buffer, sizeof (buffer));
|
|
|
|
x1 += 4;
|
|
x1 += print_text (context, font, buffer, PANGO_ALIGN_LEFT, x1, x2, y1, y2);
|
|
}
|
|
|
|
/* If the event ends before the end of the last day being printed,
|
|
* we need to print the end time. */
|
|
if (event->end < pdi->day_starts[1]) {
|
|
date_tm.tm_year = 2001;
|
|
date_tm.tm_mon = 0;
|
|
date_tm.tm_mday = 1;
|
|
date_tm.tm_hour = event->end_minute / 60;
|
|
date_tm.tm_min = event->end_minute % 60;
|
|
date_tm.tm_sec = 0;
|
|
date_tm.tm_isdst = -1;
|
|
|
|
e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
|
|
buffer, sizeof (buffer));
|
|
|
|
x2 -= 4;
|
|
x2 -= print_text (context, font, buffer, PANGO_ALIGN_RIGHT, x1, x2, y1, y2);
|
|
}
|
|
|
|
/* Print the text. */
|
|
text = get_summary_with_location (event->comp_data->icalcomp);
|
|
|
|
x1 += 4;
|
|
x2 -= 4;
|
|
print_text_line (context, font, text, PANGO_ALIGN_CENTER, x1, x2, y1, y2, TRUE);
|
|
|
|
g_free (text);
|
|
}
|
|
|
|
static void
|
|
print_day_event (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom,
|
|
EDayViewEvent *event,
|
|
struct pdinfo *pdi,
|
|
ECalModel *model)
|
|
{
|
|
gdouble x1, x2, y1, y2, col_width, row_height;
|
|
gint start_offset, end_offset, start_row, end_row;
|
|
gchar *text, start_buffer[32], end_buffer[32];
|
|
gboolean display_times = FALSE;
|
|
struct tm date_tm;
|
|
gdouble red, green, blue;
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return;
|
|
|
|
if ((event->start_minute >= pdi->end_minute_offset)
|
|
|| (event->end_minute <= pdi->start_minute_offset))
|
|
return;
|
|
|
|
start_offset = event->start_minute - pdi->start_minute_offset;
|
|
end_offset = event->end_minute - pdi->start_minute_offset;
|
|
|
|
start_row = start_offset / pdi->mins_per_row;
|
|
start_row = MAX (0, start_row);
|
|
end_row = (end_offset - 1) / pdi->mins_per_row;
|
|
end_row = MIN (pdi->rows - 1, end_row);
|
|
col_width = (right - left) /
|
|
pdi->cols_per_row[event->start_minute / pdi->mins_per_row];
|
|
|
|
if (start_offset != start_row * pdi->mins_per_row
|
|
|| end_offset != (end_row + 1) * pdi->mins_per_row)
|
|
display_times = TRUE;
|
|
|
|
x1 = left + event->start_row_or_col * col_width;
|
|
x2 = x1 + event->num_columns * col_width - DAY_VIEW_EVENT_X_PAD;
|
|
|
|
row_height = (bottom - top) / pdi->rows;
|
|
y1 = top + start_row * row_height;
|
|
y2 = top + (end_row + 1) * row_height;
|
|
#if 0
|
|
g_print ("Event: %g,%g %g,%g\n row_height: %g start_row: %i top: %g rows: %i\n",
|
|
x1, y1, x2, y2, row_height, start_row, top, pdi->rows);
|
|
#endif
|
|
|
|
red = green = blue = 0.95;
|
|
e_cal_model_get_rgb_color_for_component (
|
|
model, event->comp_data, &red, &green, &blue);
|
|
print_border_rgb (context, x1, x2, y1, y2, 1.0, red, green, blue);
|
|
|
|
text = get_summary_with_location (event->comp_data->icalcomp);
|
|
|
|
if (display_times) {
|
|
gchar *t = NULL;
|
|
|
|
date_tm.tm_year = 2001;
|
|
date_tm.tm_mon = 0;
|
|
date_tm.tm_mday = 1;
|
|
date_tm.tm_hour = event->start_minute / 60;
|
|
date_tm.tm_min = event->start_minute % 60;
|
|
date_tm.tm_sec = 0;
|
|
date_tm.tm_isdst = -1;
|
|
|
|
e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
|
|
start_buffer, sizeof (start_buffer));
|
|
|
|
date_tm.tm_hour = event->end_minute / 60;
|
|
date_tm.tm_min = event->end_minute % 60;
|
|
|
|
e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE,
|
|
end_buffer, sizeof (end_buffer));
|
|
|
|
t = text;
|
|
text = g_strdup_printf ("%s - %s %s ", start_buffer,
|
|
end_buffer, text);
|
|
|
|
g_free (t);
|
|
}
|
|
|
|
bound_text (context, font, text, -1, x1 + 2, y1, x2 - 2, y2, FALSE, NULL, NULL);
|
|
|
|
g_free (text);
|
|
}
|
|
|
|
static void
|
|
print_day_details (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t whence,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom)
|
|
{
|
|
ECalModel *model;
|
|
icaltimezone *zone;
|
|
EDayViewEvent *event;
|
|
PangoFontDescription *font;
|
|
time_t start, end;
|
|
struct pdinfo pdi = { 0 };
|
|
gint rows_in_top_display, i, rows_with_30_mins;
|
|
gdouble font_size, max_font_size;
|
|
cairo_t *cr;
|
|
GdkPixbuf *pixbuf = NULL;
|
|
#define LONG_DAY_EVENTS_TOP_SPACING 4
|
|
#define LONG_DAY_EVENTS_BOTTOM_SPACING 2
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
zone = e_cal_model_get_timezone (model);
|
|
|
|
start = time_day_begin_with_zone (whence, zone);
|
|
end = time_day_end_with_zone (start, zone);
|
|
|
|
pdi.days_shown = 1;
|
|
pdi.day_starts[0] = start;
|
|
pdi.day_starts[1] = end;
|
|
pdi.long_events = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
|
|
pdi.events[0] = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
|
|
pdi.start_hour = e_cal_model_get_work_day_start_hour (model);
|
|
pdi.end_hour = e_cal_model_get_work_day_end_hour (model);
|
|
if (e_cal_model_get_work_day_end_minute (model) != 0)
|
|
pdi.end_hour++;
|
|
pdi.mins_per_row = get_day_view_time_divisions ();
|
|
pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
|
|
pdi.start_minute_offset = pdi.start_hour * 60;
|
|
pdi.end_minute_offset = pdi.end_hour * 60;
|
|
pdi.use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
|
|
pdi.zone = e_cal_model_get_timezone (model);
|
|
|
|
/* Get the events from the server. */
|
|
e_cal_model_generate_instances_sync (model, start, end, print_day_details_cb, &pdi);
|
|
qsort (pdi.long_events->data, pdi.long_events->len,
|
|
sizeof (EDayViewEvent), e_day_view_event_sort_func);
|
|
qsort (pdi.events[0]->data, pdi.events[0]->len,
|
|
sizeof (EDayViewEvent), e_day_view_event_sort_func);
|
|
|
|
/* Also print events outside of work hours */
|
|
if (pdi.events[0]->len > 0) {
|
|
struct icaltimetype tt;
|
|
|
|
event = &g_array_index (pdi.events[0], EDayViewEvent, 0);
|
|
tt = icaltime_from_timet_with_zone (event->start, FALSE, zone);
|
|
if (tt.hour < pdi.start_hour)
|
|
pdi.start_hour = tt.hour;
|
|
pdi.start_minute_offset = pdi.start_hour * 60;
|
|
|
|
event = &g_array_index (pdi.events[0], EDayViewEvent, pdi.events[0]->len - 1);
|
|
tt = icaltime_from_timet_with_zone (event->end, FALSE, zone);
|
|
if (tt.hour > pdi.end_hour || tt.hour == 0) {
|
|
pdi.end_hour = tt.hour ? tt.hour : 24;
|
|
if (tt.minute > 0)
|
|
pdi.end_hour++;
|
|
}
|
|
pdi.end_minute_offset = pdi.end_hour * 60;
|
|
|
|
pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
|
|
}
|
|
|
|
/* Lay them out the long events, across the top of the page. */
|
|
e_day_view_layout_long_events (pdi.long_events, pdi.days_shown,
|
|
pdi.day_starts, &rows_in_top_display);
|
|
|
|
/*Print the long events. */
|
|
font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
|
|
|
|
/* We always leave space for DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY in the
|
|
* top display, but we may have more rows than that, in which case
|
|
* the main display area will be compressed. */
|
|
/* Limit long day event to half the height of the panel */
|
|
rows_in_top_display = MIN (MAX (rows_in_top_display,
|
|
DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY),
|
|
(bottom - top) * 0.5 / DAY_VIEW_ROW_HEIGHT);
|
|
|
|
if (rows_in_top_display > pdi.long_events->len)
|
|
rows_in_top_display = pdi.long_events->len;
|
|
|
|
for (i = 0; i < rows_in_top_display && i < pdi.long_events->len; i++) {
|
|
event = &g_array_index (pdi.long_events, EDayViewEvent, i);
|
|
print_day_long_event (
|
|
context, font, left, right,
|
|
top + LONG_DAY_EVENTS_TOP_SPACING, bottom,
|
|
DAY_VIEW_ROW_HEIGHT, event, &pdi, model);
|
|
}
|
|
|
|
if (rows_in_top_display < pdi.long_events->len) {
|
|
/* too many events */
|
|
cairo_t *cr = gtk_print_context_get_cairo_context (context);
|
|
gint x, y;
|
|
|
|
if (!pixbuf) {
|
|
const gchar **xpm = (const gchar **) jump_xpm;
|
|
|
|
/* this ugly thing is here only to get rid of compiler warning
|
|
* about unused 'jump_xpm_focused' */
|
|
if (pixbuf)
|
|
xpm = (const gchar **) jump_xpm_focused;
|
|
|
|
pixbuf = gdk_pixbuf_new_from_xpm_data (xpm);
|
|
}
|
|
|
|
/* Right align - 10 comes from print_day_long_event too */
|
|
x = right - gdk_pixbuf_get_width (pixbuf) * 0.5 - 10;
|
|
/* Placing '...' over the last all day event entry printed. '-1 -1' comes
|
|
from print_long_day_event (top / bottom spacing in each cell) */
|
|
y = top + LONG_DAY_EVENTS_TOP_SPACING
|
|
+ DAY_VIEW_ROW_HEIGHT * (i - 1)
|
|
+ (DAY_VIEW_ROW_HEIGHT - 1 - 1) * 0.5;
|
|
|
|
cairo_save (cr);
|
|
cairo_scale (cr, 0.5, 0.5);
|
|
gdk_cairo_set_source_pixbuf (cr, pixbuf, x * 2.0, y * 2.0);
|
|
cairo_paint (cr);
|
|
cairo_restore (cr);
|
|
}
|
|
|
|
if (!rows_in_top_display)
|
|
rows_in_top_display++;
|
|
|
|
/* Draw the border around the long events. */
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
print_border (
|
|
context, left, right,
|
|
top, top + rows_in_top_display * DAY_VIEW_ROW_HEIGHT +
|
|
LONG_DAY_EVENTS_TOP_SPACING + LONG_DAY_EVENTS_BOTTOM_SPACING,
|
|
1.0, -1.0);
|
|
|
|
/* Adjust the area containing the main display. */
|
|
top += rows_in_top_display * DAY_VIEW_ROW_HEIGHT
|
|
+ LONG_DAY_EVENTS_TOP_SPACING
|
|
+ LONG_DAY_EVENTS_BOTTOM_SPACING;
|
|
|
|
/* Draw the borders, lines, and times down the left. */
|
|
print_day_background (context, gcal, whence, &pdi,
|
|
left, right, top, bottom);
|
|
/* Now adjust to get rid of the time column. */
|
|
left += DAY_VIEW_TIME_COLUMN_WIDTH;
|
|
|
|
/* lay out the short events, within the day. */
|
|
e_day_view_layout_day_events (pdi.events[0], CALC_DAY_VIEW_ROWS (pdi.mins_per_row),
|
|
pdi.mins_per_row, pdi.cols_per_row, -1);
|
|
|
|
/* use font like with 30 minutes time division */
|
|
rows_with_30_mins = (pdi.end_hour - pdi.start_hour) * (60 / 30);
|
|
|
|
/* print the short events. */
|
|
if (top > bottom )
|
|
max_font_size = ((top - bottom) / rows_with_30_mins) - 4;
|
|
else
|
|
max_font_size = ((bottom - top ) / rows_with_30_mins) - 4;
|
|
font_size = MIN (DAY_NORMAL_FONT_SIZE, max_font_size);
|
|
font = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
|
|
|
|
for (i = 0; i < pdi.events[0]->len; i++) {
|
|
event = &g_array_index (pdi.events[0], EDayViewEvent, i);
|
|
print_day_event (context, font, left, right, top, bottom,
|
|
event, &pdi, model);
|
|
}
|
|
|
|
/* Free everything. */
|
|
if (pixbuf)
|
|
g_object_unref (pixbuf);
|
|
free_event_array (pdi.long_events);
|
|
pango_font_description_free (font);
|
|
g_array_free (pdi.long_events, TRUE);
|
|
free_event_array (pdi.events[0]);
|
|
g_array_free (pdi.events[0], TRUE);
|
|
}
|
|
|
|
/* Returns TRUE if the event is a one-day event (i.e. not a long event). */
|
|
static gboolean
|
|
print_is_one_day_week_event (EWeekViewEvent *event,
|
|
EWeekViewEventSpan *span,
|
|
time_t *day_starts)
|
|
{
|
|
if (event->start == day_starts[span->start_day]
|
|
&& event->end == day_starts[span->start_day + 1])
|
|
return FALSE;
|
|
|
|
if (span->num_days == 1
|
|
&& event->start >= day_starts[span->start_day]
|
|
&& event->end <= day_starts[span->start_day + 1])
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
print_week_long_event (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
struct psinfo *psi,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble row_height,
|
|
EWeekViewEvent *event,
|
|
EWeekViewEventSpan *span,
|
|
gchar *text,
|
|
gdouble red,
|
|
gdouble green,
|
|
gdouble blue)
|
|
{
|
|
gdouble left_triangle_width = -1.0, right_triangle_width = -1.0;
|
|
struct tm date_tm;
|
|
gchar buffer[32];
|
|
|
|
/* If the event starts before the first day of the span, draw a
|
|
* triangle to indicate it continues. */
|
|
if (event->start < psi->day_starts[span->start_day])
|
|
left_triangle_width = 4;
|
|
|
|
/* If the event ends after the last day of the span, draw a
|
|
* triangle. */
|
|
if (event->end > psi->day_starts[span->start_day + span->num_days])
|
|
right_triangle_width = 4;
|
|
|
|
print_border_with_triangles (
|
|
context, x1 + 6, x2 - 6, y1, y1 + row_height, 0.0, red, green, blue,
|
|
left_triangle_width, right_triangle_width);
|
|
|
|
x1 += 6;
|
|
x2 -= 6;
|
|
|
|
/* If the event starts after the first day being printed, we need to
|
|
* print the start time. */
|
|
if (event->start > psi->day_starts[span->start_day]) {
|
|
date_tm.tm_year = 2001;
|
|
date_tm.tm_mon = 0;
|
|
date_tm.tm_mday = 1;
|
|
date_tm.tm_hour = event->start_minute / 60;
|
|
date_tm.tm_min = event->start_minute % 60;
|
|
date_tm.tm_sec = 0;
|
|
date_tm.tm_isdst = -1;
|
|
|
|
e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
|
|
buffer, sizeof (buffer));
|
|
|
|
x1 += 2;
|
|
x1 += print_text_line (
|
|
context, font, buffer, PANGO_ALIGN_LEFT,
|
|
x1, x2 - 2, y1, y1 + row_height, TRUE);
|
|
}
|
|
|
|
/* If the event ends before the end of the last day being printed,
|
|
* we need to print the end time. */
|
|
if (event->end < psi->day_starts[span->start_day + span->num_days]) {
|
|
date_tm.tm_year = 2001;
|
|
date_tm.tm_mon = 0;
|
|
date_tm.tm_mday = 1;
|
|
date_tm.tm_hour = event->end_minute / 60;
|
|
date_tm.tm_min = event->end_minute % 60;
|
|
date_tm.tm_sec = 0;
|
|
date_tm.tm_isdst = -1;
|
|
|
|
e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
|
|
buffer, sizeof (buffer));
|
|
|
|
x2 -= 2;
|
|
x2 -= print_text_line (
|
|
context, font, buffer, PANGO_ALIGN_RIGHT,
|
|
x1 + 2, x2, y1, y1 + row_height, TRUE);
|
|
}
|
|
|
|
x1 += 2;
|
|
x2 -= 2;
|
|
print_text_line (context, font, text, PANGO_ALIGN_CENTER, x1, x2, y1, y1 + row_height, TRUE);
|
|
}
|
|
|
|
static void
|
|
print_week_day_event (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
struct psinfo *psi,
|
|
gdouble x1,
|
|
gdouble x2,
|
|
gdouble y1,
|
|
gdouble row_height,
|
|
EWeekViewEvent *event,
|
|
EWeekViewEventSpan *span,
|
|
gchar *text,
|
|
gdouble red,
|
|
gdouble green,
|
|
gdouble blue)
|
|
{
|
|
struct tm date_tm;
|
|
gchar buffer[32];
|
|
|
|
date_tm.tm_year = 2001;
|
|
date_tm.tm_mon = 0;
|
|
date_tm.tm_mday = 1;
|
|
date_tm.tm_hour = event->start_minute / 60;
|
|
date_tm.tm_min = event->start_minute % 60;
|
|
date_tm.tm_sec = 0;
|
|
date_tm.tm_isdst = -1;
|
|
|
|
e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
|
|
buffer, sizeof (buffer));
|
|
print_rectangle (context, x1 + 1, y1, x2 - x1 - 2, row_height, red, green, blue);
|
|
x1 += print_text_line (
|
|
context, font, buffer, PANGO_ALIGN_LEFT,
|
|
x1 + 2, x2 - 3, y1, y1 + row_height, TRUE) + 4;
|
|
|
|
if (psi->weeks_shown <= 2) {
|
|
date_tm.tm_hour = event->end_minute / 60;
|
|
date_tm.tm_min = event->end_minute % 60;
|
|
|
|
e_time_format_time (&date_tm, psi->use_24_hour_format, FALSE,
|
|
buffer, sizeof (buffer));
|
|
|
|
x1 += print_text_line (
|
|
context, font, buffer, PANGO_ALIGN_LEFT,
|
|
x1, x2 - 3, y1, y1 + row_height, TRUE) + 4;
|
|
}
|
|
|
|
print_text_line (
|
|
context, font, text, PANGO_ALIGN_LEFT,
|
|
x1, x2 - 3, y1, y1 + row_height, TRUE);
|
|
}
|
|
|
|
static void
|
|
print_week_event (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
struct psinfo *psi,
|
|
gdouble left,
|
|
gdouble top,
|
|
gdouble cell_width,
|
|
gdouble cell_height,
|
|
ECalModel *model,
|
|
EWeekViewEvent *event,
|
|
GArray *spans)
|
|
{
|
|
EWeekViewEventSpan *span;
|
|
gint span_num;
|
|
gchar *text;
|
|
gint num_days, start_x, start_y, start_h, end_x, end_y, end_h;
|
|
gdouble x1, x2, y1;
|
|
gdouble red, green, blue;
|
|
GdkPixbuf *pixbuf = NULL;
|
|
|
|
if (!is_comp_data_valid (event))
|
|
return;
|
|
|
|
text = get_summary_with_location (event->comp_data->icalcomp);
|
|
|
|
for (span_num = 0; span_num < event->num_spans; span_num++) {
|
|
span = &g_array_index (spans, EWeekViewEventSpan,
|
|
event->spans_index + span_num);
|
|
|
|
if (e_week_view_layout_get_span_position
|
|
(event, span,
|
|
psi->rows_per_cell,
|
|
psi->rows_per_compressed_cell,
|
|
psi->display_start_weekday,
|
|
psi->multi_week_view,
|
|
psi->compress_weekend,
|
|
&num_days)) {
|
|
|
|
e_week_view_layout_get_day_position
|
|
(span->start_day,
|
|
psi->multi_week_view,
|
|
psi->weeks_shown,
|
|
psi->display_start_weekday,
|
|
psi->compress_weekend,
|
|
&start_x, &start_y, &start_h);
|
|
|
|
if (num_days == 1) {
|
|
end_x = start_x;
|
|
end_y = start_y;
|
|
end_h = start_h;
|
|
} else {
|
|
e_week_view_layout_get_day_position
|
|
(span->start_day + num_days - 1,
|
|
psi->multi_week_view,
|
|
psi->weeks_shown,
|
|
psi->display_start_weekday,
|
|
psi->compress_weekend,
|
|
&end_x, &end_y, &end_h);
|
|
}
|
|
|
|
x1 = left + start_x * cell_width;
|
|
x2 = left + (end_x + 1) * cell_width;
|
|
y1 = top + start_y * cell_height
|
|
+ psi->header_row_height
|
|
+ span->row * (psi->row_height + 2);
|
|
|
|
red = .9;
|
|
green = .9;
|
|
blue = .9;
|
|
e_cal_model_get_rgb_color_for_component (
|
|
model, event->comp_data, &red, &green, &blue);
|
|
if (print_is_one_day_week_event (event, span,
|
|
psi->day_starts)) {
|
|
print_week_day_event (context, font, psi,
|
|
x1, x2, y1, psi->row_height,
|
|
event, span, text, red, green, blue);
|
|
} else {
|
|
print_week_long_event (context, font, psi,
|
|
x1, x2, y1, psi->row_height,
|
|
event, span, text, red, green, blue);
|
|
}
|
|
} else {
|
|
cairo_t *cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
e_week_view_layout_get_day_position
|
|
(span->start_day,
|
|
psi->multi_week_view,
|
|
psi->weeks_shown,
|
|
psi->display_start_weekday,
|
|
psi->compress_weekend,
|
|
&start_x, &start_y, &start_h);
|
|
|
|
y1 = top + start_y * cell_height
|
|
+ psi->header_row_height
|
|
+ psi->rows_per_cell * (psi->row_height + 2);
|
|
|
|
if (span->row >= psi->rows_per_compressed_cell && psi->compress_weekend) {
|
|
gint end_day_of_week =
|
|
(psi->display_start_weekday +
|
|
span->start_day) % 7;
|
|
|
|
if (end_day_of_week == 5 || end_day_of_week == 6) {
|
|
/* Sat or Sun */
|
|
y1 = top + start_y * cell_height
|
|
+ psi->header_row_height
|
|
+ psi->rows_per_compressed_cell * (psi->row_height + 2);
|
|
|
|
}
|
|
}
|
|
|
|
if (!pixbuf) {
|
|
const gchar **xpm = (const gchar **) jump_xpm;
|
|
|
|
/* this ugly thing is here only to get rid of compiler warning
|
|
* about unused 'jump_xpm_focused' */
|
|
if (pixbuf)
|
|
xpm = (const gchar **) jump_xpm_focused;
|
|
|
|
pixbuf = gdk_pixbuf_new_from_xpm_data (xpm);
|
|
}
|
|
|
|
x1 = left + (start_x + 1) * cell_width - 6 -
|
|
gdk_pixbuf_get_width (pixbuf) * 0.5;
|
|
|
|
cairo_save (cr);
|
|
cairo_scale (cr, 0.5, 0.5);
|
|
gdk_cairo_set_source_pixbuf (cr, pixbuf, x1 * 2.0, y1 * 2.0);
|
|
cairo_paint (cr);
|
|
cairo_restore (cr);
|
|
}
|
|
}
|
|
|
|
if (pixbuf)
|
|
g_object_unref (pixbuf);
|
|
|
|
g_free (text);
|
|
}
|
|
|
|
static void
|
|
print_week_view_background (GtkPrintContext *context,
|
|
PangoFontDescription *font,
|
|
struct psinfo *psi,
|
|
gdouble left,
|
|
gdouble top,
|
|
gdouble cell_width,
|
|
gdouble cell_height)
|
|
{
|
|
struct tm tm;
|
|
gint day, day_x, day_y, day_h;
|
|
gdouble x1, x2, y1, y2, font_size, fillcolor;
|
|
const gchar *format_string;
|
|
gchar buffer[128];
|
|
cairo_t *cr;
|
|
|
|
font_size = get_font_size (font);
|
|
|
|
for (day = 0; day < psi->days_shown; day++) {
|
|
e_week_view_layout_get_day_position
|
|
(day, psi->multi_week_view, psi->weeks_shown,
|
|
psi->display_start_weekday, psi->compress_weekend,
|
|
&day_x, &day_y, &day_h);
|
|
|
|
x1 = left + day_x * cell_width;
|
|
x2 = left + (day_x + 1) * cell_width;
|
|
y1 = top + day_y * cell_height;
|
|
y2 = y1 + day_h * cell_height;
|
|
|
|
convert_timet_to_struct_tm (psi->day_starts[day], psi->zone, &tm);
|
|
|
|
/* In the month view we draw a grey background for the end
|
|
* of the previous month and the start of the following. */
|
|
fillcolor = -1.0;
|
|
if (psi->multi_week_view && (tm.tm_mon != psi->month))
|
|
fillcolor = 0.9;
|
|
|
|
print_border (context, x1, x2, y1, y2, 1.0, fillcolor);
|
|
|
|
if (psi->multi_week_view) {
|
|
if (tm.tm_mday == 1)
|
|
format_string = _("%d %B");
|
|
else
|
|
format_string = "%d";
|
|
} else {
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
cairo_move_to (cr, x1 + 0.1 * cell_width,
|
|
y1 + psi->header_row_height - 4);
|
|
cairo_line_to (cr, x2,
|
|
y1 + psi->header_row_height - 4);
|
|
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
cairo_set_line_width (cr, 0.5);
|
|
cairo_stroke (cr);
|
|
|
|
/* strftime format %A = full weekday name, %d = day of
|
|
* month, %B = full month name. You can change the
|
|
* order but don't change the specifiers or add
|
|
* anything. */
|
|
format_string = _("%A %d %B");
|
|
|
|
}
|
|
|
|
e_utf8_strftime (buffer, sizeof (buffer), format_string, &tm);
|
|
|
|
print_text_line (context, font, buffer, PANGO_ALIGN_RIGHT,
|
|
x1, x2 - 4, y1 + 2, y1 + 2 + font_size, TRUE);
|
|
}
|
|
}
|
|
|
|
/* This adds one event to the view, adding it to the appropriate array. */
|
|
static gboolean
|
|
print_week_summary_cb (ECalComponent *comp,
|
|
time_t start,
|
|
time_t end,
|
|
gpointer data)
|
|
|
|
{
|
|
EWeekViewEvent event;
|
|
struct icaltimetype start_tt, end_tt;
|
|
ECalModelGenerateInstancesData *mdata = (ECalModelGenerateInstancesData *) data;
|
|
struct psinfo *psi = (struct psinfo *) mdata->cb_data;
|
|
|
|
/* Check that the event times are valid. */
|
|
|
|
#if 0
|
|
g_print ("View start:%li end:%li Event start:%li end:%li\n",
|
|
psi->day_starts[0], psi->day_starts[psi->days_shown],
|
|
start, end);
|
|
#endif
|
|
|
|
g_return_val_if_fail (start <= end, TRUE);
|
|
g_return_val_if_fail (start < psi->day_starts[psi->days_shown], TRUE);
|
|
g_return_val_if_fail (end > psi->day_starts[0], TRUE);
|
|
|
|
start_tt = icaltime_from_timet_with_zone (start, FALSE, psi->zone);
|
|
end_tt = icaltime_from_timet_with_zone (end, FALSE, psi->zone);
|
|
|
|
event.comp_data = g_object_ref (mdata->comp_data);
|
|
|
|
event.start = start;
|
|
event.end = end;
|
|
event.spans_index = 0;
|
|
event.num_spans = 0;
|
|
|
|
event.start_minute = start_tt.hour * 60 + start_tt.minute;
|
|
event.end_minute = end_tt.hour * 60 + end_tt.minute;
|
|
if (event.end_minute == 0 && start != end)
|
|
event.end_minute = 24 * 60;
|
|
|
|
g_array_append_val (psi->events, event);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
print_week_summary (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t whence,
|
|
gboolean multi_week_view,
|
|
gint weeks_shown,
|
|
gint month,
|
|
gdouble font_size,
|
|
gdouble font_size_background,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom)
|
|
{
|
|
icaltimezone *zone;
|
|
EWeekViewEvent *event;
|
|
struct psinfo psi = { 0 };
|
|
time_t day_start;
|
|
gint rows_per_day[E_WEEK_VIEW_MAX_WEEKS * 7], day, event_num;
|
|
GArray *spans;
|
|
PangoFontDescription *font, *font_background;
|
|
gdouble cell_width, cell_height;
|
|
ECalModel *model;
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
zone = e_cal_model_get_timezone (model);
|
|
|
|
psi.days_shown = weeks_shown * 7;
|
|
psi.events = g_array_new (FALSE, FALSE, sizeof (EWeekViewEvent));
|
|
psi.multi_week_view = multi_week_view;
|
|
psi.weeks_shown = weeks_shown;
|
|
psi.month = month;
|
|
psi.zone = zone;
|
|
|
|
/* Get a few config settings. */
|
|
if (multi_week_view)
|
|
psi.compress_weekend = e_cal_model_get_compress_weekend (model);
|
|
else
|
|
psi.compress_weekend = TRUE;
|
|
psi.use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
|
|
|
|
/* We convert this from (0 = Sun, 6 = Sat) to (0 = Mon, 6 = Sun). */
|
|
psi.display_start_weekday = e_cal_model_get_week_start_day (model);
|
|
psi.display_start_weekday = (psi.display_start_weekday + 6) % 7;
|
|
|
|
/* If weekends are compressed then we can't start on a Sunday. */
|
|
if (psi.compress_weekend && psi.display_start_weekday == 6)
|
|
psi.display_start_weekday = 5;
|
|
|
|
day_start = time_day_begin_with_zone (whence, zone);
|
|
for (day = 0; day <= psi.days_shown; day++) {
|
|
psi.day_starts[day] = day_start;
|
|
day_start = time_add_day_with_zone (day_start, 1, zone);
|
|
}
|
|
|
|
/* Get the events from the server. */
|
|
e_cal_model_generate_instances_sync (model,
|
|
psi.day_starts[0], psi.day_starts[psi.days_shown],
|
|
print_week_summary_cb, &psi);
|
|
qsort (psi.events->data, psi.events->len,
|
|
sizeof (EWeekViewEvent), e_week_view_event_sort_func);
|
|
|
|
/* Layout the events. */
|
|
spans = e_week_view_layout_events (psi.events, NULL,
|
|
psi.multi_week_view,
|
|
psi.weeks_shown,
|
|
psi.compress_weekend,
|
|
psi.display_start_weekday,
|
|
psi.day_starts, rows_per_day);
|
|
|
|
/* Calculate the size of the cells. */
|
|
if (multi_week_view) {
|
|
cell_width = (right - left) / (psi.compress_weekend ? 6 : 7);
|
|
cell_height = (bottom - top) / (weeks_shown * 2);
|
|
} else {
|
|
cell_width = (right - left) / 2;
|
|
cell_height = (bottom - top) / 6;
|
|
}
|
|
|
|
/* Calculate the row height, using the normal font and with room for
|
|
* space or a rectangle around it. */
|
|
psi.row_height = font_size * 1.2;
|
|
psi.header_row_height = font_size * 1.5;
|
|
|
|
/* Calculate how many rows we can fit into each type of cell. */
|
|
psi.rows_per_cell = ((cell_height * 2) - psi.header_row_height)
|
|
/ (psi.row_height + 2);
|
|
psi.rows_per_compressed_cell = (cell_height - psi.header_row_height)
|
|
/ (psi.row_height + 2);
|
|
|
|
/* Draw the grid and the day names/numbers. */
|
|
font_background = get_font_for_size (font_size_background, PANGO_WEIGHT_NORMAL);
|
|
print_week_view_background (context, font_background, &psi, left, top,
|
|
cell_width, cell_height);
|
|
pango_font_description_free (font_background);
|
|
|
|
/* Print the events. */
|
|
font = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
|
|
for (event_num = 0; event_num < psi.events->len; event_num++) {
|
|
event = &g_array_index (psi.events, EWeekViewEvent, event_num);
|
|
print_week_event (context, font, &psi, left, top,
|
|
cell_width, cell_height, model, event, spans);
|
|
}
|
|
|
|
pango_font_description_free (font);
|
|
|
|
/* Free everything. */
|
|
for (event_num = 0; event_num < psi.events->len; event_num++) {
|
|
event = &g_array_index (psi.events, EWeekViewEvent, event_num);
|
|
g_object_unref (event->comp_data);
|
|
}
|
|
g_array_free (psi.events, TRUE);
|
|
g_array_free (spans, TRUE);
|
|
}
|
|
|
|
static void
|
|
print_month_summary (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t whence,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom)
|
|
{
|
|
icaltimezone *zone;
|
|
time_t date;
|
|
struct tm tm;
|
|
struct icaltimetype tt;
|
|
gchar buffer[100];
|
|
ECalModel *model;
|
|
PangoFontDescription *font;
|
|
gboolean compress_weekend;
|
|
gint columns, col, weekday, month, weeks;
|
|
gdouble font_size, cell_width, x1, x2, y1, y2;
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
zone = e_cal_model_get_timezone (model);
|
|
weekday = e_cal_model_get_week_start_day (model);
|
|
compress_weekend = e_cal_model_get_compress_weekend (model);
|
|
|
|
date = 0;
|
|
weeks = 6;
|
|
if (gnome_calendar_get_view (gcal) == GNOME_CAL_MONTH_VIEW) {
|
|
GnomeCalendarViewType view_type;
|
|
ECalendarView *calendar_view;
|
|
EWeekView *week_view;
|
|
|
|
view_type = gnome_calendar_get_view (gcal);
|
|
calendar_view = gnome_calendar_get_calendar_view (gcal, view_type);
|
|
week_view = E_WEEK_VIEW (calendar_view);
|
|
|
|
if (week_view && week_view->multi_week_view
|
|
&& !(week_view->weeks_shown >= 4 &&
|
|
g_date_valid (&week_view->first_day_shown))) {
|
|
weeks = week_view->weeks_shown;
|
|
date = whence;
|
|
}
|
|
}
|
|
|
|
/* Remember which month we want. */
|
|
tt = icaltime_from_timet_with_zone (whence, FALSE, zone);
|
|
month = tt.month - 1;
|
|
|
|
/* Find the start of the month, and then the start of the week on
|
|
* or before that day. */
|
|
if (!date)
|
|
date = time_month_begin_with_zone (whence, zone);
|
|
date = time_week_begin_with_zone (date, weekday, zone);
|
|
|
|
/* If weekends are compressed then we can't start on a Sunday. */
|
|
if (compress_weekend && weekday == 0)
|
|
date = time_add_day_with_zone (date, -1, zone);
|
|
|
|
/* do day names ... */
|
|
|
|
/* We are only interested in outputting the weekday here, but we want
|
|
* to be able to step through the week without worrying about
|
|
* overflows making strftime choke, so we move near to the start of
|
|
* the month. */
|
|
convert_timet_to_struct_tm (date, zone, &tm);
|
|
tm.tm_mday = (tm.tm_mday % 7) + 7;
|
|
|
|
font = get_font_for_size (MONTH_NORMAL_FONT_SIZE, PANGO_WEIGHT_BOLD);
|
|
font_size = get_font_size (font);
|
|
|
|
columns = compress_weekend ? 6 : 7;
|
|
cell_width = (right - left) / columns;
|
|
y1 = top;
|
|
y2 = top + font_size * 1.5;
|
|
|
|
for (col = 0; col < columns; col++) {
|
|
if (tm.tm_wday == 6 && compress_weekend)
|
|
g_snprintf (
|
|
buffer, sizeof (buffer), "%s/%s",
|
|
e_get_weekday_name (G_DATE_SATURDAY, TRUE),
|
|
e_get_weekday_name (G_DATE_SUNDAY, TRUE));
|
|
else
|
|
g_snprintf (
|
|
buffer, sizeof (buffer), "%s",
|
|
e_get_weekday_name (
|
|
tm.tm_wday ? tm.tm_wday : 7, FALSE));
|
|
|
|
x1 = left + cell_width * col;
|
|
x2 = x1 + cell_width;
|
|
|
|
print_border (context, x1, x2, y1, y2, 1.0, -1.0);
|
|
print_text_line (context, font, buffer, PANGO_ALIGN_CENTER, x1, x2, y1, y2, TRUE);
|
|
|
|
tm.tm_mday++;
|
|
tm.tm_wday = (tm.tm_wday + 1) % 7;
|
|
}
|
|
pango_font_description_free (font);
|
|
|
|
top = y2;
|
|
print_week_summary (context, gcal, date, TRUE, weeks, month,
|
|
MONTH_NORMAL_FONT_SIZE, MONTH_NORMAL_FONT_SIZE,
|
|
left, right, top, bottom);
|
|
}
|
|
|
|
static void
|
|
print_todo_details (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t start,
|
|
time_t end,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom)
|
|
{
|
|
PangoFontDescription *font_summary;
|
|
gdouble y, yend, x, xend;
|
|
struct icaltimetype *tt;
|
|
GtkWidget *task_table;
|
|
ETable *table;
|
|
ECalModel *model;
|
|
gint rows, row;
|
|
cairo_t *cr;
|
|
|
|
/* We get the tasks directly from the TaskPad ETable. This means we
|
|
* get them filtered & sorted for free. */
|
|
task_table = gnome_calendar_get_task_table (gcal);
|
|
table = E_TABLE (task_table);
|
|
g_return_if_fail (table != NULL);
|
|
model = e_task_table_get_model (E_TASK_TABLE (task_table));
|
|
|
|
font_summary = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
|
|
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
cairo_set_line_width (cr, 0.0);
|
|
top +=2;
|
|
|
|
titled_box (context, _("Tasks"), font_summary, PANGO_ALIGN_CENTER,
|
|
&left, &top, &right, &bottom, 1.0);
|
|
|
|
y = top;
|
|
yend = bottom - 2;
|
|
|
|
rows = e_table_model_row_count (E_TABLE_MODEL (model));
|
|
for (row = 0; row < rows; row++) {
|
|
ECalModelComponent *comp_data;
|
|
ECalComponent *comp;
|
|
ECalComponentText summary;
|
|
gint model_row;
|
|
|
|
model_row = e_table_view_to_model_row (table, row);
|
|
comp_data = e_cal_model_get_component_at (model, model_row);
|
|
if (!comp_data)
|
|
continue;
|
|
|
|
comp = e_cal_component_new ();
|
|
e_cal_component_set_icalcomponent (
|
|
comp, icalcomponent_new_clone (comp_data->icalcomp));
|
|
|
|
e_cal_component_get_summary (comp, &summary);
|
|
if (!summary.value) {
|
|
g_object_unref (comp);
|
|
continue;
|
|
}
|
|
|
|
x = left;
|
|
xend = right - 2;
|
|
if (y > bottom) {
|
|
g_object_unref (comp);
|
|
break;
|
|
}
|
|
|
|
/* Print the box to put the tick in. */
|
|
print_border (context, x + 2, x + 8, y + 6, y + 15, 0.1, -1.0);
|
|
|
|
/* If the task is complete, print a tick in the box. */
|
|
e_cal_component_get_completed (comp, &tt);
|
|
if (tt) {
|
|
e_cal_component_free_icaltimetype (tt);
|
|
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
cairo_move_to (cr, x + 3, y + 11);
|
|
cairo_line_to (cr, x + 5, y + 14);
|
|
cairo_line_to (cr, x + 7, y + 5.5);
|
|
cairo_set_line_width (cr, 1);
|
|
cairo_stroke (cr);
|
|
}
|
|
|
|
y = bound_text (context, font_summary, summary.value, -1,
|
|
x + 14, y + 4, xend, yend, FALSE, NULL, NULL);
|
|
|
|
y += get_font_size (font_summary) - 5;
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_move_to (cr, x, y);
|
|
cairo_line_to (cr, xend, y);
|
|
cairo_set_line_width (cr, 1);
|
|
cairo_stroke (cr);
|
|
|
|
g_object_unref (comp);
|
|
}
|
|
|
|
pango_font_description_free (font_summary);
|
|
}
|
|
|
|
static void
|
|
print_day_view (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t date)
|
|
{
|
|
ECalModel *model;
|
|
GtkPageSetup *setup;
|
|
icaltimezone *zone;
|
|
gint i, days = 1;
|
|
gdouble todo, l, week_numbers_inc, small_month_width;
|
|
gchar buf[100];
|
|
gdouble width, height;
|
|
struct tm tm;
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
zone = e_cal_model_get_timezone (model);
|
|
|
|
setup = gtk_print_context_get_page_setup (context);
|
|
|
|
width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
|
|
height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
|
|
small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
|
|
week_numbers_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;
|
|
|
|
for (i = 0; i < days; i++) {
|
|
todo = width * 0.75;
|
|
|
|
/* Print the main view with all the events in. */
|
|
print_day_details (context, gcal, date,
|
|
0.0, todo - 2.0, HEADER_HEIGHT + 4,
|
|
height);
|
|
|
|
/* Print the TaskPad down the right. */
|
|
print_todo_details (context, gcal, 0, INT_MAX,
|
|
todo, width, HEADER_HEIGHT + 4,
|
|
height);
|
|
|
|
/* Print the filled border around the header. */
|
|
print_border (context, 0.0, width,
|
|
0.0, HEADER_HEIGHT + 4, 1.0, 0.9);
|
|
|
|
/* Print the 2 mini calendar-months. */
|
|
l = width - SMALL_MONTH_PAD -
|
|
(small_month_width + week_numbers_inc) * 2 -
|
|
SMALL_MONTH_SPACING;
|
|
|
|
print_month_small (context, gcal, date,
|
|
l, 2, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 2,
|
|
DATE_MONTH | DATE_YEAR, date, date, FALSE);
|
|
|
|
l += SMALL_MONTH_SPACING + small_month_width + week_numbers_inc;
|
|
print_month_small (context, gcal,
|
|
time_add_month_with_zone (date, 1, zone),
|
|
l, 2, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 2,
|
|
DATE_MONTH | DATE_YEAR, 0, 0, FALSE);
|
|
|
|
/* Print the date, e.g. '8th May, 2001'. */
|
|
convert_timet_to_struct_tm (date, zone, &tm);
|
|
format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR,
|
|
buf, 100);
|
|
|
|
print_text_size_bold (context, buf, PANGO_ALIGN_LEFT,
|
|
4, todo, 4,
|
|
4 + 24);
|
|
|
|
/* Print the day, e.g. 'Tuesday'. */
|
|
format_date (&tm, DATE_DAYNAME, buf, 100);
|
|
|
|
print_text_size_bold (context, buf, PANGO_ALIGN_LEFT,
|
|
4, todo,
|
|
HEADER_HEIGHT + 9,
|
|
HEADER_HEIGHT + 9 + 18);
|
|
|
|
date = time_add_day_with_zone (date, 1, zone);
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_work_week_background (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t whence,
|
|
struct pdinfo *pdi,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom)
|
|
{
|
|
ECalModel *model;
|
|
PangoFontDescription *font_hour, *font_minute;
|
|
gdouble yinc, y;
|
|
gdouble width = DAY_VIEW_TIME_COLUMN_WIDTH;
|
|
gdouble day_width;
|
|
gdouble font_size, max_font_size, hour_font_size, minute_font_size;
|
|
gchar buf[20];
|
|
const gchar *minute;
|
|
const gint LONG_EVENT_OFFSET = 6;
|
|
gboolean use_24_hour;
|
|
gint i, hour, row;
|
|
gdouble hour_minute_xl, hour_minute_xr;
|
|
cairo_t *cr;
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
use_24_hour = e_cal_model_get_use_24_hour_format (model);
|
|
|
|
/* Fill the left time column in light-gray. */
|
|
print_border (context, left, left + width, top, bottom, -1.0, 0.9);
|
|
/* Fill the right time column in light-gray */
|
|
print_border (context, right - width, right, top, bottom, -1.0, 0.9);
|
|
|
|
/* Draw the border around the entire view. */
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
print_border (context, left, right, top, bottom, 1.0, -1.0);
|
|
|
|
/* Draw the vertical line on the right of the time column. */
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_set_line_width (cr, 0.0);
|
|
cairo_move_to (cr, left + width, bottom);
|
|
cairo_line_to (cr, left + width, top);
|
|
cairo_stroke (cr);
|
|
|
|
cairo_move_to (cr, right - width, bottom);
|
|
cairo_line_to (cr, right - width, top);
|
|
cairo_stroke (cr);
|
|
|
|
/* Calculate the row height. */
|
|
if (top > bottom)
|
|
yinc = (top - bottom) / (pdi->end_hour - pdi->start_hour);
|
|
else
|
|
yinc = (bottom - top) / (pdi->end_hour - pdi->start_hour);
|
|
|
|
/* Get the 2 fonts we need. */
|
|
font_size = yinc * 0.6;
|
|
max_font_size = width * 0.45;
|
|
hour_font_size = MIN (font_size, max_font_size);
|
|
font_hour = get_font_for_size (hour_font_size, PANGO_WEIGHT_BOLD);
|
|
|
|
font_size = yinc * 0.33;
|
|
max_font_size = width * 0.2;
|
|
minute_font_size = MIN (font_size, max_font_size);
|
|
font_minute = get_font_for_size (minute_font_size, PANGO_WEIGHT_BOLD);
|
|
hour_minute_xr = evo_calendar_print_renderer_get_width (
|
|
context, font_minute, use_24_hour ? "00" : _("am"));
|
|
if (!use_24_hour)
|
|
hour_minute_xr =
|
|
MAX (hour_minute_xr,
|
|
evo_calendar_print_renderer_get_width (
|
|
context, font_minute, _("pm")));
|
|
|
|
row = 0;
|
|
hour_minute_xl = left + width - hour_minute_xr - 3;
|
|
hour_minute_xr = right - hour_minute_xr - 3;
|
|
for (i = pdi->start_hour; i < pdi->end_hour; i++) {
|
|
y = top + yinc * (row + 1);
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
|
|
if (use_24_hour) {
|
|
hour = i;
|
|
minute = "00";
|
|
} else {
|
|
if (i < 12)
|
|
minute = _("am");
|
|
else
|
|
minute = _("pm");
|
|
|
|
hour = i % 12;
|
|
if (hour == 0)
|
|
hour = 12;
|
|
}
|
|
|
|
/* the hour label/minute */
|
|
sprintf (buf, "%d", hour);
|
|
print_text (context, font_hour, buf, PANGO_ALIGN_RIGHT,
|
|
left, hour_minute_xl,
|
|
y - yinc, y - yinc + hour_font_size);
|
|
print_text (context, font_minute, minute, PANGO_ALIGN_LEFT,
|
|
hour_minute_xl, left + width - 3,
|
|
y - yinc, y - yinc + minute_font_size);
|
|
|
|
/* To the right */
|
|
print_text (context, font_hour, buf, PANGO_ALIGN_RIGHT,
|
|
right - width, hour_minute_xr,
|
|
y - yinc, y - yinc + hour_font_size);
|
|
print_text (context, font_minute, minute, PANGO_ALIGN_LEFT,
|
|
hour_minute_xr, right - 3,
|
|
y - yinc, y - yinc + minute_font_size);
|
|
|
|
/* Draw the horizontal line between hours, across the entire
|
|
width of the day view. */
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_move_to (cr, left, y);
|
|
cairo_line_to (cr, right, y);
|
|
cairo_set_line_width (cr, 1);
|
|
cairo_stroke (cr);
|
|
|
|
/* Draw the horizontal line for the 1/2-hours, across the
|
|
* entire width except for part of the time column. */
|
|
cairo_move_to (cr, left + width * 0.6, y - yinc / 2);
|
|
cairo_line_to (cr, right, y - yinc / 2);
|
|
cairo_set_line_width (cr, 1);
|
|
cairo_stroke (cr);
|
|
row++;
|
|
}
|
|
|
|
/* Draw the vertical lines for the days */
|
|
day_width = (right - left - 2 *width) / pdi->days_shown;
|
|
for (i = 0; i < pdi->days_shown - 1; ++i) {
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_move_to (cr, left + width + day_width * (i + 1), top);
|
|
cairo_line_to (cr, left + width + day_width * (i + 1), bottom);
|
|
cairo_set_line_width (cr, 1);
|
|
cairo_stroke (cr);
|
|
}
|
|
|
|
/* And now the ones from the border to the hours, looks weird otherwise */
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
cairo_move_to (cr, left, HEADER_HEIGHT);
|
|
cairo_line_to (cr, left, HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET);
|
|
|
|
cairo_move_to (cr, right, HEADER_HEIGHT);
|
|
cairo_line_to (cr, right, HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET);
|
|
cairo_stroke (cr);
|
|
|
|
pango_font_description_free (font_hour);
|
|
pango_font_description_free (font_minute);
|
|
}
|
|
|
|
static void
|
|
print_work_week_day_details (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t whence,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom,
|
|
struct pdinfo *_pdi)
|
|
{
|
|
ECalModel *model;
|
|
icaltimezone *zone;
|
|
EDayViewEvent *event;
|
|
PangoFontDescription *font;
|
|
time_t start, end;
|
|
struct pdinfo pdi = { 0 };
|
|
gint rows_in_top_display, i, rows_with_30_mins;
|
|
gdouble font_size, max_font_size;
|
|
cairo_t *cr;
|
|
GdkPixbuf *pixbuf = NULL;
|
|
#define LONG_DAY_EVENTS_TOP_SPACING 4
|
|
#define LONG_DAY_EVENTS_BOTTOM_SPACING 2
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
zone = e_cal_model_get_timezone (model);
|
|
|
|
start = time_day_begin_with_zone (whence, zone);
|
|
end = time_day_end_with_zone (start, zone);
|
|
|
|
pdi.days_shown = 1;
|
|
pdi.day_starts[0] = start;
|
|
pdi.day_starts[1] = end;
|
|
pdi.long_events = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
|
|
pdi.events[0] = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent));
|
|
pdi.start_hour = e_cal_model_get_work_day_start_hour (model);
|
|
pdi.end_hour = e_cal_model_get_work_day_end_hour (model);
|
|
if (e_cal_model_get_work_day_end_minute (model) != 0)
|
|
pdi.end_hour++;
|
|
pdi.mins_per_row = get_day_view_time_divisions ();
|
|
pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
|
|
pdi.start_minute_offset = pdi.start_hour * 60;
|
|
pdi.end_minute_offset = pdi.end_hour * 60;
|
|
pdi.use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
|
|
pdi.zone = e_cal_model_get_timezone (model);
|
|
|
|
/* Get the events from the server. */
|
|
e_cal_model_generate_instances_sync (model, start, end, print_day_details_cb, &pdi);
|
|
qsort (pdi.long_events->data, pdi.long_events->len,
|
|
sizeof (EDayViewEvent), e_day_view_event_sort_func);
|
|
qsort (pdi.events[0]->data, pdi.events[0]->len,
|
|
sizeof (EDayViewEvent), e_day_view_event_sort_func);
|
|
|
|
pdi.start_hour = MIN (pdi.start_hour, _pdi->start_hour);
|
|
pdi.end_hour = MAX (pdi.end_hour, _pdi->end_hour);
|
|
|
|
/* TODO: This should be redundant */
|
|
/* Also print events outside of work hours */
|
|
if (pdi.events[0]->len > 0) {
|
|
struct icaltimetype tt;
|
|
|
|
event = &g_array_index (pdi.events[0], EDayViewEvent, 0);
|
|
tt = icaltime_from_timet_with_zone (event->start, FALSE, zone);
|
|
if (tt.hour < pdi.start_hour)
|
|
pdi.start_hour = tt.hour;
|
|
pdi.start_minute_offset = pdi.start_hour * 60;
|
|
|
|
event = &g_array_index (pdi.events[0], EDayViewEvent, pdi.events[0]->len - 1);
|
|
tt = icaltime_from_timet_with_zone (event->end, FALSE, zone);
|
|
if (tt.hour > pdi.end_hour || tt.hour == 0) {
|
|
pdi.end_hour = tt.hour ? tt.hour : 24;
|
|
if (tt.minute > 0)
|
|
pdi.end_hour++;
|
|
}
|
|
pdi.end_minute_offset = pdi.end_hour * 60;
|
|
|
|
pdi.rows = (pdi.end_hour - pdi.start_hour) * (60 / pdi.mins_per_row);
|
|
}
|
|
|
|
/* Lay them out the long events, across the top of the page. */
|
|
e_day_view_layout_long_events (pdi.long_events, pdi.days_shown,
|
|
pdi.day_starts, &rows_in_top_display);
|
|
|
|
/*Print the long events. */
|
|
font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
|
|
|
|
/* We always leave space for DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY in the
|
|
* top display, but we may have more rows than that, in which case
|
|
* the main display area will be compressed. */
|
|
/* Limit long day event to half the height of the panel */
|
|
rows_in_top_display = MIN (MAX (rows_in_top_display,
|
|
DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY),
|
|
(bottom - top) * 0.5 / DAY_VIEW_ROW_HEIGHT);
|
|
|
|
if (rows_in_top_display > pdi.long_events->len)
|
|
rows_in_top_display = pdi.long_events->len;
|
|
|
|
for (i = 0; i < rows_in_top_display && i < pdi.long_events->len; i++) {
|
|
event = &g_array_index (pdi.long_events, EDayViewEvent, i);
|
|
print_day_long_event (
|
|
context, font, left, right,
|
|
top + LONG_DAY_EVENTS_TOP_SPACING, bottom,
|
|
DAY_VIEW_ROW_HEIGHT, event, &pdi, model);
|
|
}
|
|
|
|
if (rows_in_top_display < pdi.long_events->len) {
|
|
/* too many events */
|
|
cairo_t *cr = gtk_print_context_get_cairo_context (context);
|
|
gint x, y;
|
|
|
|
if (!pixbuf) {
|
|
const gchar **xpm = (const gchar **) jump_xpm;
|
|
|
|
/* this ugly thing is here only to get rid of compiler warning
|
|
* about unused 'jump_xpm_focused' */
|
|
if (pixbuf)
|
|
xpm = (const gchar **) jump_xpm_focused;
|
|
|
|
pixbuf = gdk_pixbuf_new_from_xpm_data (xpm);
|
|
}
|
|
|
|
/* Right align - 10 comes from print_day_long_event too */
|
|
x = right - gdk_pixbuf_get_width (pixbuf) * 0.5 - 10;
|
|
/* Placing '...' over the last all day event entry printed. '-1 -1' comes
|
|
from print_long_day_event (top / bottom spacing in each cell) */
|
|
y = top + LONG_DAY_EVENTS_TOP_SPACING
|
|
+ DAY_VIEW_ROW_HEIGHT * (i - 1)
|
|
+ (DAY_VIEW_ROW_HEIGHT - 1 - 1) * 0.5;
|
|
|
|
cairo_save (cr);
|
|
cairo_scale (cr, 0.5, 0.5);
|
|
gdk_cairo_set_source_pixbuf (cr, pixbuf, x * 2.0, y * 2.0);
|
|
cairo_paint (cr);
|
|
cairo_restore (cr);
|
|
}
|
|
|
|
if (!rows_in_top_display)
|
|
rows_in_top_display++;
|
|
|
|
/* Draw the border around the long events. */
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
print_border (
|
|
context, left, right,
|
|
top, top + rows_in_top_display * DAY_VIEW_ROW_HEIGHT +
|
|
LONG_DAY_EVENTS_TOP_SPACING + LONG_DAY_EVENTS_BOTTOM_SPACING,
|
|
1.0, -1.0);
|
|
|
|
/* Adjust the area containing the main display. */
|
|
top += rows_in_top_display * DAY_VIEW_ROW_HEIGHT
|
|
+ LONG_DAY_EVENTS_TOP_SPACING
|
|
+ LONG_DAY_EVENTS_BOTTOM_SPACING;
|
|
|
|
/* lay out the short events, within the day. */
|
|
e_day_view_layout_day_events (pdi.events[0], CALC_DAY_VIEW_ROWS (pdi.mins_per_row),
|
|
pdi.mins_per_row, pdi.cols_per_row, -1);
|
|
|
|
/* use font like with 30 minutes time division */
|
|
rows_with_30_mins = (pdi.end_hour - pdi.start_hour) * (60 / 30);
|
|
|
|
/* print the short events. */
|
|
if (top > bottom )
|
|
max_font_size = ((top - bottom) / rows_with_30_mins) - 4;
|
|
else
|
|
max_font_size = ((bottom - top ) / rows_with_30_mins) - 4;
|
|
font_size = MIN (DAY_NORMAL_FONT_SIZE, max_font_size);
|
|
font = get_font_for_size (font_size, PANGO_WEIGHT_NORMAL);
|
|
|
|
for (i = 0; i < pdi.events[0]->len; i++) {
|
|
event = &g_array_index (pdi.events[0], EDayViewEvent, i);
|
|
print_day_event (context, font, left,
|
|
right, top, bottom, event, &pdi, model);
|
|
}
|
|
|
|
/* Free everything. */
|
|
if (pixbuf)
|
|
g_object_unref (pixbuf);
|
|
free_event_array (pdi.long_events);
|
|
pango_font_description_free (font);
|
|
g_array_free (pdi.long_events, TRUE);
|
|
free_event_array (pdi.events[0]);
|
|
g_array_free (pdi.events[0], TRUE);
|
|
}
|
|
|
|
/* Figure out what the overal hour limits are */
|
|
static gboolean
|
|
print_work_week_view_cb (ECalComponent *comp,
|
|
time_t istart,
|
|
time_t iend,
|
|
gpointer data)
|
|
{
|
|
ECalModelGenerateInstancesData *mdata = (ECalModelGenerateInstancesData *) data;
|
|
struct pdinfo *pdi = (struct pdinfo *) mdata->cb_data;
|
|
struct icaltimetype tt;
|
|
|
|
tt = icaltime_from_timet_with_zone (istart, FALSE, pdi->zone);
|
|
pdi->start_hour = MIN (pdi->start_hour, tt.hour);
|
|
|
|
tt = icaltime_from_timet_with_zone (iend, FALSE, pdi->zone);
|
|
/* If we're past the hour, use the next one */
|
|
pdi->end_hour = MAX (pdi->end_hour, tt.minute ? tt.hour + 1 : tt.hour);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
print_work_week_view (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t date)
|
|
{
|
|
GtkPageSetup *setup;
|
|
icaltimezone *zone;
|
|
time_t when, start, end;
|
|
gdouble width, height, l;
|
|
gdouble small_month_width;
|
|
gdouble weeknum_inc;
|
|
gint i, days = 5;
|
|
gchar buf[100];
|
|
const gint LONG_EVENT_OFFSET = 6;
|
|
struct pdinfo pdi = { 0 };
|
|
struct tm tm;
|
|
gdouble day_width, day_x;
|
|
ECalModel *model;
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
zone = e_cal_model_get_timezone (model);
|
|
|
|
setup = gtk_print_context_get_page_setup (context);
|
|
|
|
width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
|
|
height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
|
|
|
|
small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
|
|
weeknum_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;
|
|
|
|
/* We always start on a Monday */
|
|
start = time_week_begin_with_zone (date, 1, zone);
|
|
end = time_add_day_with_zone (start, days, zone);
|
|
|
|
pdi.days_shown = days;
|
|
pdi.start_hour = e_cal_model_get_work_day_start_hour (model);
|
|
pdi.end_hour = e_cal_model_get_work_day_end_hour (model);
|
|
pdi.zone = zone;
|
|
|
|
e_cal_model_generate_instances_sync (model, start, end, print_work_week_view_cb, &pdi);
|
|
|
|
print_work_week_background (context, gcal, date, &pdi, 0.0, width,
|
|
HEADER_HEIGHT + DAY_VIEW_ROW_HEIGHT + LONG_EVENT_OFFSET,
|
|
height);
|
|
|
|
print_border (context, 0.0, width, 0.0, HEADER_HEIGHT, 1.0, 0.9);
|
|
|
|
/* Print the 2 mini calendar-months. */
|
|
l = width - SMALL_MONTH_PAD - (small_month_width + weeknum_inc) * 2 -
|
|
SMALL_MONTH_SPACING;
|
|
|
|
print_month_small (context, gcal, start,
|
|
l, 4, l + small_month_width + weeknum_inc, HEADER_HEIGHT + 4,
|
|
DATE_MONTH | DATE_YEAR, start, end, FALSE);
|
|
|
|
l += SMALL_MONTH_SPACING + small_month_width + weeknum_inc;
|
|
print_month_small (context, gcal,
|
|
time_add_month_with_zone (start, 1, zone),
|
|
l, 4, l + small_month_width + weeknum_inc, HEADER_HEIGHT + 4,
|
|
DATE_MONTH | DATE_YEAR, 0, 0, FALSE);
|
|
|
|
/* Print the start day of the week, e.g. '7th May 2001'. */
|
|
convert_timet_to_struct_tm (start, zone, &tm);
|
|
format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
|
|
print_text_size_bold (context, buf, PANGO_ALIGN_LEFT,
|
|
3, width,
|
|
4, 4 + 24);
|
|
|
|
/* Print the end day of the week, e.g. '13th May 2001'. */
|
|
/* We need to substract one or the wrong day will be printed */
|
|
convert_timet_to_struct_tm (
|
|
time_add_day_with_zone (end, -1, zone), zone, &tm);
|
|
format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
|
|
print_text_size_bold (context, buf, PANGO_ALIGN_LEFT,
|
|
3, width,
|
|
24 + 3, 24 + 3 + 24);
|
|
|
|
/* Now print each days' events */
|
|
day_width = (width - 2 *DAY_VIEW_TIME_COLUMN_WIDTH) / days;
|
|
when = start;
|
|
for (i = 0; i < days; ++i) {
|
|
day_x = DAY_VIEW_TIME_COLUMN_WIDTH + day_width * i;
|
|
|
|
/* Print the day, e.g. 'Tuesday'. */
|
|
convert_timet_to_struct_tm (when, zone, &tm);
|
|
format_date (&tm, DATE_DAYNAME, buf, 100);
|
|
|
|
print_text_size_bold (context, buf, PANGO_ALIGN_LEFT,
|
|
day_x + 4, day_x + day_width,
|
|
HEADER_HEIGHT + 4, HEADER_HEIGHT + 4 + 18);
|
|
|
|
print_work_week_day_details (context, gcal, when,
|
|
day_x, day_x + day_width,
|
|
HEADER_HEIGHT, height, &pdi);
|
|
when = time_add_day_with_zone (when, 1, zone);
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_week_view (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t date)
|
|
{
|
|
GtkPageSetup *setup;
|
|
ECalModel *model;
|
|
icaltimezone *zone;
|
|
gdouble l, week_numbers_inc, small_month_width;
|
|
gchar buf[100];
|
|
time_t when;
|
|
gint week_start_day;
|
|
struct tm tm;
|
|
gdouble width, height;
|
|
|
|
setup = gtk_print_context_get_page_setup (context);
|
|
|
|
width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
|
|
height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
|
|
small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
|
|
week_numbers_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
zone = e_cal_model_get_timezone (model);
|
|
|
|
convert_timet_to_struct_tm (date, zone, &tm);
|
|
week_start_day = e_cal_model_get_week_start_day (model);
|
|
when = time_week_begin_with_zone (date, week_start_day, zone);
|
|
|
|
/* If the week starts on a Sunday, we have to show the Saturday first,
|
|
* since the weekend is compressed. */
|
|
if (week_start_day == 0) {
|
|
if (tm.tm_wday == 6)
|
|
when = time_add_day_with_zone (when, 6, zone);
|
|
else
|
|
when = time_add_day_with_zone (when, -1, zone);
|
|
}
|
|
|
|
/* Print the main week view. */
|
|
print_week_summary (context, gcal, when, FALSE, 1, 0,
|
|
WEEK_EVENT_FONT_SIZE, WEEK_SMALL_FONT_SIZE,
|
|
0.0, width,
|
|
HEADER_HEIGHT + 20, height);
|
|
|
|
/* Print the border around the main view. */
|
|
print_border (context, 0.0, width, HEADER_HEIGHT ,
|
|
height, 1.0, -1.0);
|
|
|
|
/* Print the border around the header area. */
|
|
print_border (context, 0.0, width,
|
|
0.0, HEADER_HEIGHT + 2.0 + 20, 1.0, 0.9);
|
|
|
|
/* Print the 2 mini calendar-months. */
|
|
l = width - SMALL_MONTH_PAD - (small_month_width + week_numbers_inc) * 2
|
|
- SMALL_MONTH_SPACING;
|
|
print_month_small (context, gcal, when,
|
|
l, 4, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 10,
|
|
DATE_MONTH | DATE_YEAR, when,
|
|
time_add_week_with_zone (when, 1, zone), FALSE);
|
|
|
|
l += SMALL_MONTH_SPACING + small_month_width + week_numbers_inc;
|
|
print_month_small (context, gcal,
|
|
time_add_month_with_zone (when, 1, zone),
|
|
l, 4, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 10,
|
|
DATE_MONTH | DATE_YEAR, when,
|
|
time_add_week_with_zone (when, 1, zone), FALSE);
|
|
|
|
/* Print the start day of the week, e.g. '7th May 2001'. */
|
|
convert_timet_to_struct_tm (when, zone, &tm);
|
|
format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
|
|
print_text_abs_bold (context, buf, WEEK_NORMAL_FONT_SIZE, PANGO_ALIGN_LEFT,
|
|
3, width, 4, 4 + 24);
|
|
|
|
/* Print the end day of the week, e.g. '13th May 2001'. */
|
|
when = time_add_day_with_zone (when, 6, zone);
|
|
convert_timet_to_struct_tm (when, zone, &tm);
|
|
format_date (&tm, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100);
|
|
print_text_abs_bold (context, buf, WEEK_NORMAL_FONT_SIZE, PANGO_ALIGN_LEFT,
|
|
3, width, 24 + 3, 24 + 3 + 24);
|
|
}
|
|
|
|
static void
|
|
print_month_view (GtkPrintContext *context,
|
|
GnomeCalendar *gcal,
|
|
time_t date)
|
|
{
|
|
ECalModel *model;
|
|
GtkPageSetup *setup;
|
|
icaltimezone *zone;
|
|
gchar buf[100];
|
|
gdouble width, height;
|
|
gdouble l, week_numbers_inc, small_month_width;
|
|
struct tm tm;
|
|
|
|
model = gnome_calendar_get_model (gcal);
|
|
zone = e_cal_model_get_timezone (model);
|
|
|
|
setup = gtk_print_context_get_page_setup (context);
|
|
|
|
width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
|
|
height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
|
|
small_month_width = calc_small_month_width (context, HEADER_HEIGHT);
|
|
week_numbers_inc = get_show_week_numbers () ? small_month_width / 7.0 : 0;
|
|
|
|
/* Print the main month view. */
|
|
print_month_summary (context, gcal, date, 0.0, width, HEADER_HEIGHT, height);
|
|
|
|
/* Print the border around the header. */
|
|
print_border (context, 0.0, width, 0.0, HEADER_HEIGHT + 10, 1.0, 0.9);
|
|
|
|
l = width - SMALL_MONTH_PAD - small_month_width - week_numbers_inc;
|
|
|
|
/* Print the 2 mini calendar-months. */
|
|
print_month_small (context, gcal,
|
|
time_add_month_with_zone (date, 1, zone),
|
|
l, 4, l + small_month_width + week_numbers_inc, HEADER_HEIGHT + 4,
|
|
DATE_MONTH | DATE_YEAR, 0, 0, FALSE);
|
|
|
|
print_month_small (context, gcal,
|
|
time_add_month_with_zone (date, -1, zone),
|
|
SMALL_MONTH_PAD, 4, SMALL_MONTH_PAD + small_month_width + week_numbers_inc, HEADER_HEIGHT + 4,
|
|
DATE_MONTH | DATE_YEAR, 0, 0, FALSE);
|
|
|
|
/* Print the month, e.g. 'May 2001'. */
|
|
convert_timet_to_struct_tm (date, zone, &tm);
|
|
format_date (&tm, DATE_MONTH | DATE_YEAR, buf, 100);
|
|
print_text_size_bold (context, buf, PANGO_ALIGN_CENTER,
|
|
3, width - 3,
|
|
3, 3 + 24);
|
|
|
|
}
|
|
|
|
static gboolean
|
|
same_date (struct tm tm1,
|
|
time_t t2,
|
|
icaltimezone *zone)
|
|
{
|
|
struct tm tm2;
|
|
|
|
convert_timet_to_struct_tm (t2, zone, &tm2);
|
|
|
|
return
|
|
tm1.tm_mday == tm2.tm_mday &&
|
|
tm1.tm_mon == tm2.tm_mon &&
|
|
tm1.tm_year == tm2.tm_year;
|
|
}
|
|
|
|
static void
|
|
write_label_piece (time_t t,
|
|
time_t *start_cmp,
|
|
icaltimezone *zone,
|
|
gboolean use_24_hour_format,
|
|
gchar *buffer,
|
|
gint size,
|
|
gchar *stext,
|
|
const gchar *etext)
|
|
{
|
|
struct tm tmp_tm;
|
|
gint len;
|
|
|
|
convert_timet_to_struct_tm (t, zone, &tmp_tm);
|
|
|
|
if (stext != NULL)
|
|
strcat (buffer, stext);
|
|
|
|
len = strlen (buffer);
|
|
if (start_cmp && same_date (tmp_tm, *start_cmp, zone))
|
|
e_time_format_time (
|
|
&tmp_tm, use_24_hour_format,
|
|
FALSE, &buffer[len], size - len);
|
|
else
|
|
e_time_format_date_and_time (
|
|
&tmp_tm, use_24_hour_format, FALSE,
|
|
FALSE, &buffer[len], size - len);
|
|
if (etext != NULL)
|
|
strcat (buffer, etext);
|
|
}
|
|
|
|
static icaltimezone *
|
|
get_zone_from_tzid (ECalClient *client,
|
|
const gchar *tzid)
|
|
{
|
|
icaltimezone *zone;
|
|
|
|
/* Note that the timezones may not be on the server, so we try to get
|
|
* the builtin timezone with the TZID first. */
|
|
zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
|
|
if (!zone && tzid) {
|
|
GError *error = NULL;
|
|
|
|
if (!e_cal_client_get_timezone_sync (client, tzid, &zone, NULL, &error)) {
|
|
g_warning ("Couldn't get timezone '%s' from server: %s",
|
|
tzid ? tzid : "", error ? error->message : "Unknown error");
|
|
if (error)
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
return zone;
|
|
}
|
|
|
|
static void
|
|
print_date_label (GtkPrintContext *context,
|
|
ECalComponent *comp,
|
|
ECalClient *client,
|
|
icaltimezone *zone,
|
|
gboolean use_24_hour_format,
|
|
gdouble left,
|
|
gdouble right,
|
|
gdouble top,
|
|
gdouble bottom)
|
|
{
|
|
icaltimezone *start_zone, *end_zone, *due_zone, *completed_zone;
|
|
ECalComponentDateTime datetime;
|
|
time_t start = 0, end = 0, complete = 0, due = 0;
|
|
static gchar buffer[1024];
|
|
|
|
e_cal_component_get_dtstart (comp, &datetime);
|
|
if (datetime.value) {
|
|
start_zone = get_zone_from_tzid (client, datetime.tzid);
|
|
if (!start_zone || datetime.value->is_date)
|
|
start_zone = zone;
|
|
start = icaltime_as_timet_with_zone (*datetime.value,
|
|
start_zone);
|
|
}
|
|
e_cal_component_free_datetime (&datetime);
|
|
|
|
e_cal_component_get_dtend (comp, &datetime);
|
|
if (datetime.value) {
|
|
end_zone = get_zone_from_tzid (client, datetime.tzid);
|
|
if (!end_zone || datetime.value->is_date)
|
|
end_zone = zone;
|
|
end = icaltime_as_timet_with_zone (*datetime.value,
|
|
end_zone);
|
|
}
|
|
e_cal_component_free_datetime (&datetime);
|
|
|
|
e_cal_component_get_due (comp, &datetime);
|
|
if (datetime.value) {
|
|
due_zone = get_zone_from_tzid (client, datetime.tzid);
|
|
if (!due_zone || datetime.value->is_date)
|
|
due_zone = zone;
|
|
due = icaltime_as_timet_with_zone (*datetime.value,
|
|
due_zone);
|
|
}
|
|
e_cal_component_free_datetime (&datetime);
|
|
|
|
e_cal_component_get_completed (comp, &datetime.value);
|
|
if (datetime.value) {
|
|
completed_zone = icaltimezone_get_utc_timezone ();
|
|
complete = icaltime_as_timet_with_zone (*datetime.value,
|
|
completed_zone);
|
|
e_cal_component_free_icaltimetype (datetime.value);
|
|
}
|
|
|
|
buffer[0] = '\0';
|
|
|
|
if (start > 0)
|
|
write_label_piece (
|
|
start, NULL, zone, use_24_hour_format,
|
|
buffer, 1024, NULL, NULL);
|
|
|
|
if (end > 0 && start > 0) {
|
|
write_label_piece (
|
|
end, &start, zone, use_24_hour_format,
|
|
/* Translators: This is part of "START to END" text,
|
|
* where START and END are date/times. */
|
|
buffer, 1024, _(" to "), NULL);
|
|
}
|
|
|
|
if (complete > 0) {
|
|
if (start > 0) {
|
|
write_label_piece (
|
|
complete, NULL, zone, use_24_hour_format,
|
|
/* Translators: This is part of "START to END
|
|
* (Completed COMPLETED)", where COMPLETED is a
|
|
* completed date/time. */
|
|
buffer, 1024, _(" (Completed "), ")");
|
|
} else {
|
|
write_label_piece (
|
|
complete, &start, zone, use_24_hour_format,
|
|
/* Translators: This is part of "Completed COMPLETED",
|
|
* where COMPLETED is a completed date/time. */
|
|
buffer, 1024, _("Completed "), NULL);
|
|
}
|
|
}
|
|
|
|
if (due > 0 && complete == 0) {
|
|
if (start > 0) {
|
|
write_label_piece (
|
|
due, NULL, zone, use_24_hour_format,
|
|
/* Translators: This is part of "START (Due DUE)",
|
|
* where START and DUE are dates/times. */
|
|
buffer, 1024, _(" (Due "), ")");
|
|
} else {
|
|
write_label_piece (
|
|
due, &start, zone, use_24_hour_format,
|
|
/* Translators: This is part of "Due DUE",
|
|
* where DUE is a date/time due the event
|
|
* should be finished. */
|
|
buffer, 1024, _("Due "), NULL);
|
|
}
|
|
}
|
|
|
|
print_text_size_bold (context, buffer, PANGO_ALIGN_LEFT,
|
|
left, right, top, top + 24);
|
|
}
|
|
|
|
static void
|
|
print_calendar_draw_page (GtkPrintOperation *operation,
|
|
GtkPrintContext *context,
|
|
gint page_nr,
|
|
PrintCalItem *pcali)
|
|
{
|
|
switch (gnome_calendar_get_view (pcali->gcal)) {
|
|
case GNOME_CAL_DAY_VIEW:
|
|
print_day_view (context, pcali->gcal, pcali->start);
|
|
break;
|
|
case GNOME_CAL_WORK_WEEK_VIEW:
|
|
print_work_week_view (context, pcali->gcal, pcali->start);
|
|
break;
|
|
case GNOME_CAL_WEEK_VIEW:
|
|
print_week_view (context, pcali->gcal, pcali->start);
|
|
break;
|
|
case GNOME_CAL_MONTH_VIEW:
|
|
print_month_view (context, pcali->gcal, pcali->start);
|
|
break;
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
}
|
|
|
|
void
|
|
print_calendar (GnomeCalendar *gcal,
|
|
GtkPrintOperationAction action,
|
|
time_t start)
|
|
{
|
|
GtkPrintOperation *operation;
|
|
PrintCalItem pcali;
|
|
|
|
g_return_if_fail (gcal != NULL);
|
|
g_return_if_fail (GNOME_IS_CALENDAR (gcal));
|
|
|
|
if (gnome_calendar_get_view (gcal) == GNOME_CAL_MONTH_VIEW) {
|
|
GnomeCalendarViewType view_type;
|
|
ECalendarView *calendar_view;
|
|
EWeekView *week_view;
|
|
|
|
view_type = gnome_calendar_get_view (gcal);
|
|
calendar_view = gnome_calendar_get_calendar_view (gcal, view_type);
|
|
week_view = E_WEEK_VIEW (calendar_view);
|
|
|
|
if (week_view && week_view->multi_week_view &&
|
|
week_view->weeks_shown >= 4 &&
|
|
g_date_valid (&week_view->first_day_shown)) {
|
|
|
|
GDate date = week_view->first_day_shown;
|
|
struct icaltimetype start_tt;
|
|
|
|
g_date_add_days (&date, 7);
|
|
|
|
start_tt = icaltime_null_time ();
|
|
start_tt.is_date = TRUE;
|
|
start_tt.year = g_date_get_year (&date);
|
|
start_tt.month = g_date_get_month (&date);
|
|
start_tt.day = g_date_get_day (&date);
|
|
|
|
start = icaltime_as_timet (start_tt);
|
|
} else if (week_view && week_view->multi_week_view) {
|
|
start = week_view->day_starts[0];
|
|
}
|
|
}
|
|
|
|
pcali.gcal = (GnomeCalendar *) gcal;
|
|
pcali.start = start;
|
|
|
|
operation = e_print_operation_new ();
|
|
gtk_print_operation_set_n_pages (operation, 1);
|
|
|
|
g_signal_connect (
|
|
operation, "draw_page",
|
|
G_CALLBACK (print_calendar_draw_page), &pcali);
|
|
|
|
gtk_print_operation_run (operation, action, NULL, NULL);
|
|
|
|
g_object_unref (operation);
|
|
}
|
|
|
|
/* returns number of required pages, when page_nr is -1 */
|
|
static gint
|
|
print_comp_draw_real (GtkPrintOperation *operation,
|
|
GtkPrintContext *context,
|
|
gint page_nr,
|
|
PrintCompItem *pci)
|
|
{
|
|
GtkPageSetup *setup;
|
|
PangoFontDescription *font;
|
|
ECalClient *client;
|
|
ECalComponent *comp;
|
|
ECalComponentVType vtype;
|
|
ECalComponentText text;
|
|
GSList *desc, *l;
|
|
GSList *contact_list, *elem;
|
|
|
|
const gchar *title, *categories, *location;
|
|
gchar *categories_string, *location_string, *summary_string;
|
|
gdouble header_size;
|
|
cairo_t *cr;
|
|
gdouble width, height, page_start;
|
|
gdouble top;
|
|
gint pages = 1;
|
|
|
|
setup = gtk_print_context_get_page_setup (context);
|
|
|
|
width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
|
|
height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
|
|
|
|
top = 0.0;
|
|
|
|
/* Either draw only the right page or do not draw
|
|
* anything when calculating number of pages. */
|
|
if (page_nr != -1)
|
|
top = top - ((page_nr) * height);
|
|
else
|
|
top = height;
|
|
|
|
page_start = top;
|
|
|
|
/* PrintCompItem structure contains elements to be used
|
|
* with the Print Context , obtained in comp_draw_page
|
|
*/
|
|
client = pci->client;
|
|
comp = pci->comp;
|
|
|
|
vtype = e_cal_component_get_vtype (comp);
|
|
|
|
/* We should only be asked to print VEVENTs, VTODOs, or VJOURNALs. */
|
|
if (vtype == E_CAL_COMPONENT_EVENT)
|
|
title = _("Appointment");
|
|
else if (vtype == E_CAL_COMPONENT_TODO)
|
|
title = _("Task");
|
|
else if (vtype == E_CAL_COMPONENT_JOURNAL)
|
|
title = _("Memo");
|
|
else
|
|
return pages;
|
|
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
/* Print the title in a box at the top of the page. */
|
|
font = get_font_for_size (18, PANGO_WEIGHT_BOLD);
|
|
header_size = 40;
|
|
|
|
if (page_nr == 0) {
|
|
print_border (context, 0.0, width, 0.0, header_size,
|
|
1.0, 0.9);
|
|
print_text (context, font, title, PANGO_ALIGN_CENTER, 0.0, width,
|
|
0.1, header_size - 0.1);
|
|
pango_font_description_free (font);
|
|
}
|
|
|
|
top += header_size + 30;
|
|
|
|
/* Summary */
|
|
font = get_font_for_size (18, PANGO_WEIGHT_BOLD);
|
|
e_cal_component_get_summary (comp, &text);
|
|
summary_string = g_strdup_printf (_("Summary: %s"), text.value);
|
|
top = bound_text (context, font, summary_string, -1, 0.0, top, width,
|
|
height, FALSE, &page_start, &pages);
|
|
|
|
g_free (summary_string);
|
|
|
|
/* Location */
|
|
e_cal_component_get_location (comp, &location);
|
|
if (location && location[0]) {
|
|
location_string = g_strdup_printf (_("Location: %s"),
|
|
location);
|
|
top = bound_text (context, font, location_string, -1, 0.0,
|
|
top + 3, width, height, FALSE, &page_start, &pages);
|
|
g_free (location_string);
|
|
}
|
|
|
|
/* Date information */
|
|
if (page_nr == 0)
|
|
print_date_label (
|
|
context, comp, client,
|
|
pci->zone, pci->use_24_hour_format,
|
|
0.0, width, top + 3, top + 15);
|
|
top += 20;
|
|
|
|
/* Attendees */
|
|
if ((page_nr == 0) && e_cal_component_has_attendees (comp)) {
|
|
top = bound_text (
|
|
context, font, _("Attendees: "), -1, 0.0,
|
|
top, width, height, FALSE, &page_start, &pages);
|
|
pango_font_description_free (font);
|
|
font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
|
|
top = print_attendees (
|
|
context, font, cr, 0.0, width,
|
|
top, height, comp, page_nr, &pages);
|
|
top += get_font_size (font) - 6;
|
|
}
|
|
|
|
pango_font_description_free (font);
|
|
|
|
font = get_font_for_size (12, PANGO_WEIGHT_NORMAL);
|
|
|
|
/* For a VTODO we print the Status, Priority, % Complete and URL. */
|
|
if (vtype == E_CAL_COMPONENT_TODO) {
|
|
icalproperty_status status;
|
|
const gchar *status_string = NULL;
|
|
gint *percent;
|
|
gint *priority;
|
|
const gchar *url;
|
|
|
|
/* Status */
|
|
e_cal_component_get_status (comp, &status);
|
|
if (status != ICAL_STATUS_NONE) {
|
|
switch (status) {
|
|
case ICAL_STATUS_NEEDSACTION:
|
|
status_string = _("Not Started");
|
|
break;
|
|
case ICAL_STATUS_INPROCESS:
|
|
status_string = _("In Progress");
|
|
break;
|
|
case ICAL_STATUS_COMPLETED:
|
|
status_string = _("Completed");
|
|
break;
|
|
case ICAL_STATUS_CANCELLED:
|
|
status_string = _("Canceled");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (status_string) {
|
|
gchar *status_text = g_strdup_printf (_("Status: %s"),
|
|
status_string);
|
|
top = bound_text (context, font, status_text, -1,
|
|
0.0, top, width, height, FALSE, &page_start, &pages);
|
|
top += get_font_size (font) - 6;
|
|
g_free (status_text);
|
|
}
|
|
}
|
|
|
|
/* Priority */
|
|
e_cal_component_get_priority (comp, &priority);
|
|
if (priority && *priority >= 0) {
|
|
gchar *pri_text;
|
|
|
|
pri_text = g_strdup_printf (
|
|
_("Priority: %s"),
|
|
e_cal_util_priority_to_string (*priority));
|
|
top = bound_text (
|
|
context, font, pri_text, -1,
|
|
0.0, top, width, height, FALSE,
|
|
&page_start, &pages);
|
|
top += get_font_size (font) - 6;
|
|
g_free (pri_text);
|
|
}
|
|
|
|
if (priority)
|
|
e_cal_component_free_priority (priority);
|
|
|
|
/* Percent Complete */
|
|
e_cal_component_get_percent (comp, &percent);
|
|
if (percent) {
|
|
gchar *percent_string;
|
|
|
|
percent_string = g_strdup_printf (_("Percent Complete: %i"), *percent);
|
|
e_cal_component_free_percent (percent);
|
|
|
|
top = bound_text (context, font, percent_string, -1,
|
|
0.0, top, width, height, FALSE, &page_start, &pages);
|
|
top += get_font_size (font) - 6;
|
|
}
|
|
|
|
/* URL */
|
|
e_cal_component_get_url (comp, &url);
|
|
if (url && url[0]) {
|
|
gchar *url_string = g_strdup_printf (_("URL: %s"),
|
|
url);
|
|
|
|
top = bound_text (context, font, url_string, -1,
|
|
0.0, top, width, height, TRUE, &page_start, &pages);
|
|
top += get_font_size (font) - 6;
|
|
g_free (url_string);
|
|
}
|
|
}
|
|
|
|
/* Categories */
|
|
e_cal_component_get_categories (comp, &categories);
|
|
if (categories && categories[0]) {
|
|
categories_string = g_strdup_printf (_("Categories: %s"),
|
|
categories);
|
|
top = bound_text (context, font, categories_string, -1,
|
|
0.0, top, width, height, TRUE, &page_start, &pages);
|
|
top += get_font_size (font) - 6;
|
|
g_free (categories_string);
|
|
}
|
|
|
|
/* Contacts */
|
|
e_cal_component_get_contact_list (comp, &contact_list);
|
|
if (contact_list) {
|
|
GString *contacts = g_string_new (_("Contacts: "));
|
|
for (elem = contact_list; elem; elem = elem->next) {
|
|
ECalComponentText *t = elem->data;
|
|
/* Put a comma between contacts. */
|
|
if (elem != contact_list)
|
|
g_string_append (contacts, ", ");
|
|
g_string_append (contacts, t->value);
|
|
}
|
|
e_cal_component_free_text_list (contact_list);
|
|
|
|
top = bound_text (context, font, contacts->str, -1,
|
|
0.0, top, width, height, TRUE, &page_start, &pages);
|
|
top += get_font_size (font) - 6;
|
|
g_string_free (contacts, TRUE);
|
|
}
|
|
top += 16;
|
|
|
|
/* Description */
|
|
e_cal_component_get_description_list (comp, &desc);
|
|
for (l = desc; l != NULL; l = l->next) {
|
|
ECalComponentText *ptext = l->data;
|
|
const gchar *line, *next_line;
|
|
|
|
for (line = ptext->value; line != NULL; line = next_line) {
|
|
next_line = strchr (line, '\n');
|
|
|
|
top = bound_text (
|
|
context, font, line,
|
|
next_line ? next_line - line : -1,
|
|
0.0, top + 3, width, height, TRUE,
|
|
&page_start, &pages);
|
|
|
|
if (next_line) {
|
|
next_line++;
|
|
if (!*next_line)
|
|
next_line = NULL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
e_cal_component_free_text_list (desc);
|
|
pango_font_description_free (font);
|
|
|
|
return pages;
|
|
}
|
|
|
|
static void
|
|
print_comp_draw_page (GtkPrintOperation *operation,
|
|
GtkPrintContext *context,
|
|
gint page_nr,
|
|
PrintCompItem *pci)
|
|
{
|
|
print_comp_draw_real (operation, context, page_nr, pci);
|
|
}
|
|
|
|
static void
|
|
print_comp_begin_print (GtkPrintOperation *operation,
|
|
GtkPrintContext *context,
|
|
PrintCompItem *pci)
|
|
{
|
|
gint pages;
|
|
|
|
pages = print_comp_draw_real (operation, context, -1, pci);
|
|
|
|
gtk_print_operation_set_n_pages (operation, pages);
|
|
}
|
|
|
|
void
|
|
print_comp (ECalComponent *comp,
|
|
ECalClient *cal_client,
|
|
icaltimezone *zone,
|
|
gboolean use_24_hour_format,
|
|
GtkPrintOperationAction action)
|
|
{
|
|
GtkPrintOperation *operation;
|
|
PrintCompItem pci;
|
|
|
|
g_return_if_fail (E_IS_CAL_COMPONENT (comp));
|
|
|
|
pci.comp = comp;
|
|
pci.client = cal_client;
|
|
pci.zone = zone;
|
|
pci.use_24_hour_format = use_24_hour_format;
|
|
|
|
operation = e_print_operation_new ();
|
|
gtk_print_operation_set_n_pages (operation, 1);
|
|
|
|
g_signal_connect (
|
|
operation, "begin-print",
|
|
G_CALLBACK (print_comp_begin_print), &pci);
|
|
|
|
g_signal_connect (
|
|
operation, "draw-page",
|
|
G_CALLBACK (print_comp_draw_page), &pci);
|
|
|
|
gtk_print_operation_run (operation, action, NULL, NULL);
|
|
|
|
g_object_unref (operation);
|
|
}
|
|
|
|
static void
|
|
print_title (GtkPrintContext *context,
|
|
const gchar *text,
|
|
gdouble page_width)
|
|
{
|
|
PangoFontDescription *desc;
|
|
PangoLayout *layout;
|
|
cairo_t *cr;
|
|
|
|
cr = gtk_print_context_get_cairo_context (context);
|
|
|
|
desc = pango_font_description_from_string (FONT_FAMILY " Bold 18");
|
|
|
|
layout = gtk_print_context_create_pango_layout (context);
|
|
pango_layout_set_text (layout, text, -1);
|
|
pango_layout_set_font_description (layout, desc);
|
|
pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
|
|
pango_layout_set_width (layout, pango_units_from_double (page_width));
|
|
|
|
cairo_save (cr);
|
|
|
|
cairo_move_to (cr, 0.0, 0.0);
|
|
pango_cairo_show_layout (cr, layout);
|
|
cairo_translate (cr, 0.0, 18);
|
|
cairo_save (cr);
|
|
cairo_restore (cr);
|
|
|
|
g_object_unref (layout);
|
|
|
|
pango_font_description_free (desc);
|
|
}
|
|
|
|
struct print_opts {
|
|
EPrintable *printable;
|
|
const gchar *print_header;
|
|
};
|
|
|
|
static void
|
|
print_table_draw_page (GtkPrintOperation *operation,
|
|
GtkPrintContext *context,
|
|
gint page_nr,
|
|
struct print_opts *opts)
|
|
{
|
|
GtkPageSetup *setup;
|
|
gdouble width;
|
|
|
|
setup = gtk_print_context_get_page_setup (context);
|
|
|
|
width = gtk_page_setup_get_page_width (setup, GTK_UNIT_POINTS);
|
|
|
|
do {
|
|
/* TODO Allow the user to customize the title. */
|
|
print_title (context, opts->print_header, width);
|
|
|
|
if (e_printable_data_left (opts->printable))
|
|
e_printable_print_page (
|
|
opts->printable, context, width, 24, TRUE);
|
|
|
|
} while (e_printable_data_left (opts->printable));
|
|
|
|
g_free (opts);
|
|
}
|
|
|
|
void
|
|
print_table (ETable *table,
|
|
const gchar *dialog_title,
|
|
const gchar *print_header,
|
|
GtkPrintOperationAction action)
|
|
{
|
|
GtkPrintOperation *operation;
|
|
EPrintable *printable;
|
|
struct print_opts *opts;
|
|
|
|
printable = e_table_get_printable (table);
|
|
g_object_ref_sink (printable);
|
|
e_printable_reset (printable);
|
|
|
|
operation = e_print_operation_new ();
|
|
gtk_print_operation_set_n_pages (operation, 1);
|
|
|
|
opts = g_malloc (sizeof (struct print_opts));
|
|
opts->printable = printable;
|
|
opts->print_header = print_header;
|
|
|
|
g_signal_connect (
|
|
operation, "draw_page",
|
|
G_CALLBACK (print_table_draw_page), opts);
|
|
|
|
gtk_print_operation_run (operation, action, NULL, NULL);
|
|
|
|
g_object_unref (operation);
|
|
}
|