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 }