evolution-3.6.4/calendar/gui/e-day-view-layout.c

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];
Assigned value is garbage or undefined
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Assigned value is garbage or undefined
(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 }