Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
e-day-view-layout.c:255:14 | clang-analyzer | Assigned value is garbage or undefined | ||
e-day-view-layout.c:255:14 | clang-analyzer | Assigned value is garbage or undefined |
1 /*
2 *
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with the program; if not, see <http://www.gnu.org/licenses/>
15 *
16 *
17 * Authors:
18 * Damon Chaplin <damon@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 /*
25 * Lays out events for the Day & Work-Week views of the calendar. It is also
26 * used for printing.
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33 #include "e-day-view-layout.h"
34 #include "e-util/e-bit-array.h"
35
36 static void e_day_view_layout_long_event (EDayViewEvent *event,
37 guint8 *grid,
38 gint days_shown,
39 time_t *day_starts,
40 gint *rows_in_top_display);
41
42 static void e_day_view_layout_day_event (EDayViewEvent *event,
43 EBitArray **grid,
44 guint16 *group_starts,
45 guint8 *cols_per_row,
46 gint rows,
47 gint mins_per_row,
48 gint max_cols);
49 static void e_day_view_expand_day_event (EDayViewEvent *event,
50 EBitArray **grid,
51 guint8 *cols_per_row,
52 gint mins_per_row);
53 static void e_day_view_recalc_cols_per_row (gint rows,
54 guint8 *cols_per_row,
55 guint16 *group_starts);
56
57 void
58 e_day_view_layout_long_events (GArray *events,
59 gint days_shown,
60 time_t *day_starts,
61 gint *rows_in_top_display)
62 {
63 EDayViewEvent *event;
64 gint event_num;
65 guint8 *grid;
66
67 /* This is a temporary 2-d grid which is used to place events.
68 * Each element is 0 if the position is empty, or 1 if occupied.
69 * We allocate the maximum size possible here, assuming that each
70 * event will need its own row. */
71 grid = g_new0 (guint8, events->len * E_DAY_VIEW_MAX_DAYS);
72
73 /* Reset the number of rows in the top display to 0. It will be
74 * updated as events are layed out below. */
75 *rows_in_top_display = 0;
76
77 /* Iterate over the events, finding which days they cover, and putting
78 * them in the first free row available. */
79 for (event_num = 0; event_num < events->len; event_num++) {
80 event = &g_array_index (events, EDayViewEvent, event_num);
81 e_day_view_layout_long_event (
82 event, grid,
83 days_shown, day_starts,
84 rows_in_top_display);
85 }
86
87 /* Free the grid. */
88 g_free (grid);
89 }
90
91 static void
92 e_day_view_layout_long_event (EDayViewEvent *event,
93 guint8 *grid,
94 gint days_shown,
95 time_t *day_starts,
96 gint *rows_in_top_display)
97 {
98 gint start_day, end_day, free_row, day, row;
99
100 event->num_columns = 0;
101
102 if (!e_day_view_find_long_event_days (event,
103 days_shown, day_starts,
104 &start_day, &end_day))
105 return;
106
107 /* Try each row until we find a free one. */
108 row = 0;
109 do {
110 free_row = row;
111 for (day = start_day; day <= end_day; day++) {
112 if (grid[row * E_DAY_VIEW_MAX_DAYS + day]) {
113 free_row = -1;
114 break;
115 }
116 }
117 row++;
118 } while (free_row == -1);
119
120 event->start_row_or_col = free_row;
121 event->num_columns = 1;
122
123 /* Mark the cells as full. */
124 for (day = start_day; day <= end_day; day++) {
125 grid[free_row * E_DAY_VIEW_MAX_DAYS + day] = 1;
126 }
127
128 /* Update the number of rows in the top canvas if necessary. */
129 *rows_in_top_display = MAX (*rows_in_top_display, free_row + 1);
130 }
131
132 /* returns maximum number of columns among all rows */
133 gint
134 e_day_view_layout_day_events (GArray *events,
135 gint rows,
136 gint mins_per_row,
137 guint8 *cols_per_row,
138 gint max_cols)
139 {
140 EDayViewEvent *event;
141 gint row, event_num, res;
142 EBitArray **grid;
143
144 /* This is a temporary array which keeps track of rows which are
145 * connected. When an appointment spans multiple rows then the number
146 * of columns in each of these rows must be the same (i.e. the maximum
147 * of all of them). Each element in the array corresponds to one row
148 * and contains the index of the first row in the group of connected
149 * rows. */
150 guint16 group_starts[12 * 24];
151
152 /* This is a temporary 2-d grid which is used to place events.
153 * Each element is 0 if the position is empty, or 1 if occupied. */
154 grid = g_new0 (EBitArray *, rows);
155
156 /* Reset the cols_per_row array, and initialize the connected rows so
157 * that all rows are not connected - each row is the start of a new
158 * group. */
159 for (row = 0; row < rows; row++) {
160 cols_per_row[row] = 0;
161 group_starts[row] = row;
162
163 /* row doesn't contain any event at the moment */
164 grid[row] = e_bit_array_new (0);
165 }
166
167 /* Iterate over the events, finding which rows they cover, and putting
168 * them in the first free column available. Increment the number of
169 * events in each of the rows it covers, and make sure they are all
170 * in one group. */
171 for (event_num = 0; event_num < events->len; event_num++) {
172 event = &g_array_index (events, EDayViewEvent, event_num);
173
174 e_day_view_layout_day_event (
175 event, grid, group_starts,
176 cols_per_row, rows, mins_per_row, max_cols);
177 }
178
179 /* Recalculate the number of columns needed in each row. */
180 e_day_view_recalc_cols_per_row (rows, cols_per_row, group_starts);
181
182 /* Iterate over the events again, trying to expand events horizontally
183 * if there is enough space. */
184 for (event_num = 0; event_num < events->len; event_num++) {
185 event = &g_array_index (events, EDayViewEvent, event_num);
186 e_day_view_expand_day_event (
187 event, grid, cols_per_row,
188 mins_per_row);
189 }
190
191 /* Free the grid and compute maximum number of columns used. */
192 res = 0;
193 for (row = 0; row < rows; row++) {
194 res = MAX (res, e_bit_array_bit_count (grid[row]));
195 g_object_unref (grid[row]);
196 }
197 g_free (grid);
198
199 return res;
200 }
201
202 /* Finds the first free position to place the event in.
203 * Increments the number of events in each of the rows it covers, and makes
204 * sure they are all in one group. */
205 static void
206 e_day_view_layout_day_event (EDayViewEvent *event,
207 EBitArray **grid,
208 guint16 *group_starts,
209 guint8 *cols_per_row,
210 gint rows,
211 gint mins_per_row,
212 gint max_cols)
213 {
214 gint start_row, end_row, free_col, col, row, group_start;
215
216 start_row = event->start_minute / mins_per_row;
217 end_row = (event->end_minute - 1) / mins_per_row;
218 if (end_row < start_row)
219 end_row = start_row;
220
221 event->num_columns = 0;
222
223 /* If the event can't currently be seen, just return. */
224 if (start_row >= rows || end_row < 0)
225 return;
226
227 /* Make sure we don't go outside the visible times. */
228 start_row = CLAMP (start_row, 0, rows - 1);
229 end_row = CLAMP (end_row, 0, rows - 1);
230
231 /* Try each column until we find a free one. */
232 for (col = 0; max_cols <= 0 || col < max_cols; col++) {
233 free_col = col;
234 for (row = start_row; row <= end_row; row++) {
235 if (e_bit_array_bit_count (grid[row]) > col &&
236 e_bit_array_value_at (grid[row], col)) {
237 free_col = -1;
238 break;
239 }
240 }
241
242 if (free_col != -1)
243 break;
244 }
245
246 /* If we can't find space for the event, just return. */
247 if (free_col == -1)
248 return;
249
250 /* The event is assigned 1 col initially, but may be expanded later. */
251 event->start_row_or_col = free_col;
252 event->num_columns = 1;
253
254 /* Determine the start index of the group. */
255 group_start = group_starts[start_row];
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
256
257 /* Increment number of events in each of the rows the event covers.
258 * We use the cols_per_row array for this. It will be sorted out after
259 * all the events have been layed out. Also make sure all the rows that
260 * the event covers are in one group. */
261 for (row = start_row; row <= end_row; row++) {
262 /* resize the array if necessary */
263 if (e_bit_array_bit_count (grid[row]) <= free_col)
264 e_bit_array_insert (
265 grid[row], e_bit_array_bit_count (grid[row]),
266 free_col - e_bit_array_bit_count (grid[row]) + 1);
267
268 e_bit_array_change_one_row (grid[row], free_col, TRUE);
269 cols_per_row[row]++;
270 group_starts[row] = group_start;
271 }
272
273 /* If any following rows should be in the same group, add them. */
274 for (row = end_row + 1; row < rows; row++) {
275 if (group_starts[row] > end_row)
276 break;
277 group_starts[row] = group_start;
278 }
279 }
280
281 /* For each group of rows, find the max number of events in all the
282 * rows, and set the number of cols in each of the rows to that. */
283 static void
284 e_day_view_recalc_cols_per_row (gint rows,
285 guint8 *cols_per_row,
286 guint16 *group_starts)
287 {
288 gint start_row = 0, row, next_start_row, max_events;
289
290 while (start_row < rows) {
291 max_events = 0;
292 for (row = start_row; row < rows && group_starts[row] == start_row; row++)
293 max_events = MAX (max_events, cols_per_row[row]);
294
295 next_start_row = row;
296
297 for (row = start_row; row < next_start_row; row++)
298 cols_per_row[row] = max_events;
299
300 start_row = next_start_row;
301 }
302 }
303
304 /* Expands the event horizontally to fill any free space. */
305 static void
306 e_day_view_expand_day_event (EDayViewEvent *event,
307 EBitArray **grid,
308 guint8 *cols_per_row,
309 gint mins_per_row)
310 {
311 gint start_row, end_row, col, row;
312 gboolean clashed;
313
314 start_row = event->start_minute / mins_per_row;
315 end_row = (event->end_minute - 1) / mins_per_row;
316 if (end_row < start_row)
317 end_row = start_row;
318
319 /* Try each column until we find a free one. */
320 clashed = FALSE;
321 for (col = event->start_row_or_col + 1; col < cols_per_row[start_row]; col++) {
322 for (row = start_row; row <= end_row; row++) {
323 if (e_bit_array_bit_count (grid[row]) > col &&
324 e_bit_array_value_at (grid[row], col)) {
325 clashed = TRUE;
326 break;
327 }
328 }
329
330 if (clashed)
331 break;
332
333 event->num_columns++;
334 }
335 }
336
337 /* Find the start and end days for the event. */
338 gboolean
339 e_day_view_find_long_event_days (EDayViewEvent *event,
340 gint days_shown,
341 time_t *day_starts,
342 gint *start_day_return,
343 gint *end_day_return)
344 {
345 gint day, start_day, end_day;
346
347 start_day = -1;
348 end_day = -1;
349
350 for (day = 0; day < days_shown; day++) {
351 if (start_day == -1
352 && event->start < day_starts[day + 1])
353 start_day = day;
354 if (event->end > day_starts[day])
355 end_day = day;
356 }
357
358 /* Sanity check. */
359 if (start_day < 0 || start_day >= days_shown
360 || end_day < 0 || end_day >= days_shown
361 || end_day < start_day) {
362 g_warning ("Invalid date range for event");
363 return FALSE;
364 }
365
366 *start_day_return = start_day;
367 *end_day_return = end_day;
368
369 return TRUE;
370 }