diff options
author | Damon Chaplin <damon@ximian.com> | 2001-05-29 10:18:00 +0800 |
---|---|---|
committer | Damon Chaplin <damon@src.gnome.org> | 2001-05-29 10:18:00 +0800 |
commit | ff523035dd2592f6ff25955e7f1e379f24640347 (patch) | |
tree | 586ea9b1c89726cace082c42865d82d61db5eb19 | |
parent | aaa9b9bb2ab4ff9fba2a6b9cc68fbc7fdfaa71bc (diff) | |
download | gsoc2013-evolution-ff523035dd2592f6ff25955e7f1e379f24640347.tar gsoc2013-evolution-ff523035dd2592f6ff25955e7f1e379f24640347.tar.gz gsoc2013-evolution-ff523035dd2592f6ff25955e7f1e379f24640347.tar.bz2 gsoc2013-evolution-ff523035dd2592f6ff25955e7f1e379f24640347.tar.lz gsoc2013-evolution-ff523035dd2592f6ff25955e7f1e379f24640347.tar.xz gsoc2013-evolution-ff523035dd2592f6ff25955e7f1e379f24640347.tar.zst gsoc2013-evolution-ff523035dd2592f6ff25955e7f1e379f24640347.zip |
new files to contain layout code split off from EDayView an EWeekView, so
2001-05-28 Damon Chaplin <damon@ximian.com>
* gui/e-week-view-layout.[hc]:
* gui/e-day-view-layout.[hc]: new files to contain layout code split
off from EDayView an EWeekView, so we can use it for printing.
* gui/print.c: rewritten to use the same layout code as the EDayView
and EWeekView widgets.
* gui/gnome-cal.c (gnome_calendar_get_task_pad_cal_client): added
function so we can get the CalClient used for the TaskPad for printing.
* gui/Makefile.am (evolution_calendar_SOURCES): added
e-day-view-layout.[hc] amd e-week-view-layout.[hc].
* cal-util/timeutil.c (time_week_begin):
(time_week_end): added week_start_day argument.
* cal-util/cal-recur.c: added comments describing problems in it.
svn path=/trunk/; revision=10037
-rw-r--r-- | calendar/ChangeLog | 20 | ||||
-rw-r--r-- | calendar/cal-util/cal-recur.c | 18 | ||||
-rw-r--r-- | calendar/cal-util/timeutil.c | 26 | ||||
-rw-r--r-- | calendar/cal-util/timeutil.h | 41 | ||||
-rw-r--r-- | calendar/gui/Makefile.am | 4 | ||||
-rw-r--r-- | calendar/gui/e-day-view-layout.c | 349 | ||||
-rw-r--r-- | calendar/gui/e-day-view-layout.h | 57 | ||||
-rw-r--r-- | calendar/gui/e-day-view-main-item.c | 5 | ||||
-rw-r--r-- | calendar/gui/e-day-view.c | 369 | ||||
-rw-r--r-- | calendar/gui/e-day-view.h | 7 | ||||
-rw-r--r-- | calendar/gui/e-week-view-layout.c | 426 | ||||
-rw-r--r-- | calendar/gui/e-week-view-layout.h | 70 | ||||
-rw-r--r-- | calendar/gui/e-week-view.c | 373 | ||||
-rw-r--r-- | calendar/gui/e-week-view.h | 9 | ||||
-rw-r--r-- | calendar/gui/gnome-cal.c | 22 | ||||
-rw-r--r-- | calendar/gui/gnome-cal.h | 1 | ||||
-rw-r--r-- | calendar/gui/print.c | 1897 |
17 files changed, 2526 insertions, 1168 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 0fcee177e8..de14638714 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,23 @@ +2001-05-28 Damon Chaplin <damon@ximian.com> + + * gui/e-week-view-layout.[hc]: + * gui/e-day-view-layout.[hc]: new files to contain layout code split + off from EDayView an EWeekView, so we can use it for printing. + + * gui/print.c: rewritten to use the same layout code as the EDayView + and EWeekView widgets. + + * gui/gnome-cal.c (gnome_calendar_get_task_pad_cal_client): added + function so we can get the CalClient used for the TaskPad for printing. + + * gui/Makefile.am (evolution_calendar_SOURCES): added + e-day-view-layout.[hc] amd e-week-view-layout.[hc]. + + * cal-util/timeutil.c (time_week_begin): + (time_week_end): added week_start_day argument. + + * cal-util/cal-recur.c: added comments describing problems in it. + 2001-05-27 Rodrigo Moya <rodrigo@ximian.com> * gui/component-factory.c (remove_folder): implemented the diff --git a/calendar/cal-util/cal-recur.c b/calendar/cal-util/cal-recur.c index 0b4f114484..8ac6f771d9 100644 --- a/calendar/cal-util/cal-recur.c +++ b/calendar/cal-util/cal-recur.c @@ -84,6 +84,24 @@ * the BYSETPOS property is used to select which of the occurrences are * finally output. If BYSETPOS is not specified then all the occurrences are * output. + * + * + * FIXME: I think there are a few errors in this code: + * + * 1) I'm not sure it should be generating events in parallel like it says + * above. That needs to be checked. + * + * 2) I didn't think about timezone changes when implementing this. I just + * assumed all the occurrences of the event would be in local time. + * But when clocks go back or forwards due to daylight-saving time, some + * special handling may be needed, especially for the shorter frequencies. + * e.g. for a MINUTELY frequency it should probably iterate over all the + * minutes before and after clocks go back (i.e. some may be the same local + * time but have different UTC offsets). For longer frequencies, if an + * occurrence lands on the overlapping or non-existant time when clocks + * go back/forward, then it may need to choose which of the times to use + * or move the time forward or something. I'm not sure this is clear in the + * spec. */ /* Define this for some debugging output. */ diff --git a/calendar/cal-util/timeutil.c b/calendar/cal-util/timeutil.c index 764ec5d078..7bb4c904ec 100644 --- a/calendar/cal-util/timeutil.c +++ b/calendar/cal-util/timeutil.c @@ -319,35 +319,45 @@ time_month_end (time_t t) return mktime (&tm); } +/* Returns the start of the week. week_start_day should use the same values + as mktime(), i.e. 0 (Sun) to 6 (Sat). */ time_t -time_week_begin (time_t t) +time_week_begin (time_t t, int week_start_day) { struct tm tm; - - /* FIXME: make it take week_starts_on_monday into account */ + int offset; tm = *localtime (&t); + + /* Calculate the current offset from the week start day. */ + offset = (tm.tm_wday + 7 - week_start_day) % 7; + tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; - tm.tm_mday -= tm.tm_wday; + tm.tm_mday -= offset; tm.tm_isdst = -1; return mktime (&tm); } +/* Returns the end of the week. week_start_day should use the same values + as mktime(), i.e. 0 (Sun) to 6 (Sat). */ time_t -time_week_end (time_t t) +time_week_end (time_t t, int week_start_day) { struct tm tm; - - /* FIXME: make it take week_starts_on_monday into account */ + int offset; tm = *localtime (&t); + + /* Calculate the current offset from the week start day. */ + offset = (tm.tm_wday + 7 - week_start_day) % 7; + tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; - tm.tm_mday += 7 - tm.tm_wday; + tm.tm_mday += 7 - offset; tm.tm_isdst = -1; return mktime (&tm); diff --git a/calendar/cal-util/timeutil.h b/calendar/cal-util/timeutil.h index 04f1ca4867..3907a11b8c 100644 --- a/calendar/cal-util/timeutil.h +++ b/calendar/cal-util/timeutil.h @@ -26,41 +26,36 @@ time_t time_add_month (time_t time, int months); time_t time_add_year (time_t time, int years); -/* Returns the number of days in the specified month. Years are full years (starting from year 1). - * Months are in [0, 11]. - */ +/* Returns the number of days in the specified month. Years are full years + (starting from year 1). Months are in [0, 11]. */ int time_days_in_month (int year, int month); -/* Converts the specified date to a time_t at the start of the specified day. Years are full years - * (starting from year 1). Months are in [0, 11]. Days are 1-based. - */ +/* Converts the specified date to a time_t at the start of the specified day. + Years are full years (starting from year 1). Months are in [0, 11]. + Days are 1-based. */ time_t time_from_day (int year, int month, int day); -/* For the functions below, time ranges are considered to contain the start time, but not the end - * time. - */ +/* For the functions below, time ranges are considered to contain the start + time, but not the end time. */ -/* These two functions take a time value and return the beginning or end of the corresponding year, - * respectively. - */ +/* These two functions take a time value and return the beginning or end of + the corresponding year, respectively. */ time_t time_year_begin (time_t t); time_t time_year_end (time_t t); -/* These two functions take a time value and return the beginning or end of the corresponding month, - * respectively. - */ +/* These two functions take a time value and return the beginning or end of + the corresponding month, respectively. */ time_t time_month_begin (time_t t); time_t time_month_end (time_t t); -/* These functions take a time value and return the beginning or end of the corresponding week, - * respectively. This takes into account the global week_starts_on_monday flag. - */ -time_t time_week_begin (time_t t); -time_t time_week_end (time_t t); +/* These functions take a time value and return the beginning or end of the + corresponding week, respectively. week_start_day should use the same values + as mktime(), i.e. 0 (Sun) to 6 (Sat). */ +time_t time_week_begin (time_t t, int week_start_day); +time_t time_week_end (time_t t, int week_start_day); -/* These two functions take a time value and return the beginning or end of the corresponding day, - * respectively. - */ +/* These two functions take a time value and return the beginning or end of + the corresponding day, respectively. */ time_t time_day_begin (time_t t); time_t time_day_end (time_t t); diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am index 29bb739a56..2910efc322 100644 --- a/calendar/gui/Makefile.am +++ b/calendar/gui/Makefile.am @@ -65,6 +65,8 @@ evolution_calendar_SOURCES = \ component-factory.h \ e-calendar-table.h \ e-calendar-table.c \ + e-day-view-layout.c \ + e-day-view-layout.h \ e-day-view-main-item.c \ e-day-view-main-item.h \ e-day-view-time-item.c \ @@ -79,6 +81,8 @@ evolution_calendar_SOURCES = \ e-meeting-edit.c \ e-week-view-event-item.c \ e-week-view-event-item.h \ + e-week-view-layout.c \ + e-week-view-layout.h \ e-week-view-main-item.c \ e-week-view-main-item.h \ e-week-view-titles-item.c \ diff --git a/calendar/gui/e-day-view-layout.c b/calendar/gui/e-day-view-layout.c new file mode 100644 index 0000000000..00a448d7ec --- /dev/null +++ b/calendar/gui/e-day-view-layout.c @@ -0,0 +1,349 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * Author : + * Damon Chaplin <damon@ximian.com> + * + * Copyright 2001, Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* + * Lays out events for the Day & Work-Week views of the calendar. It is also + * used for printing. + */ + +#include <config.h> + +#include "e-day-view-layout.h" + +static void e_day_view_layout_long_event (EDayViewEvent *event, + guint8 *grid, + gint days_shown, + time_t *day_starts, + gint *rows_in_top_display); + +static void e_day_view_layout_day_event (EDayViewEvent *event, + guint8 *grid, + guint16 *group_starts, + gint8 *cols_per_row, + gint rows, + gint mins_per_row); +static void e_day_view_expand_day_event (EDayViewEvent *event, + guint8 *grid, + gint8 *cols_per_row, + gint mins_per_row); +static void e_day_view_recalc_cols_per_row (gint rows, + gint8 *cols_per_row, + guint16 *group_starts); + + +void +e_day_view_layout_long_events (GArray *events, + gint days_shown, + time_t *day_starts, + gint *rows_in_top_display) +{ + EDayViewEvent *event; + gint event_num; + guint8 *grid; + + /* This is a temporary 2-d grid which is used to place events. + Each element is 0 if the position is empty, or 1 if occupied. + We allocate the maximum size possible here, assuming that each + event will need its own row. */ + grid = g_new0 (guint8, events->len * E_DAY_VIEW_MAX_DAYS); + + /* Reset the number of rows in the top display to 0. It will be + updated as events are layed out below. */ + *rows_in_top_display = 0; + + /* Iterate over the events, finding which days they cover, and putting + them in the first free row available. */ + for (event_num = 0; event_num < events->len; event_num++) { + event = &g_array_index (events, EDayViewEvent, event_num); + e_day_view_layout_long_event (event, grid, + days_shown, day_starts, + rows_in_top_display); + } + + /* Free the grid. */ + g_free (grid); +} + + +static void +e_day_view_layout_long_event (EDayViewEvent *event, + guint8 *grid, + gint days_shown, + time_t *day_starts, + gint *rows_in_top_display) +{ + gint start_day, end_day, free_row, day, row; + + event->num_columns = 0; + + if (!e_day_view_find_long_event_days (event, + days_shown, day_starts, + &start_day, &end_day)) + return; + + /* Try each row until we find a free one. */ + row = 0; + do { + free_row = row; + for (day = start_day; day <= end_day; day++) { + if (grid[row * E_DAY_VIEW_MAX_DAYS + day]) { + free_row = -1; + break; + } + } + row++; + } while (free_row == -1); + + event->start_row_or_col = free_row; + event->num_columns = 1; + + /* Mark the cells as full. */ + for (day = start_day; day <= end_day; day++) { + grid[free_row * E_DAY_VIEW_MAX_DAYS + day] = 1; + } + + /* Update the number of rows in the top canvas if necessary. */ + *rows_in_top_display = MAX (*rows_in_top_display, free_row + 1); +} + + +void +e_day_view_layout_day_events (GArray *events, + gint rows, + gint mins_per_row, + gint8 *cols_per_row) +{ + EDayViewEvent *event; + gint row, event_num; + guint8 *grid; + + /* This is a temporary array which keeps track of rows which are + connected. When an appointment spans multiple rows then the number + of columns in each of these rows must be the same (i.e. the maximum + of all of them). Each element in the array corresponds to one row + and contains the index of the first row in the group of connected + rows. */ + guint16 group_starts[12 * 24]; + + /* Reset the cols_per_row array, and initialize the connected rows so + that all rows are not connected - each row is the start of a new + group. */ + for (row = 0; row < rows; row++) { + cols_per_row[row] = 0; + group_starts[row] = row; + } + + /* This is a temporary 2-d grid which is used to place events. + Each element is 0 if the position is empty, or 1 if occupied. */ + grid = g_new0 (guint8, rows * E_DAY_VIEW_MAX_COLUMNS); + + + /* Iterate over the events, finding which rows they cover, and putting + them in the first free column available. Increment the number of + events in each of the rows it covers, and make sure they are all + in one group. */ + for (event_num = 0; event_num < events->len; event_num++) { + event = &g_array_index (events, EDayViewEvent, event_num); + + e_day_view_layout_day_event (event, grid, group_starts, + cols_per_row, rows, mins_per_row); + } + + /* Recalculate the number of columns needed in each row. */ + e_day_view_recalc_cols_per_row (rows, cols_per_row, group_starts); + + /* Iterate over the events again, trying to expand events horizontally + if there is enough space. */ + for (event_num = 0; event_num < events->len; event_num++) { + event = &g_array_index (events, EDayViewEvent, event_num); + e_day_view_expand_day_event (event, grid, cols_per_row, + mins_per_row); + } + + /* Free the grid. */ + g_free (grid); +} + + +/* Finds the first free position to place the event in. + Increments the number of events in each of the rows it covers, and makes + sure they are all in one group. */ +static void +e_day_view_layout_day_event (EDayViewEvent *event, + guint8 *grid, + guint16 *group_starts, + gint8 *cols_per_row, + gint rows, + gint mins_per_row) +{ + gint start_row, end_row, free_col, col, row, group_start; + + start_row = event->start_minute / mins_per_row; + end_row = (event->end_minute - 1) / mins_per_row; + + event->num_columns = 0; + + /* If the event can't currently be seen, just return. */ + if (start_row >= rows || end_row < 0) + return; + + /* Make sure we don't go outside the visible times. */ + start_row = CLAMP (start_row, 0, rows - 1); + end_row = CLAMP (end_row, 0, rows - 1); + + /* Try each column until we find a free one. */ + for (col = 0; col < E_DAY_VIEW_MAX_COLUMNS; col++) { + free_col = col; + for (row = start_row; row <= end_row; row++) { + if (grid[row * E_DAY_VIEW_MAX_COLUMNS + col]) { + free_col = -1; + break; + } + } + + if (free_col != -1) + break; + } + + /* If we can't find space for the event, just return. */ + if (free_col == -1) + return; + + /* The event is assigned 1 col initially, but may be expanded later. */ + event->start_row_or_col = free_col; + event->num_columns = 1; + + /* Determine the start index of the group. */ + group_start = group_starts[start_row]; + + /* Increment number of events in each of the rows the event covers. + We use the cols_per_row array for this. It will be sorted out after + all the events have been layed out. Also make sure all the rows that + the event covers are in one group. */ + for (row = start_row; row <= end_row; row++) { + grid[row * E_DAY_VIEW_MAX_COLUMNS + free_col] = 1; + cols_per_row[row]++; + group_starts[row] = group_start; + } + + /* If any following rows should be in the same group, add them. */ + for (row = end_row + 1; row < rows; row++) { + if (group_starts[row] > end_row) + break; + group_starts[row] = group_start; + } +} + + +/* For each group of rows, find the max number of events in all the + rows, and set the number of cols in each of the rows to that. */ +static void +e_day_view_recalc_cols_per_row (gint rows, + gint8 *cols_per_row, + guint16 *group_starts) +{ + gint start_row = 0, row, next_start_row, max_events; + + while (start_row < rows) { + max_events = 0; + for (row = start_row; row < rows && group_starts[row] == start_row; row++) + max_events = MAX (max_events, cols_per_row[row]); + + next_start_row = row; + + for (row = start_row; row < next_start_row; row++) + cols_per_row[row] = max_events; + + start_row = next_start_row; + } +} + + +/* Expands the event horizontally to fill any free space. */ +static void +e_day_view_expand_day_event (EDayViewEvent *event, + guint8 *grid, + gint8 *cols_per_row, + gint mins_per_row) +{ + gint start_row, end_row, col, row; + gboolean clashed; + + start_row = event->start_minute / mins_per_row; + end_row = (event->end_minute - 1) / mins_per_row; + + /* Try each column until we find a free one. */ + clashed = FALSE; + for (col = event->start_row_or_col + 1; col < cols_per_row[start_row]; col++) { + for (row = start_row; row <= end_row; row++) { + if (grid[row * E_DAY_VIEW_MAX_COLUMNS + col]) { + clashed = TRUE; + break; + } + } + + if (clashed) + break; + + event->num_columns++; + } +} + + +/* Find the start and end days for the event. */ +gboolean +e_day_view_find_long_event_days (EDayViewEvent *event, + gint days_shown, + time_t *day_starts, + gint *start_day_return, + gint *end_day_return) +{ + gint day, start_day, end_day; + + start_day = -1; + end_day = -1; + + for (day = 0; day < days_shown; day++) { + if (start_day == -1 + && event->start < day_starts[day + 1]) + start_day = day; + if (event->end > day_starts[day]) + end_day = day; + } + + /* Sanity check. */ + if (start_day < 0 || start_day >= days_shown + || end_day < 0 || end_day >= days_shown + || end_day < start_day) { + g_warning ("Invalid date range for event"); + return FALSE; + } + + *start_day_return = start_day; + *end_day_return = end_day; + + return TRUE; +} + + diff --git a/calendar/gui/e-day-view-layout.h b/calendar/gui/e-day-view-layout.h new file mode 100644 index 0000000000..353d024e3c --- /dev/null +++ b/calendar/gui/e-day-view-layout.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * Author : + * Damon Chaplin <damon@ximian.com> + * + * Copyright 2001, Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +#ifndef _E_DAY_VIEW_LAYOUT_H_ +#define _E_DAY_VIEW_LAYOUT_H_ + +#include "e-day-view.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* I've split these functions away from EDayView so we can use them for + printing. */ + +void e_day_view_layout_long_events (GArray *events, + gint days_shown, + time_t *day_starts, + gint *rows_in_top_display); + + +void e_day_view_layout_day_events (GArray *events, + gint rows, + gint mins_per_row, + gint8 *cols_per_row); + +gboolean e_day_view_find_long_event_days (EDayViewEvent *event, + gint days_shown, + time_t *day_starts, + gint *start_day, + gint *end_day); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _E_DAY_VIEW_LAYOUT_H_ */ diff --git a/calendar/gui/e-day-view-main-item.c b/calendar/gui/e-day-view-main-item.c index 3cab1f1a05..fd202bdfbe 100644 --- a/calendar/gui/e-day-view-main-item.c +++ b/calendar/gui/e-day-view-main-item.c @@ -29,6 +29,7 @@ */ #include <config.h> +#include "e-day-view-layout.h" #include "e-day-view-main-item.h" static void e_day_view_main_item_class_init (EDayViewMainItemClass *class); @@ -390,7 +391,9 @@ e_day_view_main_item_draw_long_events_in_vbars (EDayViewMainItem *dvmitem, event = &g_array_index (day_view->long_events, EDayViewEvent, event_num); - if (!e_day_view_find_long_event_days (day_view, event, + if (!e_day_view_find_long_event_days (event, + day_view->days_shown, + day_view->day_starts, &start_day, &end_day)) continue; diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c index eac99a18aa..b08ed848d8 100644 --- a/calendar/gui/e-day-view.c +++ b/calendar/gui/e-day-view.c @@ -53,6 +53,7 @@ #include "e-meeting-edit.h" #include "e-day-view-time-item.h" #include "e-day-view-top-item.h" +#include "e-day-view-layout.h" #include "e-day-view-main-item.h" /* Images */ @@ -266,27 +267,9 @@ static void e_day_view_update_event_label (EDayView *day_view, static void e_day_view_update_long_event_label (EDayView *day_view, gint event_num); -static void e_day_view_layout_long_events (EDayView *day_view); -static void e_day_view_layout_long_event (EDayView *day_view, - EDayViewEvent *event, - guint8 *grid); static void e_day_view_reshape_long_events (EDayView *day_view); static void e_day_view_reshape_long_event (EDayView *day_view, gint event_num); -static void e_day_view_layout_day_events (EDayView *day_view, - gint day); -static void e_day_view_layout_day_event (EDayView *day_view, - gint day, - EDayViewEvent *event, - guint8 *grid, - guint16 *group_starts); -static void e_day_view_expand_day_event (EDayView *day_view, - gint day, - EDayViewEvent *event, - guint8 *grid); -static void e_day_view_recalc_cols_per_row (EDayView *day_view, - gint day, - guint16 *group_starts); static void e_day_view_reshape_day_events (EDayView *day_view, gint day); static void e_day_view_reshape_day_event (EDayView *day_view, @@ -297,8 +280,6 @@ static void e_day_view_reshape_resize_long_event_rect_item (EDayView *day_view); static void e_day_view_reshape_resize_rect_item (EDayView *day_view); static void e_day_view_ensure_events_sorted (EDayView *day_view); -static gint e_day_view_event_sort_func (const void *arg1, - const void *arg2); static void e_day_view_start_editing_event (EDayView *day_view, gint day, @@ -2909,7 +2890,9 @@ e_day_view_on_long_event_click (EDayView *day_view, if (!(cal_component_has_recurrences (event->comp)) && (pos == E_DAY_VIEW_POS_LEFT_EDGE || pos == E_DAY_VIEW_POS_RIGHT_EDGE)) { - if (!e_day_view_find_long_event_days (day_view, event, + if (!e_day_view_find_long_event_days (event, + day_view->days_shown, + day_view->day_starts, &start_day, &end_day)) return; @@ -4163,7 +4146,7 @@ e_day_view_add_event (CalComponent *comp, void e_day_view_check_layout (EDayView *day_view) { - gint day; + gint day, rows_in_top_display, top_canvas_height, top_rows; /* Don't bother if we aren't visible. */ if (!GTK_WIDGET_VISIBLE (day_view)) @@ -4174,7 +4157,10 @@ e_day_view_check_layout (EDayView *day_view) for (day = 0; day < day_view->days_shown; day++) { if (day_view->need_layout[day]) - e_day_view_layout_day_events (day_view, day); + e_day_view_layout_day_events (day_view->events[day], + day_view->rows, + day_view->mins_per_row, + day_view->cols_per_row[day]); if (day_view->need_layout[day] || day_view->need_reshape[day]) { @@ -4188,8 +4174,25 @@ e_day_view_check_layout (EDayView *day_view) day_view->need_reshape[day] = FALSE; } - if (day_view->long_events_need_layout) - e_day_view_layout_long_events (day_view); + if (day_view->long_events_need_layout) { + e_day_view_layout_long_events (day_view->long_events, + day_view->days_shown, + day_view->day_starts, + &rows_in_top_display); + + /* Set the height of the top canvas based on the row height + and the number of rows needed (min 1 + 1 for the dates + 1 + space for DnD).*/ + if (day_view->rows_in_top_display != rows_in_top_display) { + day_view->rows_in_top_display = rows_in_top_display; + top_rows = MAX (1, rows_in_top_display); + top_canvas_height = (top_rows + 2) + * day_view->top_row_height; + gtk_widget_set_usize (day_view->top_canvas, -1, + top_canvas_height); + } + } + if (day_view->long_events_need_layout || day_view->long_events_need_reshape) @@ -4201,88 +4204,6 @@ e_day_view_check_layout (EDayView *day_view) static void -e_day_view_layout_long_events (EDayView *day_view) -{ - EDayViewEvent *event; - gint event_num, old_rows_in_top_display, top_canvas_height, top_rows; - guint8 *grid; - - /* This is a temporary 2-d grid which is used to place events. - Each element is 0 if the position is empty, or 1 if occupied. - We allocate the maximum size possible here, assuming that each - event will need its own row. */ - grid = g_new0 (guint8, - day_view->long_events->len * E_DAY_VIEW_MAX_DAYS); - - /* Reset the number of rows in the top display to 0. It will be - updated as events are layed out below. */ - old_rows_in_top_display = day_view->rows_in_top_display; - day_view->rows_in_top_display = 0; - - /* Iterate over the events, finding which days they cover, and putting - them in the first free row available. */ - for (event_num = 0; event_num < day_view->long_events->len; - event_num++) { - event = &g_array_index (day_view->long_events, - EDayViewEvent, event_num); - e_day_view_layout_long_event (day_view, event, grid); - } - - /* Free the grid. */ - g_free (grid); - - /* Set the height of the top canvas based on the row height and the - number of rows needed (min 1 + 1 for the dates + 1 space for DnD).*/ - if (day_view->rows_in_top_display != old_rows_in_top_display) { - top_rows = MAX (1, day_view->rows_in_top_display); - top_canvas_height = (top_rows + 2) * day_view->top_row_height; - gtk_widget_set_usize (day_view->top_canvas, -1, - top_canvas_height); - } -} - - -static void -e_day_view_layout_long_event (EDayView *day_view, - EDayViewEvent *event, - guint8 *grid) -{ - gint start_day, end_day, free_row, day, row; - - event->num_columns = 0; - - if (!e_day_view_find_long_event_days (day_view, event, - &start_day, &end_day)) - return; - - /* Try each row until we find a free one. */ - row = 0; - do { - free_row = row; - for (day = start_day; day <= end_day; day++) { - if (grid[row * E_DAY_VIEW_MAX_DAYS + day]) { - free_row = -1; - break; - } - } - row++; - } while (free_row == -1); - - event->start_row_or_col = free_row; - event->num_columns = 1; - - /* Mark the cells as full. */ - for (day = start_day; day <= end_day; day++) { - grid[free_row * E_DAY_VIEW_MAX_DAYS + day] = 1; - } - - /* Update the number of rows in the top canvas if necessary. */ - day_view->rows_in_top_display = MAX (day_view->rows_in_top_display, - free_row + 1); -} - - -static void e_day_view_reshape_long_events (EDayView *day_view) { EDayViewEvent *event; @@ -4438,225 +4359,6 @@ e_day_view_reshape_long_event (EDayView *day_view, } -/* Find the start and end days for the event. */ -gboolean -e_day_view_find_long_event_days (EDayView *day_view, - EDayViewEvent *event, - gint *start_day_return, - gint *end_day_return) -{ - gint day, start_day, end_day; - - start_day = -1; - end_day = -1; - - for (day = 0; day < day_view->days_shown; day++) { - if (start_day == -1 - && event->start < day_view->day_starts[day + 1]) - start_day = day; - if (event->end > day_view->day_starts[day]) - end_day = day; - } - - /* Sanity check. */ - if (start_day < 0 || start_day >= day_view->days_shown - || end_day < 0 || end_day >= day_view->days_shown - || end_day < start_day) { - g_warning ("Invalid date range for event"); - return FALSE; - } - - *start_day_return = start_day; - *end_day_return = end_day; - - return TRUE; -} - - -static void -e_day_view_layout_day_events (EDayView *day_view, - gint day) -{ - EDayViewEvent *event; - gint row, event_num; - guint8 *grid; - - /* This is a temporary array which keeps track of rows which are - connected. When an appointment spans multiple rows then the number - of columns in each of these rows must be the same (i.e. the maximum - of all of them). Each element in the array corresponds to one row - and contains the index of the first row in the group of connected - rows. */ - guint16 group_starts[12 * 24]; - - /* Reset the cols_per_row array, and initialize the connected rows so - that all rows are not connected - each row is the start of a new - group. */ - for (row = 0; row < day_view->rows; row++) { - day_view->cols_per_row[day][row] = 0; - group_starts[row] = row; - } - - /* This is a temporary 2-d grid which is used to place events. - Each element is 0 if the position is empty, or 1 if occupied. */ - grid = g_new0 (guint8, day_view->rows * E_DAY_VIEW_MAX_COLUMNS); - - - /* Iterate over the events, finding which rows they cover, and putting - them in the first free column available. Increment the number of - events in each of the rows it covers, and make sure they are all - in one group. */ - for (event_num = 0; event_num < day_view->events[day]->len; - event_num++) { - event = &g_array_index (day_view->events[day], EDayViewEvent, - event_num); - - e_day_view_layout_day_event (day_view, day, event, - grid, group_starts); - } - - /* Recalculate the number of columns needed in each row. */ - e_day_view_recalc_cols_per_row (day_view, day, group_starts); - - /* Iterate over the events again, trying to expand events horizontally - if there is enough space. */ - for (event_num = 0; event_num < day_view->events[day]->len; - event_num++) { - event = &g_array_index (day_view->events[day], EDayViewEvent, - event_num); - e_day_view_expand_day_event (day_view, day, event, grid); - } - - /* Free the grid. */ - g_free (grid); -} - - -/* Finds the first free position to place the event in. - Increments the number of events in each of the rows it covers, and makes - sure they are all in one group. */ -static void -e_day_view_layout_day_event (EDayView *day_view, - gint day, - EDayViewEvent *event, - guint8 *grid, - guint16 *group_starts) -{ - gint start_row, end_row, free_col, col, row, group_start; - - start_row = event->start_minute / day_view->mins_per_row; - end_row = (event->end_minute - 1) / day_view->mins_per_row; - - event->num_columns = 0; - - /* If the event can't currently be seen, just return. */ - if (start_row >= day_view->rows || end_row < 0) - return; - - /* Make sure we don't go outside the visible times. */ - start_row = CLAMP (start_row, 0, day_view->rows - 1); - end_row = CLAMP (end_row, 0, day_view->rows - 1); - - /* Try each column until we find a free one. */ - for (col = 0; col < E_DAY_VIEW_MAX_COLUMNS; col++) { - free_col = col; - for (row = start_row; row <= end_row; row++) { - if (grid[row * E_DAY_VIEW_MAX_COLUMNS + col]) { - free_col = -1; - break; - } - } - - if (free_col != -1) - break; - } - - /* If we can't find space for the event, just return. */ - if (free_col == -1) - return; - - /* The event is assigned 1 col initially, but may be expanded later. */ - event->start_row_or_col = free_col; - event->num_columns = 1; - - /* Determine the start index of the group. */ - group_start = group_starts[start_row]; - - /* Increment number of events in each of the rows the event covers. - We use the cols_per_row array for this. It will be sorted out after - all the events have been layed out. Also make sure all the rows that - the event covers are in one group. */ - for (row = start_row; row <= end_row; row++) { - grid[row * E_DAY_VIEW_MAX_COLUMNS + free_col] = 1; - day_view->cols_per_row[day][row]++; - group_starts[row] = group_start; - } - - /* If any following rows should be in the same group, add them. */ - for (row = end_row + 1; row < day_view->rows; row++) { - if (group_starts[row] > end_row) - break; - group_starts[row] = group_start; - } -} - - -/* For each group of rows, find the max number of events in all the - rows, and set the number of cols in each of the rows to that. */ -static void -e_day_view_recalc_cols_per_row (EDayView *day_view, - gint day, - guint16 *group_starts) -{ - gint start_row = 0, row, next_start_row, max_events; - - while (start_row < day_view->rows) { - - max_events = 0; - for (row = start_row; row < day_view->rows && group_starts[row] == start_row; row++) - max_events = MAX (max_events, day_view->cols_per_row[day][row]); - - next_start_row = row; - - for (row = start_row; row < next_start_row; row++) - day_view->cols_per_row[day][row] = max_events; - - start_row = next_start_row; - } -} - - -/* Expands the event horizontally to fill any free space. */ -static void -e_day_view_expand_day_event (EDayView *day_view, - gint day, - EDayViewEvent *event, - guint8 *grid) -{ - gint start_row, end_row, col, row; - gboolean clashed; - - start_row = event->start_minute / day_view->mins_per_row; - end_row = (event->end_minute - 1) / day_view->mins_per_row; - - /* Try each column until we find a free one. */ - clashed = FALSE; - for (col = event->start_row_or_col + 1; col < day_view->cols_per_row[day][start_row]; col++) { - for (row = start_row; row <= end_row; row++) { - if (grid[row * E_DAY_VIEW_MAX_COLUMNS + col]) { - clashed = TRUE; - break; - } - } - - if (clashed) - break; - - event->num_columns++; - } -} - - /* This creates or updates the sizes of the canvas items for one day of the main canvas. */ static void @@ -4837,7 +4539,7 @@ e_day_view_ensure_events_sorted (EDayView *day_view) } -static gint +gint e_day_view_event_sort_func (const void *arg1, const void *arg2) { @@ -5774,7 +5476,9 @@ e_day_view_get_long_event_position (EDayView *day_view, if (event->num_columns == 0) return FALSE; - if (!e_day_view_find_long_event_days (day_view, event, + if (!e_day_view_find_long_event_days (event, + day_view->days_shown, + day_view->day_starts, start_day, end_day)) return FALSE; @@ -6037,7 +5741,9 @@ e_day_view_update_top_canvas_drag (EDayView *day_view, day_view->drag_event_num); row = event->start_row_or_col + 1; - if (!e_day_view_find_long_event_days (day_view, event, + if (!e_day_view_find_long_event_days (event, + day_view->days_shown, + day_view->day_starts, &start_day, &end_day)) return; @@ -6450,8 +6156,9 @@ e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget, day -= day_view->drag_event_offset; day = MAX (day, 0); - e_day_view_find_long_event_days (day_view, - event, + e_day_view_find_long_event_days (event, + day_view->days_shown, + day_view->day_starts, &start_day, &end_day); num_days = end_day - start_day + 1; diff --git a/calendar/gui/e-day-view.h b/calendar/gui/e-day-view.h index 18f8187951..9eb247a59f 100644 --- a/calendar/gui/e-day-view.h +++ b/calendar/gui/e-day-view.h @@ -582,10 +582,6 @@ gboolean e_day_view_get_long_event_position (EDayView *day_view, gint *item_y, gint *item_w, gint *item_h); -gboolean e_day_view_find_long_event_days (EDayView *day_view, - EDayViewEvent *event, - gint *start_day, - gint *end_day); void e_day_view_start_selection (EDayView *day_view, gint day, @@ -608,6 +604,9 @@ void e_day_view_convert_time_to_display (EDayView *day_view, gint e_day_view_get_time_string_width (EDayView *day_view); +gint e_day_view_event_sort_func (const void *arg1, + const void *arg2); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/calendar/gui/e-week-view-layout.c b/calendar/gui/e-week-view-layout.c new file mode 100644 index 0000000000..21552e5f17 --- /dev/null +++ b/calendar/gui/e-week-view-layout.c @@ -0,0 +1,426 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * Author : + * Damon Chaplin <damon@ximian.com> + * + * Copyright 2001, Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* + * Lays out events for the Week & Month views of the calendar. It is also + * used for printing. + */ + +#include <config.h> + +#include "e-week-view-layout.h" + + +static void e_week_view_layout_event (EWeekViewEvent *event, + guint8 *grid, + GArray *spans, + GArray *old_spans, + gboolean multi_week_view, + gint weeks_shown, + gboolean compress_weekend, + gint start_weekday, + time_t *day_starts, + gint *rows_per_day); +static gint e_week_view_find_day (time_t time_to_find, + gboolean include_midnight_in_prev_day, + gint days_shown, + time_t *day_starts); +static gint e_week_view_find_span_end (gboolean multi_week_view, + gboolean compress_weekend, + gint display_start_day, + gint day); + + +GArray* +e_week_view_layout_events (GArray *events, + GArray *old_spans, + gboolean multi_week_view, + gint weeks_shown, + gboolean compress_weekend, + gint start_weekday, + time_t *day_starts, + gint *rows_per_day) +{ + EWeekViewEvent *event; + EWeekViewEventSpan *span; + gint num_days, day, event_num, span_num; + guint8 *grid; + GArray *spans; + + /* This is a temporary 2-d grid which is used to place events. + Each element is 0 if the position is empty, or 1 if occupied. + We allocate the maximum size possible here, assuming that each + event will need its own row. */ + grid = g_new0 (guint8, E_WEEK_VIEW_MAX_ROWS_PER_CELL * 7 + * E_WEEK_VIEW_MAX_WEEKS); + + /* We create a new array of spans, which will replace the old one. */ + spans = g_array_new (FALSE, FALSE, sizeof (EWeekViewEventSpan)); + + /* Clear the number of rows used per day. */ + num_days = multi_week_view ? weeks_shown * 7 : 7; + for (day = 0; day <= num_days; day++) { + rows_per_day[day] = 0; + } + + /* Iterate over the events, finding which weeks they cover, and putting + them in the first free row available. */ + for (event_num = 0; event_num < events->len; event_num++) { + event = &g_array_index (events, EWeekViewEvent, event_num); + e_week_view_layout_event (event, grid, spans, old_spans, + multi_week_view, + weeks_shown, compress_weekend, + start_weekday, day_starts, + rows_per_day); + } + + /* Free the grid. */ + g_free (grid); + + /* Destroy the old spans array, destroying any unused canvas items. */ + if (old_spans) { + for (span_num = 0; span_num < old_spans->len; span_num++) { + span = &g_array_index (old_spans, EWeekViewEventSpan, + span_num); + if (span->background_item) + gtk_object_destroy (GTK_OBJECT (span->background_item)); + if (span->text_item) + gtk_object_destroy (GTK_OBJECT (span->text_item)); + } + g_array_free (old_spans, TRUE); + } + + return spans; +} + + +static void +e_week_view_layout_event (EWeekViewEvent *event, + guint8 *grid, + GArray *spans, + GArray *old_spans, + gboolean multi_week_view, + gint weeks_shown, + gboolean compress_weekend, + gint start_weekday, + time_t *day_starts, + gint *rows_per_day) +{ + gint start_day, end_day, span_start_day, span_end_day, rows_per_cell; + gint free_row, row, day, span_num, spans_index, num_spans, days_shown; + EWeekViewEventSpan span, *old_span; + + days_shown = multi_week_view ? weeks_shown * 7 - 1 : 7 - 1; + start_day = e_week_view_find_day (event->start, FALSE, days_shown, + day_starts); + end_day = e_week_view_find_day (event->end, TRUE, days_shown, + day_starts); + start_day = CLAMP (start_day, 0, days_shown - 1); + end_day = CLAMP (end_day, 0, days_shown - 1); + +#if 0 + g_print ("In e_week_view_layout_event Start:%i End: %i\n", + start_day, end_day); +#endif + + /* Iterate through each of the spans of the event, where each span + is a sequence of 1 or more days displayed next to each other. */ + span_start_day = start_day; + rows_per_cell = E_WEEK_VIEW_MAX_ROWS_PER_CELL; + span_num = 0; + spans_index = spans->len; + num_spans = 0; + while (span_start_day <= end_day) { + span_end_day = e_week_view_find_span_end (multi_week_view, + compress_weekend, + start_weekday, + span_start_day); + span_end_day = MIN (span_end_day, end_day); +#if 0 + g_print (" Span start:%i end:%i\n", span_start_day, + span_end_day); +#endif + /* Try each row until we find a free one or we fall off the + bottom of the available rows. */ + row = 0; + free_row = -1; + while (free_row == -1 && row < rows_per_cell) { + free_row = row; + for (day = span_start_day; day <= span_end_day; + day++) { + if (grid[day * rows_per_cell + row]) { + free_row = -1; + break; + } + } + row++; + }; + + if (free_row != -1) { + /* Mark the cells as full. */ + for (day = span_start_day; day <= span_end_day; + day++) { + grid[day * rows_per_cell + free_row] = 1; + rows_per_day[day] = MAX (rows_per_day[day], + free_row + 1); + } +#if 0 + g_print (" Span start:%i end:%i row:%i\n", + span_start_day, span_end_day, free_row); +#endif + /* Add the span to the array, and try to reuse any + canvas items from the old spans. */ + span.start_day = span_start_day; + span.num_days = span_end_day - span_start_day + 1; + span.row = free_row; + span.background_item = NULL; + span.text_item = NULL; + if (event->num_spans > span_num) { + old_span = &g_array_index (old_spans, EWeekViewEventSpan, event->spans_index + span_num); + span.background_item = old_span->background_item; + span.text_item = old_span->text_item; + old_span->background_item = NULL; + old_span->text_item = NULL; + } + + g_array_append_val (spans, span); + num_spans++; + } + + span_start_day = span_end_day + 1; + span_num++; + } + + /* Set the event's spans. */ + event->spans_index = spans_index; + event->num_spans = num_spans; +} + + +/* Finds the day containing the given time. + If include_midnight_in_prev_day is TRUE then if the time exactly + matches the start of a day the previous day is returned. This is useful + when calculating the end day of an event. */ +static gint +e_week_view_find_day (time_t time_to_find, + gboolean include_midnight_in_prev_day, + gint days_shown, + time_t *day_starts) +{ + gint day; + + if (time_to_find < day_starts[0]) + return -1; + if (time_to_find > day_starts[days_shown]) + return days_shown; + + for (day = 1; day <= days_shown; day++) { + if (time_to_find <= day_starts[day]) { + if (time_to_find == day_starts[day] + && !include_midnight_in_prev_day) + return day; + return day - 1; + } + } + + g_assert_not_reached (); + return days_shown; +} + + +/* This returns the last possible day in the same span as the given day. + A span is all the days which are displayed next to each other from left to + right. In the week view all spans are only 1 day, since Tuesday is below + Monday rather than beside it etc. In the month view, if the weekends are not + compressed then each week is a span, otherwise we have to break a span up + on Saturday, use a separate span for Sunday, and start again on Monday. */ +static gint +e_week_view_find_span_end (gboolean multi_week_view, + gboolean compress_weekend, + gint display_start_day, + gint day) +{ + gint week, col, sat_col, end_col; + + if (multi_week_view) { + week = day / 7; + col = day % 7; + + /* We default to the last column in the row. */ + end_col = 6; + + /* If the weekend is compressed we must end any spans on + Saturday and Sunday. */ + if (compress_weekend) { + sat_col = (5 + 7 - display_start_day) % 7; + if (col <= sat_col) + end_col = sat_col; + else if (col == sat_col + 1) + end_col = sat_col + 1; + } + + return week * 7 + end_col; + } else { + return day; + } +} + + +void +e_week_view_layout_get_day_position (gint day, + gboolean multi_week_view, + gint weeks_shown, + gint display_start_day, + gboolean compress_weekend, + gint *day_x, + gint *day_y, + gint *rows) +{ + gint week, day_of_week, row, col, weekend_col, box, weekend_box; + + *day_x = *day_y = *rows = 0; + g_return_if_fail (day >= 0); + + if (multi_week_view) { + g_return_if_fail (day < weeks_shown * 7); + + week = day / 7; + col = day % 7; + day_of_week = (display_start_day + day) % 7; + if (compress_weekend && day_of_week >= 5) { + /* In the compressed view Saturday is above Sunday and + both have just one row as opposed to 2 for all the + other days. */ + if (day_of_week == 5) { + *day_y = week * 2; + *rows = 1; + } else { + *day_y = week * 2 + 1; + *rows = 1; + col--; + } + /* Both Saturday and Sunday are in the same column. */ + *day_x = col; + } else { + /* If the weekend is compressed and the day is after + the weekend we have to move back a column. */ + if (compress_weekend) { + /* Calculate where the weekend column is. + Note that 5 is Saturday. */ + weekend_col = (5 + 7 - display_start_day) % 7; + if (col > weekend_col) + col--; + } + + *day_y = week * 2; + *rows = 2; + *day_x = col; + } + } else { + g_return_if_fail (day < 7); + + /* Calculate which box to place the day in, from 0-5. + Note that in the week view the weekends are always + compressed and share a box. */ + box = day; + day_of_week = (display_start_day + day) % 7; + weekend_box = (5 + 7 - display_start_day) % 7; + if (box > weekend_box) + box--; + + if (box < 3) + *day_x = 0; + else + *day_x = 1; + + row = (box % 3) * 2; + if (day_of_week < 5) { + *day_y = row; + *rows = 2; + } else if (day_of_week == 5) { + /* Saturday. */ + *day_y = row; + *rows = 1; + + } else { + /* Sunday. */ + *day_y = row + 1; + *rows = 1; + } + } +} + + +/* Returns TRUE if the event span is visible or FALSE if it isn't. + It also returns the number of days of the span that are visible. + Usually this can easily be determined by the start & end days and row of + the span, which are set in e_week_view_layout_event(). Though we need a + special case for the weekends when they are compressed, since the span may + not fit. */ +gboolean +e_week_view_layout_get_span_position (EWeekViewEvent *event, + EWeekViewEventSpan *span, + gint rows_per_cell, + gint rows_per_compressed_cell, + gint display_start_day, + gboolean multi_week_view, + gboolean compress_weekend, + gint *span_num_days) +{ + gint end_day_of_week; + + if (span->row >= rows_per_cell) + return FALSE; + + end_day_of_week = (display_start_day + span->start_day + + span->num_days - 1) % 7; + *span_num_days = span->num_days; + /* Check if the row will not be visible in compressed cells. */ + if (span->row >= rows_per_compressed_cell) { + if (multi_week_view) { + if (compress_weekend) { + /* If it ends on a Saturday and is 1 day long + we skip it, else we shorten it. If it ends + on a Sunday it must be 1 day long and we + skip it. */ + if (end_day_of_week == 5) { /* Sat */ + if (*span_num_days == 1) { + return FALSE; + } else { + (*span_num_days)--; + } + } else if (end_day_of_week == 6) { /* Sun */ + return FALSE; + } + } + } else { + /* All spans are 1 day long in the week view, so we + just skip it. */ + if (end_day_of_week > 4) + return FALSE; + } + } + + return TRUE; +} diff --git a/calendar/gui/e-week-view-layout.h b/calendar/gui/e-week-view-layout.h new file mode 100644 index 0000000000..e4d679e793 --- /dev/null +++ b/calendar/gui/e-week-view-layout.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * Author : + * Damon Chaplin <damon@ximian.com> + * + * Copyright 2001, Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +#ifndef _E_WEEK_VIEW_LAYOUT_H_ +#define _E_WEEK_VIEW_LAYOUT_H_ + +#include "e-week-view.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* I've split these functions away from EWeekView so we can use them for + printing. */ + +GArray* e_week_view_layout_events (GArray *events, + GArray *old_spans, + gboolean multi_week_view, + gint weeks_shown, + gboolean compress_weekend, + gint start_weekday, + time_t *day_starts, + gint *rows_per_day); + +/* Returns which 'cell' in the table the day appears in. Note that most days + have a height of 2 rows, but Sat/Sun are sometimes compressed so they have + a height of only 1 row. */ +void e_week_view_layout_get_day_position(gint day, + gboolean multi_week_view, + gint weeks_shown, + gint display_start_day, + gboolean compress_weekend, + gint *cell_x, + gint *cell_y, + gint *rows); + +gboolean e_week_view_layout_get_span_position (EWeekViewEvent *event, + EWeekViewEventSpan *span, + gint rows_per_cell, + gint rows_per_compressed_cell, + gint display_start_day, + gboolean multi_week_view, + gboolean compress_weekend, + gint *span_num_days); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _E_WEEK_VIEW_LAYOUT_H_ */ diff --git a/calendar/gui/e-week-view.c b/calendar/gui/e-week-view.c index 447fce8b77..adadd5d42a 100644 --- a/calendar/gui/e-week-view.c +++ b/calendar/gui/e-week-view.c @@ -50,6 +50,7 @@ #include "goto.h" #include "e-meeting-edit.h" #include "e-week-view-event-item.h" +#include "e-week-view-layout.h" #include "e-week-view-main-item.h" #include "e-week-view-titles-item.h" @@ -63,11 +64,6 @@ #define E_WEEK_VIEW_SMALL_FONT_FALLBACK \ "-adobe-helvetica-medium-r-normal-*-*-80-*-*-p-*-iso8859-*" -/* We use a 7-bit field to store row numbers in EWeekViewEventSpan, so the - maximum number or rows we can allow is 127. It is very unlikely to be - reached anyway. */ -#define E_WEEK_VIEW_MAX_ROWS_PER_CELL 127 - #define E_WEEK_VIEW_JUMP_BUTTON_WIDTH 16 #define E_WEEK_VIEW_JUMP_BUTTON_HEIGHT 8 @@ -115,23 +111,11 @@ static gboolean e_week_view_add_event (CalComponent *comp, time_t end, gpointer data); static void e_week_view_check_layout (EWeekView *week_view); -static void e_week_view_layout_events (EWeekView *week_view); -static void e_week_view_layout_event (EWeekView *week_view, - EWeekViewEvent *event, - guint8 *grid, - GArray *spans); static void e_week_view_ensure_events_sorted (EWeekView *week_view); -static gint e_week_view_event_sort_func (const void *arg1, - const void *arg2); static void e_week_view_reshape_events (EWeekView *week_view); static void e_week_view_reshape_event_span (EWeekView *week_view, gint event_num, gint span_num); -static gint e_week_view_find_day (EWeekView *week_view, - time_t time_to_find, - gboolean include_midnight_in_prev_day); -static gint e_week_view_find_span_end (EWeekView *week_view, - gint day); static void e_week_view_recalc_day_starts (EWeekView *week_view, time_t lower); static void e_week_view_on_adjustment_changed (GtkAdjustment *adjustment, @@ -1751,85 +1735,22 @@ e_week_view_get_day_position (EWeekView *week_view, gint *day_w, gint *day_h) { - gint week, day_of_week, row, col, weekend_col, box, weekend_box; - - *day_x = *day_y = *day_w = *day_h = 0; - g_return_if_fail (day >= 0); - - if (week_view->multi_week_view) { - g_return_if_fail (day < week_view->weeks_shown * 7); - - week = day / 7; - col = day % 7; - day_of_week = (week_view->display_start_day + day) % 7; - if (week_view->compress_weekend && day_of_week >= 5) { - /* In the compressed view Saturday is above Sunday and - both have just one row as opposed to 2 for all the - other days. */ - if (day_of_week == 5) { - *day_y = week_view->row_offsets[week * 2]; - *day_h = week_view->row_heights[week * 2]; - } else { - *day_y = week_view->row_offsets[week * 2 + 1]; - *day_h = week_view->row_heights[week * 2 + 1]; - col--; - } - /* Both Saturday and Sunday are in the same column. */ - *day_x = week_view->col_offsets[col]; - *day_w = week_view->col_widths[col]; - } else { - /* If the weekend is compressed and the day is after - the weekend we have to move back a column. */ - if (week_view->compress_weekend) { - /* Calculate where the weekend column is. - Note that 5 is Saturday. */ - weekend_col = (5 + 7 - week_view->display_start_day) % 7; - if (col > weekend_col) - col--; - } + gint cell_x, cell_y, cell_h; - *day_y = week_view->row_offsets[week * 2]; - *day_h = week_view->row_heights[week * 2] - + week_view->row_heights[week * 2 + 1]; - *day_x = week_view->col_offsets[col]; - *day_w = week_view->col_widths[col]; - } - } else { - g_return_if_fail (day < 7); - - /* Calculate which box to place the day in, from 0-5. - Note that in the week view the weekends are always - compressed and share a box. */ - box = day; - day_of_week = (week_view->display_start_day + day) % 7; - weekend_box = (5 + 7 - week_view->display_start_day) % 7; - if (box > weekend_box) - box--; - - if (box < 3) { - *day_x = week_view->col_offsets[0]; - *day_w = week_view->col_widths[0]; - } else { - *day_x = week_view->col_offsets[1]; - *day_w = week_view->col_widths[1]; - } + e_week_view_layout_get_day_position (day, + week_view->multi_week_view, + week_view->weeks_shown, + week_view->display_start_day, + week_view->compress_weekend, + &cell_x, &cell_y, &cell_h); - row = (box % 3) * 2; - if (day_of_week < 5) { - *day_y = week_view->row_offsets[row]; - *day_h = week_view->row_heights[row] - + week_view->row_heights[row + 1]; - } else if (day_of_week == 5) { - /* Saturday. */ - *day_y = week_view->row_offsets[row]; - *day_h = week_view->row_heights[row]; + *day_x = week_view->col_offsets[cell_x]; + *day_y = week_view->row_offsets[cell_y]; - } else { - /* Sunday. */ - *day_y = week_view->row_offsets[row + 1]; - *day_h = week_view->row_heights[row + 1]; - } - } + *day_w = week_view->col_widths[cell_x]; + *day_h = week_view->row_heights[cell_y]; + if (cell_h == 2) + *day_h += week_view->row_heights[cell_y + 1]; } @@ -1851,7 +1772,7 @@ e_week_view_get_span_position (EWeekView *week_view, { EWeekViewEvent *event; EWeekViewEventSpan *span; - gint end_day_of_week, num_days; + gint num_days; gint start_x, start_y, start_w, start_h; gint end_x, end_y, end_w, end_h; @@ -1865,35 +1786,14 @@ e_week_view_get_span_position (EWeekView *week_view, span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num); - if (span->row >= week_view->rows_per_cell) + if (!e_week_view_layout_get_span_position (event, span, + week_view->rows_per_cell, + week_view->rows_per_compressed_cell, + week_view->display_start_day, + week_view->multi_week_view, + week_view->compress_weekend, + &num_days)) { return FALSE; - - end_day_of_week = (week_view->display_start_day + span->start_day + span->num_days - 1) % 7; - num_days = span->num_days; - /* Check if the row will not be visible in compressed cells. */ - if (span->row >= week_view->rows_per_compressed_cell) { - if (week_view->multi_week_view) { - if (week_view->compress_weekend) { - /* If it ends on a Saturday and is 1 day long - we skip it, else we shorten it. If it ends - on a Sunday it must be 1 day long and we - skip it. */ - if (end_day_of_week == 5) { /* Sat */ - if (num_days == 1) { - return FALSE; - } else { - num_days--; - } - } else if (end_day_of_week == 6) { /* Sun */ - return FALSE; - } - } - } else { - /* All spans are 1 day long in the week view, so we - just skip it. */ - if (end_day_of_week > 4) - return FALSE; - } } e_week_view_get_day_position (week_view, span->start_day, @@ -2235,7 +2135,14 @@ e_week_view_check_layout (EWeekView *week_view) e_week_view_ensure_events_sorted (week_view); if (week_view->events_need_layout) - e_week_view_layout_events (week_view); + week_view->spans = e_week_view_layout_events + (week_view->events, week_view->spans, + week_view->multi_week_view, + week_view->weeks_shown, + week_view->compress_weekend, + week_view->display_start_day, + week_view->day_starts, + week_view->rows_per_day); if (week_view->events_need_layout || week_view->events_need_reshape) e_week_view_reshape_events (week_view); @@ -2246,154 +2153,6 @@ e_week_view_check_layout (EWeekView *week_view) static void -e_week_view_layout_events (EWeekView *week_view) -{ - EWeekViewEvent *event; - EWeekViewEventSpan *span; - gint num_days, day, event_num, span_num; - guint8 *grid; - GArray *spans, *old_spans; - - /* This is a temporary 2-d grid which is used to place events. - Each element is 0 if the position is empty, or 1 if occupied. - We allocate the maximum size possible here, assuming that each - event will need its own row. */ - grid = g_new0 (guint8, E_WEEK_VIEW_MAX_ROWS_PER_CELL * 7 - * E_WEEK_VIEW_MAX_WEEKS); - - /* We create a new array of spans, which will replace the old one. */ - spans = g_array_new (FALSE, FALSE, sizeof (EWeekViewEventSpan)); - - /* Clear the number of rows used per day. */ - num_days = week_view->multi_week_view ? week_view->weeks_shown * 7 : 7; - for (day = 0; day <= num_days; day++) { - week_view->rows_per_day[day] = 0; - } - - /* Iterate over the events, finding which weeks they cover, and putting - them in the first free row available. */ - for (event_num = 0; event_num < week_view->events->len; event_num++) { - event = &g_array_index (week_view->events, EWeekViewEvent, - event_num); - e_week_view_layout_event (week_view, event, grid, spans); - } - - /* Free the grid. */ - g_free (grid); - - /* Replace the spans array. */ - old_spans = week_view->spans; - week_view->spans = spans; - - /* Destroy the old spans array, destroying any unused canvas items. */ - if (old_spans) { - for (span_num = 0; span_num < old_spans->len; span_num++) { - span = &g_array_index (old_spans, EWeekViewEventSpan, - span_num); - if (span->background_item) - gtk_object_destroy (GTK_OBJECT (span->background_item)); - if (span->text_item) - gtk_object_destroy (GTK_OBJECT (span->text_item)); - } - g_array_free (old_spans, TRUE); - } -} - - -static void -e_week_view_layout_event (EWeekView *week_view, - EWeekViewEvent *event, - guint8 *grid, - GArray *spans) -{ - gint start_day, end_day, span_start_day, span_end_day, rows_per_cell; - gint free_row, row, day, span_num, spans_index, num_spans, max_day; - EWeekViewEventSpan span, *old_span; - - start_day = e_week_view_find_day (week_view, event->start, FALSE); - end_day = e_week_view_find_day (week_view, event->end, TRUE); - max_day = week_view->multi_week_view ? week_view->weeks_shown * 7 - 1 - : 7 - 1; - start_day = CLAMP (start_day, 0, max_day); - end_day = CLAMP (end_day, 0, max_day); - -#if 0 - g_print ("In e_week_view_layout_event Start:%i End: %i\n", - start_day, end_day); -#endif - - /* Iterate through each of the spans of the event, where each span - is a sequence of 1 or more days displayed next to each other. */ - span_start_day = start_day; - rows_per_cell = E_WEEK_VIEW_MAX_ROWS_PER_CELL; - span_num = 0; - spans_index = spans->len; - num_spans = 0; - while (span_start_day <= end_day) { - span_end_day = e_week_view_find_span_end (week_view, - span_start_day); - span_end_day = MIN (span_end_day, end_day); -#if 0 - g_print (" Span start:%i end:%i\n", span_start_day, - span_end_day); -#endif - /* Try each row until we find a free one or we fall off the - bottom of the available rows. */ - row = 0; - free_row = -1; - while (free_row == -1 && row < rows_per_cell) { - free_row = row; - for (day = span_start_day; day <= span_end_day; - day++) { - if (grid[day * rows_per_cell + row]) { - free_row = -1; - break; - } - } - row++; - }; - - if (free_row != -1) { - /* Mark the cells as full. */ - for (day = span_start_day; day <= span_end_day; - day++) { - grid[day * rows_per_cell + free_row] = 1; - week_view->rows_per_day[day] = MAX (week_view->rows_per_day[day], free_row + 1); - } -#if 0 - g_print (" Span start:%i end:%i row:%i\n", - span_start_day, span_end_day, free_row); -#endif - /* Add the span to the array, and try to reuse any - canvas items from the old spans. */ - span.start_day = span_start_day; - span.num_days = span_end_day - span_start_day + 1; - span.row = free_row; - span.background_item = NULL; - span.text_item = NULL; - if (event->num_spans > span_num) { - old_span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num); - span.background_item = old_span->background_item; - span.text_item = old_span->text_item; - old_span->background_item = NULL; - old_span->text_item = NULL; - } - - g_array_append_val (spans, span); - num_spans++; - } - - span_start_day = span_end_day + 1; - span_num++; - } - - /* Set the event's spans. */ - event->spans_index = spans_index; - event->num_spans = num_spans; -} - - -static void e_week_view_ensure_events_sorted (EWeekView *week_view) { if (!week_view->events_sorted) { @@ -2406,7 +2165,7 @@ e_week_view_ensure_events_sorted (EWeekView *week_view) } -static gint +gint e_week_view_event_sort_func (const void *arg1, const void *arg2) { @@ -2708,76 +2467,6 @@ e_week_view_reshape_event_span (EWeekView *week_view, } -/* Finds the day containing the given time. - If include_midnight_in_prev_day is TRUE then if the time exactly - matches the start of a day the previous day is returned. This is useful - when calculating the end day of an event. */ -static gint -e_week_view_find_day (EWeekView *week_view, - time_t time_to_find, - gboolean include_midnight_in_prev_day) -{ - gint num_days, day; - time_t *day_starts; - - num_days = week_view->multi_week_view ? week_view->weeks_shown * 7 : 7; - day_starts = week_view->day_starts; - - if (time_to_find < day_starts[0]) - return -1; - if (time_to_find > day_starts[num_days]) - return num_days; - - for (day = 1; day <= num_days; day++) { - if (time_to_find <= day_starts[day]) { - if (time_to_find == day_starts[day] - && !include_midnight_in_prev_day) - return day; - return day - 1; - } - } - - g_assert_not_reached (); - return num_days; -} - - -/* This returns the last possible day in the same span as the given day. - A span is all the days which are displayed next to each other from left to - right. In the week view all spans are only 1 day, since Tuesday is below - Monday rather than beside it etc. In the month view, if the weekends are not - compressed then each week is a span, otherwise we have to break a span up - on Saturday, use a separate span for Sunday, and start again on Monday. */ -static gint -e_week_view_find_span_end (EWeekView *week_view, - gint day) -{ - gint week, col, sat_col, end_col; - - if (week_view->multi_week_view) { - week = day / 7; - col = day % 7; - - /* We default to the last column in the row. */ - end_col = 6; - - /* If the weekend is compressed we must end any spans on - Saturday and Sunday. */ - if (week_view->compress_weekend) { - sat_col = (5 + 7 - week_view->display_start_day) % 7; - if (col <= sat_col) - end_col = sat_col; - else if (col == sat_col + 1) - end_col = sat_col + 1; - } - - return week * 7 + end_col; - } else { - return day; - } -} - - static void e_week_view_on_adjustment_changed (GtkAdjustment *adjustment, EWeekView *week_view) diff --git a/calendar/gui/e-week-view.h b/calendar/gui/e-week-view.h index 6717da9ea3..7fde751b3f 100644 --- a/calendar/gui/e-week-view.h +++ b/calendar/gui/e-week-view.h @@ -89,6 +89,11 @@ extern "C" { /* The padding below the date string in the Month view. */ #define E_WEEK_VIEW_DATE_B_PAD 1 +/* We use a 7-bit field to store row numbers in EWeekViewEventSpan, so the + maximum number or rows we can allow is 127. It is very unlikely to be + reached anyway. */ +#define E_WEEK_VIEW_MAX_ROWS_PER_CELL 127 + /* These index our colors array. */ typedef enum { @@ -445,6 +450,10 @@ void e_week_view_convert_time_to_display (EWeekView *week_view, gint *suffix_width); gint e_week_view_get_time_string_width (EWeekView *week_view); +gint e_week_view_event_sort_func (const void *arg1, + const void *arg2); + + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c index eda8617b0b..96f88d79a7 100644 --- a/calendar/gui/gnome-cal.c +++ b/calendar/gui/gnome-cal.c @@ -1031,6 +1031,28 @@ gnome_calendar_get_cal_client (GnomeCalendar *gcal) return priv->client; } +/** + * gnome_calendar_get_task_pad_cal_client: + * @gcal: A calendar view. + * + * Queries the calendar client interface object that a calendar view is using + * for the Task Pad. + * + * Return value: A calendar client interface object. + **/ +CalClient * +gnome_calendar_get_task_pad_cal_client (GnomeCalendar *gcal) +{ + GnomeCalendarPrivate *priv; + + g_return_val_if_fail (gcal != NULL, NULL); + g_return_val_if_fail (GNOME_IS_CALENDAR (gcal), NULL); + + priv = gcal->priv; + + return priv->task_pad_client; +} + gboolean gnome_calendar_open (GnomeCalendar *gcal, const char *str_uri) { diff --git a/calendar/gui/gnome-cal.h b/calendar/gui/gnome-cal.h index 0192efabe4..b9b5cd42a3 100644 --- a/calendar/gui/gnome-cal.h +++ b/calendar/gui/gnome-cal.h @@ -65,6 +65,7 @@ GtkWidget *gnome_calendar_construct (GnomeCalendar *gcal); GtkWidget *gnome_calendar_new (void); CalClient *gnome_calendar_get_cal_client (GnomeCalendar *gcal); +CalClient *gnome_calendar_get_task_pad_cal_client(GnomeCalendar *gcal); gboolean gnome_calendar_open (GnomeCalendar *gcal, const char *str_uri); /* diff --git a/calendar/gui/print.c b/calendar/gui/print.c index f89b1dae52..f6f798b88b 100644 --- a/calendar/gui/print.c +++ b/calendar/gui/print.c @@ -1,3 +1,5 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + /* Evolution calendar - Print support * * Copyright (C) 2000 Helix Code, Inc. @@ -38,14 +40,59 @@ #include <libgnomeprint/gnome-print-preview.h> #include <libgnomeprint/gnome-printer-dialog.h> #include <e-util/e-dialog-widgets.h> +#include <e-util/e-time-utils.h> #include <gal/widgets/e-unicode.h> #include <cal-util/timeutil.h> #include "calendar-commands.h" +#include "calendar-config.h" +#include "e-day-view.h" +#include "e-day-view-layout.h" +#include "e-week-view.h" +#include "e-week-view-layout.h" #include "gnome-cal.h" #include "print.h" +/* + * Note that most dimensions are in points (1/72 of an inch) since that is + * what gnome-print uses. + */ + +/* 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 + +/* 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 SMALL_MONTH_WIDTH 80 +#define SMALL_MONTH_PAD 4 +#define SMALL_MONTH_SPACING 12 + +/* 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 20 + +/* The minutes per row in the day view printout. */ +#define DAY_VIEW_MINS_PER_ROW 30 + +#define DAY_VIEW_ROWS ((60 / DAY_VIEW_MINS_PER_ROW) * 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 + + /* copied from gnome-month-item.c this should be shared?? */ /* Number of days in a month, for normal and leap years */ @@ -73,19 +120,42 @@ static const int sept_1752[42] = { #define SEPT_1752_START 2 /* Start day within month */ #define SEPT_1752_END 20 /* End day within month */ -/* FIXME: This needs to be able to render using all the options from the new - * calendar configuration stuff. - */ -#define WEEK_STARTS_ON_MONDAY FALSE struct pdinfo { - GList *slots; + 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; + gint8 cols_per_row[DAY_VIEW_ROWS]; + gboolean use_24_hour_format; }; struct psinfo { - GList *events; + gint days_shown; + time_t day_starts[E_WEEK_VIEW_MAX_WEEKS * 7 + 1]; + + GArray *events; + + int rows_per_cell; + int rows_per_compressed_cell; + int display_start_weekday; + gboolean multi_week_view; + int weeks_shown; + int month; + gboolean compress_weekend; + gboolean use_24_hour_format; + double row_height; + double header_row_height; }; struct ptinfo @@ -159,10 +229,10 @@ day_in_week (int day, int month, int year) * returned in the start and end arguments. */ static void -build_month (int month, int year, int start_on_monday, int *days, int *start, int *end) +build_month (int month, int year, int *days, int *start, int *end) { int i; - int d_month, d_week; + int d_month, d_week, week_start_day; /* Note that months are zero-based, so September is month 8 */ @@ -182,10 +252,15 @@ build_month (int month, int year, int start_on_monday, int *days, int *start, in days[i] = 0; d_month = days_in_month[is_leap_year (year)][month]; + /* Get the start weekday in the month, 0=Sun to 6=Sat. */ d_week = day_in_week (1, month, year); - if (start_on_monday) - d_week = (d_week + 6) % 7; + /* Get the configuration setting specifying which weekday we put on + the left column, 0=Sun to 6=Sat. */ + week_start_day = calendar_config_get_week_start_day (); + + /* 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; @@ -201,70 +276,135 @@ build_month (int month, int year, int start_on_monday, int *days, int *start, in enum align_box { ALIGN_LEFT=1, ALIGN_RIGHT, - ALIGN_CENTRE, + ALIGN_CENTER, ALIGN_BORDER= 1<<8 }; -/* width = width of border, -'ve is no border - fillcolour = shade of fill, -'ve is no fill */ + +/* 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. + fillcolor = shade of 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(GnomePrintContext *pc, double l, double r, double t, double b, double width, double fillcolour) +print_border_with_triangles (GnomePrintContext *pc, + double l, double r, double t, double b, + double width, double fillcolor, + double left_triangle_width, + double right_triangle_width) { - int i; gnome_print_gsave (pc); - if (fillcolour<0.0) - i=1; - else - i=0; - for (;i<2;i++) { - gnome_print_moveto(pc, l, t); - gnome_print_lineto(pc, l, b); - gnome_print_lineto(pc, r, b); - gnome_print_lineto(pc, r, t); - gnome_print_lineto(pc, l, t); - if (i==0) { - gnome_print_setrgbcolor(pc, fillcolour, fillcolour, fillcolour); - gnome_print_fill(pc); - if (width<0.0) - i=2; - } else { - gnome_print_setrgbcolor(pc, 0, 0, 0); - gnome_print_setlinewidth(pc, width); - gnome_print_stroke(pc); - } + + /* Fill in the interior of the rectangle, if desired. */ + if (fillcolor >= 0.0) { + gnome_print_moveto (pc, l, t); + if (left_triangle_width > 0.0) + gnome_print_lineto (pc, l - left_triangle_width, + (t + b) / 2); + gnome_print_lineto (pc, l, b); + gnome_print_lineto (pc, r, b); + if (right_triangle_width > 0.0) + gnome_print_lineto (pc, r + right_triangle_width, + (t + b) / 2); + gnome_print_lineto (pc, r, t); + gnome_print_closepath (pc); + gnome_print_setrgbcolor (pc, fillcolor, fillcolor, + fillcolor); + gnome_print_fill (pc); + } + + /* Draw the outline, if desired. */ + if (width >= 0.0) { + gnome_print_moveto (pc, l, t); + if (left_triangle_width > 0.0) + gnome_print_lineto (pc, l - left_triangle_width, + (t + b) / 2); + gnome_print_lineto (pc, l, b); + gnome_print_lineto (pc, r, b); + if (right_triangle_width > 0.0) + gnome_print_lineto (pc, r + right_triangle_width, + (t + b) / 2); + gnome_print_lineto (pc, r, t); + gnome_print_closepath (pc); + gnome_print_setrgbcolor (pc, 0, 0, 0); + gnome_print_setlinewidth (pc, width); + gnome_print_stroke (pc); } + gnome_print_grestore (pc); } -/* outputs 1 line of aligned text in a box */ + +/* 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 (GnomePrintContext *pc, + double l, double r, double t, double b, + double width, double fillcolor) +{ + print_border_with_triangles (pc, l, r, t, b, width, fillcolor, + -1.0, -1.0); +} + + +/* Prints 1 line of aligned text in a box. It is centered vertically, and + the horizontal alignment can be either ALIGN_LEFT, ALIGN_RIGHT, or + ALIGN_CENTER. */ static void -print_text(GnomePrintContext *pc, GnomeFont *font, const char *text, enum align_box align, double l, double r, double t, double b) +print_text(GnomePrintContext *pc, GnomeFont *font, const char *text, + enum align_box align, double l, double r, double t, double b) { - double w, x; + double w, x, y; + gnome_print_gsave (pc); - w = gnome_font_get_width_string(font, text); - switch (align&3) { - default: + + w = gnome_font_get_width_string (font, text); + + switch (align & 3) { case ALIGN_LEFT: + default: x = l; break; case ALIGN_RIGHT: - x = l+(r-l)-w-2; + x = r - w; break; - case ALIGN_CENTRE: - x = l+((r-l)-w)/2; + case ALIGN_CENTER: + x = l + ((r - l) - w) / 2; break; } - gnome_print_moveto(pc, x, t - gnome_font_get_size (font)); - gnome_print_setfont(pc, font); - gnome_print_setrgbcolor (pc, 0,0,0); - gnome_print_show(pc, text); + + /* Make sure we don't go off the left edge. */ + x = MAX (l, x); + + /* Calculate the top of where the text should be. */ + y = (t + b + gnome_font_get_size (font)) / 2; + + /* Now calculate the baseline. */ + y -= gnome_font_get_ascender (font) + 1; + + /* Set a clipping rectangle. */ + gnome_print_moveto (pc, l, t); + gnome_print_lineto (pc, r, t); + gnome_print_lineto (pc, r, b); + gnome_print_lineto (pc, l, b); + gnome_print_closepath (pc); + gnome_print_clip (pc); + + gnome_print_newpath (pc); + gnome_print_moveto (pc, x, y); + gnome_print_setfont (pc, font); + gnome_print_setrgbcolor (pc, 0, 0, 0); + gnome_print_show (pc, text); + gnome_print_grestore (pc); } /* gets/frees the font for you, as a bold font */ static void -print_text_size(GnomePrintContext *pc, double size, const char *text, enum align_box align, double l, double r, double t, double b) +print_text_size(GnomePrintContext *pc, double size, const char *text, + enum align_box align, double l, double r, double t, double b) { GnomeFont *font; @@ -274,19 +414,27 @@ print_text_size(GnomePrintContext *pc, double size, const char *text, enum align } static void -titled_box(GnomePrintContext *pc, const char *text, GnomeFont *font, enum align_box align, double *l, double *r, double *t, double *b, double linewidth) +titled_box (GnomePrintContext *pc, const char *text, GnomeFont *font, + enum align_box align, double *l, double *r, double *t, double *b, + double linewidth) { - if (align&ALIGN_BORDER) { - gnome_print_gsave(pc); - print_border(pc, *l, *r, *t, *t-gnome_font_get_size(font)-gnome_font_get_size(font)*0.4, linewidth, 0.9); - print_border(pc, *l, *r, *t-gnome_font_get_size(font)-gnome_font_get_size(font)*0.4, *b, linewidth, -1.0); - gnome_print_grestore(pc); - *l+=2; - *r-=2; - *b+=2; + double size; + + size = gnome_font_get_size (font); + + if (align & ALIGN_BORDER) { + print_border (pc, *l, *r, *t, *t - size * 1.4, + linewidth, 0.9); + print_border (pc, *l, *r, *t - size * 1.4, *b, + linewidth, -1.0); + + *l += 2; + *r -= 2; + *b += 2; } - print_text(pc, font, text, align, *l, *r, *t, *b); - *t-=gnome_font_get_size(font)*1.4; + + print_text (pc, font, text, align, *l, *r, *t, *t - size * 1.4); + *t -= size * 1.4; } enum datefmt { @@ -346,67 +494,110 @@ format_date(time_t time, int flags, char *buffer, int bufflen) print out the month small, embolden any days with events. */ static void -print_month_small (GnomePrintContext *pc, GnomeCalendar *gcal, - time_t month, double left, double right, double top, double bottom, - int titleflags, time_t greystart, time_t greyend, int bordertitle) +print_month_small (GnomePrintContext *pc, GnomeCalendar *gcal, time_t month, + double left, double right, double top, double bottom, + int titleflags, time_t greystart, time_t greyend, + int bordertitle) { CalClient *client; GnomeFont *font, *font_bold, *font_normal; time_t now, next; int x, y; int days[42]; - int day; + int day, weekday, week_start_day; char buf[100]; struct tm tm; - double xpad, ypad, size; - char *daynames[] = { _("Su"), _("Mo"), _("Tu"), _("We"), _("Th"), _("Fr"), _("Sa") }; + double font_size, max_font_size; + double header_size, col_width, row_height, text_xpad, w; + double cell_top, cell_bottom, cell_left, cell_right, text_right; + char *daynames[] = { N_("Su"), N_("Mo"), N_("Tu"), N_("We"), + N_("Th"), N_("Fr"), N_("Sa") }; + + /* Print the title, e.g. 'June 2001', in the top 16% of the area. */ + format_date (month, titleflags, buf, 100); + header_size = (top - bottom) * 0.16; + font = gnome_font_new_closest ("Times", GNOME_FONT_BOLD, FALSE, + header_size * 0.8); + if (bordertitle) { + print_border (pc, left, right, top, top - header_size, + 1.0, 0.9); + } + print_text (pc, font, buf, ALIGN_CENTER, left, right, + top - header_size * 0.1, top - header_size); + gtk_object_unref (GTK_OBJECT (font)); + + top -= header_size; client = gnome_calendar_get_cal_client (gcal); - xpad = (right-left)/7; - ypad = (top-bottom)/8.3; - if (xpad>ypad) - size=ypad; - else - size=xpad; + col_width = (right - left) / 7; - size = (xpad+ypad)/3.0; + /* The top row with the day abbreviations gets an extra bit of + vertical space around it. */ + row_height = (top - bottom) / 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 * 0.9; + + /* Check that it isn't going to be too wide. The characters are about + twice as high as they are wide, but we need to fit two characters + into each cell, so we don't want to go over col_width. */ + max_font_size = col_width * 0.65; + + font_size = MIN (font_size, max_font_size); - tm = *localtime (&month); /* get month days */ - build_month(tm.tm_mon, tm.tm_year+1900, WEEK_STARTS_ON_MONDAY, days, 0, 0); - - /* build day-busy bits */ - now = time_month_begin(month); - - /* get title */ - format_date(month, titleflags, buf, 100); - font = gnome_font_new_closest ("Times", GNOME_FONT_BOLD, 1, size*1.2); /* title font */ - if (bordertitle) - print_border(pc, - left, left+7*xpad, top, top-gnome_font_get_size(font)*1.3, - 1.0, 0.9); - print_text(pc, font, buf, ALIGN_CENTRE, - left, left+7*xpad, top, top - gnome_font_get_size (font)); - gtk_object_unref (GTK_OBJECT (font)); + tm = *localtime (&month); + build_month (tm.tm_mon, tm.tm_year + 1900, days, 0, 0); + + font_normal = gnome_font_new_closest ("Times", GNOME_FONT_BOOK, 0, + font_size); + font_bold = gnome_font_new_closest ("Times", GNOME_FONT_BOLD, 0, + font_size); - font_normal = gnome_font_new_closest ("Times", GNOME_FONT_BOOK, 0, size); - font_bold = gnome_font_new_closest ("Times", GNOME_FONT_BOLD, 0, size); + /* 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 = gnome_font_get_width_string (font_bold, "23"); + text_xpad = (col_width - w) / 2; - gnome_print_setrgbcolor (pc, 0,0,0); - for (x=0;x<7;x++) { - print_text(pc, font_bold, daynames[(WEEK_STARTS_ON_MONDAY?x+1:x)%7], ALIGN_CENTRE, - left+x*xpad, left+(x+1)*xpad, bottom+7*ypad, bottom+7*ypad-gnome_font_get_size(font_bold)); + gnome_print_setrgbcolor (pc, 0, 0, 0); + + /* Print the abbreviated day names across the top in bold. */ + week_start_day = calendar_config_get_week_start_day (); + weekday = week_start_day; + for (x = 0; x < 7; x++) { + print_text (pc, font_bold, _(daynames[weekday]), ALIGN_CENTER, + left + x * col_width, left + (x + 1) * col_width, + top, top - row_height * 1.4); + weekday = (weekday + 1) % 7; } - for (y=0;y<6;y++) { - for (x=0;x<7;x++) { - day = days[y*7+x]; - if (day!=0) { + top -= row_height * 1.4; + + now = time_month_begin (month); + for (y = 0; y < 6; y++) { + + cell_top = top - y * row_height; + cell_bottom = cell_top - row_height; + + for (x = 0; x < 7; x++) { + + cell_left = left + x * 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) { GList *uids; - sprintf(buf, "%d", day); + sprintf (buf, "%d", day); /* this is a slow messy way to do this ... but easy ... */ uids = cal_client_get_objects_in_range (client, @@ -415,18 +606,18 @@ print_month_small (GnomePrintContext *pc, GnomeCalendar *gcal, font = uids ? font_bold : font_normal; cal_obj_uid_list_free (uids); - next = time_add_day(now, 1); - if ((now>=greystart && now<greyend) - || (greystart>=now && greystart<next)) { - print_border(pc, - left+x*xpad+xpad*0.1, - left+(x+1)*xpad+xpad*0.1, - bottom+(5-y)*ypad+gnome_font_get_size(font)-ypad*0.15, - bottom+(5-y)*ypad-ypad*0.15, - -1.0, 0.75); + next = time_add_day (now, 1); + if ((now >= greystart && now < greyend) + || (greystart >= now && greystart < next)) { + print_border (pc, + cell_left, cell_right, + cell_top, cell_bottom, + -1.0, 0.75); } - print_text(pc, font, buf, ALIGN_RIGHT, - left+x*xpad, left+(x+1)*xpad, bottom+(5-y)*ypad+gnome_font_get_size(font), bottom+(5-y)*ypad); + print_text (pc, font, buf, ALIGN_RIGHT, + cell_left, text_right, + cell_top, cell_bottom); + now = next; } } @@ -530,151 +721,464 @@ bound_text(GnomePrintContext *pc, GnomeFont *font, const char *text, return top; } -/* - * Print Day Details - */ -static gboolean -print_day_details_cb (CalComponent *comp, time_t istart, time_t iend, gpointer data) + +/* Draw the borders, lines, and times down the left of the day view. */ +static void +print_day_background (GnomePrintContext *pc, GnomeCalendar *gcal, + time_t whence, struct pdinfo *pdi, + double left, double right, double top, double bottom) { - CalComponentText text; - GList *l, *col = NULL; - struct pdinfo *pdi = (struct pdinfo *)data; - struct einfo *ei; + GnomeFont *font_hour, *font_minute; + double yinc, y; + double width = DAY_VIEW_TIME_COLUMN_WIDTH; + double font_size, max_font_size, hour_font_size, minute_font_size; + char buf[20], *minute; + gboolean use_24_hour; + int i, hour, row; + + /* Fill the time column in light-gray. */ + print_border (pc, left, left + width, top, bottom, -1.0, 0.9); + + /* Draw the border around the entire view. */ + gnome_print_setrgbcolor (pc, 0, 0, 0); + print_border (pc, left, right, top, bottom, 1.0, -1.0); - ei = g_new (struct einfo, 1); + /* Draw the vertical line on the right of the time column. */ + gnome_print_setlinewidth (pc, 0.0); + gnome_print_moveto (pc, left + width, bottom); + gnome_print_lineto (pc, left + width, top); + gnome_print_stroke (pc); - cal_component_get_summary (comp, &text); - ei->text = g_strdup (text.value); + /* Calculate the row height. */ + yinc = (top - bottom) / (pdi->end_hour - pdi->start_hour); + + /* Get the 2 fonts we need. */ + font_size = yinc * 0.6; + max_font_size = width * 0.5; + font_size = MIN (font_size, max_font_size); + font_hour = gnome_font_new_closest ("Times", GNOME_FONT_BOLD, + 0, font_size); + hour_font_size = gnome_font_get_size (font_hour); + + font_size = yinc * 0.33; + max_font_size = width * 0.25; + font_size = MIN (font_size, max_font_size); + font_minute = gnome_font_new_closest ("Times", GNOME_FONT_BOOK, + 0, font_size); + minute_font_size = gnome_font_get_size (font_minute); + + use_24_hour = calendar_config_get_24_hour_format (); + + row = 0; + for (i = pdi->start_hour; i < pdi->end_hour; i++) { + y = top - yinc * (row + 1); + gnome_print_setrgbcolor (pc, 0, 0, 0); - ei->start = istart; - ei->end = iend; - ei->count = 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; + } - for (l = pdi->slots; l; l = l->next) { - struct einfo *testei; + /* the hour label/minute */ + sprintf (buf, "%d", hour); + print_text (pc, font_hour, buf, ALIGN_RIGHT, + left, left + width * 0.58, + y + yinc - 4, y + yinc - 4 - hour_font_size); + print_text (pc, font_minute, minute, ALIGN_RIGHT, + left, left + width - 3, + y + yinc - 3, y + yinc - 3 - minute_font_size); + + /* Draw the horizontal line between hours, across the entire + width of the day view. */ + gnome_print_moveto (pc, left, y); + gnome_print_lineto (pc, right, y); + gnome_print_stroke (pc); + + /* Draw the horizontal line for the 1/2-hours, across the + entire width except for part of the time column. */ + gnome_print_moveto (pc, left + width * 0.6, y + yinc / 2); + gnome_print_lineto (pc, right, y + yinc / 2); + gnome_print_stroke (pc); - col = (GList *)l->data; - testei = (struct einfo *)col->data; + row++; + } - if (ei->start >= testei->end) { - col = g_list_prepend (col, ei); - l->data = col; - return TRUE; - } + gtk_object_unref (GTK_OBJECT (font_hour)); + gtk_object_unref (GTK_OBJECT (font_minute)); +} - testei->count++; - ei->count++; + +/* This adds one event to the view, adding it to the appropriate array. */ +static gint +print_day_add_event (CalComponent *comp, + time_t start, + time_t end, + gint days_shown, + time_t *day_starts, + GArray *long_events, + GArray **events) + +{ + EDayViewEvent event; + gint day, offset; + struct tm start_tm, end_tm; + +#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_tm = *(localtime (&start)); + end_tm = *(localtime (&end)); + + event.comp = comp; + gtk_object_ref (GTK_OBJECT (comp)); + 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_tm.tm_hour * 60 + start_tm.tm_min - offset; + event.end_minute = end_tm.tm_hour * 60 + end_tm.tm_min - offset; + + event.start_row_or_col = -1; + event.num_columns = -1; + + /* 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; + } } - col = NULL; - col = g_list_prepend (col, ei); - pdi->slots = g_list_append (pdi->slots, col); + /* 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 (CalComponent *comp, time_t istart, time_t iend, + gpointer data) +{ + struct pdinfo *pdi = (struct pdinfo *)data; + + print_day_add_event (comp, istart, iend, + pdi->days_shown, pdi->day_starts, + pdi->long_events, pdi->events); return TRUE; } + static void -print_day_details (GnomePrintContext *pc, GnomeCalendar *gcal, time_t whence, - double left, double right, double top, double bottom) +free_event_array (GArray *array) { - CalClient *client; - struct pdinfo pdi; - time_t start, end; - GList *l; - int num_slots, i; - GnomeFont *font_hour, *font_minute, *font_summary; - double yinc, y, yend, x, xend; - double width=40, slot_width; - char buf[20]; + 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) + gtk_object_destroy (GTK_OBJECT (event->canvas_item)); + gtk_object_unref (GTK_OBJECT (event->comp)); + } - client = gnome_calendar_get_cal_client (gcal); + g_array_set_size (array, 0); +} - yinc = (top-bottom)/24; - /* fill static detail */ - font_hour = gnome_font_new_closest ("Times", GNOME_FONT_BOLD, 0, yinc/2); - font_minute = gnome_font_new_closest ("Times", GNOME_FONT_BOLD, 0, yinc/3); - font_summary = gnome_font_new_closest ("Times", GNOME_FONT_BOOK, 0, yinc/3); +static void +print_day_long_event (GnomePrintContext *pc, GnomeFont *font, + double left, double right, double top, double bottom, + double row_height, EDayViewEvent *event, + struct pdinfo *pdi) +{ + CalComponentText summary; + double x1, x2, y1, y2; + double left_triangle_width = -1.0, right_triangle_width = -1.0; + char *text; + char buffer[32]; + struct tm date_tm; + + /* 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 - 4; + y2 = y1 - row_height + 4; + print_border_with_triangles (pc, x1, x2, y1, y2, 0.0, 0.95, + 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; + print_text (pc, font, buffer, ALIGN_LEFT, x1, x2, y1, y2); + x1 += gnome_font_get_width_string (font, buffer); + } - gnome_print_setrgbcolor (pc, 0, 0, 0); + /* 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; + print_text (pc, font, buffer, ALIGN_RIGHT, x1, x2, y1, y2); + x2 -= gnome_font_get_width_string (font, buffer); + } - /* internal lines */ - gnome_print_setlinewidth(pc, 0.0); - gnome_print_moveto(pc, left+width, bottom); - gnome_print_lineto(pc, left+width, top); - gnome_print_stroke (pc); + /* Print the text. */ + cal_component_get_summary (event->comp, &summary); + text = summary.value ? (char*) summary.value : ""; - for (i=0;i<24;i++) { - y = top - yinc*(i+1); - print_border(pc, left+1, left+width-1, y, y+yinc-1, -1.0, 0.9); - gnome_print_setrgbcolor (pc, 0, 0, 0); + x1 += 4; + x2 -= 4; + print_text (pc, font, text, ALIGN_CENTER, x1, x2, y1, y2); +} - /* the hour label/minute */ - sprintf(buf, "%d", i); - print_text(pc, font_hour, buf, ALIGN_RIGHT, left, left+width/2, y+yinc, y); - switch(i) { - case 12: sprintf(buf, _("pm")); break; - case 0: sprintf(buf, _("am")); break; - default: sprintf(buf, "00"); break; - } - print_text(pc, font_minute, buf, ALIGN_LEFT, left+width/2, left+width/2, y+yinc, y); - /* internal lines */ - gnome_print_moveto(pc, left+width, y); - gnome_print_lineto(pc, right, y); - gnome_print_stroke (pc); - gnome_print_moveto(pc, left+width/2, y+yinc/2); - gnome_print_lineto(pc, right, y+yinc/2); - gnome_print_stroke (pc); +static void +print_day_event (GnomePrintContext *pc, GnomeFont *font, + double left, double right, double top, double bottom, + EDayViewEvent *event, struct pdinfo *pdi) +{ + CalComponentText summary; + double x1, x2, y1, y2, col_width, row_height; + int start_offset, end_offset, start_row, end_row; + char *text, start_buffer[32], end_buffer[32]; + gboolean display_times = FALSE, free_text = FALSE; + struct tm date_tm; + + 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 = time_day_begin(whence); - end = time_day_end(start); + 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]; - pdi.slots = NULL; + if (start_offset != start_row * pdi->mins_per_row + || end_offset != (end_row + 1) * pdi->mins_per_row) + display_times = TRUE; - cal_client_generate_instances (client, CALOBJ_TYPE_EVENT, start, end, - print_day_details_cb, &pdi); + x1 = left + event->start_row_or_col * col_width; + x2 = x1 + event->num_columns * col_width - DAY_VIEW_EVENT_X_PAD; + + row_height = (top - bottom) / pdi->rows; + y1 = top - start_row * row_height; + y2 = top - (end_row + 1) * row_height; - num_slots = g_list_length (pdi.slots); - slot_width = (right-left-width)/num_slots; +#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 - for (i = num_slots, l = pdi.slots; l; i--, l = l->next) { - GList *e = (GList *)l->data; + print_border (pc, x1, x2, y1, y2, 1.0, 0.95); - for (; e; e = e->next) { - struct einfo *ei = (struct einfo *)e->data; + cal_component_get_summary (event->comp, &summary); + text = summary.value ? (char*) summary.value : ""; - y = top - (top - bottom) * (ei->start - start) / (end - start) - 1; - yend = top - (top - bottom) * (ei->end - start) / (end - start) + 1; - x = left + width + slot_width * (num_slots - i); - if (num_slots > 0) - x++; + if (display_times) { + 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; - if (i == 0) - xend = x + (num_slots - ei->count) * slot_width - 2; - else - xend = x + slot_width - 2; + e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE, + start_buffer, sizeof (start_buffer)); - print_border (pc, x, xend, y, yend, 0.0, 0.9); + date_tm.tm_hour = event->end_minute / 60; + date_tm.tm_min = event->end_minute % 60; - bound_text (pc, font_summary, ei->text, x, xend, y, yend, 0); + e_time_format_time (&date_tm, pdi->use_24_hour_format, FALSE, + end_buffer, sizeof (end_buffer)); - g_free (ei); - } - g_list_free (e); + text = g_strdup_printf ("%s - %s %s ", start_buffer, + end_buffer, text); + + free_text = TRUE; } - g_list_free (pdi.slots); - print_border (pc, left, right, top, bottom, 1.0, -1.0); + bound_text (pc, font, text, x1 + 2, x2 - 2, y1, y2, 0); - gtk_object_unref (GTK_OBJECT (font_hour)); - gtk_object_unref (GTK_OBJECT (font_minute)); - gtk_object_unref (GTK_OBJECT (font_summary)); + if (free_text) + g_free (text); } + +static void +print_day_details (GnomePrintContext *pc, GnomeCalendar *gcal, time_t whence, + double left, double right, double top, double bottom) +{ + CalClient *client; + EDayViewEvent *event; + GnomeFont *font; + time_t start, end; + struct pdinfo pdi; + gint rows_in_top_display, i; + double font_size, max_font_size; + + start = time_day_begin (whence); + end = time_day_end (start); + + 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 = calendar_config_get_day_start_hour (); + pdi.end_hour = calendar_config_get_day_end_hour (); + if (calendar_config_get_day_end_minute () != 0) + pdi.end_hour++; + pdi.rows = (pdi.end_hour - pdi.start_hour) * 2; + pdi.mins_per_row = 30; + pdi.start_minute_offset = pdi.start_hour * 60; + pdi.end_minute_offset = pdi.end_hour * 60; + pdi.use_24_hour_format = calendar_config_get_24_hour_format (); + + /* Get the events from the server. */ + client = gnome_calendar_get_cal_client (gcal); + cal_client_generate_instances (client, CALOBJ_TYPE_EVENT, 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); + + /* 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 = gnome_font_new_closest ("Times", GNOME_FONT_BOOK, 0, 12); + for (i = 0; i < pdi.long_events->len; i++) { + event = &g_array_index (pdi.long_events, EDayViewEvent, i); + print_day_long_event (pc, font, left, right, top, bottom, + DAY_VIEW_ROW_HEIGHT, event, &pdi); + } + gtk_object_unref (GTK_OBJECT (font)); + + /* 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. */ + rows_in_top_display = MAX (rows_in_top_display, + DAY_VIEW_MIN_ROWS_IN_TOP_DISPLAY); + + /* Draw the border around the long events. */ + gnome_print_setrgbcolor (pc, 0, 0, 0); + print_border (pc, left, right, + top, top - rows_in_top_display * DAY_VIEW_ROW_HEIGHT - 4, + 1.0, -1.0); + + /* Adjust the area containing the main display. */ + top -= rows_in_top_display * DAY_VIEW_ROW_HEIGHT + 4; + + /* Draw the borders, lines, and times down the left. */ + print_day_background (pc, 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], DAY_VIEW_ROWS, + DAY_VIEW_MINS_PER_ROW, pdi.cols_per_row); + + /* Print the short events. */ + max_font_size = ((top - bottom) / pdi.rows) - 4; + font_size = MIN (DAY_NORMAL_FONT_SIZE, max_font_size); + font = gnome_font_new_closest ("Times", GNOME_FONT_BOOK, 0, font_size); + for (i = 0; i < pdi.events[0]->len; i++) { + event = &g_array_index (pdi.events[0], EDayViewEvent, i); + print_day_event (pc, font, left, right, top, bottom, + event, &pdi); + } + gtk_object_unref (GTK_OBJECT (font)); + + /* Free everything. */ + free_event_array (pdi.long_events); + g_array_free (pdi.long_events, TRUE); + free_event_array (pdi.events[0]); + g_array_free (pdi.events[0], TRUE); +} + + /* * Print Day Summary */ @@ -684,6 +1188,7 @@ print_day_details (GnomePrintContext *pc, GnomeCalendar *gcal, time_t whence, #define TIME_FMT "%l:%M%p" #endif +#if 0 static gboolean print_day_summary_cb (CalComponent *comp, time_t istart, time_t iend, gpointer data) { @@ -800,79 +1305,447 @@ print_day_summary (GnomePrintContext *pc, GnomeCalendar *gcal, time_t whence, gtk_object_unref (GTK_OBJECT (font_summary)); } +#endif + +/* This adds one event to the view, adding it to the appropriate array. */ +static gboolean +print_week_summary_cb (CalComponent *comp, + time_t start, + time_t end, + gpointer data) + +{ + EWeekViewEvent event; + struct tm start_tm, end_tm; + + struct psinfo *psi = (struct psinfo *)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_tm = *(localtime (&start)); + end_tm = *(localtime (&end)); + + event.comp = comp; + gtk_object_ref (GTK_OBJECT (event.comp)); + event.start = start; + event.end = end; + event.spans_index = 0; + event.num_spans = 0; + + event.start_minute = start_tm.tm_hour * 60 + start_tm.tm_min; + event.end_minute = end_tm.tm_hour * 60 + end_tm.tm_min; + if (event.end_minute == 0 && start != end) + event.end_minute = 24 * 60; + + g_array_append_val (psi->events, event); + + return 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_summary (GnomePrintContext *pc, GnomeCalendar *gcal, time_t whence, - double left, double right, double top, double bottom) +print_week_long_event (GnomePrintContext *pc, GnomeFont *font, + struct psinfo *psi, + double x1, double x2, double y1, double y2, + EWeekViewEvent *event, EWeekViewEventSpan *span, + char *text) { - double y, l, r, t, b; - time_t now; - int i; + double left_triangle_width = -1.0, right_triangle_width = -1.0; + struct tm date_tm; + char 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 (pc, x1, x2, y1, y2, 0.5, 0.9, + 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 > 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 += 4; + print_text (pc, font, buffer, ALIGN_LEFT, x1, x2, y1, y2); + x1 += gnome_font_get_width_string (font, buffer); + } - l = left; - r = (right-left)/2+left; - t = top; - y = (top-bottom)/3; - b = top-y; - now = time_week_begin(whence); /* returns sunday, we need monday */ - now = time_add_day(now, 1); - for (i = 0; i < 7; i++) { - print_day_summary (pc, gcal, now, l, r, t, b, - 10, TRUE, DATE_DAY | DATE_DAYNAME | DATE_MONTH); - now = time_add_day (now, 1); - switch (i) { - case 5: - y /= 2.0; - b += y; - case 0: - case 1: - case 3: - case 4: - t -= y; - b -= y; - break; - case 2: - l = r; - r = right; - t = top; - b = t-y; - break; - case 6: - break; + /* 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 -= 4; + print_text (pc, font, buffer, ALIGN_RIGHT, x1, x2, y1, y2); + x2 -= gnome_font_get_width_string (font, buffer); + } + + x1 += 4; + x2 -= 4; + print_text (pc, font, text, ALIGN_CENTER, x1, x2, y1, y2); +} + + +static void +print_week_day_event (GnomePrintContext *pc, GnomeFont *font, + struct psinfo *psi, + double x1, double x2, double y1, double y2, + EWeekViewEvent *event, EWeekViewEventSpan *span, + char *text) +{ + struct tm date_tm; + char 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_text (pc, font, buffer, ALIGN_LEFT, x1, x2, y1, y2); + x1 += gnome_font_get_width_string (font, buffer); + + x1 += 4; + print_text (pc, font, text, ALIGN_LEFT, x1, x2, y1, y2); +} + + +static void +print_week_event (GnomePrintContext *pc, GnomeFont *font, + struct psinfo *psi, + double left, double top, + double cell_width, double cell_height, + EWeekViewEvent *event, GArray *spans) +{ + EWeekViewEventSpan *span; + gint span_num; + CalComponentText summary; + char *text; + int num_days, start_x, start_y, start_h, end_x, end_y, end_h; + double x1, x2, y1, y2; + + cal_component_get_summary (event->comp, &summary); + text = summary.value ? (char*) summary.value : ""; + + 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 + 6; + x2 = left + (end_x + 1) * cell_width - 6; + y1 = top - start_y * cell_height + - psi->header_row_height + - span->row * psi->row_height; + y2 = y1 - psi->row_height * 0.9; + + if (print_is_one_day_week_event (event, span, + psi->day_starts)) { + print_week_day_event (pc, font, psi, + x1, x2, y1, y2, + event, span, text); + } else { + print_week_long_event (pc, font, psi, + x1, x2, y1, y2, + event, span, text); + } } } } + +static void +print_week_view_background (GnomePrintContext *pc, GnomeFont *font, + struct psinfo *psi, + double left, double top, + double cell_width, double cell_height) +{ + int day, day_x, day_y, day_h; + double x1, x2, y1, y2, font_size, fillcolor; + struct tm tm; + char *format_string, buffer[128]; + + font_size = gnome_font_get_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; + + tm = *localtime (&psi->day_starts[day]); + + /* 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 (pc, 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 { + gnome_print_moveto (pc, x1 + 0.1 * cell_width, + y1 - psi->header_row_height + 3); + gnome_print_lineto (pc, x2, + y1 - psi->header_row_height + 3); + gnome_print_setrgbcolor (pc, 0, 0, 0); + gnome_print_setlinewidth (pc, 0.0); + gnome_print_stroke (pc); + + /* 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"); + + } + + strftime (buffer, sizeof (buffer), format_string, &tm); + print_text (pc, font, buffer, ALIGN_RIGHT, + x1, x2 - 4, y1 - 2, y1 - 2 - font_size); + } +} + + + +static void +print_week_summary (GnomePrintContext *pc, GnomeCalendar *gcal, + time_t whence, gboolean multi_week_view, int weeks_shown, + int month, double font_size, + double left, double right, double top, double bottom) +{ + CalClient *client; + EWeekViewEvent *event; + struct psinfo psi; + time_t day_start; + gint rows_per_day[E_WEEK_VIEW_MAX_WEEKS * 7], day, event_num; + GArray *spans; + GnomeFont *font; + double cell_width, cell_height; + + 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; + + /* Get a few config settings. */ + psi.compress_weekend = calendar_config_get_compress_weekend (); + psi.use_24_hour_format = calendar_config_get_24_hour_format (); + + /* We convert this from (0 = Sun, 6 = Sat) to (0 = Mon, 6 = Sun). */ + psi.display_start_weekday = calendar_config_get_week_start_day (); + 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 (whence); + for (day = 0; day <= psi.days_shown; day++) { + psi.day_starts[day] = day_start; + day_start = time_add_day (day_start, 1); + } + + /* Get the events from the server. */ + client = gnome_calendar_get_cal_client (gcal); + cal_client_generate_instances (client, CALOBJ_TYPE_EVENT, + 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 = (top - bottom) / (weeks_shown * 2); + } else { + cell_width = (right - left) / 2; + cell_height = (top - bottom) / 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; + psi.rows_per_compressed_cell = (cell_height - psi.header_row_height) + / psi.row_height; + + font = gnome_font_new_closest ("Times", GNOME_FONT_BOOK, 0, font_size); + + /* Draw the grid and the day names/numbers. */ + print_week_view_background (pc, font, &psi, left, top, + cell_width, cell_height); + + /* Print the events. */ + for (event_num = 0; event_num < psi.events->len; event_num++) { + event = &g_array_index (psi.events, EWeekViewEvent, event_num); + print_week_event (pc, font, &psi, left, top, + cell_width, cell_height, event, spans); + } + + gtk_object_unref (GTK_OBJECT (font)); + + /* Free everything. */ + for (event_num = 0; event_num < psi.events->len; event_num++) { + event = &g_array_index (psi.events, EWeekViewEvent, event_num); + gtk_object_unref (GTK_OBJECT (event->comp)); + } + g_array_free (psi.events, TRUE); + g_array_free (spans, TRUE); +} + + static void print_year_summary (GnomePrintContext *pc, GnomeCalendar *gcal, time_t whence, - double left, double right, double top, double bottom, int morerows) + double left, double right, double top, double bottom, + int morerows) { - double y, x, l, r, t, b; + double row_height, col_width, l, r, t, b; time_t now; - int xx, yy, rows, cols; + int col, row, rows, cols; l = left; t = top; + + /* If morerows is set we do 4 rows and 3 columns instead of 3 rows and + 4 columns. This is useful if we switch paper orientation. */ if (morerows) { - rows=4; - cols=3; + rows = 4; + cols = 3; } else { - rows=3; - cols=4; + rows = 3; + cols = 4; } - y = (top-bottom)/rows; - x = (right-left)/cols; - r = l+x; - b = top-y; - now = time_year_begin(whence); - for (yy = 0; yy < rows; yy++) { - t = top - y * yy; - b = t - y; - for (xx = 0; xx < cols; xx++) { - l = left + x * xx; - r = l + x; + + row_height = (top - bottom) / rows; + col_width = (right - left) / cols; + r = l + col_width; + b = top - row_height; + now = time_year_begin (whence); + + for (row = 0; row < rows; row++) { + t = top - row_height * row; + b = t - row_height; + for (col = 0; col < cols; col++) { + l = left + col_width * col; + r = l + col_width; print_month_small (pc, gcal, now, - l + 8, r - 8, t - 8, b + 8, DATE_MONTH, 0, 0, TRUE); + l + 8, r - 8, t - 8, b + 8, + DATE_MONTH, 0, 0, TRUE); now = time_add_month (now, 1); } } @@ -882,54 +1755,75 @@ static void print_month_summary (GnomePrintContext *pc, GnomeCalendar *gcal, time_t whence, double left, double right, double top, double bottom) { - time_t now, today; - int days[42]; - int day; + time_t date; struct tm tm; - int x, y; - char buf[100]; - GnomeFont *font_days; + char buffer[100]; + GnomeFont *font; + gboolean compress_weekend; + int columns, col, weekday, len, month; + double font_size, cell_width, x1, x2, y1, y2; - now = time_month_begin(whence); - tm = *localtime (&now); + weekday = calendar_config_get_week_start_day (); + compress_weekend = calendar_config_get_compress_weekend (); - /* get month days */ - build_month(tm.tm_mon, tm.tm_year+1900, WEEK_STARTS_ON_MONDAY, days, 0, 0); + /* Remember which month we want. */ + tm = *localtime (&whence); + month = tm.tm_mon; + + /* Find the start of the month, and then the start of the week on + or before that day. */ + date = time_month_begin (whence); + date = time_week_begin (date, weekday); + + /* If weekends are compressed then we can't start on a Sunday. */ + if (compress_weekend && weekday == 0) + date = time_add_day (date, -1); - /* a little margin */ - top -= 4; + tm = *localtime (&date); /* do day names ... */ - font_days = gnome_font_new_closest ("Times", GNOME_FONT_BOLD, 0, 10); - gnome_print_setfont(pc, font_days); - for (x=0;x<7;x++) { - today = time_add_day(now, days[6+x]); - format_date(today, DATE_DAYNAME, buf, 100); - print_text(pc, font_days, buf, ALIGN_CENTRE, - (right-left)*x/7+left, (right-left)*(x+1)/7+left, - top, top-gnome_font_get_size(font_days)); - } - top -= gnome_font_get_size(font_days)*1.5; - gtk_object_unref (GTK_OBJECT (font_days)); - - for (y=0;y<6;y++) { - for (x=0;x<7;x++) { - day = days[y*7+x]; - if (day!=0) { - print_day_summary (pc, gcal, now, - (right-left)*x/7+left, - (right-left)*(x+1)/7+left, - top - (top-bottom)*y/6, - top - (top-bottom)*(y+1)/6, 6, FALSE, - day==1?(DATE_DAY|DATE_MONTH):DATE_DAY); - now = time_add_day(now, 1); - } + font = gnome_font_new_closest ("Times", GNOME_FONT_BOLD, 0, + MONTH_NORMAL_FONT_SIZE); + font_size = gnome_font_get_size (font); + gnome_print_setfont (pc, 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) { + strftime (buffer, sizeof (buffer), "%a/", &tm); + len = strlen (buffer); + tm.tm_mday++; + mktime (&tm); + strftime (buffer + len, sizeof (buffer) - len, + "%a", &tm); + } else { + strftime (buffer, sizeof (buffer), "%A", &tm); } + + x1 = left + cell_width * col; + x2 = x1 + cell_width; + + print_border (pc, x1, x2, y1, y2, 1.0, -1.0); + print_text (pc, font, buffer, ALIGN_CENTER, x1, x2, y1, y2); + + tm.tm_mday++; + mktime (&tm); } + gtk_object_unref (GTK_OBJECT (font)); + + top = y2; + print_week_summary (pc, gcal, date, TRUE, 6, month, + MONTH_NORMAL_FONT_SIZE, + left, right, top, bottom); } static void -print_todo_details (GnomePrintContext *pc, GnomeCalendar *gcal, time_t start, time_t end, +print_todo_details (GnomePrintContext *pc, GnomeCalendar *gcal, + time_t start, time_t end, double left, double right, double top, double bottom) { CalClient *client; @@ -937,16 +1831,18 @@ print_todo_details (GnomePrintContext *pc, GnomeCalendar *gcal, time_t start, ti GList *l; GnomeFont *font_summary; double y, yend, x, xend; + struct icaltimetype *tt; - client = gnome_calendar_get_cal_client (gcal); + client = gnome_calendar_get_task_pad_cal_client (gcal); - font_summary = gnome_font_new_closest ("Times", GNOME_FONT_BOOK, 0, 10); + font_summary = gnome_font_new_closest ("Times", GNOME_FONT_BOOK, + 0, 10); gnome_print_setrgbcolor (pc, 0, 0, 0); gnome_print_setlinewidth (pc, 0.0); - titled_box (pc, _("Tasks"), font_summary, - ALIGN_CENTRE | ALIGN_BORDER, &left, &right, &top, &bottom, 1.0); + titled_box (pc, _("Tasks"), font_summary, ALIGN_CENTER | ALIGN_BORDER, + &left, &right, &top, &bottom, 1.0); y = top - 3; yend = bottom - 2; @@ -991,10 +1887,27 @@ print_todo_details (GnomePrintContext *pc, GnomeCalendar *gcal, time_t start, ti if (y < bottom) break; - y = bound_text (pc, font_summary, summary.value, x + 2, xend, y, yend, 0); - y += gnome_font_get_size (font_summary); - gnome_print_moveto (pc, x, y - 3); - gnome_print_lineto (pc, xend, y - 3); + /* Print the box to put the tick in. */ + print_border (pc, x + 2, x + 8, y - 3, y - 11, 0.0, -1.0); + + /* If the task is complete, print a tick in the box. */ + cal_component_get_completed (comp, &tt); + if (tt) { + cal_component_free_icaltimetype (tt); + + gnome_print_setrgbcolor (pc, 0, 0, 0); + gnome_print_setlinewidth (pc, 1.0); + gnome_print_moveto (pc, x + 3, y - 8); + gnome_print_lineto (pc, x + 5, y - 10); + gnome_print_lineto (pc, x + 7, y - 3.5); + gnome_print_stroke (pc); + } + + y = bound_text (pc, font_summary, summary.value, + x + 10, xend, y, yend, 0); + y += gnome_font_get_size (font_summary) - 6; + gnome_print_moveto (pc, x, y); + gnome_print_lineto (pc, xend, y); gnome_print_stroke (pc); y -= 3; } @@ -1004,47 +1917,6 @@ print_todo_details (GnomePrintContext *pc, GnomeCalendar *gcal, time_t start, ti gtk_object_unref (GTK_OBJECT (font_summary)); } -#if 0 - -static GnomePrintContext * -print_context (int preview, char *paper) -{ - GtkWidget *toplevel, *canvas, *sw; - GnomePrinter *printer; - GnomePrintContext *pc; - - if (preview) { - gtk_widget_push_colormap (gdk_rgb_get_cmap ()); - gtk_widget_push_visual (gdk_rgb_get_visual ()); - - toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_widget_set_usize (toplevel, 700, 700); - sw = gtk_scrolled_window_new (NULL, NULL); - canvas = gnome_canvas_new_aa (); - gtk_container_add (GTK_CONTAINER (toplevel), sw); - gtk_container_add (GTK_CONTAINER (sw), canvas); - - gnome_canvas_set_pixels_per_unit((GnomeCanvas *)canvas, 1); - - pc = gnome_print_preview_new ((GnomeCanvas *)canvas, paper); - - gtk_widget_show_all (toplevel); - - gtk_widget_pop_visual (); - gtk_widget_pop_colormap (); - } else { - printer = gnome_printer_dialog_new_modal (); - - if (!printer) - return NULL; - - pc = gnome_print_context_new_with_paper_size (printer, paper); - } - - return pc; -} - -#endif /* Value for the PrintView enum */ static const int print_view_map[] = { @@ -1068,6 +1940,7 @@ range_selector_new (GtkWidget *dialog, time_t at, int *view) struct tm tm; time_t week_begin, week_end; struct tm week_begin_tm, week_end_tm; + gint week_start_day; box = gtk_vbox_new (FALSE, GNOME_PAD_SMALL); @@ -1082,8 +1955,13 @@ range_selector_new (GtkWidget *dialog, time_t at, int *view) /* Week */ - week_begin = time_week_begin (at); - week_end = time_add_day (time_week_end (at), -1); + week_start_day = calendar_config_get_week_start_day (); + week_begin = time_week_begin (at, week_start_day); + /* If the week starts on a Sunday, we have to show the Saturday first, + since the weekend is compressed. */ + if (week_start_day == 0) + week_begin = time_add_day (week_begin, -1); + week_end = time_add_day (week_end, 6); week_begin_tm = *localtime (&week_begin); week_end_tm = *localtime (&week_end); @@ -1129,17 +2007,195 @@ range_selector_new (GtkWidget *dialog, time_t at, int *view) return box; } + +static void +print_day_view (GnomePrintContext *pc, GnomeCalendar *gcal, time_t date, + double left, double right, double top, double bottom) +{ + int i, days = 1; + double todo, header, l; + char buf[100]; + + for (i = 0; i < days; i++) { + todo = (right - left) * 0.75 + left; + header = top - HEADER_HEIGHT; + + /* FIXME: What is the name supposed to be for? */ + gnome_print_beginpage (pc, "Calendar Day View"); + + /* Print the main view with all the events in. */ + print_day_details (pc, gcal, date, + left, todo - 2.0, header, bottom); + + /* Print the TaskPad down the right. */ + print_todo_details (pc, gcal, 0, INT_MAX, + todo, right, header, bottom); + + /* Print the filled border around the header. */ + print_border (pc, left, right, top, header + 2.0, 1.0, 0.9); + + /* Print the 2 mini calendar-months. */ + l = right - SMALL_MONTH_PAD - SMALL_MONTH_WIDTH * 2 + - SMALL_MONTH_SPACING; + print_month_small (pc, gcal, date, + l, l + SMALL_MONTH_WIDTH, + top - 4, header + 4, + DATE_MONTH | DATE_YEAR, date, date, FALSE); + + l += SMALL_MONTH_SPACING + SMALL_MONTH_WIDTH; + print_month_small (pc, gcal, time_add_month (date, 1), + l, l + SMALL_MONTH_WIDTH, + top - 4, header + 4, + DATE_MONTH | DATE_YEAR, 0, 0, FALSE); + + /* Print the date, e.g. '8th May, 2001'. */ + format_date (date, DATE_DAY | DATE_MONTH | DATE_YEAR, + buf, 100); + print_text_size (pc, 24, buf, ALIGN_LEFT, + left + 4, todo, top - 4, top - 4 - 24); + + /* Print the day, e.g. 'Tuesday'. */ + format_date (date, DATE_DAYNAME, buf, 100); + print_text_size (pc, 18, buf, ALIGN_LEFT, + left + 4, todo, top - 32, top - 32 - 18); + + gnome_print_showpage (pc); + date = time_add_day (date, 1); + } +} + + +static void +print_week_view (GnomePrintContext *pc, GnomeCalendar *gcal, time_t date, + double left, double right, double top, double bottom) +{ + double header, l; + char buf[100]; + time_t when; + gint week_start_day; + + header = top - HEADER_HEIGHT; + + /* FIXME: What is the name supposed to be for? */ + gnome_print_beginpage (pc, "Calendar Week View"); + + week_start_day = calendar_config_get_week_start_day (); + when = time_week_begin (date, week_start_day); + /* If the week starts on a Sunday, we have to show the Saturday first, + since the weekend is compressed. */ + if (week_start_day == 0) + when = time_add_day (when, -1); + + /* Print the main week view. */ + print_week_summary (pc, gcal, when, FALSE, 1, 0, + WEEK_NORMAL_FONT_SIZE, + left, right, header, bottom); + + /* Print the border around the main view. */ + print_border (pc, left, right, header, bottom, 1.0, -1.0); + + /* Print the border around the header area. */ + print_border (pc, left, right, top, header + 2.0, 1.0, 0.9); + + /* Print the 2 mini calendar-months. */ + l = right - SMALL_MONTH_PAD - SMALL_MONTH_WIDTH * 2 + - SMALL_MONTH_SPACING; + print_month_small (pc, gcal, when, + l, l + SMALL_MONTH_WIDTH, + top - 4, header + 4, + DATE_MONTH | DATE_YEAR, when, + time_add_week (when, 1), FALSE); + + l += SMALL_MONTH_SPACING + SMALL_MONTH_WIDTH; + print_month_small (pc, gcal, time_add_month (when, 1), + l, l + SMALL_MONTH_WIDTH, + top - 4, header + 4, + DATE_MONTH | DATE_YEAR, when, + time_add_week (when, 1), FALSE); + + /* Print the start day of the week, e.g. '7th May 2001'. */ + format_date (when, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100); + print_text_size (pc, 24, buf, ALIGN_LEFT, + left + 3, right, top - 4, top - 4 - 24); + + /* Print the end day of the week, e.g. '13th May 2001'. */ + when = time_add_day (when, 6); + format_date (when, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100); + print_text_size (pc, 24, buf, ALIGN_LEFT, + left + 3, right, top - 24 - 3, top - 24 - 3 - 24); + + gnome_print_showpage (pc); +} + + +static void +print_month_view (GnomePrintContext *pc, GnomeCalendar *gcal, time_t date, + double left, double right, double top, double bottom) +{ + double header; + char buf[100]; + + header = top - HEADER_HEIGHT; + + /* FIXME: What is the name supposed to be for? */ + gnome_print_beginpage (pc, "Calendar Month View"); + + /* Print the main month view. */ + print_month_summary (pc, gcal, date, left, right, header, bottom); + + /* Print the border around the header. */ + print_border (pc, left, right, top, header, 1.0, 0.9); + + /* Print the 2 mini calendar-months. */ + print_month_small (pc, gcal, time_add_month (date, 1), + right - (right - left) / 7 + 2, right - 8, + top - 4, header, + DATE_MONTH | DATE_YEAR, 0, 0, FALSE); + print_month_small (pc, gcal, time_add_month (date, -1), + left + 8, left + (right - left) / 7 - 2, + top - 4, header, + DATE_MONTH | DATE_YEAR, 0, 0, FALSE); + + /* Print the month, e.g. 'May 2001'. */ + format_date (date, DATE_MONTH | DATE_YEAR, buf, 100); + print_text_size (pc, 24, buf, ALIGN_CENTER, + left + 3, right - 3, top - 3, top - 3 - 24); + + gnome_print_showpage (pc); +} + + +static void +print_year_view (GnomePrintContext *pc, GnomeCalendar *gcal, time_t date, + double left, double right, double top, double bottom) +{ + char buf[100]; + + /* FIXME: What is the name supposed to be for? */ + gnome_print_beginpage (pc, "Calendar Year View"); + + print_year_summary (pc, gcal, date, left, right, top - 50, bottom, + TRUE); + + /* centered title */ + format_date (date, DATE_YEAR, buf, 100); + print_text_size (pc, 24, buf, ALIGN_CENTER, + left+3, right, top-3, top - 27); + + gnome_print_showpage(pc); +} + + void -print_calendar (GnomeCalendar *gcal, gboolean preview, time_t at, PrintView default_view) +print_calendar (GnomeCalendar *gcal, gboolean preview, time_t date, + PrintView default_view) { GnomePrinter *printer; GnomePrintMaster *gpm; GnomePrintContext *pc; int copies, collate; const GnomePaper *paper_info; - double l, r, t, b, todo, header; - char buf[100]; - time_t when; + double l, r, t, b; g_return_if_fail (gcal != NULL); g_return_if_fail (GNOME_IS_CALENDAR (gcal)); @@ -1154,13 +2210,15 @@ print_calendar (GnomeCalendar *gcal, gboolean preview, time_t at, PrintView defa int view; gpd = gnome_print_dialog_new (_("Print Calendar"), - GNOME_PRINT_DIALOG_RANGE | GNOME_PRINT_DIALOG_COPIES); + GNOME_PRINT_DIALOG_RANGE + | GNOME_PRINT_DIALOG_COPIES); view = (int) default_view; - range = range_selector_new (gpd, at, &view); + range = range_selector_new (gpd, date, &view); gnome_print_dialog_construct_range_custom (GNOME_PRINT_DIALOG (gpd), range); - gnome_dialog_set_default (GNOME_DIALOG (gpd), GNOME_PRINT_PRINT); + gnome_dialog_set_default (GNOME_DIALOG (gpd), + GNOME_PRINT_PRINT); /* Run dialog */ @@ -1183,7 +2241,8 @@ print_calendar (GnomeCalendar *gcal, gboolean preview, time_t at, PrintView defa e_dialog_get_values (gpd); default_view = (PrintView) view; - gnome_print_dialog_get_copies (GNOME_PRINT_DIALOG (gpd), &copies, &collate); + gnome_print_dialog_get_copies (GNOME_PRINT_DIALOG (gpd), + &copies, &collate); printer = gnome_print_dialog_get_printer (GNOME_PRINT_DIALOG (gpd)); gnome_dialog_close (GNOME_DIALOG (gpd)); @@ -1204,111 +2263,29 @@ print_calendar (GnomeCalendar *gcal, gboolean preview, time_t at, PrintView defa pc = gnome_print_master_get_context (gpm); l = gnome_paper_lmargin (paper_info); - r = gnome_paper_pswidth (paper_info) - gnome_paper_rmargin (paper_info); - t = gnome_paper_psheight (paper_info) - gnome_paper_tmargin (paper_info); + r = gnome_paper_pswidth (paper_info) + - gnome_paper_rmargin (paper_info); + t = gnome_paper_psheight (paper_info) + - gnome_paper_tmargin (paper_info); b = gnome_paper_bmargin (paper_info); /* depending on the view, do a different output */ switch (default_view) { - case PRINT_VIEW_DAY: { - int i, days = 1; - - for (i = 0; i < days; i++) { - todo = ((r - l) / 5) * 4 + l; - header = t - 70; - print_todo_details (pc, gcal, 0, INT_MAX, todo, r, header, b); - print_day_details (pc, gcal, at, l, todo - 2.0, header, b); - - print_border (pc, l, r, t, header + 2.0, 1.0, 0.9); - - print_month_small (pc, gcal, at, r - 190, r - 104, t - 4, - header + 8, DATE_MONTH | DATE_YEAR, at, at, FALSE); - print_month_small (pc, gcal, time_add_month (at, 1), r - 90, r - 4, t - 4, - header + 8, DATE_MONTH | DATE_YEAR, 0, 0, FALSE); - - format_date (at, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100); - print_text_size (pc, 24, buf, ALIGN_LEFT, l + 3, todo, t - 3, header); - - format_date (at, DATE_DAYNAME, buf, 100); - print_text_size (pc, 18, buf, ALIGN_LEFT, l + 3, todo, t - 27 - 4, header); - gnome_print_showpage (pc); - at = time_add_day (at, 1); - } + case PRINT_VIEW_DAY: + print_day_view (pc, gcal, date, l, r, t, b); break; - } - case PRINT_VIEW_WEEK: - header = t - 70; - print_week_summary (pc, gcal, at, l, r, header, b); - - /* more solid total outline */ - print_border (pc, l, r, header, b, 1.0, -1.0); - - /* header border */ - print_border (pc, l, r, t, header + 2.0, 1.0, 0.9); - - when = time_week_begin (at); - when = time_add_day (when, 1); - - print_month_small (pc, gcal, at, r - 90, r - 4, t - 4, - header + 8, DATE_MONTH | DATE_YEAR, when, time_add_week (when, 1), - FALSE); - print_month_small (pc, gcal, time_add_month (at, -1), r - 190, r - 104, t - 4, - header + 8, DATE_MONTH | DATE_YEAR, when, time_add_week (when, 1), - FALSE); - - format_date (when, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100); - print_text_size (pc, 24, buf, ALIGN_LEFT, l + 3, r, t - 4, header); - - when = time_add_day (when, 6); - format_date (when, DATE_DAY | DATE_MONTH | DATE_YEAR, buf, 100); - print_text_size (pc, 24, buf, ALIGN_LEFT, l + 3, r, t - 24 - 3, header); - gnome_print_showpage (pc); + print_week_view (pc, gcal, date, l, r, t, b); break; - case PRINT_VIEW_MONTH: - header = t - 70; gnome_print_rotate (pc, 90); - gnome_print_translate (pc, 0, -gnome_paper_pswidth (paper_info)); - /*print_month_summary(pc, cal, at, l, r, header, b);*/ - print_month_summary (pc, gcal, at, b, t, r - 70, l); - - print_border (pc, b, t, r, r - 72.0, 1.0, 0.9); - - print_month_small (pc, gcal, time_add_month (at, 1), - t - (t - b) / 7 + 2, t - 8, r - 4, r - 68, - DATE_MONTH | DATE_YEAR, 0, 0, FALSE); - print_month_small (pc, gcal, time_add_month (at, -1), - b + 8, b + (t - b) / 7 - 2, r - 4, r - 68, - DATE_MONTH | DATE_YEAR, 0, 0, FALSE); - - /* centered title */ - format_date (at, DATE_MONTH | DATE_YEAR, buf, 100); - print_text_size (pc, 24, buf, ALIGN_CENTRE, b + 3, t, r - 3, l); - gnome_print_showpage (pc); + gnome_print_translate (pc, 0, + -gnome_paper_pswidth (paper_info)); + print_month_view (pc, gcal, date, b, t, r, l); break; - case PRINT_VIEW_YEAR: -#if 0 - /* landscape */ - gnome_print_rotate(pc, 90); - gnome_print_translate(pc, 0, -gnome_paper_pswidth(paper_info)); - print_year_summary(pc, gcal, at, b, t, r-50, l, FALSE); - - /* centered title */ - format_date(at, DATE_YEAR, buf, 100); - print_text_size(pc, 24, buf, ALIGN_CENTRE, b+3, t, r-3, l); -#else - /* portrait */ - print_year_summary(pc, gcal, at, l, r, t-50, b, TRUE); - - /* centered title */ - format_date(at, DATE_YEAR, buf, 100); - print_text_size(pc, 24, buf, ALIGN_CENTRE, l+3, r, t-3, b); -#endif - gnome_print_showpage(pc); + print_year_view (pc, gcal, date, l, r, t, b); break; - default: g_assert_not_reached (); } @@ -1318,10 +2295,12 @@ print_calendar (GnomeCalendar *gcal, gboolean preview, time_t at, PrintView defa if (preview) { GnomePrintMasterPreview *gpmp; - gpmp = gnome_print_master_preview_new (gpm, _("Print Preview")); + gpmp = gnome_print_master_preview_new (gpm, + _("Print Preview")); gtk_widget_show (GTK_WIDGET (gpmp)); - } else + } else { gnome_print_master_print (gpm); + } gtk_object_unref (GTK_OBJECT (gpm)); } |