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 /calendar/gui/e-day-view-layout.c | |
parent | aaa9b9bb2ab4ff9fba2a6b9cc68fbc7fdfaa71bc (diff) | |
download | gsoc2013-evolution-ff523035dd2592f6ff25955e7f1e379f24640347.tar.gz 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
Diffstat (limited to 'calendar/gui/e-day-view-layout.c')
-rw-r--r-- | calendar/gui/e-day-view-layout.c | 349 |
1 files changed, 349 insertions, 0 deletions
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; +} + + |