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 | GObject *object = g_list_model_get_item (list: model, position); |
32 | guint ret; |
33 | g_assert_nonnull (object); |
34 | ret = GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark)); |
35 | g_object_unref (object); |
36 | return ret; |
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; |
101 | guint i; |
102 | |
103 | objects = g_new0 (GObject *, added); |
104 | |
105 | for (i = 0; i < added; i++) |
106 | objects[i] = make_object (number: numbers[i]); |
107 | |
108 | g_list_store_splice (store, position: pos, n_removals: removed, additions: (gpointer *) objects, n_additions: added); |
109 | |
110 | for (i = 0; i < added; i++) |
111 | g_object_unref (object: objects[i]); |
112 | |
113 | g_free (mem: objects); |
114 | } |
115 | |
116 | static void |
117 | add (GListStore *store, |
118 | guint number) |
119 | { |
120 | GObject *object = make_object (number); |
121 | g_list_store_append (store, item: object); |
122 | g_object_unref (object); |
123 | } |
124 | |
125 | static void |
126 | insert (GListStore *store, |
127 | guint position, |
128 | guint number) |
129 | { |
130 | GObject *object = make_object (number); |
131 | g_list_store_insert (store, position, item: object); |
132 | g_object_unref (object); |
133 | } |
134 | |
135 | #define assert_model(model, expected) G_STMT_START{ \ |
136 | char *s = model_to_string (G_LIST_MODEL (model)); \ |
137 | if (!g_str_equal (s, expected)) \ |
138 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
139 | #model " == " #expected, s, "==", expected); \ |
140 | g_free (s); \ |
141 | }G_STMT_END |
142 | |
143 | #define ignore_changes(model) G_STMT_START{ \ |
144 | GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ |
145 | g_string_set_size (changes, 0); \ |
146 | }G_STMT_END |
147 | |
148 | #define assert_changes(model, expected) G_STMT_START{ \ |
149 | GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ |
150 | if (!g_str_equal (changes->str, expected)) \ |
151 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
152 | #model " == " #expected, changes->str, "==", expected); \ |
153 | g_string_set_size (changes, 0); \ |
154 | }G_STMT_END |
155 | |
156 | #define assert_selection(model, expected) G_STMT_START{ \ |
157 | char *s = selection_to_string (G_LIST_MODEL (model)); \ |
158 | if (!g_str_equal (s, expected)) \ |
159 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
160 | #model " == " #expected, s, "==", expected); \ |
161 | g_free (s); \ |
162 | }G_STMT_END |
163 | |
164 | #define ignore_selection_changes(model) G_STMT_START{ \ |
165 | GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \ |
166 | g_string_set_size (changes, 0); \ |
167 | }G_STMT_END |
168 | |
169 | #define assert_selection_changes(model, expected) G_STMT_START{ \ |
170 | GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \ |
171 | if (!g_str_equal (changes->str, expected)) \ |
172 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
173 | #model " == " #expected, changes->str, "==", expected); \ |
174 | g_string_set_size (changes, 0); \ |
175 | }G_STMT_END |
176 | |
177 | static GListStore * |
178 | new_empty_store (void) |
179 | { |
180 | return g_list_store_new (G_TYPE_OBJECT); |
181 | } |
182 | |
183 | static GListStore * |
184 | new_store (guint start, |
185 | guint end, |
186 | guint step) |
187 | { |
188 | GListStore *store = new_empty_store (); |
189 | guint i; |
190 | |
191 | for (i = start; i <= end; i += step) |
192 | add (store, number: i); |
193 | |
194 | return store; |
195 | } |
196 | |
197 | static void |
198 | items_changed (GListModel *model, |
199 | guint position, |
200 | guint removed, |
201 | guint added, |
202 | GString *changes) |
203 | { |
204 | g_assert_true (removed != 0 || added != 0); |
205 | |
206 | if (changes->len) |
207 | g_string_append (string: changes, val: ", " ); |
208 | |
209 | if (removed == 1 && added == 0) |
210 | { |
211 | g_string_append_printf (string: changes, format: "-%u" , position); |
212 | } |
213 | else if (removed == 0 && added == 1) |
214 | { |
215 | g_string_append_printf (string: changes, format: "+%u" , position); |
216 | } |
217 | else |
218 | { |
219 | g_string_append_printf (string: changes, format: "%u" , position); |
220 | if (removed > 0) |
221 | g_string_append_printf (string: changes, format: "-%u" , removed); |
222 | if (added > 0) |
223 | g_string_append_printf (string: changes, format: "+%u" , added); |
224 | } |
225 | } |
226 | |
227 | static void |
228 | selection_changed (GListModel *model, |
229 | guint position, |
230 | guint n_items, |
231 | GString *changes) |
232 | { |
233 | if (changes->len) |
234 | g_string_append (string: changes, val: ", " ); |
235 | |
236 | g_string_append_printf (string: changes, format: "%u:%u" , position, n_items); |
237 | } |
238 | |
239 | static void |
240 | free_changes (gpointer data) |
241 | { |
242 | GString *changes = data; |
243 | |
244 | /* all changes must have been checked via assert_changes() before */ |
245 | g_assert_cmpstr (changes->str, ==, "" ); |
246 | |
247 | g_string_free (string: changes, TRUE); |
248 | } |
249 | |
250 | static GtkSelectionModel * |
251 | new_model (GListStore *store) |
252 | { |
253 | GtkSelectionModel *result; |
254 | GString *changes; |
255 | |
256 | result = GTK_SELECTION_MODEL (ptr: gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (store)))); |
257 | |
258 | changes = g_string_new (init: "" ); |
259 | g_object_set_qdata_full (G_OBJECT(result), quark: changes_quark, data: changes, destroy: free_changes); |
260 | g_signal_connect (result, "items-changed" , G_CALLBACK (items_changed), changes); |
261 | |
262 | changes = g_string_new (init: "" ); |
263 | g_object_set_qdata_full (G_OBJECT(result), quark: selection_quark, data: changes, destroy: free_changes); |
264 | g_signal_connect (result, "selection-changed" , G_CALLBACK (selection_changed), changes); |
265 | |
266 | return result; |
267 | } |
268 | |
269 | static GtkSelectionFilterModel * |
270 | new_filter_model (GtkSelectionModel *model) |
271 | { |
272 | GtkSelectionFilterModel *result; |
273 | GString *changes; |
274 | |
275 | result = gtk_selection_filter_model_new (model); |
276 | |
277 | changes = g_string_new (init: "" ); |
278 | g_object_set_qdata_full (G_OBJECT(result), quark: changes_quark, data: changes, destroy: free_changes); |
279 | g_signal_connect (result, "items-changed" , G_CALLBACK (items_changed), changes); |
280 | |
281 | return result; |
282 | } |
283 | |
284 | static void |
285 | test_create (void) |
286 | { |
287 | GtkSelectionModel *selection; |
288 | GListStore *store; |
289 | |
290 | store = new_store (start: 1, end: 5, step: 2); |
291 | selection = new_model (store); |
292 | |
293 | assert_model (selection, "1 3 5" ); |
294 | assert_changes (selection, "" ); |
295 | assert_selection (selection, "" ); |
296 | assert_selection_changes (selection, "" ); |
297 | |
298 | g_object_unref (object: store); |
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 | GtkMultiSelection *selection; |
311 | |
312 | selection = gtk_multi_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 | gboolean ret; |
324 | |
325 | store = new_store (start: 1, end: 5, step: 1); |
326 | selection = new_model (store); |
327 | assert_model (selection, "1 2 3 4 5" ); |
328 | assert_changes (selection, "" ); |
329 | assert_selection (selection, "" ); |
330 | assert_selection_changes (selection, "" ); |
331 | |
332 | g_list_store_remove (store, position: 3); |
333 | assert_model (selection, "1 2 3 5" ); |
334 | assert_changes (selection, "-3" ); |
335 | assert_selection (selection, "" ); |
336 | assert_selection_changes (selection, "" ); |
337 | |
338 | insert (store, position: 3, number: 99); |
339 | assert_model (selection, "1 2 3 99 5" ); |
340 | assert_changes (selection, "+3" ); |
341 | assert_selection (selection, "" ); |
342 | assert_selection_changes (selection, "" ); |
343 | |
344 | splice (store, pos: 3, removed: 2, numbers: (guint[]) { 97 }, added: 1); |
345 | assert_model (selection, "1 2 3 97" ); |
346 | assert_changes (selection, "3-2+1" ); |
347 | assert_selection (selection, "" ); |
348 | assert_selection_changes (selection, "" ); |
349 | |
350 | ret = gtk_selection_model_select_range (model: selection, position: 1, n_items: 2, FALSE); |
351 | g_assert_true (ret); |
352 | assert_selection (selection, "2 3" ); |
353 | assert_selection_changes (selection, "1:2" ); |
354 | |
355 | insert (store, position: 2, number: 22); |
356 | assert_model (selection, "1 2 22 3 97" ); |
357 | assert_changes (selection, "+2" ); |
358 | assert_selection (selection, "2 3" ); |
359 | assert_selection_changes (selection, "" ); |
360 | |
361 | g_object_unref (object: store); |
362 | g_object_unref (object: selection); |
363 | } |
364 | |
365 | static void |
366 | test_selection (void) |
367 | { |
368 | GtkSelectionModel *selection; |
369 | GListStore *store; |
370 | gboolean ret; |
371 | |
372 | store = new_store (start: 1, end: 5, step: 1); |
373 | selection = new_model (store); |
374 | assert_selection (selection, "" ); |
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, "3:1" ); |
381 | |
382 | ret = gtk_selection_model_unselect_item (model: selection, position: 3); |
383 | g_assert_true (ret); |
384 | assert_selection (selection, "" ); |
385 | assert_selection_changes (selection, "3:1" ); |
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:1" ); |
391 | |
392 | ret = gtk_selection_model_select_range (model: selection, position: 3, n_items: 2, FALSE); |
393 | g_assert_true (ret); |
394 | assert_selection (selection, "2 4 5" ); |
395 | assert_selection_changes (selection, "3:2" ); |
396 | |
397 | ret = gtk_selection_model_unselect_range (model: selection, position: 3, n_items: 2); |
398 | g_assert_true (ret); |
399 | assert_selection (selection, "2" ); |
400 | assert_selection_changes (selection, "3:2" ); |
401 | |
402 | ret = gtk_selection_model_select_all (model: selection); |
403 | g_assert_true (ret); |
404 | assert_selection (selection, "1 2 3 4 5" ); |
405 | assert_selection_changes (selection, "0:5" ); |
406 | |
407 | ret = gtk_selection_model_unselect_all (model: selection); |
408 | g_assert_true (ret); |
409 | assert_selection (selection, "" ); |
410 | assert_selection_changes (selection, "0:5" ); |
411 | |
412 | g_object_unref (object: store); |
413 | g_object_unref (object: selection); |
414 | } |
415 | |
416 | /* Verify that select_range with exclusive = TRUE |
417 | * sends a selection-changed signal that covers |
418 | * preexisting items that got unselected |
419 | */ |
420 | static void |
421 | test_select_range (void) |
422 | { |
423 | GtkSelectionModel *selection; |
424 | GListStore *store; |
425 | gboolean ret; |
426 | |
427 | store = new_store (start: 1, end: 5, step: 1); |
428 | selection = new_model (store); |
429 | assert_selection (selection, "" ); |
430 | assert_selection_changes (selection, "" ); |
431 | |
432 | ret = gtk_selection_model_select_range (model: selection, position: 2, n_items: 2, FALSE); |
433 | g_assert_true (ret); |
434 | assert_selection (selection, "3 4" ); |
435 | assert_selection_changes (selection, "2:2" ); |
436 | |
437 | ret = gtk_selection_model_select_range (model: selection, position: 3, n_items: 2, FALSE); |
438 | g_assert_true (ret); |
439 | assert_selection (selection, "3 4 5" ); |
440 | assert_selection_changes (selection, "4:1" ); |
441 | |
442 | ret = gtk_selection_model_select_range (model: selection, position: 0, n_items: 1, TRUE); |
443 | g_assert_true (ret); |
444 | assert_selection (selection, "1" ); |
445 | assert_selection_changes (selection, "0:5" ); |
446 | |
447 | g_object_unref (object: store); |
448 | g_object_unref (object: selection); |
449 | } |
450 | |
451 | /* Test that removing and readding items |
452 | * doesn't clear the selected state. |
453 | */ |
454 | static void |
455 | test_readd (void) |
456 | { |
457 | GtkSelectionModel *selection; |
458 | GListStore *store; |
459 | gboolean ret; |
460 | |
461 | store = new_store (start: 1, end: 5, step: 1); |
462 | |
463 | selection = new_model (store); |
464 | assert_model (selection, "1 2 3 4 5" ); |
465 | assert_selection (selection, "" ); |
466 | assert_selection_changes (selection, "" ); |
467 | |
468 | ret = gtk_selection_model_select_range (model: selection, position: 2, n_items: 2, FALSE); |
469 | g_assert_true (ret); |
470 | assert_model (selection, "1 2 3 4 5" ); |
471 | assert_selection (selection, "3 4" ); |
472 | assert_selection_changes (selection, "2:2" ); |
473 | |
474 | g_list_model_items_changed (list: G_LIST_MODEL (ptr: store), position: 1, removed: 3, added: 3); |
475 | assert_changes (selection, "1-3+3" ); |
476 | assert_selection (selection, "3 4" ); |
477 | |
478 | g_object_unref (object: store); |
479 | g_object_unref (object: selection); |
480 | } |
481 | |
482 | static void |
483 | test_set_selection (void) |
484 | { |
485 | GtkSelectionModel *selection; |
486 | gboolean ret; |
487 | GListStore *store; |
488 | GtkBitset *selected, *mask; |
489 | |
490 | store = new_store (start: 1, end: 10, step: 1); |
491 | |
492 | selection = new_model (store); |
493 | assert_model (selection, "1 2 3 4 5 6 7 8 9 10" ); |
494 | assert_selection (selection, "" ); |
495 | assert_selection_changes (selection, "" ); |
496 | |
497 | selected = gtk_bitset_new_empty (); |
498 | gtk_bitset_add_range (self: selected, start: 2, n_items: 3); |
499 | gtk_bitset_add_range (self: selected, start: 6, n_items: 3); |
500 | mask = gtk_bitset_new_empty (); |
501 | gtk_bitset_add_range (self: mask, start: 0, n_items: 100); /* too big on purpose */ |
502 | ret = gtk_selection_model_set_selection (model: selection, selected, mask); |
503 | g_assert_true (ret); |
504 | gtk_bitset_unref (self: selected); |
505 | gtk_bitset_unref (self: mask); |
506 | assert_selection (selection, "3 4 5 7 8 9" ); |
507 | assert_selection_changes (selection, "2:7" ); |
508 | |
509 | selected = gtk_bitset_new_empty (); |
510 | mask = gtk_bitset_new_empty (); |
511 | gtk_bitset_add (self: mask, value: 3); |
512 | gtk_bitset_add (self: mask, value: 7); |
513 | ret = gtk_selection_model_set_selection (model: selection, selected, mask); |
514 | g_assert_true (ret); |
515 | gtk_bitset_unref (self: selected); |
516 | gtk_bitset_unref (self: mask); |
517 | assert_selection (selection, "3 5 7 9" ); |
518 | assert_selection_changes (selection, "3:5" ); |
519 | |
520 | g_object_unref (object: store); |
521 | g_object_unref (object: selection); |
522 | } |
523 | |
524 | static void |
525 | test_selection_filter (void) |
526 | { |
527 | GtkSelectionModel *selection; |
528 | GtkSelectionFilterModel *filter; |
529 | GListStore *store; |
530 | gboolean ret; |
531 | |
532 | store = new_store (start: 1, end: 5, step: 1); |
533 | selection = new_model (store); |
534 | assert_selection (selection, "" ); |
535 | assert_selection_changes (selection, "" ); |
536 | |
537 | filter = new_filter_model (model: selection); |
538 | assert_model (filter, "" ); |
539 | assert_changes (filter, "" ); |
540 | |
541 | ret = gtk_selection_model_select_item (model: selection, position: 3, FALSE); |
542 | g_assert_true (ret); |
543 | assert_selection (selection, "4" ); |
544 | assert_selection_changes (selection, "3:1" ); |
545 | assert_model (filter, "4" ); |
546 | assert_changes (filter, "+0" ); |
547 | |
548 | ret = gtk_selection_model_unselect_item (model: selection, position: 3); |
549 | g_assert_true (ret); |
550 | assert_selection (selection, "" ); |
551 | assert_selection_changes (selection, "3:1" ); |
552 | assert_model (filter, "" ); |
553 | assert_changes (filter, "-0" ); |
554 | |
555 | ret = gtk_selection_model_select_item (model: selection, position: 1, FALSE); |
556 | g_assert_true (ret); |
557 | assert_selection (selection, "2" ); |
558 | assert_selection_changes (selection, "1:1" ); |
559 | assert_model (filter, "2" ); |
560 | assert_changes (filter, "+0" ); |
561 | |
562 | ret = gtk_selection_model_select_item (model: selection, position: 0, FALSE); |
563 | g_assert_true (ret); |
564 | assert_selection (selection, "1 2" ); |
565 | assert_selection_changes (selection, "0:1" ); |
566 | assert_model (filter, "1 2" ); |
567 | assert_changes (filter, "+0" ); |
568 | |
569 | ret = gtk_selection_model_unselect_item (model: selection, position: 0); |
570 | g_assert_true (ret); |
571 | assert_selection (selection, "2" ); |
572 | assert_selection_changes (selection, "0:1" ); |
573 | assert_model (filter, "2" ); |
574 | assert_changes (filter, "-0" ); |
575 | |
576 | ret = gtk_selection_model_select_range (model: selection, position: 3, n_items: 2, FALSE); |
577 | g_assert_true (ret); |
578 | assert_selection (selection, "2 4 5" ); |
579 | assert_selection_changes (selection, "3:2" ); |
580 | assert_model (filter, "2 4 5" ); |
581 | assert_changes (filter, "1+2" ); |
582 | |
583 | ret = gtk_selection_model_unselect_range (model: selection, position: 3, n_items: 2); |
584 | g_assert_true (ret); |
585 | assert_selection (selection, "2" ); |
586 | assert_selection_changes (selection, "3:2" ); |
587 | assert_model (filter, "2" ); |
588 | assert_changes (filter, "1-2" ); |
589 | |
590 | ret = gtk_selection_model_select_all (model: selection); |
591 | g_assert_true (ret); |
592 | assert_selection (selection, "1 2 3 4 5" ); |
593 | assert_selection_changes (selection, "0:5" ); |
594 | assert_model (filter, "1 2 3 4 5" ); |
595 | assert_changes (filter, "0-1+5" ); |
596 | |
597 | ret = gtk_selection_model_unselect_all (model: selection); |
598 | g_assert_true (ret); |
599 | assert_selection (selection, "" ); |
600 | assert_selection_changes (selection, "0:5" ); |
601 | assert_model (filter, "" ); |
602 | assert_changes (filter, "0-5" ); |
603 | |
604 | ret = gtk_selection_model_select_range (model: selection, position: 1, n_items: 3, FALSE); |
605 | g_assert_true (ret); |
606 | assert_selection (selection, "2 3 4" ); |
607 | assert_selection_changes (selection, "1:3" ); |
608 | assert_model (filter, "2 3 4" ); |
609 | assert_changes (filter, "0+3" ); |
610 | |
611 | insert (store, position: 2, number: 22); |
612 | assert_model (selection, "1 2 22 3 4 5" ); |
613 | assert_changes (selection, "+2" ); |
614 | assert_selection (selection, "2 3 4" ); |
615 | assert_selection_changes (selection, "" ); |
616 | assert_model (filter, "2 3 4" ); |
617 | assert_changes (filter, "" ); |
618 | |
619 | g_list_store_remove (store, position: 2); |
620 | assert_model (selection, "1 2 3 4 5" ); |
621 | assert_changes (selection, "-2" ); |
622 | assert_selection (selection, "2 3 4" ); |
623 | assert_selection_changes (selection, "" ); |
624 | assert_model (filter, "2 3 4" ); |
625 | assert_changes (filter, "" ); |
626 | |
627 | g_object_unref (object: store); |
628 | g_object_unref (object: selection); |
629 | g_object_unref (object: filter); |
630 | } |
631 | |
632 | static void |
633 | test_set_model (void) |
634 | { |
635 | GtkSelectionModel *selection; |
636 | GListStore *store; |
637 | GListModel *m1, *m2; |
638 | gboolean ret; |
639 | |
640 | store = new_store (start: 1, end: 5, step: 1); |
641 | m1 = G_LIST_MODEL (ptr: store); |
642 | m2 = G_LIST_MODEL (ptr: gtk_slice_list_model_new (g_object_ref (m1), offset: 0, size: 3)); |
643 | selection = new_model (store); |
644 | assert_selection (selection, "" ); |
645 | assert_selection_changes (selection, "" ); |
646 | |
647 | ret = gtk_selection_model_select_range (model: selection, position: 1, n_items: 3, FALSE); |
648 | g_assert_true (ret); |
649 | assert_selection (selection, "2 3 4" ); |
650 | assert_selection_changes (selection, "1:3" ); |
651 | |
652 | /* we retain the selected item across model changes */ |
653 | gtk_multi_selection_set_model (self: GTK_MULTI_SELECTION (ptr: selection), model: m2); |
654 | assert_changes (selection, "0-5+3" ); |
655 | assert_selection (selection, "2 3" ); |
656 | assert_selection_changes (selection, "" ); |
657 | |
658 | gtk_multi_selection_set_model (self: GTK_MULTI_SELECTION (ptr: selection), NULL); |
659 | assert_changes (selection, "0-3" ); |
660 | assert_selection (selection, "" ); |
661 | assert_selection_changes (selection, "" ); |
662 | |
663 | gtk_multi_selection_set_model (self: GTK_MULTI_SELECTION (ptr: selection), model: m2); |
664 | assert_changes (selection, "0+3" ); |
665 | assert_selection (selection, "" ); |
666 | assert_selection_changes (selection, "" ); |
667 | |
668 | ret = gtk_selection_model_select_all (model: selection); |
669 | g_assert_true (ret); |
670 | assert_selection (selection, "1 2 3" ); |
671 | assert_selection_changes (selection, "0:3" ); |
672 | |
673 | /* we retain no selected item across model changes */ |
674 | gtk_multi_selection_set_model (self: GTK_MULTI_SELECTION (ptr: selection), model: m1); |
675 | assert_changes (selection, "0-3+5" ); |
676 | assert_selection (selection, "1 2 3" ); |
677 | assert_selection_changes (selection, "" ); |
678 | |
679 | g_object_unref (object: m2); |
680 | g_object_unref (object: m1); |
681 | g_object_unref (object: selection); |
682 | } |
683 | |
684 | static void |
685 | test_empty (void) |
686 | { |
687 | GtkMultiSelection *selection; |
688 | GListStore *store; |
689 | |
690 | selection = gtk_multi_selection_new (NULL); |
691 | |
692 | g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (selection)), ==, 0); |
693 | g_assert_null (g_list_model_get_item (G_LIST_MODEL (selection), 11)); |
694 | |
695 | store = g_list_store_new (G_TYPE_OBJECT); |
696 | gtk_multi_selection_set_model (self: selection, model: G_LIST_MODEL (ptr: store)); |
697 | g_object_unref (object: store); |
698 | |
699 | g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (selection)), ==, 0); |
700 | g_assert_null (g_list_model_get_item (G_LIST_MODEL (selection), 11)); |
701 | |
702 | g_object_unref (object: selection); |
703 | } |
704 | |
705 | static void |
706 | test_empty_filter (void) |
707 | { |
708 | GtkStringList *stringlist; |
709 | GtkMultiSelection *selection; |
710 | GtkSelectionFilterModel *selection_filter; |
711 | |
712 | stringlist = gtk_string_list_new (NULL); |
713 | gtk_string_list_append (self: stringlist, string: "first item" ); |
714 | |
715 | selection = gtk_multi_selection_new (model: G_LIST_MODEL (ptr: stringlist)); |
716 | selection_filter = gtk_selection_filter_model_new (model: GTK_SELECTION_MODEL (ptr: selection)); |
717 | |
718 | g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (selection_filter)), ==, 0); |
719 | g_assert_null (g_list_model_get_item (G_LIST_MODEL (selection_filter), 11)); |
720 | |
721 | g_object_unref (object: selection_filter); |
722 | g_object_unref (object: selection); |
723 | } |
724 | |
725 | int |
726 | main (int argc, char *argv[]) |
727 | { |
728 | (g_test_init) (argc: &argc, argv: &argv, NULL); |
729 | setlocale (LC_ALL, locale: "C" ); |
730 | g_test_bug_base (uri_pattern: "http://bugzilla.gnome.org/show_bug.cgi?id=%s" ); |
731 | |
732 | number_quark = g_quark_from_static_string (string: "Hell and fire was spawned to be released." ); |
733 | changes_quark = g_quark_from_static_string (string: "What did I see? Can I believe what I saw?" ); |
734 | selection_quark = g_quark_from_static_string (string: "Mana mana, badibidibi" ); |
735 | |
736 | g_test_add_func (testpath: "/multiselection/create" , test_func: test_create); |
737 | g_test_add_func (testpath: "/multiselection/create-empty" , test_func: test_create_empty); |
738 | #if GLIB_CHECK_VERSION (2, 58, 0) /* g_list_store_splice() is broken before 2.58 */ |
739 | g_test_add_func (testpath: "/multiselection/changes" , test_func: test_changes); |
740 | #endif |
741 | g_test_add_func (testpath: "/multiselection/selection" , test_func: test_selection); |
742 | g_test_add_func (testpath: "/multiselection/select-range" , test_func: test_select_range); |
743 | g_test_add_func (testpath: "/multiselection/readd" , test_func: test_readd); |
744 | g_test_add_func (testpath: "/multiselection/set_selection" , test_func: test_set_selection); |
745 | g_test_add_func (testpath: "/multiselection/selection-filter" , test_func: test_selection_filter); |
746 | g_test_add_func (testpath: "/multiselection/set-model" , test_func: test_set_model); |
747 | g_test_add_func (testpath: "/multiselection/empty" , test_func: test_empty); |
748 | g_test_add_func (testpath: "/multiselection/selection-filter/empty" , test_func: test_empty_filter); |
749 | |
750 | return g_test_run (); |
751 | } |
752 | |