2 * Lays out events for the Week & Month views of the calendar. It is also
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 * Damon Chaplin <damon@ximian.com>
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
23 #include "evolution-config.h"
25 #include "e-week-view-layout.h"
26 #include "calendar-config.h"
28 static void e_week_view_layout_event (EWeekViewEvent
*event
,
32 gboolean multi_week_view
,
34 gboolean compress_weekend
,
38 static gint
e_week_view_find_day (time_t time_to_find
,
39 gboolean include_midnight_in_prev_day
,
42 static gint
e_week_view_find_span_end (gboolean multi_week_view
,
43 gboolean compress_weekend
,
44 GDateWeekday display_start_day
,
48 e_week_view_layout_events (GArray
*events
,
50 gboolean multi_week_view
,
52 gboolean compress_weekend
,
57 EWeekViewEvent
*event
;
58 EWeekViewEventSpan
*span
;
59 gint num_days
, day
, event_num
, span_num
;
63 /* This is a temporary 2-d grid which is used to place events.
64 * Each element is 0 if the position is empty, or 1 if occupied.
65 * We allocate the maximum size possible here, assuming that each
66 * event will need its own row. */
67 grid
= g_new0 (guint8
, E_WEEK_VIEW_MAX_ROWS_PER_CELL
* 7
68 * E_WEEK_VIEW_MAX_WEEKS
);
70 /* We create a new array of spans, which will replace the old one. */
71 spans
= g_array_new (FALSE
, FALSE
, sizeof (EWeekViewEventSpan
));
73 /* Clear the number of rows used per day. */
74 num_days
= multi_week_view
? weeks_shown
* 7 : 7;
75 for (day
= 0; day
< num_days
; day
++) {
76 rows_per_day
[day
] = 0;
79 /* Iterate over the events, finding which weeks they cover, and putting
80 * them in the first free row available. */
81 for (event_num
= 0; event_num
< events
->len
; event_num
++) {
82 event
= &g_array_index (events
, EWeekViewEvent
, event_num
);
83 e_week_view_layout_event (
84 event
, grid
, spans
, old_spans
,
86 weeks_shown
, compress_weekend
,
87 start_weekday
, day_starts
,
94 /* Destroy the old spans array, destroying any unused canvas items. */
96 for (span_num
= 0; span_num
< old_spans
->len
; span_num
++) {
97 span
= &g_array_index (old_spans
, EWeekViewEventSpan
,
99 if (span
->background_item
)
100 g_object_run_dispose (G_OBJECT (span
->background_item
));
102 g_object_run_dispose (G_OBJECT (span
->text_item
));
104 g_array_free (old_spans
, TRUE
);
111 e_week_view_layout_event (EWeekViewEvent
*event
,
115 gboolean multi_week_view
,
117 gboolean compress_weekend
,
122 gint start_day
, end_day
, span_start_day
, span_end_day
, rows_per_cell
;
123 gint free_row
, row
, day
, span_num
, spans_index
, num_spans
, days_shown
;
124 EWeekViewEventSpan span
, *old_span
;
126 days_shown
= multi_week_view
? weeks_shown
* 7 : 7;
127 start_day
= e_week_view_find_day (
128 event
->start
, FALSE
, days_shown
,
130 end_day
= e_week_view_find_day (
131 event
->end
, event
->start
!= event
->end
, days_shown
,
133 start_day
= CLAMP (start_day
, 0, days_shown
- 1);
134 end_day
= CLAMP (end_day
, 0, days_shown
- 1);
138 "In e_week_view_layout_event Start:%i End: %i\n",
142 /* Iterate through each of the spans of the event, where each span
143 * is a sequence of 1 or more days displayed next to each other. */
144 span_start_day
= start_day
;
145 rows_per_cell
= E_WEEK_VIEW_MAX_ROWS_PER_CELL
;
147 spans_index
= spans
->len
;
149 while (span_start_day
<= end_day
) {
150 span_end_day
= e_week_view_find_span_end (
155 span_end_day
= MIN (span_end_day
, end_day
);
158 " Span start:%i end:%i\n", span_start_day
,
161 /* Try each row until we find a free one or we fall off the
162 * bottom of the available rows. */
165 while (free_row
== -1 && row
< rows_per_cell
) {
167 for (day
= span_start_day
; day
<= span_end_day
;
169 if (grid
[day
* rows_per_cell
+ row
]) {
177 if (free_row
!= -1) {
178 /* Mark the cells as full. */
179 for (day
= span_start_day
; day
<= span_end_day
;
181 grid
[day
* rows_per_cell
+ free_row
] = 1;
182 rows_per_day
[day
] = MAX (
188 " Span start:%i end:%i row:%i\n",
189 span_start_day
, span_end_day
, free_row
);
191 /* Add the span to the array, and try to reuse any
192 * canvas items from the old spans. */
193 span
.start_day
= span_start_day
;
194 span
.num_days
= span_end_day
- span_start_day
+ 1;
196 span
.background_item
= NULL
;
197 span
.text_item
= NULL
;
198 if (event
->num_spans
> span_num
) {
199 old_span
= &g_array_index (
200 old_spans
, EWeekViewEventSpan
,
201 event
->spans_index
+ span_num
);
202 span
.background_item
= old_span
->background_item
;
203 span
.text_item
= old_span
->text_item
;
204 old_span
->background_item
= NULL
;
205 old_span
->text_item
= NULL
;
208 g_array_append_val (spans
, span
);
212 span_start_day
= span_end_day
+ 1;
216 /* Set the event's spans. */
217 event
->spans_index
= spans_index
;
218 event
->num_spans
= num_spans
;
221 /* Finds the day containing the given time.
222 * If include_midnight_in_prev_day is TRUE then if the time exactly
223 * matches the start of a day the previous day is returned. This is useful
224 * when calculating the end day of an event. */
226 e_week_view_find_day (time_t time_to_find
,
227 gboolean include_midnight_in_prev_day
,
233 if (time_to_find
< day_starts
[0])
235 if (time_to_find
> day_starts
[days_shown
])
238 for (day
= 1; day
<= days_shown
; day
++) {
239 if (time_to_find
<= day_starts
[day
]) {
240 if (time_to_find
== day_starts
[day
]
241 && !include_midnight_in_prev_day
)
247 g_return_val_if_reached (days_shown
);
250 /* This returns the last possible day in the same span as the given day.
251 * A span is all the days which are displayed next to each other from left to
252 * right. In the week view all spans are only 1 day, since Tuesday is below
253 * Monday rather than beside it etc. In the month view, if the weekends are not
254 * compressed then each week is a span, otherwise we have to break a span up
255 * on Saturday, use a separate span for Sunday, and start again on Monday. */
257 e_week_view_find_span_end (gboolean multi_week_view
,
258 gboolean compress_weekend
,
259 GDateWeekday display_start_day
,
262 gint week
, col
, sat_col
, end_col
;
264 if (multi_week_view
) {
268 /* We default to the last column in the row. */
271 /* If the weekend is compressed we must end any spans on
272 * Saturday and Sunday. */
273 if (compress_weekend
) {
274 sat_col
= e_weekday_get_days_between (
275 display_start_day
, G_DATE_SATURDAY
);
278 else if (col
== sat_col
+ 1)
279 end_col
= sat_col
+ 1;
282 return week
* 7 + end_col
;
289 e_week_view_layout_get_day_position (gint day
,
290 gboolean multi_week_view
,
292 GDateWeekday display_start_day
,
293 gboolean compress_weekend
,
298 GDateWeekday day_of_week
;
299 gint week
, col
, weekend_col
;
301 *day_x
= *day_y
= *rows
= 0;
302 g_return_if_fail (day
>= 0);
304 if (multi_week_view
) {
305 g_return_if_fail (day
< weeks_shown
* 7);
309 day_of_week
= e_weekday_add_days (display_start_day
, day
);
310 if (compress_weekend
&& day_of_week
>= G_DATE_SATURDAY
) {
311 /* In the compressed view Saturday is above Sunday and
312 * both have just one row as opposed to 2 for all the
314 if (day_of_week
== G_DATE_SATURDAY
) {
318 *day_y
= week
* 2 + 1;
322 /* Both Saturday and Sunday are in the same column. */
325 /* If the weekend is compressed and the day is after
326 * the weekend we have to move back a column. */
327 if (compress_weekend
) {
328 /* Calculate where the weekend column is. */
329 weekend_col
= e_weekday_get_days_between (
330 display_start_day
, G_DATE_SATURDAY
);
331 if (col
> weekend_col
)
341 gint arr
[4] = {1, 1, 1, 1};
342 gint edge
, i
, wd
, m
, M
;
344 gboolean days_left_to_right
;
345 gint n_work_days_mon_wed
= 0;
346 gint n_work_days_thu_sun
= 0;
348 /* 0 = Monday, 6 = Sunday */
349 gint work_days
[7] = { 0, 0, 0, 0, 0, 0, 0 };
351 g_return_if_fail (day
< 7);
353 settings
= e_util_ref_settings ("org.gnome.evolution.calendar");
355 days_left_to_right
= g_settings_get_boolean (settings
, "week-view-days-left-to-right");
357 if (g_settings_get_boolean (settings
, "work-day-monday"))
358 work_days
[0] = 1, n_work_days_mon_wed
++;
359 if (g_settings_get_boolean (settings
, "work-day-tuesday"))
360 work_days
[1] = 1, n_work_days_mon_wed
++;
361 if (g_settings_get_boolean (settings
, "work-day-wednesday"))
362 work_days
[2] = 1, n_work_days_mon_wed
++;
363 if (g_settings_get_boolean (settings
, "work-day-thursday"))
364 work_days
[3] = 1, n_work_days_thu_sun
++;
365 if (g_settings_get_boolean (settings
, "work-day-friday"))
366 work_days
[4] = 1, n_work_days_thu_sun
++;
367 if (g_settings_get_boolean (settings
, "work-day-saturday"))
368 work_days
[5] = 1, n_work_days_thu_sun
++;
369 if (g_settings_get_boolean (settings
, "work-day-sunday"))
370 work_days
[6] = 1, n_work_days_thu_sun
++;
372 g_object_unref (settings
);
374 if (n_work_days_mon_wed
< n_work_days_thu_sun
)
375 edge
= 4; /* Friday */
377 edge
= 3; /* Thursday */
379 if (days_left_to_right
) {
380 /* The default view is always with two columns:
391 const gint transform
[] = { 0, 3, 1, 4, 2, 5, 6 };
392 day
= transform
[day
];
394 const gint transform
[] = { 0, 4, 1, 5, 2, 3, 6 };
395 day
= transform
[day
];
409 wd
= 0; /* number of used rows in column */
410 for (i
= m
; i
< M
; i
++) {
411 arr
[i
- m
] += work_days
[i
];
415 while (wd
!= 6 && any
) {
418 for (i
= M
- 1; i
>= m
; i
--) {
419 if (arr
[i
- m
] > 1) {
422 /* too many rows, make last shorter */
427 /* free rows left, enlarge those bigger */
438 if (!any
&& wd
!= 6) {
441 for (i
= m
; i
< M
; i
++) {
448 *rows
= arr
[day
- m
];
451 for (i
= m
; i
< day
; i
++) {
452 *day_y
+= arr
[i
- m
];
459 /* Returns TRUE if the event span is visible or FALSE if it isn't.
460 * It also returns the number of days of the span that are visible.
461 * Usually this can easily be determined by the start & end days and row of
462 * the span, which are set in e_week_view_layout_event (). Though we need a
463 * special case for the weekends when they are compressed, since the span may
466 e_week_view_layout_get_span_position (EWeekViewEvent
*event
,
467 EWeekViewEventSpan
*span
,
469 gint rows_per_compressed_cell
,
470 GDateWeekday display_start_day
,
471 gboolean multi_week_view
,
472 gboolean compress_weekend
,
475 GDateWeekday end_day_of_week
;
478 if (multi_week_view
&& span
->row
>= rows_per_cell
)
481 n_days
= span
->start_day
+ span
->num_days
- 1;
482 end_day_of_week
= e_weekday_add_days (display_start_day
, n_days
);
484 *span_num_days
= span
->num_days
;
485 /* Check if the row will not be visible in compressed cells. */
486 if (span
->row
>= rows_per_compressed_cell
) {
487 if (multi_week_view
) {
488 if (compress_weekend
) {
489 /* If it ends on a Saturday and is 1 day glong
490 * we skip it, else we shorten it. If it ends
491 * on a Sunday it must be 1 day long and we
493 if (end_day_of_week
== G_DATE_SATURDAY
) {
494 if (*span_num_days
== 1) {
499 } else if (end_day_of_week
== G_DATE_SUNDAY
) {
504 gint day_x
, day_y
, rows
= 0;
505 e_week_view_layout_get_day_position (
506 end_day_of_week
- 1, multi_week_view
, 1,
507 display_start_day
, compress_weekend
,
508 &day_x
, &day_y
, &rows
);
510 if (((rows
/ 2) * rows_per_cell
) + ((rows
% 2) *
511 rows_per_compressed_cell
) <= span
->row
)