use std::{cell::Cell, cmp::Ordering, rc::Rc};
use glib::{prelude::*, translate::*, Object};
use crate::{ffi, prelude::*, ListModel, ListStore};
impl ListStore {
#[doc(alias = "g_list_store_new")]
pub fn new<T: IsA<Object>>() -> Self {
Self::with_type(T::static_type())
}
#[doc(alias = "g_list_store_new")]
pub fn with_type(type_: glib::types::Type) -> Self {
unsafe { from_glib_full(ffi::g_list_store_new(type_.into_glib())) }
}
#[doc(alias = "g_list_store_insert_sorted")]
pub fn insert_sorted<P: IsA<glib::Object>, F: FnMut(&Object, &Object) -> Ordering>(
&self,
item: &P,
compare_func: F,
) -> u32 {
unsafe {
let mut func = compare_func;
let func_obj: &mut (dyn FnMut(&Object, &Object) -> Ordering) = &mut func;
let func_ptr = &func_obj as *const &mut (dyn FnMut(&Object, &Object) -> Ordering)
as glib::ffi::gpointer;
ffi::g_list_store_insert_sorted(
self.to_glib_none().0,
item.as_ref().to_glib_none().0,
Some(compare_func_trampoline),
func_ptr,
)
}
}
#[doc(alias = "g_list_store_sort")]
pub fn sort<F: FnMut(&Object, &Object) -> Ordering>(&self, compare_func: F) {
unsafe {
let mut func = compare_func;
let func_obj: &mut (dyn FnMut(&Object, &Object) -> Ordering) = &mut func;
let func_ptr = &func_obj as *const &mut (dyn FnMut(&Object, &Object) -> Ordering)
as glib::ffi::gpointer;
ffi::g_list_store_sort(
self.to_glib_none().0,
Some(compare_func_trampoline),
func_ptr,
)
}
}
#[doc(alias = "g_list_store_splice")]
pub fn splice(&self, position: u32, n_removals: u32, additions: &[impl IsA<glib::Object>]) {
let n_additions = additions.len() as u32;
unsafe {
let additions = additions.as_ptr() as *mut *mut glib::gobject_ffi::GObject;
ffi::g_list_store_splice(
self.to_glib_none().0,
position,
n_removals,
additions,
n_additions,
);
}
}
pub fn extend_from_slice(&self, additions: &[impl IsA<glib::Object>]) {
self.splice(self.n_items(), 0, additions)
}
pub fn retain(&self, mut f: impl FnMut(&glib::Object) -> bool) {
let mut consec_removed = 0;
let mut i = 0;
const ADDITIONS: &[glib::Object] = &[]; let changed = Rc::new(Cell::new(false));
let changed_clone = changed.clone();
let signal_id = self.connect_items_changed(move |_list, _, _, _| changed_clone.set(true));
let _signal_guard = {
struct Guard<'a> {
list_store: &'a ListStore,
signal_id: Option<glib::SignalHandlerId>,
}
impl Drop for Guard<'_> {
fn drop(&mut self) {
self.list_store.disconnect(self.signal_id.take().unwrap());
}
}
Guard {
list_store: self,
signal_id: Some(signal_id),
}
};
while i < self.n_items() {
let keep = f(self.item(i).unwrap().as_ref());
if changed.get() {
panic!("The closure passed to ListStore::retain() must not mutate the list store");
}
if !keep {
consec_removed += 1;
} else if consec_removed > 0 {
self.splice(i - consec_removed, consec_removed, ADDITIONS);
changed.set(false);
i -= consec_removed;
consec_removed = 0;
}
i += 1;
}
if consec_removed > 0 {
self.splice(i - consec_removed, consec_removed, ADDITIONS);
}
}
#[cfg(feature = "v2_74")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
#[doc(alias = "g_list_store_find_with_equal_func_full")]
#[doc(alias = "g_list_store_find_with_equal_func")]
pub fn find_with_equal_func<F: FnMut(&glib::Object) -> bool>(
&self,
equal_func: F,
) -> Option<u32> {
unsafe extern "C" fn equal_func_trampoline(
a: glib::ffi::gconstpointer,
_b: glib::ffi::gconstpointer,
func: glib::ffi::gpointer,
) -> glib::ffi::gboolean {
let func = func as *mut &mut (dyn FnMut(&Object) -> bool);
let a = from_glib_borrow(a as *mut glib::gobject_ffi::GObject);
(*func)(&a).into_glib()
}
unsafe {
let item = glib::gobject_ffi::GObject {
g_type_instance: glib::gobject_ffi::GTypeInstance {
g_class: glib::gobject_ffi::g_type_class_peek(self.item_type().into_glib())
as *mut _,
},
ref_count: 1,
qdata: std::ptr::null_mut(),
};
let mut func = equal_func;
let func_obj: &mut (dyn FnMut(&Object) -> bool) = &mut func;
let func_ptr =
&func_obj as *const &mut (dyn FnMut(&Object) -> bool) as glib::ffi::gpointer;
let mut position = std::mem::MaybeUninit::uninit();
let found = bool::from_glib(ffi::g_list_store_find_with_equal_func_full(
self.to_glib_none().0,
mut_override(&item as *const _),
Some(equal_func_trampoline),
func_ptr,
position.as_mut_ptr(),
));
found.then(|| position.assume_init())
}
}
}
impl<P: IsA<glib::Object>> std::iter::FromIterator<P> for ListStore {
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
let store = Self::new::<P>();
for item in iter.into_iter() {
store.append(&item)
}
store
}
}
impl<'a> std::iter::IntoIterator for &'a ListStore {
type Item = <&'a ListModel as IntoIterator>::Item;
type IntoIter = <&'a ListModel as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.upcast_ref::<ListModel>().into_iter()
}
}
unsafe extern "C" fn compare_func_trampoline(
a: glib::ffi::gconstpointer,
b: glib::ffi::gconstpointer,
func: glib::ffi::gpointer,
) -> i32 {
let func = func as *mut &mut (dyn FnMut(&Object, &Object) -> Ordering);
let a = from_glib_borrow(a as *mut glib::gobject_ffi::GObject);
let b = from_glib_borrow(b as *mut glib::gobject_ffi::GObject);
(*func)(&a, &b).into_glib()
}
impl<A: AsRef<glib::Object>> std::iter::Extend<A> for ListStore {
fn extend<T: IntoIterator<Item = A>>(&mut self, iter: T) {
let additions = iter
.into_iter()
.map(|o| o.as_ref().clone())
.collect::<Vec<_>>();
self.splice(self.n_items(), 0, &additions)
}
}
#[cfg(test)]
mod tests {
use crate::{prelude::*, ListStore};
#[test]
fn splice() {
let item0 = ListStore::new::<ListStore>();
let item1 = ListStore::new::<ListStore>();
let list = ListStore::new::<ListStore>();
list.splice(0, 0, &[item0.clone(), item1.clone()]);
assert_eq!(list.item(0), Some(item0.upcast()));
assert_eq!(list.item(1), Some(item1.upcast()));
}
#[test]
fn extend() {
let item0 = ListStore::new::<ListStore>();
let item1 = ListStore::new::<ListStore>();
let mut list = ListStore::new::<ListStore>();
list.extend([&item0, &item1]);
assert_eq!(list.item(0).as_ref(), Some(item0.upcast_ref()));
assert_eq!(list.item(1).as_ref(), Some(item1.upcast_ref()));
list.extend([item0.clone(), item1.clone()]);
assert_eq!(list.item(2).as_ref(), Some(item0.upcast_ref()));
assert_eq!(list.item(3).as_ref(), Some(item1.upcast_ref()));
let list_from_slice = ListStore::new::<ListStore>();
list_from_slice.extend_from_slice(&[item0, item1.clone()]);
assert_eq!(list_from_slice.item(1).as_ref(), Some(item1.upcast_ref()));
}
#[test]
fn from_iterator() {
let item0 = ListStore::new::<ListStore>();
let item1 = ListStore::new::<ListStore>();
let v = vec![item0.clone(), item1.clone()];
let list = ListStore::from_iter(v);
assert_eq!(list.item(0).as_ref(), Some(item0.upcast_ref()));
assert_eq!(list.item(1).as_ref(), Some(item1.upcast_ref()));
assert_eq!(list.item(2).as_ref(), None);
}
#[cfg(feature = "v2_74")]
#[test]
fn find() {
let item0 = ListStore::new::<ListStore>();
let item1 = ListStore::new::<ListStore>();
let list = ListStore::new::<ListStore>();
list.append(&item0);
list.append(&item1);
let res = list.find_with_equal_func(|item| item == &item1);
assert_eq!(res, Some(1));
}
#[test]
fn retain() {
let list = {
let list = ListStore::new::<ListStore>();
for _ in 0..10 {
list.append(&ListStore::new::<ListStore>());
}
list
};
use std::cell::Cell;
use std::rc::Rc;
let signal_count = Rc::new(Cell::new(0));
let signal_count_clone = signal_count.clone();
list.connect_items_changed(move |_, _, _, _| {
signal_count_clone.set(signal_count_clone.get() + 1);
});
let to_keep = [
list.item(1).unwrap(),
list.item(3).unwrap(),
list.item(7).unwrap(),
];
list.retain(|item| to_keep.contains(item));
assert_eq!(list.n_items(), 3);
assert_eq!(list.item(0).as_ref(), Some(&to_keep[0]));
assert_eq!(list.item(1).as_ref(), Some(&to_keep[1]));
assert_eq!(list.item(2).as_ref(), Some(&to_keep[2]));
assert_eq!(signal_count.get(), 4);
}
}