1 | /* |
2 | * Copyright (C) 2019, Red Hat, Inc. |
3 | * Authors: Matthias Clasen <mclasen@redhat.com> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | #include <locale.h> |
20 | |
21 | #include <gtk/gtk.h> |
22 | |
23 | static GQuark number_quark; |
24 | static GQuark changes_quark; |
25 | static GQuark selection_quark; |
26 | |
27 | static guint |
28 | get (GListModel *model, |
29 | guint position) |
30 | { |
31 | guint number; |
32 | GObject *object = g_list_model_get_item (list: model, position); |
33 | g_assert_nonnull (object); |
34 | number = GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark)); |
35 | g_object_unref (object); |
36 | return number; |
37 | } |
38 | |
39 | static char * |
40 | model_to_string (GListModel *model) |
41 | { |
42 | GString *string = g_string_new (NULL); |
43 | guint i; |
44 | |
45 | for (i = 0; i < g_list_model_get_n_items (list: model); i++) |
46 | { |
47 | if (i > 0) |
48 | g_string_append (string, val: " " ); |
49 | g_string_append_printf (string, format: "%u" , get (model, position: i)); |
50 | } |
51 | |
52 | return g_string_free (string, FALSE); |
53 | } |
54 | |
55 | static char * |
56 | selection_to_string (GListModel *model) |
57 | { |
58 | GString *string = g_string_new (NULL); |
59 | guint i; |
60 | |
61 | for (i = 0; i < g_list_model_get_n_items (list: model); i++) |
62 | { |
63 | if (!gtk_selection_model_is_selected (model: GTK_SELECTION_MODEL (ptr: model), position: i)) |
64 | continue; |
65 | |
66 | if (string->len > 0) |
67 | g_string_append (string, val: " " ); |
68 | g_string_append_printf (string, format: "%u" , get (model, position: i)); |
69 | } |
70 | |
71 | return g_string_free (string, FALSE); |
72 | } |
73 | |
74 | static GListStore * |
75 | new_store (guint start, |
76 | guint end, |
77 | guint step); |
78 | |
79 | static GObject * |
80 | make_object (guint number) |
81 | { |
82 | GObject *object; |
83 | |
84 | /* 0 cannot be differentiated from NULL, so don't use it */ |
85 | g_assert_cmpint (number, !=, 0); |
86 | |
87 | object = g_object_new (G_TYPE_OBJECT, NULL); |
88 | g_object_set_qdata (object, quark: number_quark, GUINT_TO_POINTER (number)); |
89 | |
90 | return object; |
91 | } |
92 | |
93 | static void |
94 | splice (GListStore *store, |
95 | guint pos, |
96 | guint removed, |
97 | guint *numbers, |
98 | guint added) |
99 | { |
100 | GObject **objects = g_newa (GObject *, added); |
101 | guint i; |
102 | |
103 | for (i = 0; i < added; i++) |
104 | objects[i] = make_object (number: numbers[i]); |
105 | |
106 | g_list_store_splice (store, position: pos, n_removals: removed, additions: (gpointer *) objects, n_additions: added); |
107 | |
108 | for (i = 0; i < added; i++) |
109 | g_object_unref (object: objects[i]); |
110 | } |
111 | |
112 | static void |
113 | add (GListStore *store, |
114 | guint number) |
115 | { |
116 | GObject *object = make_object (number); |
117 | g_list_store_append (store, item: object); |
118 | g_object_unref (object); |
119 | } |
120 | |
121 | static void |
122 | insert (GListStore *store, |
123 | guint position, |
124 | guint number) |
125 | { |
126 | GObject *object = make_object (number); |
127 | g_list_store_insert (store, position, item: object); |
128 | g_object_unref (object); |
129 | } |
130 | |
131 | #define assert_model(model, expected) G_STMT_START{ \ |
132 | char *s = model_to_string (G_LIST_MODEL (model)); \ |
133 | if (!g_str_equal (s, expected)) \ |
134 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
135 | #model " == " #expected, s, "==", expected); \ |
136 | g_free (s); \ |
137 | }G_STMT_END |
138 | |
139 | #define ignore_changes(model) G_STMT_START{ \ |
140 | GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ |
141 | g_string_set_size (changes, 0); \ |
142 | }G_STMT_END |
143 | |
144 | #define assert_changes(model, expected) G_STMT_START{ \ |
145 | GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ |
146 | if (!g_str_equal (changes->str, expected)) \ |
147 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
148 | #model " == " #expected, changes->str, "==", expected); \ |
149 | g_string_set_size (changes, 0); \ |
150 | }G_STMT_END |
151 | |
152 | #define assert_selection(model, expected) G_STMT_START{ \ |
153 | char *s = selection_to_string (G_LIST_MODEL (model)); \ |
154 | if (!g_str_equal (s, expected)) \ |
155 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
156 | #model " == " #expected, s, "==", expected); \ |
157 | g_free (s); \ |
158 | }G_STMT_END |
159 | |
160 | #define assert_selection_changes(model, expected) G_STMT_START{ \ |
161 | GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \ |
162 | if (!g_str_equal (changes->str, expected)) \ |
163 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
164 | #model " == " #expected, changes->str, "==", expected); \ |
165 | g_string_set_size (changes, 0); \ |
166 | }G_STMT_END |
167 | |
168 | #define ignore_selection_changes(model) G_STMT_START{ \ |
169 | GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \ |
170 | g_string_set_size (changes, 0); \ |
171 | }G_STMT_END |
172 | |
173 | static GListStore * |
174 | new_empty_store (void) |
175 | { |
176 | return g_list_store_new (G_TYPE_OBJECT); |
177 | } |
178 | |
179 | static GListStore * |
180 | new_store (guint start, |
181 | guint end, |
182 | guint step) |
183 | { |
184 | GListStore *store = new_empty_store (); |
185 | guint i; |
186 | |
187 | for (i = start; i <= end; i += step) |
188 | add (store, number: i); |
189 | |
190 | return store; |
191 | } |
192 | |
193 | static void |
194 | items_changed (GListModel *model, |
195 | guint position, |
196 | guint removed, |
197 | guint added, |
198 | GString *changes) |
199 | { |
200 | g_assert_true (removed != 0 || added != 0); |
201 | |
202 | if (changes->len) |
203 | g_string_append (string: changes, val: ", " ); |
204 | |
205 | if (removed == 1 && added == 0) |
206 | { |
207 | g_string_append_printf (string: changes, format: "-%u" , position); |
208 | } |
209 | else if (removed == 0 && added == 1) |
210 | { |
211 | g_string_append_printf (string: changes, format: "+%u" , position); |
212 | } |
213 | else |
214 | { |
215 | g_string_append_printf (string: changes, format: "%u" , position); |
216 | if (removed > 0) |
217 | g_string_append_printf (string: changes, format: "-%u" , removed); |
218 | if (added > 0) |
219 | g_string_append_printf (string: changes, format: "+%u" , added); |
220 | } |
221 | } |
222 | |
223 | static void |
224 | selection_changed (GListModel *model, |
225 | guint position, |
226 | guint n_items, |
227 | GString *changes) |
228 | { |
229 | if (changes->len) |
230 | g_string_append (string: changes, val: ", " ); |
231 | |
232 | g_string_append_printf (string: changes, format: "%u:%u" , position, n_items); |
233 | } |
234 | |
235 | static void |
236 | free_changes (gpointer data) |
237 | { |
238 | GString *changes = data; |
239 | |
240 | /* all changes must have been checked via assert_changes() before */ |
241 | g_assert_cmpstr (changes->str, ==, "" ); |
242 | |
243 | g_string_free (string: changes, TRUE); |
244 | } |
245 | |
246 | static GtkSelectionModel * |
247 | new_model (GListStore *store, gboolean autoselect, gboolean can_unselect) |
248 | { |
249 | GtkSelectionModel *result; |
250 | GString *changes; |
251 | |
252 | result = GTK_SELECTION_MODEL (ptr: gtk_single_selection_new (g_object_ref (G_LIST_MODEL (store)))); |
253 | |
254 | /* We want to return an empty selection unless autoselect is true, |
255 | * so undo the initial selection due to autoselect defaulting to TRUE. |
256 | */ |
257 | gtk_single_selection_set_autoselect (self: GTK_SINGLE_SELECTION (ptr: result), FALSE); |
258 | gtk_single_selection_set_can_unselect (self: GTK_SINGLE_SELECTION (ptr: result), TRUE); |
259 | gtk_selection_model_unselect_item (model: result, position: 0); |
260 | assert_selection (result, "" ); |
261 | |
262 | gtk_single_selection_set_autoselect (self: GTK_SINGLE_SELECTION (ptr: result), autoselect); |
263 | gtk_single_selection_set_can_unselect (self: GTK_SINGLE_SELECTION (ptr: result), can_unselect); |
264 | |
265 | changes = g_string_new (init: "" ); |
266 | g_object_set_qdata_full (G_OBJECT(result), quark: changes_quark, data: changes, destroy: free_changes); |
267 | g_signal_connect (result, "items-changed" , G_CALLBACK (items_changed), changes); |
268 | |
269 | changes = g_string_new (init: "" ); |
270 | g_object_set_qdata_full (G_OBJECT(result), quark: selection_quark, data: changes, destroy: free_changes); |
271 | g_signal_connect (result, "selection-changed" , G_CALLBACK (selection_changed), changes); |
272 | |
273 | return result; |
274 | } |
275 | |
276 | static void |
277 | test_create (void) |
278 | { |
279 | GtkSelectionModel *selection; |
280 | GListStore *store; |
281 | |
282 | if (glib_check_version (required_major: 2, required_minor: 59, required_micro: 0) != NULL) |
283 | { |
284 | g_test_skip (msg: "g_list_store_get_item() has overflow issues before GLIB 2.59.0" ); |
285 | return; |
286 | } |
287 | |
288 | store = new_store (start: 1, end: 5, step: 2); |
289 | selection = new_model (store, FALSE, FALSE); |
290 | g_assert_false (gtk_single_selection_get_autoselect (GTK_SINGLE_SELECTION (selection))); |
291 | |
292 | assert_model (selection, "1 3 5" ); |
293 | assert_changes (selection, "" ); |
294 | assert_selection (selection, "" ); |
295 | assert_selection_changes (selection, "" ); |
296 | |
297 | g_object_unref (object: store); |
298 | |
299 | assert_model (selection, "1 3 5" ); |
300 | assert_changes (selection, "" ); |
301 | assert_selection (selection, "" ); |
302 | assert_selection_changes (selection, "" ); |
303 | |
304 | g_object_unref (object: selection); |
305 | } |
306 | |
307 | static void |
308 | test_create_empty (void) |
309 | { |
310 | GtkSingleSelection *selection; |
311 | |
312 | selection = gtk_single_selection_new (NULL); |
313 | g_assert_cmpint (g_list_model_get_n_items (G_LIST_MODEL (selection)), ==, 0); |
314 | |
315 | g_object_unref (object: selection); |
316 | } |
317 | |
318 | static void |
319 | test_changes (void) |
320 | { |
321 | GtkSelectionModel *selection; |
322 | GListStore *store; |
323 | |
324 | if (glib_check_version (required_major: 2, required_minor: 58, required_micro: 0) != NULL) |
325 | { |
326 | g_test_skip (msg: "g_list_store_splice() is broken before GLIB 2.58.0" ); |
327 | return; |
328 | } |
329 | |
330 | store = new_store (start: 1, end: 5, step: 1); |
331 | selection = new_model (store, FALSE, FALSE); |
332 | assert_model (selection, "1 2 3 4 5" ); |
333 | assert_changes (selection, "" ); |
334 | assert_selection (selection, "" ); |
335 | assert_selection_changes (selection, "" ); |
336 | |
337 | g_list_store_remove (store, position: 3); |
338 | assert_model (selection, "1 2 3 5" ); |
339 | assert_changes (selection, "-3" ); |
340 | assert_selection (selection, "" ); |
341 | assert_selection_changes (selection, "" ); |
342 | |
343 | insert (store, position: 3, number: 99); |
344 | assert_model (selection, "1 2 3 99 5" ); |
345 | assert_changes (selection, "+3" ); |
346 | assert_selection (selection, "" ); |
347 | assert_selection_changes (selection, "" ); |
348 | |
349 | splice (store, pos: 3, removed: 2, numbers: (guint[]) { 97 }, added: 1); |
350 | assert_model (selection, "1 2 3 97" ); |
351 | assert_changes (selection, "3-2+1" ); |
352 | assert_selection (selection, "" ); |
353 | assert_selection_changes (selection, "" ); |
354 | |
355 | g_object_unref (object: selection); |
356 | g_object_unref (object: store); |
357 | } |
358 | |
359 | static void |
360 | test_selection (void) |
361 | { |
362 | GtkSelectionModel *selection; |
363 | GListStore *store; |
364 | gboolean ret; |
365 | |
366 | if (glib_check_version (required_major: 2, required_minor: 59, required_micro: 0) != NULL) |
367 | { |
368 | g_test_skip (msg: "g_list_store_get_item() has overflow issues before GLIB 2.59.0" ); |
369 | return; |
370 | } |
371 | |
372 | store = new_store (start: 1, end: 5, step: 1); |
373 | selection = new_model (store, TRUE, FALSE); |
374 | assert_selection (selection, "1" ); |
375 | assert_selection_changes (selection, "" ); |
376 | |
377 | ret = gtk_selection_model_select_item (model: selection, position: 3, FALSE); |
378 | g_assert_true (ret); |
379 | assert_selection (selection, "4" ); |
380 | assert_selection_changes (selection, "0:4" ); |
381 | |
382 | ret = gtk_selection_model_unselect_item (model: selection, position: 3); |
383 | g_assert_false (ret); |
384 | assert_selection (selection, "4" ); |
385 | assert_selection_changes (selection, "" ); |
386 | |
387 | ret = gtk_selection_model_select_item (model: selection, position: 1, FALSE); |
388 | g_assert_true (ret); |
389 | assert_selection (selection, "2" ); |
390 | assert_selection_changes (selection, "1:3" ); |
391 | |
392 | ret = gtk_selection_model_select_range (model: selection, position: 3, n_items: 2, FALSE); |
393 | g_assert_false (ret); |
394 | assert_selection (selection, "2" ); |
395 | assert_selection_changes (selection, "" ); |
396 | |
397 | ret = gtk_selection_model_unselect_range (model: selection, position: 4, n_items: 2); |
398 | g_assert_false (ret); |
399 | assert_selection (selection, "2" ); |
400 | assert_selection_changes (selection, "" ); |
401 | |
402 | ret = gtk_selection_model_select_all (model: selection); |
403 | g_assert_false (ret); |
404 | assert_selection (selection, "2" ); |
405 | assert_selection_changes (selection, "" ); |
406 | |
407 | ret = gtk_selection_model_unselect_all (model: selection); |
408 | g_assert_false (ret); |
409 | assert_selection (selection, "2" ); |
410 | assert_selection_changes (selection, "" ); |
411 | |
412 | g_object_unref (object: store); |
413 | g_object_unref (object: selection); |
414 | } |
415 | |
416 | static void |
417 | test_autoselect (void) |
418 | { |
419 | GtkSelectionModel *selection; |
420 | GListStore *store; |
421 | |
422 | if (glib_check_version (required_major: 2, required_minor: 59, required_micro: 0) != NULL) |
423 | { |
424 | g_test_skip (msg: "g_list_store_get_item() has overflow issues before GLIB 2.59.0" ); |
425 | return; |
426 | } |
427 | |
428 | store = new_empty_store (); |
429 | selection = new_model (store, TRUE, FALSE); |
430 | assert_model (selection, "" ); |
431 | assert_changes (selection, "" ); |
432 | assert_selection (selection, "" ); |
433 | assert_selection_changes (selection, "" ); |
434 | |
435 | add (store, number: 1); |
436 | assert_model (selection, "1" ); |
437 | assert_changes (selection, "+0" ); |
438 | assert_selection (selection, "1" ); |
439 | assert_selection_changes (selection, "" ); |
440 | |
441 | splice (store, pos: 0, removed: 1, numbers: (guint[]) { 7, 8, 9 }, added: 3); |
442 | assert_model (selection, "7 8 9" ); |
443 | assert_changes (selection, "0-1+3" ); |
444 | assert_selection (selection, "7" ); |
445 | assert_selection_changes (selection, "" ); |
446 | |
447 | splice (store, pos: 0, removed: 0, numbers: (guint[]) { 5, 6 }, added: 2); |
448 | assert_model (selection, "5 6 7 8 9" ); |
449 | assert_changes (selection, "0+2" ); |
450 | assert_selection (selection, "7" ); |
451 | assert_selection_changes (selection, "" ); |
452 | |
453 | g_list_store_remove (store, position: 2); |
454 | assert_model (selection, "5 6 8 9" ); |
455 | assert_changes (selection, "2-2+1" ); |
456 | assert_selection (selection, "8" ); |
457 | assert_selection_changes (selection, "" ); |
458 | |
459 | splice (store, pos: 2, removed: 2, NULL, added: 0); |
460 | assert_model (selection, "5 6" ); |
461 | assert_changes (selection, "1-3+1" ); |
462 | assert_selection (selection, "6" ); |
463 | assert_selection_changes (selection, "" ); |
464 | |
465 | splice (store, pos: 0, removed: 2, numbers: (guint[]) { 1, 2 }, added: 2); |
466 | assert_model (selection, "1 2" ); |
467 | assert_changes (selection, "0-2+2" ); |
468 | assert_selection (selection, "2" ); |
469 | assert_selection_changes (selection, "" ); |
470 | |
471 | g_list_store_remove (store, position: 0); |
472 | assert_model (selection, "2" ); |
473 | assert_changes (selection, "-0" ); |
474 | assert_selection (selection, "2" ); |
475 | assert_selection_changes (selection, "" ); |
476 | |
477 | g_list_store_remove (store, position: 0); |
478 | assert_model (selection, "" ); |
479 | assert_changes (selection, "-0" ); |
480 | assert_selection (selection, "" ); |
481 | assert_selection_changes (selection, "" ); |
482 | |
483 | g_object_unref (object: store); |
484 | g_object_unref (object: selection); |
485 | } |
486 | |
487 | static void |
488 | test_autoselect_toggle (void) |
489 | { |
490 | GtkSelectionModel *selection; |
491 | GListStore *store; |
492 | |
493 | if (glib_check_version (required_major: 2, required_minor: 59, required_micro: 0) != NULL) |
494 | { |
495 | g_test_skip (msg: "g_list_store_get_item() has overflow issues before GLIB 2.59.0" ); |
496 | return; |
497 | } |
498 | |
499 | store = new_store (start: 1, end: 1, step: 1); |
500 | selection = new_model (store, TRUE, TRUE); |
501 | assert_model (selection, "1" ); |
502 | assert_changes (selection, "" ); |
503 | assert_selection (selection, "1" ); |
504 | assert_selection_changes (selection, "" ); |
505 | |
506 | gtk_single_selection_set_autoselect (self: GTK_SINGLE_SELECTION (ptr: selection), FALSE); |
507 | assert_model (selection, "1" ); |
508 | assert_changes (selection, "" ); |
509 | assert_selection (selection, "1" ); |
510 | assert_selection_changes (selection, "" ); |
511 | |
512 | gtk_selection_model_unselect_item (model: selection, position: 0); |
513 | assert_model (selection, "1" ); |
514 | assert_changes (selection, "" ); |
515 | assert_selection (selection, "" ); |
516 | assert_selection_changes (selection, "0:1" ); |
517 | |
518 | gtk_single_selection_set_autoselect (self: GTK_SINGLE_SELECTION (ptr: selection), TRUE); |
519 | assert_model (selection, "1" ); |
520 | assert_changes (selection, "" ); |
521 | assert_selection (selection, "1" ); |
522 | assert_selection_changes (selection, "0:1" ); |
523 | |
524 | g_object_unref (object: store); |
525 | g_object_unref (object: selection); |
526 | } |
527 | |
528 | static void |
529 | test_can_unselect (void) |
530 | { |
531 | GtkSelectionModel *selection; |
532 | GListStore *store; |
533 | gboolean ret; |
534 | |
535 | if (glib_check_version (required_major: 2, required_minor: 59, required_micro: 0) != NULL) |
536 | { |
537 | g_test_skip (msg: "g_list_store_get_item() has overflow issues before GLIB 2.59.0" ); |
538 | return; |
539 | } |
540 | |
541 | store = new_store (start: 1, end: 5, step: 1); |
542 | selection = new_model (store, TRUE, FALSE); |
543 | assert_selection (selection, "1" ); |
544 | assert_selection_changes (selection, "" ); |
545 | |
546 | ret = gtk_selection_model_unselect_item (model: selection, position: 0); |
547 | g_assert_false (ret); |
548 | assert_selection (selection, "1" ); |
549 | assert_selection_changes (selection, "" ); |
550 | |
551 | gtk_single_selection_set_can_unselect (self: GTK_SINGLE_SELECTION (ptr: selection), TRUE); |
552 | |
553 | assert_selection (selection, "1" ); |
554 | ret = gtk_selection_model_unselect_item (model: selection, position: 0); |
555 | g_assert_true (ret); |
556 | assert_selection (selection, "" ); |
557 | assert_selection_changes (selection, "0:1" ); |
558 | |
559 | ignore_changes (selection); |
560 | |
561 | g_object_unref (object: store); |
562 | g_object_unref (object: selection); |
563 | } |
564 | |
565 | static int |
566 | sort_inverse (gconstpointer a, gconstpointer b, gpointer data) |
567 | { |
568 | int ia = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (a), number_quark)); |
569 | int ib = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (b), number_quark)); |
570 | |
571 | return ib - ia; |
572 | } |
573 | |
574 | static void |
575 | test_persistence (void) |
576 | { |
577 | GtkSelectionModel *selection; |
578 | GListStore *store; |
579 | |
580 | if (glib_check_version (required_major: 2, required_minor: 59, required_micro: 0) != NULL) |
581 | { |
582 | g_test_skip (msg: "g_list_store_get_item() has overflow issues before GLIB 2.59.0" ); |
583 | return; |
584 | } |
585 | |
586 | store = new_store (start: 1, end: 5, step: 1); |
587 | selection = new_model (store, TRUE, FALSE); |
588 | assert_selection (selection, "1" ); |
589 | assert_selection_changes (selection, "" ); |
590 | g_assert_true (gtk_selection_model_is_selected (selection, 0)); |
591 | g_assert_false (gtk_selection_model_is_selected (selection, 4)); |
592 | |
593 | g_list_store_sort (store, compare_func: sort_inverse, NULL); |
594 | |
595 | assert_selection (selection, "1" ); |
596 | assert_selection_changes (selection, "" ); |
597 | g_assert_false (gtk_selection_model_is_selected (selection, 0)); |
598 | g_assert_true (gtk_selection_model_is_selected (selection, 4)); |
599 | |
600 | ignore_changes (selection); |
601 | |
602 | g_object_unref (object: store); |
603 | g_object_unref (object: selection); |
604 | } |
605 | |
606 | static void |
607 | check_get_selection (GtkSelectionModel *selection) |
608 | { |
609 | GtkBitset *set; |
610 | guint i, n_items; |
611 | |
612 | set = gtk_selection_model_get_selection (model: selection); |
613 | |
614 | n_items = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: selection)); |
615 | if (n_items == 0) |
616 | { |
617 | g_assert_true (gtk_bitset_is_empty (set)); |
618 | } |
619 | else |
620 | { |
621 | for (i = 0; i < n_items; i++) |
622 | { |
623 | g_assert_cmpint (gtk_bitset_contains (set, i), ==, gtk_selection_model_is_selected (selection, i)); |
624 | } |
625 | |
626 | /* check that out-of-range has no bits set */ |
627 | g_assert_cmpint (gtk_bitset_get_maximum (set), <, g_list_model_get_n_items (G_LIST_MODEL (selection))); |
628 | } |
629 | |
630 | gtk_bitset_unref (self: set); |
631 | } |
632 | |
633 | static void |
634 | test_query_range (void) |
635 | { |
636 | GtkSelectionModel *selection; |
637 | GListStore *store; |
638 | |
639 | store = new_store (start: 1, end: 5, step: 1); |
640 | selection = new_model (store, TRUE, TRUE); |
641 | check_get_selection (selection); |
642 | |
643 | gtk_selection_model_unselect_item (model: selection, position: 0); |
644 | check_get_selection (selection); |
645 | |
646 | gtk_selection_model_select_item (model: selection, position: 2, TRUE); |
647 | check_get_selection (selection); |
648 | |
649 | gtk_selection_model_select_item (model: selection, position: 4, TRUE); |
650 | check_get_selection (selection); |
651 | |
652 | ignore_selection_changes (selection); |
653 | |
654 | g_object_unref (object: store); |
655 | g_object_unref (object: selection); |
656 | } |
657 | |
658 | static void |
659 | test_set_model (void) |
660 | { |
661 | GtkSelectionModel *selection; |
662 | GListStore *store; |
663 | GListModel *m1, *m2; |
664 | |
665 | store = new_store (start: 1, end: 5, step: 1); |
666 | m1 = G_LIST_MODEL (ptr: store); |
667 | m2 = G_LIST_MODEL (ptr: gtk_slice_list_model_new (g_object_ref (m1), offset: 0, size: 3)); |
668 | selection = new_model (store, TRUE, TRUE); |
669 | assert_selection (selection, "1" ); |
670 | assert_selection_changes (selection, "" ); |
671 | |
672 | /* we retain the selected item across model changes */ |
673 | gtk_single_selection_set_model (self: GTK_SINGLE_SELECTION (ptr: selection), model: m2); |
674 | assert_changes (selection, "0-5+3" ); |
675 | assert_selection (selection, "1" ); |
676 | assert_selection_changes (selection, "" ); |
677 | |
678 | gtk_single_selection_set_model (self: GTK_SINGLE_SELECTION (ptr: selection), NULL); |
679 | assert_changes (selection, "0-3" ); |
680 | assert_selection (selection, "" ); |
681 | assert_selection_changes (selection, "" ); |
682 | |
683 | gtk_single_selection_set_autoselect (self: GTK_SINGLE_SELECTION (ptr: selection), FALSE); |
684 | gtk_single_selection_set_model (self: GTK_SINGLE_SELECTION (ptr: selection), model: m2); |
685 | assert_changes (selection, "0+3" ); |
686 | assert_selection (selection, "" ); |
687 | assert_selection_changes (selection, "" ); |
688 | |
689 | /* we retain no selected item across model changes */ |
690 | gtk_single_selection_set_model (self: GTK_SINGLE_SELECTION (ptr: selection), model: m1); |
691 | assert_changes (selection, "0-3+5" ); |
692 | assert_selection (selection, "" ); |
693 | assert_selection_changes (selection, "" ); |
694 | |
695 | gtk_single_selection_set_selected (self: GTK_SINGLE_SELECTION (ptr: selection), position: 4); |
696 | assert_selection (selection, "5" ); |
697 | assert_selection_changes (selection, "4:1" ); |
698 | |
699 | gtk_single_selection_set_model (self: GTK_SINGLE_SELECTION (ptr: selection), model: m2); |
700 | assert_changes (selection, "0-5+3" ); |
701 | assert_selection (selection, "" ); |
702 | assert_selection_changes (selection, "" ); |
703 | |
704 | g_object_unref (object: m2); |
705 | g_object_unref (object: m1); |
706 | g_object_unref (object: selection); |
707 | } |
708 | |
709 | static void |
710 | test_empty (void) |
711 | { |
712 | GtkSingleSelection *selection; |
713 | GListStore *store; |
714 | |
715 | selection = gtk_single_selection_new (NULL); |
716 | |
717 | g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (selection)), ==, 0); |
718 | g_assert_null (g_list_model_get_item (G_LIST_MODEL (selection), 11)); |
719 | |
720 | store = g_list_store_new (G_TYPE_OBJECT); |
721 | gtk_single_selection_set_model (self: selection, model: G_LIST_MODEL (ptr: store)); |
722 | g_object_unref (object: store); |
723 | |
724 | g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (selection)), ==, 0); |
725 | g_assert_null (g_list_model_get_item (G_LIST_MODEL (selection), 11)); |
726 | |
727 | g_object_unref (object: selection); |
728 | } |
729 | |
730 | int |
731 | main (int argc, char *argv[]) |
732 | { |
733 | (g_test_init) (argc: &argc, argv: &argv, NULL); |
734 | setlocale (LC_ALL, locale: "C" ); |
735 | |
736 | number_quark = g_quark_from_static_string (string: "Hell and fire was spawned to be released." ); |
737 | changes_quark = g_quark_from_static_string (string: "What did I see? Can I believe what I saw?" ); |
738 | selection_quark = g_quark_from_static_string (string: "Mana mana, badibidibi" ); |
739 | |
740 | g_test_add_func (testpath: "/singleselection/create" , test_func: test_create); |
741 | g_test_add_func (testpath: "/singleselection/create-empty" , test_func: test_create_empty); |
742 | g_test_add_func (testpath: "/singleselection/autoselect" , test_func: test_autoselect); |
743 | g_test_add_func (testpath: "/singleselection/autoselect-toggle" , test_func: test_autoselect_toggle); |
744 | g_test_add_func (testpath: "/singleselection/selection" , test_func: test_selection); |
745 | g_test_add_func (testpath: "/singleselection/can-unselect" , test_func: test_can_unselect); |
746 | g_test_add_func (testpath: "/singleselection/persistence" , test_func: test_persistence); |
747 | g_test_add_func (testpath: "/singleselection/query-range" , test_func: test_query_range); |
748 | g_test_add_func (testpath: "/singleselection/changes" , test_func: test_changes); |
749 | g_test_add_func (testpath: "/singleselection/set-model" , test_func: test_set_model); |
750 | g_test_add_func (testpath: "/singleselection/empty" , test_func: test_empty); |
751 | |
752 | return g_test_run (); |
753 | } |
754 | |