evolution-3.6.4/widgets/table/e-table-sorted.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 "e-util/e-util.h"
 31 
 32 #include "e-table-sorted.h"
 33 #include "e-table-sorting-utils.h"
 34 
 35 #define d(x)
 36 
 37 #define INCREMENT_AMOUNT 100
 38 
 39 /* workaround for avoding API breakage */
 40 #define ets_get_type e_table_sorted_get_type
 41 G_DEFINE_TYPE (ETableSorted, ets, E_TYPE_TABLE_SUBSET)
 42 
 43 /* maximum insertions between an idle event that we will do without scheduling an idle sort */
 44 #define ETS_INSERT_MAX (4)
 45 
 46 static void ets_sort_info_changed        (ETableSortInfo *info, ETableSorted *ets);
 47 static void ets_sort                     (ETableSorted *ets);
 48 static void ets_proxy_model_changed      (ETableSubset *etss, ETableModel *source);
 49 static void ets_proxy_model_row_changed  (ETableSubset *etss, ETableModel *source, gint row);
 50 static void ets_proxy_model_cell_changed (ETableSubset *etss, ETableModel *source, gint col, gint row);
 51 static void ets_proxy_model_rows_inserted (ETableSubset *etss, ETableModel *source, gint row, gint count);
 52 static void ets_proxy_model_rows_deleted  (ETableSubset *etss, ETableModel *source, gint row, gint count);
 53 
 54 static void
 55 ets_dispose (GObject *object)
 56 {
 57 	ETableSorted *ets = E_TABLE_SORTED (object);
 58 
 59 	if (ets->sort_idle_id)
 60 		g_source_remove (ets->sort_idle_id);
 61 	ets->sort_idle_id = 0;
 62 
 63 	if (ets->insert_idle_id)
 64 		g_source_remove (ets->insert_idle_id);
 65 	ets->insert_idle_id = 0;
 66 
 67 	if (ets->sort_info) {
 68 		g_signal_handler_disconnect (
 69 			ets->sort_info,
 70 			ets->sort_info_changed_id);
 71 		g_object_unref (ets->sort_info);
 72 		ets->sort_info = NULL;
 73 	}
 74 
 75 	if (ets->full_header)
 76 		g_object_unref (ets->full_header);
 77 	ets->full_header = NULL;
 78 
 79 	G_OBJECT_CLASS (ets_parent_class)->dispose (object);
 80 }
 81 
 82 static void
 83 ets_class_init (ETableSortedClass *class)
 84 {
 85 	ETableSubsetClass *etss_class = E_TABLE_SUBSET_CLASS (class);
 86 	GObjectClass *object_class = G_OBJECT_CLASS (class);
 87 
 88 	etss_class->proxy_model_changed = ets_proxy_model_changed;
 89 	etss_class->proxy_model_row_changed = ets_proxy_model_row_changed;
 90 	etss_class->proxy_model_cell_changed = ets_proxy_model_cell_changed;
 91 	etss_class->proxy_model_rows_inserted = ets_proxy_model_rows_inserted;
 92 	etss_class->proxy_model_rows_deleted = ets_proxy_model_rows_deleted;
 93 
 94 	object_class->dispose = ets_dispose;
 95 }
 96 
 97 static void
 98 ets_init (ETableSorted *ets)
 99 {
100 	ets->full_header = NULL;
101 	ets->sort_info = NULL;
102 
103 	ets->sort_info_changed_id = 0;
104 
105 	ets->sort_idle_id = 0;
106 	ets->insert_count = 0;
107 }
108 
109 static gboolean
110 ets_sort_idle (ETableSorted *ets)
111 {
112 	g_object_ref (ets);
113 	ets_sort (ets);
114 	ets->sort_idle_id = 0;
115 	ets->insert_count = 0;
116 	g_object_unref (ets);
117 	return FALSE;
118 }
119 
120 static gboolean
121 ets_insert_idle (ETableSorted *ets)
122 {
123 	ets->insert_count = 0;
124 	ets->insert_idle_id = 0;
125 	return FALSE;
126 }
127 
128 ETableModel *
129 e_table_sorted_new (ETableModel *source,
130                     ETableHeader *full_header,
131                     ETableSortInfo *sort_info)
132 {
133 	ETableSorted *ets = g_object_new (E_TYPE_TABLE_SORTED, NULL);
134 	ETableSubset *etss = E_TABLE_SUBSET (ets);
135 
136 	if (E_TABLE_SUBSET_CLASS (ets_parent_class)->proxy_model_pre_change)
137 		(E_TABLE_SUBSET_CLASS (ets_parent_class)->proxy_model_pre_change) (etss, source);
138 
139 	if (e_table_subset_construct (etss, source, 0) == NULL) {
140 		g_object_unref (ets);
141 		return NULL;
142 	}
143 
144 	ets->sort_info = sort_info;
145 	g_object_ref (ets->sort_info);
146 	ets->full_header = full_header;
147 	g_object_ref (ets->full_header);
148 
149 	ets_proxy_model_changed (etss, source);
150 
151 	ets->sort_info_changed_id = g_signal_connect (
152 		sort_info, "sort_info_changed",
153 		G_CALLBACK (ets_sort_info_changed), ets);
154 
155 	return E_TABLE_MODEL (ets);
156 }
157 
158 static void
159 ets_sort_info_changed (ETableSortInfo *info,
160                        ETableSorted *ets)
161 {
162 	ets_sort (ets);
163 }
164 
165 static void
166 ets_proxy_model_changed (ETableSubset *subset,
167                          ETableModel *source)
168 {
169 	gint rows, i;
170 
171 	rows = e_table_model_row_count (source);
172 
173 	g_free (subset->map_table);
174 	subset->n_map = rows;
175 	subset->map_table = g_new (int, rows);
176 
177 	for (i = 0; i < rows; i++) {
178 		subset->map_table[i] = i;
179 	}
180 
181 	if (!E_TABLE_SORTED (subset)->sort_idle_id)
182 		E_TABLE_SORTED (subset)->sort_idle_id = g_idle_add_full (50, (GSourceFunc) ets_sort_idle, subset, NULL);
183 
184 	e_table_model_changed (E_TABLE_MODEL (subset));
185 }
186 
187 static void
188 ets_proxy_model_row_changed (ETableSubset *subset,
189                              ETableModel *source,
190                              gint row)
191 {
192 	if (!E_TABLE_SORTED (subset)->sort_idle_id)
193 		E_TABLE_SORTED (subset)->sort_idle_id = g_idle_add_full (50, (GSourceFunc) ets_sort_idle, subset, NULL);
194 
195 	if (E_TABLE_SUBSET_CLASS (ets_parent_class)->proxy_model_row_changed)
196 		(E_TABLE_SUBSET_CLASS (ets_parent_class)->proxy_model_row_changed) (subset, source, row);
197 }
198 
199 static void
200 ets_proxy_model_cell_changed (ETableSubset *subset,
201                               ETableModel *source,
202                               gint col,
203                               gint row)
204 {
205 	ETableSorted *ets = E_TABLE_SORTED (subset);
206 	if (e_table_sorting_utils_affects_sort (ets->sort_info, ets->full_header, col))
207 		ets_proxy_model_row_changed (subset, source, row);
208 	else if (E_TABLE_SUBSET_CLASS (ets_parent_class)->proxy_model_cell_changed)
209 		(E_TABLE_SUBSET_CLASS (ets_parent_class)->proxy_model_cell_changed) (subset, source, col, row);
210 }
211 
212 static void
213 ets_proxy_model_rows_inserted (ETableSubset *etss,
214                                ETableModel *source,
215                                gint row,
216                                gint count)
217 {
218 	ETableModel *etm = E_TABLE_MODEL (etss);
219 	ETableSorted *ets = E_TABLE_SORTED (etss);
220 	gint i;
221 	gboolean full_change = FALSE;
222 
223 	if (count == 0) {
224 		e_table_model_no_change (etm);
225 		return;
226 	}
227 
228 	if (row != etss->n_map) {
229 		full_change = TRUE;
230 		for (i = 0; i < etss->n_map; i++) {
231 			if (etss->map_table[i] >= row) {
232 				etss->map_table[i] += count;
233 			}
234 		}
235 	}
236 
237 	etss->map_table = g_realloc (etss->map_table, (etss->n_map + count) * sizeof (gint));
238 
239 	for (; count > 0; count--) {
240 		if (!full_change)
241 			e_table_model_pre_change (etm);
242 		i = etss->n_map;
243 		if (ets->sort_idle_id == 0) {
244 			/* this is to see if we're inserting a lot of things between idle loops.
245 			 * If we are, we're busy, its faster to just append and perform a full sort later */
246 			ets->insert_count++;
247 			if (ets->insert_count > ETS_INSERT_MAX) {
248 				/* schedule a sort, and append instead */
249 				ets->sort_idle_id = g_idle_add_full (50, (GSourceFunc) ets_sort_idle, ets, NULL);
250 			} else {
251 				/* make sure we have an idle handler to reset the count every now and then */
252 				if (ets->insert_idle_id == 0) {
253 					ets->insert_idle_id = g_idle_add_full (40, (GSourceFunc) ets_insert_idle, ets, NULL);
254 				}
255 				i = e_table_sorting_utils_insert (etss->source, ets->sort_info, ets->full_header, etss->map_table, etss->n_map, row);
256 				memmove (etss->map_table + i + 1, etss->map_table + i, (etss->n_map - i) * sizeof (gint));
257 			}
258 		}
259 		etss->map_table[i] = row;
260 		etss->n_map++;
261 		if (!full_change) {
262 			e_table_model_row_inserted (etm, i);
263 		}
264 
265 		d (g_print ("inserted row %d", row));
266 		row++;
267 	}
268 	if (full_change)
269 		e_table_model_changed (etm);
270 	else
271 		e_table_model_no_change (etm);
272 	d (e_table_subset_print_debugging (etss));
273 }
274 
275 static void
276 ets_proxy_model_rows_deleted (ETableSubset *etss,
277                               ETableModel *source,
278                               gint row,
279                               gint count)
280 {
281 	ETableModel *etm = E_TABLE_MODEL (etss);
282 	gint i;
283 	gboolean shift;
284 	gint j;
285 
286 	shift = row == etss->n_map - count;
287 
288 	for (j = 0; j < count; j++) {
289 		for (i = 0; i < etss->n_map; i++) {
290 			if (etss->map_table[i] == row + j) {
291 				if (shift)
292 					e_table_model_pre_change (etm);
293 				memmove (etss->map_table + i, etss->map_table + i + 1, (etss->n_map - i - 1) * sizeof (gint));
294 				etss->n_map--;
295 				if (shift)
296 					e_table_model_row_deleted (etm, i);
297 			}
298 		}
299 	}
300 	if (!shift) {
301 		for (i = 0; i < etss->n_map; i++) {
302 			if (etss->map_table[i] >= row)
303 				etss->map_table[i] -= count;
304 		}
305 
306 		e_table_model_changed (etm);
307 	} else {
308 		e_table_model_no_change (etm);
309 	}
310 
311 	d (g_print ("deleted row %d count %d", row, count));
312 	d (e_table_subset_print_debugging (etss));
313 }
314 
315 static void
316 ets_sort (ETableSorted *ets)
317 {
318 	ETableSubset *etss = E_TABLE_SUBSET (ets);
319 	static gint reentering = 0;
320 	if (reentering)
321 		return;
322 	reentering = 1;
323 
324 	e_table_model_pre_change (E_TABLE_MODEL (ets));
325 
326 	e_table_sorting_utils_sort (etss->source, ets->sort_info, ets->full_header, etss->map_table, etss->n_map);
327 
328 	e_table_model_changed (E_TABLE_MODEL (ets));
329 	reentering = 0;
330 }