1use std::{cell::Cell, cmp::Ordering, rc::Rc};
4
5use glib::{prelude::*, translate::*, Object};
6
7use crate::{ffi, prelude::*, ListModel, ListStore};
8
9impl ListStore {
10 #[doc(alias = "g_list_store_new")]
11 pub fn new<T: IsA<Object>>() -> Self {
12 Self::with_type(T::static_type())
13 }
14
15 #[doc(alias = "g_list_store_new")]
16 pub fn with_type(type_: glib::types::Type) -> Self {
17 unsafe { from_glib_full(ffi::g_list_store_new(type_.into_glib())) }
18 }
19
20 #[doc(alias = "g_list_store_insert_sorted")]
21 pub fn insert_sorted<P: IsA<glib::Object>, F: FnMut(&Object, &Object) -> Ordering>(
22 &self,
23 item: &P,
24 compare_func: F,
25 ) -> u32 {
26 unsafe {
27 let mut func = compare_func;
28 let func_obj: &mut dyn FnMut(&Object, &Object) -> Ordering = &mut func;
29 let func_ptr = &func_obj as *const &mut dyn FnMut(&Object, &Object) -> Ordering
30 as glib::ffi::gpointer;
31
32 ffi::g_list_store_insert_sorted(
33 self.to_glib_none().0,
34 item.as_ref().to_glib_none().0,
35 Some(compare_func_trampoline),
36 func_ptr,
37 )
38 }
39 }
40
41 #[doc(alias = "g_list_store_sort")]
42 pub fn sort<F: FnMut(&Object, &Object) -> Ordering>(&self, compare_func: F) {
43 unsafe {
44 let mut func = compare_func;
45 let func_obj: &mut dyn FnMut(&Object, &Object) -> Ordering = &mut func;
46 let func_ptr = &func_obj as *const &mut dyn FnMut(&Object, &Object) -> Ordering
47 as glib::ffi::gpointer;
48
49 ffi::g_list_store_sort(
50 self.to_glib_none().0,
51 Some(compare_func_trampoline),
52 func_ptr,
53 )
54 }
55 }
56
57 #[doc(alias = "g_list_store_splice")]
58 pub fn splice(&self, position: u32, n_removals: u32, additions: &[impl IsA<glib::Object>]) {
59 let n_additions = additions.len() as u32;
60 unsafe {
61 let additions = additions.as_ptr() as *mut *mut glib::gobject_ffi::GObject;
62
63 ffi::g_list_store_splice(
64 self.to_glib_none().0,
65 position,
66 n_removals,
67 additions,
68 n_additions,
69 );
70 }
71 }
72
73 pub fn extend_from_slice(&self, additions: &[impl IsA<glib::Object>]) {
76 self.splice(self.n_items(), 0, additions)
77 }
78
79 pub fn retain(&self, mut f: impl FnMut(&glib::Object) -> bool) {
89 let mut consec_removed = 0;
90 let mut i = 0;
91 const ADDITIONS: &[glib::Object] = &[]; let changed = Rc::new(Cell::new(false));
94 let changed_clone = changed.clone();
95 let signal_id = self.connect_items_changed(move |_list, _, _, _| changed_clone.set(true));
96
97 let _signal_guard = {
98 struct Guard<'a> {
99 list_store: &'a ListStore,
100 signal_id: Option<glib::SignalHandlerId>,
101 }
102 impl Drop for Guard<'_> {
103 fn drop(&mut self) {
104 self.list_store.disconnect(self.signal_id.take().unwrap());
105 }
106 }
107 Guard {
108 list_store: self,
109 signal_id: Some(signal_id),
110 }
111 };
112
113 while i < self.n_items() {
114 let keep = f(self.item(i).unwrap().as_ref());
115 if changed.get() {
116 panic!("The closure passed to ListStore::retain() must not mutate the list store");
117 }
118 if !keep {
119 consec_removed += 1;
120 } else if consec_removed > 0 {
121 self.splice(i - consec_removed, consec_removed, ADDITIONS);
122 changed.set(false);
123 i -= consec_removed;
124 consec_removed = 0;
125 }
126 i += 1;
127 }
128 if consec_removed > 0 {
129 self.splice(i - consec_removed, consec_removed, ADDITIONS);
130 }
131 }
132
133 #[cfg(feature = "v2_74")]
134 #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
135 #[doc(alias = "g_list_store_find_with_equal_func_full")]
136 #[doc(alias = "g_list_store_find_with_equal_func")]
137 pub fn find_with_equal_func<F: FnMut(&glib::Object) -> bool>(
138 &self,
139 equal_func: F,
140 ) -> Option<u32> {
141 unsafe extern "C" fn equal_func_trampoline(
142 a: glib::ffi::gconstpointer,
143 _b: glib::ffi::gconstpointer,
144 func: glib::ffi::gpointer,
145 ) -> glib::ffi::gboolean {
146 let func = func as *mut &mut (dyn FnMut(&Object) -> bool);
147
148 let a = from_glib_borrow(a as *mut glib::gobject_ffi::GObject);
149
150 (*func)(&a).into_glib()
151 }
152
153 let mut func = equal_func;
154 let func_obj: &mut (dyn FnMut(&Object) -> bool) = &mut func;
155 let func_ptr = &func_obj as *const &mut (dyn FnMut(&Object) -> bool) as glib::ffi::gpointer;
156 let mut position = std::mem::MaybeUninit::uninit();
157
158 #[cfg(not(feature = "v2_76"))]
161 let result = unsafe {
162 let g_class: *mut glib::gobject_ffi::GTypeClass =
163 glib::gobject_ffi::g_type_class_peek(self.item_type().into_glib()) as *mut _;
164
165 if g_class.is_null() {
168 return None;
169 }
170
171 let item = glib::gobject_ffi::GObject {
172 g_type_instance: glib::gobject_ffi::GTypeInstance { g_class },
173 ref_count: 1,
174 qdata: std::ptr::null_mut(),
175 };
176
177 bool::from_glib(ffi::g_list_store_find_with_equal_func_full(
178 self.to_glib_none().0,
179 mut_override(&item as *const _),
180 Some(equal_func_trampoline),
181 func_ptr,
182 position.as_mut_ptr(),
183 ))
184 .then(|| position.assume_init())
185 };
186
187 #[cfg(feature = "v2_76")]
188 let result = unsafe {
189 bool::from_glib(ffi::g_list_store_find_with_equal_func_full(
190 self.to_glib_none().0,
191 std::ptr::null_mut(),
192 Some(equal_func_trampoline),
193 func_ptr,
194 position.as_mut_ptr(),
195 ))
196 .then(|| position.assume_init())
197 };
198
199 result
200 }
201}
202
203impl<P: IsA<glib::Object>> std::iter::FromIterator<P> for ListStore {
204 fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
205 let store = Self::new::<P>();
206 for item in iter.into_iter() {
207 store.append(&item)
208 }
209 store
210 }
211}
212
213impl<'a> std::iter::IntoIterator for &'a ListStore {
214 type Item = <&'a ListModel as IntoIterator>::Item;
215 type IntoIter = <&'a ListModel as IntoIterator>::IntoIter;
216
217 fn into_iter(self) -> Self::IntoIter {
218 self.upcast_ref::<ListModel>().into_iter()
219 }
220}
221
222unsafe extern "C" fn compare_func_trampoline(
223 a: glib::ffi::gconstpointer,
224 b: glib::ffi::gconstpointer,
225 func: glib::ffi::gpointer,
226) -> i32 {
227 let func = func as *mut &mut dyn FnMut(&Object, &Object) -> Ordering;
228
229 let a = from_glib_borrow(a as *mut glib::gobject_ffi::GObject);
230 let b = from_glib_borrow(b as *mut glib::gobject_ffi::GObject);
231
232 (*func)(&a, &b).into_glib()
233}
234
235impl<A: AsRef<glib::Object>> std::iter::Extend<A> for ListStore {
236 fn extend<T: IntoIterator<Item = A>>(&mut self, iter: T) {
237 let additions = iter
238 .into_iter()
239 .map(|o| o.as_ref().clone())
240 .collect::<Vec<_>>();
241 self.splice(self.n_items(), 0, &additions)
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use crate::{prelude::*, ListStore};
248
249 #[test]
250 fn splice() {
251 let item0 = ListStore::new::<ListStore>();
252 let item1 = ListStore::new::<ListStore>();
253 let list = ListStore::new::<ListStore>();
254 list.splice(0, 0, &[item0.clone(), item1.clone()]);
255 assert_eq!(list.item(0), Some(item0.upcast()));
256 assert_eq!(list.item(1), Some(item1.upcast()));
257 }
258
259 #[test]
260 fn extend() {
261 let item0 = ListStore::new::<ListStore>();
262 let item1 = ListStore::new::<ListStore>();
263 let mut list = ListStore::new::<ListStore>();
264 list.extend([&item0, &item1]);
265 assert_eq!(list.item(0).as_ref(), Some(item0.upcast_ref()));
266 assert_eq!(list.item(1).as_ref(), Some(item1.upcast_ref()));
267 list.extend([item0.clone(), item1.clone()]);
268 assert_eq!(list.item(2).as_ref(), Some(item0.upcast_ref()));
269 assert_eq!(list.item(3).as_ref(), Some(item1.upcast_ref()));
270
271 let list_from_slice = ListStore::new::<ListStore>();
272 list_from_slice.extend_from_slice(&[item0, item1.clone()]);
273 assert_eq!(list_from_slice.item(1).as_ref(), Some(item1.upcast_ref()));
274 }
275
276 #[test]
277 fn from_iterator() {
278 let item0 = ListStore::new::<ListStore>();
279 let item1 = ListStore::new::<ListStore>();
280 let v = vec![item0.clone(), item1.clone()];
281 let list = ListStore::from_iter(v);
282 assert_eq!(list.item(0).as_ref(), Some(item0.upcast_ref()));
283 assert_eq!(list.item(1).as_ref(), Some(item1.upcast_ref()));
284 assert_eq!(list.item(2).as_ref(), None);
285 }
286
287 #[cfg(feature = "v2_74")]
288 #[test]
289 fn find() {
290 let item0 = ListStore::new::<ListStore>();
291 let item1 = ListStore::new::<ListStore>();
292 let list = ListStore::new::<ListStore>();
293 list.append(&item0);
294 list.append(&item1);
295
296 let res = list.find_with_equal_func(|item| item == &item1);
297 assert_eq!(res, Some(1));
298 }
299
300 #[test]
301 fn retain() {
302 let list = {
303 let list = ListStore::new::<ListStore>();
304 for _ in 0..10 {
305 list.append(&ListStore::new::<ListStore>());
306 }
307 list
308 };
309
310 use std::cell::Cell;
311 use std::rc::Rc;
312
313 let signal_count = Rc::new(Cell::new(0));
314 let signal_count_clone = signal_count.clone();
315 list.connect_items_changed(move |_, _, _, _| {
316 signal_count_clone.set(signal_count_clone.get() + 1);
317 });
318
319 let to_keep = [
320 list.item(1).unwrap(),
322 list.item(3).unwrap(),
324 list.item(7).unwrap(),
328 ];
331 list.retain(|item| to_keep.contains(item));
332
333 assert_eq!(list.n_items(), 3);
335 assert_eq!(list.item(0).as_ref(), Some(&to_keep[0]));
336 assert_eq!(list.item(1).as_ref(), Some(&to_keep[1]));
337 assert_eq!(list.item(2).as_ref(), Some(&to_keep[2]));
338
339 assert_eq!(signal_count.get(), 4);
340 }
341}