1use std::{cell::Cell, cmp::Ordering, rc::Rc};
4
5use glib::{Object, prelude::*, translate::*};
6
7use crate::{ListModel, ListStore, ffi, prelude::*};
8
9impl ListStore {
10 #[doc(alias = "g_list_store_new")]
19 pub fn new<T: IsA<Object>>() -> Self {
20 Self::with_type(T::static_type())
21 }
22
23 #[doc(alias = "g_list_store_new")]
24 pub fn with_type(type_: glib::types::Type) -> Self {
25 unsafe { from_glib_full(ffi::g_list_store_new(type_.into_glib())) }
26 }
27
28 #[doc(alias = "g_list_store_insert_sorted")]
45 pub fn insert_sorted<P: IsA<glib::Object>, F: FnMut(&Object, &Object) -> Ordering>(
46 &self,
47 item: &P,
48 compare_func: F,
49 ) -> u32 {
50 unsafe {
51 let mut func = compare_func;
52 let func_obj: &mut dyn FnMut(&Object, &Object) -> Ordering = &mut func;
53 let func_ptr = &func_obj as *const &mut dyn FnMut(&Object, &Object) -> Ordering
54 as glib::ffi::gpointer;
55
56 ffi::g_list_store_insert_sorted(
57 self.to_glib_none().0,
58 item.as_ref().to_glib_none().0,
59 Some(compare_func_trampoline),
60 func_ptr,
61 )
62 }
63 }
64
65 #[doc(alias = "g_list_store_sort")]
69 pub fn sort<F: FnMut(&Object, &Object) -> Ordering>(&self, compare_func: F) {
70 unsafe {
71 let mut func = compare_func;
72 let func_obj: &mut dyn FnMut(&Object, &Object) -> Ordering = &mut func;
73 let func_ptr = &func_obj as *const &mut dyn FnMut(&Object, &Object) -> Ordering
74 as glib::ffi::gpointer;
75
76 ffi::g_list_store_sort(
77 self.to_glib_none().0,
78 Some(compare_func_trampoline),
79 func_ptr,
80 )
81 }
82 }
83
84 #[doc(alias = "g_list_store_splice")]
104 pub fn splice(&self, position: u32, n_removals: u32, additions: &[impl IsA<glib::Object>]) {
105 let n_additions = additions.len() as u32;
106 unsafe {
107 let additions = additions.as_ptr() as *mut *mut glib::gobject_ffi::GObject;
108
109 ffi::g_list_store_splice(
110 self.to_glib_none().0,
111 position,
112 n_removals,
113 additions,
114 n_additions,
115 );
116 }
117 }
118
119 pub fn extend_from_slice(&self, additions: &[impl IsA<glib::Object>]) {
122 self.splice(self.n_items(), 0, additions)
123 }
124
125 pub fn retain(&self, mut f: impl FnMut(&glib::Object) -> bool) {
135 let mut consec_removed = 0;
136 let mut i = 0;
137 const ADDITIONS: &[glib::Object] = &[]; let changed = Rc::new(Cell::new(false));
140 let changed_clone = changed.clone();
141 let signal_id = self.connect_items_changed(move |_list, _, _, _| changed_clone.set(true));
142
143 let _signal_guard = {
144 struct Guard<'a> {
145 list_store: &'a ListStore,
146 signal_id: Option<glib::SignalHandlerId>,
147 }
148 impl Drop for Guard<'_> {
149 fn drop(&mut self) {
150 self.list_store.disconnect(self.signal_id.take().unwrap());
151 }
152 }
153 Guard {
154 list_store: self,
155 signal_id: Some(signal_id),
156 }
157 };
158
159 while i < self.n_items() {
160 let keep = f(self.item(i).unwrap().as_ref());
161 if changed.get() {
162 panic!("The closure passed to ListStore::retain() must not mutate the list store");
163 }
164 if !keep {
165 consec_removed += 1;
166 } else if consec_removed > 0 {
167 self.splice(i - consec_removed, consec_removed, ADDITIONS);
168 changed.set(false);
169 i -= consec_removed;
170 consec_removed = 0;
171 }
172 i += 1;
173 }
174 if consec_removed > 0 {
175 self.splice(i - consec_removed, consec_removed, ADDITIONS);
176 }
177 }
178
179 #[cfg(feature = "v2_74")]
200 #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
201 #[doc(alias = "g_list_store_find_with_equal_func_full")]
202 #[doc(alias = "g_list_store_find_with_equal_func")]
203 pub fn find_with_equal_func<F: FnMut(&glib::Object) -> bool>(
204 &self,
205 equal_func: F,
206 ) -> Option<u32> {
207 unsafe extern "C" fn equal_func_trampoline(
208 a: glib::ffi::gconstpointer,
209 _b: glib::ffi::gconstpointer,
210 func: glib::ffi::gpointer,
211 ) -> glib::ffi::gboolean {
212 unsafe {
213 let func = func as *mut &mut dyn FnMut(&Object) -> bool;
214
215 let a = from_glib_borrow(a as *mut glib::gobject_ffi::GObject);
216
217 (*func)(&a).into_glib()
218 }
219 }
220
221 let mut func = equal_func;
222 let func_obj: &mut dyn FnMut(&Object) -> bool = &mut func;
223 let func_ptr = &func_obj as *const &mut dyn FnMut(&Object) -> bool as glib::ffi::gpointer;
224 let mut position = std::mem::MaybeUninit::uninit();
225
226 #[cfg(not(feature = "v2_76"))]
229 let result = unsafe {
230 let g_class: *mut glib::gobject_ffi::GTypeClass =
231 glib::gobject_ffi::g_type_class_peek(self.item_type().into_glib()) as *mut _;
232
233 if g_class.is_null() {
236 return None;
237 }
238
239 let item = glib::gobject_ffi::GObject {
240 g_type_instance: glib::gobject_ffi::GTypeInstance { g_class },
241 ref_count: 1,
242 qdata: std::ptr::null_mut(),
243 };
244
245 bool::from_glib(ffi::g_list_store_find_with_equal_func_full(
246 self.to_glib_none().0,
247 mut_override(&item as *const _),
248 Some(equal_func_trampoline),
249 func_ptr,
250 position.as_mut_ptr(),
251 ))
252 .then(|| position.assume_init())
253 };
254
255 #[cfg(feature = "v2_76")]
256 let result = unsafe {
257 bool::from_glib(ffi::g_list_store_find_with_equal_func_full(
258 self.to_glib_none().0,
259 std::ptr::null_mut(),
260 Some(equal_func_trampoline),
261 func_ptr,
262 position.as_mut_ptr(),
263 ))
264 .then(|| position.assume_init())
265 };
266
267 result
268 }
269}
270
271impl<P: IsA<glib::Object>> std::iter::FromIterator<P> for ListStore {
272 fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
273 let store = Self::new::<P>();
274 for item in iter.into_iter() {
275 store.append(&item)
276 }
277 store
278 }
279}
280
281impl<'a> std::iter::IntoIterator for &'a ListStore {
282 type Item = <&'a ListModel as IntoIterator>::Item;
283 type IntoIter = <&'a ListModel as IntoIterator>::IntoIter;
284
285 fn into_iter(self) -> Self::IntoIter {
286 self.upcast_ref::<ListModel>().into_iter()
287 }
288}
289
290unsafe extern "C" fn compare_func_trampoline(
291 a: glib::ffi::gconstpointer,
292 b: glib::ffi::gconstpointer,
293 func: glib::ffi::gpointer,
294) -> i32 {
295 unsafe {
296 let func = func as *mut &mut dyn FnMut(&Object, &Object) -> Ordering;
297
298 let a = from_glib_borrow(a as *mut glib::gobject_ffi::GObject);
299 let b = from_glib_borrow(b as *mut glib::gobject_ffi::GObject);
300
301 (*func)(&a, &b).into_glib()
302 }
303}
304
305impl<A: AsRef<glib::Object>> std::iter::Extend<A> for ListStore {
306 fn extend<T: IntoIterator<Item = A>>(&mut self, iter: T) {
307 let additions = iter
308 .into_iter()
309 .map(|o| o.as_ref().clone())
310 .collect::<Vec<_>>();
311 self.splice(self.n_items(), 0, &additions)
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use crate::{ListStore, prelude::*};
318
319 #[test]
320 fn splice() {
321 let item0 = ListStore::new::<ListStore>();
322 let item1 = ListStore::new::<ListStore>();
323 let list = ListStore::new::<ListStore>();
324 list.splice(0, 0, &[item0.clone(), item1.clone()]);
325 assert_eq!(list.item(0), Some(item0.upcast()));
326 assert_eq!(list.item(1), Some(item1.upcast()));
327 }
328
329 #[test]
330 fn extend() {
331 let item0 = ListStore::new::<ListStore>();
332 let item1 = ListStore::new::<ListStore>();
333 let mut list = ListStore::new::<ListStore>();
334 list.extend([&item0, &item1]);
335 assert_eq!(list.item(0).as_ref(), Some(item0.upcast_ref()));
336 assert_eq!(list.item(1).as_ref(), Some(item1.upcast_ref()));
337 list.extend([item0.clone(), item1.clone()]);
338 assert_eq!(list.item(2).as_ref(), Some(item0.upcast_ref()));
339 assert_eq!(list.item(3).as_ref(), Some(item1.upcast_ref()));
340
341 let list_from_slice = ListStore::new::<ListStore>();
342 list_from_slice.extend_from_slice(&[item0, item1.clone()]);
343 assert_eq!(list_from_slice.item(1).as_ref(), Some(item1.upcast_ref()));
344 }
345
346 #[test]
347 fn from_iterator() {
348 let item0 = ListStore::new::<ListStore>();
349 let item1 = ListStore::new::<ListStore>();
350 let v = vec![item0.clone(), item1.clone()];
351 let list = ListStore::from_iter(v);
352 assert_eq!(list.item(0).as_ref(), Some(item0.upcast_ref()));
353 assert_eq!(list.item(1).as_ref(), Some(item1.upcast_ref()));
354 assert_eq!(list.item(2).as_ref(), None);
355 }
356
357 #[cfg(feature = "v2_74")]
358 #[test]
359 fn find() {
360 let item0 = ListStore::new::<ListStore>();
361 let item1 = ListStore::new::<ListStore>();
362 let list = ListStore::new::<ListStore>();
363 list.append(&item0);
364 list.append(&item1);
365
366 let res = list.find_with_equal_func(|item| item == &item1);
367 assert_eq!(res, Some(1));
368 }
369
370 #[test]
371 fn retain() {
372 let list = {
373 let list = ListStore::new::<ListStore>();
374 for _ in 0..10 {
375 list.append(&ListStore::new::<ListStore>());
376 }
377 list
378 };
379
380 use std::cell::Cell;
381 use std::rc::Rc;
382
383 let signal_count = Rc::new(Cell::new(0));
384 let signal_count_clone = signal_count.clone();
385 list.connect_items_changed(move |_, _, _, _| {
386 signal_count_clone.set(signal_count_clone.get() + 1);
387 });
388
389 let to_keep = [
390 list.item(1).unwrap(),
392 list.item(3).unwrap(),
394 list.item(7).unwrap(),
398 ];
401 list.retain(|item| to_keep.contains(item));
402
403 assert_eq!(list.n_items(), 3);
405 assert_eq!(list.item(0).as_ref(), Some(&to_keep[0]));
406 assert_eq!(list.item(1).as_ref(), Some(&to_keep[1]));
407 assert_eq!(list.item(2).as_ref(), Some(&to_keep[2]));
408
409 assert_eq!(signal_count.get(), 4);
410 }
411}