evolution-3.6.4/widgets/table/e-table-sorter.c

No issues found

  1 /*
  2  * This program is free software; you can redistribute it and/or
  3  * modify it under the terms of the GNU Lesser General Public
  4  * License as published by the Free Software Foundation; either
  5  * version 2 of the License, or (at your option) version 3.
  6  *
  7  * This program is distributed in the hope that it will be useful,
  8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 10  * Lesser General Public License for more details.
 11  *
 12  * You should have received a copy of the GNU Lesser General Public
 13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 14  *
 15  *
 16  * Authors:
 17  *		Chris Lahey <clahey@ximian.com>
 18  *
 19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 20  *
 21  */
 22 
 23 #ifdef HAVE_CONFIG_H
 24 #include <config.h>
 25 #endif
 26 
 27 #include <stdlib.h>
 28 #include <string.h>
 29 
 30 #include <glib/gi18n.h>
 31 #include "e-util/e-util.h"
 32 
 33 #include "e-table-sorter.h"
 34 #include "e-table-sorting-utils.h"
 35 
 36 #define d(x)
 37 
 38 enum {
 39 	PROP_0,
 40 	PROP_SORT_INFO
 41 };
 42 
 43 /* workaround for avoiding API breakage */
 44 #define ets_get_type e_table_sorter_get_type
 45 G_DEFINE_TYPE (ETableSorter, ets, E_SORTER_TYPE)
 46 
 47 #define INCREMENT_AMOUNT 100
 48 
 49 static void	ets_model_changed      (ETableModel *etm, ETableSorter *ets);
 50 static void	ets_model_row_changed  (ETableModel *etm, gint row, ETableSorter *ets);
 51 static void	ets_model_cell_changed (ETableModel *etm, gint col, gint row, ETableSorter *ets);
 52 static void	ets_model_rows_inserted (ETableModel *etm, gint row, gint count, ETableSorter *ets);
 53 static void	ets_model_rows_deleted (ETableModel *etm, gint row, gint count, ETableSorter *ets);
 54 static void	ets_sort_info_changed  (ETableSortInfo *info, ETableSorter *ets);
 55 static void	ets_clean              (ETableSorter *ets);
 56 static void	ets_sort               (ETableSorter *ets);
 57 static void	ets_backsort           (ETableSorter *ets);
 58 
 59 static gint	ets_model_to_sorted           (ESorter *sorter, gint row);
 60 static gint	ets_sorted_to_model           (ESorter *sorter, gint row);
 61 static void	ets_get_model_to_sorted_array (ESorter *sorter, gint **array, gint *count);
 62 static void	ets_get_sorted_to_model_array (ESorter *sorter, gint **array, gint *count);
 63 static gboolean ets_needs_sorting             (ESorter *ets);
 64 
 65 static void
 66 ets_dispose (GObject *object)
 67 {
 68 	ETableSorter *ets = E_TABLE_SORTER (object);
 69 
 70 	if (ets->sort_info) {
 71 		if (ets->table_model_changed_id)
 72 			g_signal_handler_disconnect (
 73 				ets->source,
 74 				ets->table_model_changed_id);
 75 		if (ets->table_model_row_changed_id)
 76 			g_signal_handler_disconnect (
 77 				ets->source,
 78 				ets->table_model_row_changed_id);
 79 		if (ets->table_model_cell_changed_id)
 80 			g_signal_handler_disconnect (
 81 				ets->source,
 82 				ets->table_model_cell_changed_id);
 83 		if (ets->table_model_rows_inserted_id)
 84 			g_signal_handler_disconnect (
 85 				ets->source,
 86 				ets->table_model_rows_inserted_id);
 87 		if (ets->table_model_rows_deleted_id)
 88 			g_signal_handler_disconnect (
 89 				ets->source,
 90 				ets->table_model_rows_deleted_id);
 91 		if (ets->sort_info_changed_id)
 92 			g_signal_handler_disconnect (
 93 				ets->sort_info,
 94 				ets->sort_info_changed_id);
 95 		if (ets->group_info_changed_id)
 96 			g_signal_handler_disconnect (
 97 				ets->sort_info,
 98 				ets->group_info_changed_id);
 99 
100 		ets->table_model_changed_id = 0;
101 		ets->table_model_row_changed_id = 0;
102 		ets->table_model_cell_changed_id = 0;
103 		ets->table_model_rows_inserted_id = 0;
104 		ets->table_model_rows_deleted_id = 0;
105 		ets->sort_info_changed_id = 0;
106 		ets->group_info_changed_id = 0;
107 
108 		g_object_unref (ets->sort_info);
109 		ets->sort_info = NULL;
110 	}
111 
112 	if (ets->full_header)
113 		g_object_unref (ets->full_header);
114 	ets->full_header = NULL;
115 
116 	if (ets->source)
117 		g_object_unref (ets->source);
118 	ets->source = NULL;
119 
120 	G_OBJECT_CLASS (ets_parent_class)->dispose (object);
121 }
122 
123 static void
124 ets_set_property (GObject *object,
125                   guint property_id,
126                   const GValue *value,
127                   GParamSpec *pspec)
128 {
129 	ETableSorter *ets = E_TABLE_SORTER (object);
130 
131 	switch (property_id) {
132 	case PROP_SORT_INFO:
133 		if (ets->sort_info) {
134 			if (ets->sort_info_changed_id)
135 				g_signal_handler_disconnect (ets->sort_info, ets->sort_info_changed_id);
136 			if (ets->group_info_changed_id)
137 				g_signal_handler_disconnect (ets->sort_info, ets->group_info_changed_id);
138 			g_object_unref (ets->sort_info);
139 		}
140 
141 		ets->sort_info = E_TABLE_SORT_INFO (g_value_get_object (value));
142 		g_object_ref (ets->sort_info);
143 		ets->sort_info_changed_id = g_signal_connect (
144 			ets->sort_info, "sort_info_changed",
145 			G_CALLBACK (ets_sort_info_changed), ets);
146 		ets->group_info_changed_id = g_signal_connect (
147 			ets->sort_info, "group_info_changed",
148 			G_CALLBACK (ets_sort_info_changed), ets);
149 
150 		ets_clean (ets);
151 		break;
152 	default:
153 		break;
154 	}
155 }
156 
157 static void
158 ets_get_property (GObject *object,
159                   guint property_id,
160                   GValue *value,
161                   GParamSpec *pspec)
162 {
163 	ETableSorter *ets = E_TABLE_SORTER (object);
164 	switch (property_id) {
165 	case PROP_SORT_INFO:
166 		g_value_set_object (value, ets->sort_info);
167 		break;
168 	}
169 }
170 
171 static void
172 ets_class_init (ETableSorterClass *class)
173 {
174 	GObjectClass *object_class = G_OBJECT_CLASS (class);
175 	ESorterClass *sorter_class = E_SORTER_CLASS (class);
176 
177 	object_class->dispose                   = ets_dispose;
178 	object_class->set_property              = ets_set_property;
179 	object_class->get_property              = ets_get_property;
180 
181 	sorter_class->model_to_sorted           = ets_model_to_sorted;
182 	sorter_class->sorted_to_model           = ets_sorted_to_model;
183 	sorter_class->get_model_to_sorted_array = ets_get_model_to_sorted_array;
184 	sorter_class->get_sorted_to_model_array = ets_get_sorted_to_model_array;
185 	sorter_class->needs_sorting             = ets_needs_sorting;
186 
187 	g_object_class_install_property (
188 		object_class,
189 		PROP_SORT_INFO,
190 		g_param_spec_object (
191 			"sort_info",
192 			"Sort Info",
193 			NULL,
194 			E_TYPE_TABLE_SORT_INFO,
195 			G_PARAM_READWRITE));
196 }
197 
198 static void
199 ets_init (ETableSorter *ets)
200 {
201 	ets->full_header = NULL;
202 	ets->sort_info = NULL;
203 	ets->source = NULL;
204 
205 	ets->needs_sorting = -1;
206 
207 	ets->table_model_changed_id = 0;
208 	ets->table_model_row_changed_id = 0;
209 	ets->table_model_cell_changed_id = 0;
210 	ets->table_model_rows_inserted_id = 0;
211 	ets->table_model_rows_deleted_id = 0;
212 	ets->sort_info_changed_id = 0;
213 	ets->group_info_changed_id = 0;
214 }
215 
216 ETableSorter *
217 e_table_sorter_new (ETableModel *source,
218                     ETableHeader *full_header,
219                     ETableSortInfo *sort_info)
220 {
221 	ETableSorter *ets = g_object_new (E_TYPE_TABLE_SORTER, NULL);
222 
223 	ets->sort_info = sort_info;
224 	g_object_ref (ets->sort_info);
225 	ets->full_header = full_header;
226 	g_object_ref (ets->full_header);
227 	ets->source = source;
228 	g_object_ref (ets->source);
229 
230 	ets->table_model_changed_id = g_signal_connect (
231 		source, "model_changed",
232 		G_CALLBACK (ets_model_changed), ets);
233 
234 	ets->table_model_row_changed_id = g_signal_connect (
235 		source, "model_row_changed",
236 		G_CALLBACK (ets_model_row_changed), ets);
237 
238 	ets->table_model_cell_changed_id = g_signal_connect (
239 		source, "model_cell_changed",
240 		G_CALLBACK (ets_model_cell_changed), ets);
241 
242 	ets->table_model_rows_inserted_id = g_signal_connect (
243 		source, "model_rows_inserted",
244 		G_CALLBACK (ets_model_rows_inserted), ets);
245 
246 	ets->table_model_rows_deleted_id = g_signal_connect (
247 		source, "model_rows_deleted",
248 		G_CALLBACK (ets_model_rows_deleted), ets);
249 
250 	ets->sort_info_changed_id = g_signal_connect (
251 		sort_info, "sort_info_changed",
252 		G_CALLBACK (ets_sort_info_changed), ets);
253 
254 	ets->group_info_changed_id = g_signal_connect (
255 		sort_info, "group_info_changed",
256 		G_CALLBACK (ets_sort_info_changed), ets);
257 
258 	return ets;
259 }
260 
261 static void
262 ets_model_changed (ETableModel *etm,
263                    ETableSorter *ets)
264 {
265 	ets_clean (ets);
266 }
267 
268 static void
269 ets_model_row_changed (ETableModel *etm,
270                        gint row,
271                        ETableSorter *ets)
272 {
273 	ets_clean (ets);
274 }
275 
276 static void
277 ets_model_cell_changed (ETableModel *etm,
278                         gint col,
279                         gint row,
280                         ETableSorter *ets)
281 {
282 	ets_clean (ets);
283 }
284 
285 static void
286 ets_model_rows_inserted (ETableModel *etm,
287                          gint row,
288                          gint count,
289                          ETableSorter *ets)
290 {
291 	ets_clean (ets);
292 }
293 
294 static void
295 ets_model_rows_deleted (ETableModel *etm,
296                         gint row,
297                         gint count,
298                         ETableSorter *ets)
299 {
300 	ets_clean (ets);
301 }
302 
303 static void
304 ets_sort_info_changed (ETableSortInfo *info,
305                        ETableSorter *ets)
306 {
307 	d (g_print ("sort info changed\n"));
308 	ets_clean (ets);
309 }
310 
311 struct qsort_data {
312 	ETableSorter *ets;
313 	gpointer *vals;
314 	gint cols;
315 	gint *ascending;
316 	GCompareDataFunc *compare;
317 	gpointer cmp_cache;
318 };
319 
320 /* FIXME: Make it not cache the second and later columns (as if anyone cares.) */
321 
322 static gint
323 qsort_callback (gconstpointer data1,
324                 gconstpointer data2,
325                 gpointer user_data)
326 {
327 	struct qsort_data *qd = (struct qsort_data *) user_data;
328 	gint row1 = *(gint *) data1;
329 	gint row2 = *(gint *) data2;
330 	gint j;
331 	gint sort_count = e_table_sort_info_sorting_get_count (qd->ets->sort_info) + e_table_sort_info_grouping_get_count (qd->ets->sort_info);
332 	gint comp_val = 0;
333 	gint ascending = 1;
334 	for (j = 0; j < sort_count; j++) {
335 		comp_val = (*(qd->compare[j]))(qd->vals[qd->cols * row1 + j], qd->vals[qd->cols * row2 + j], qd->cmp_cache);
336 		ascending = qd->ascending[j];
337 		if (comp_val != 0)
338 			break;
339 	}
340 	if (comp_val == 0) {
341 		if (row1 < row2)
342 			comp_val = -1;
343 		if (row1 > row2)
344 			comp_val = 1;
345 	}
346 	if (!ascending)
347 		comp_val = -comp_val;
348 	return comp_val;
349 }
350 
351 static void
352 ets_clean (ETableSorter *ets)
353 {
354 	g_free (ets->sorted);
355 	ets->sorted = NULL;
356 
357 	g_free (ets->backsorted);
358 	ets->backsorted = NULL;
359 
360 	ets->needs_sorting = -1;
361 }
362 
363 static void
364 ets_sort (ETableSorter *ets)
365 {
366 	gint rows;
367 	gint i;
368 	gint j;
369 	gint cols;
370 	gint group_cols;
371 	struct qsort_data qd;
372 
373 	if (ets->sorted)
374 		return;
375 
376 	rows = e_table_model_row_count (ets->source);
377 	group_cols = e_table_sort_info_grouping_get_count (ets->sort_info);
378 	cols = e_table_sort_info_sorting_get_count (ets->sort_info) + group_cols;
379 
380 	ets->sorted = g_new (int, rows);
381 	for (i = 0; i < rows; i++)
382 		ets->sorted[i] = i;
383 
384 	qd.cols = cols;
385 	qd.ets = ets;
386 
387 	qd.vals = g_new (gpointer , rows * cols);
388 	qd.ascending = g_new (int, cols);
389 	qd.compare = g_new (GCompareDataFunc, cols);
390 	qd.cmp_cache = e_table_sorting_utils_create_cmp_cache ();
391 
392 	for (j = 0; j < cols; j++) {
393 		ETableSortColumn column;
394 		ETableCol *col;
395 
396 		if (j < group_cols)
397 			column = e_table_sort_info_grouping_get_nth (ets->sort_info, j);
398 		else
399 			column = e_table_sort_info_sorting_get_nth (ets->sort_info, j - group_cols);
400 
401 		col = e_table_header_get_column_by_col_idx (ets->full_header, column.column);
402 		if (col == NULL)
403 			col = e_table_header_get_column (ets->full_header, e_table_header_count (ets->full_header) - 1);
404 
405 		for (i = 0; i < rows; i++) {
406 			qd.vals[i * cols + j] = e_table_model_value_at (ets->source, col->col_idx, i);
407 		}
408 
409 		qd.compare[j] = col->compare;
410 		qd.ascending[j] = column.ascending;
411 	}
412 
413 	g_qsort_with_data (ets->sorted, rows, sizeof (gint), qsort_callback, &qd);
414 
415 	g_free (qd.vals);
416 	g_free (qd.ascending);
417 	g_free (qd.compare);
418 	e_table_sorting_utils_free_cmp_cache (qd.cmp_cache);
419 }
420 
421 static void
422 ets_backsort (ETableSorter *ets)
423 {
424 	gint i, rows;
425 
426 	if (ets->backsorted)
427 		return;
428 
429 	ets_sort (ets);
430 
431 	rows = e_table_model_row_count (ets->source);
432 	ets->backsorted = g_new0 (int, rows);
433 
434 	for (i = 0; i < rows; i++) {
435 		ets->backsorted[ets->sorted[i]] = i;
436 	}
437 }
438 
439 static gint
440 ets_model_to_sorted (ESorter *es,
441                      gint row)
442 {
443 	ETableSorter *ets = E_TABLE_SORTER (es);
444 	gint rows = e_table_model_row_count (ets->source);
445 
446 	g_return_val_if_fail (row >= 0, -1);
447 	g_return_val_if_fail (row < rows, -1);
448 
449 	if (ets_needs_sorting (es))
450 		ets_backsort (ets);
451 
452 	if (ets->backsorted)
453 		return ets->backsorted[row];
454 	else
455 		return row;
456 }
457 
458 static gint
459 ets_sorted_to_model (ESorter *es,
460                      gint row)
461 {
462 	ETableSorter *ets = E_TABLE_SORTER (es);
463 	gint rows = e_table_model_row_count (ets->source);
464 
465 	g_return_val_if_fail (row >= 0, -1);
466 	g_return_val_if_fail (row < rows, -1);
467 
468 	if (ets_needs_sorting (es))
469 		ets_sort (ets);
470 
471 	if (ets->sorted)
472 		return ets->sorted[row];
473 	else
474 		return row;
475 }
476 
477 static void
478 ets_get_model_to_sorted_array (ESorter *es,
479                                gint **array,
480                                gint *count)
481 {
482 	ETableSorter *ets = E_TABLE_SORTER (es);
483 	if (array || count) {
484 		ets_backsort (ets);
485 
486 		if (array)
487 			*array = ets->backsorted;
488 		if (count)
489 			*count = e_table_model_row_count(ets->source);
490 	}
491 }
492 
493 static void
494 ets_get_sorted_to_model_array (ESorter *es,
495                                gint **array,
496                                gint *count)
497 {
498 	ETableSorter *ets = E_TABLE_SORTER (es);
499 	if (array || count) {
500 		ets_sort (ets);
501 
502 		if (array)
503 			*array = ets->sorted;
504 		if (count)
505 			*count = e_table_model_row_count(ets->source);
506 	}
507 }
508 
509 static gboolean
510 ets_needs_sorting (ESorter *es)
511 {
512 	ETableSorter *ets = E_TABLE_SORTER (es);
513 	if (ets->needs_sorting < 0) {
514 		if (e_table_sort_info_sorting_get_count (ets->sort_info) + e_table_sort_info_grouping_get_count (ets->sort_info))
515 			ets->needs_sorting = 1;
516 		else
517 			ets->needs_sorting = 0;
518 	}
519 	return ets->needs_sorting;
520 }