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