Skip to main content

gtk/
tree_sortable.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{SortType, ffi};
4use glib::object::IsA;
5use glib::translate::*;
6use std::cmp::Ordering;
7use std::fmt;
8use std::mem;
9
10use crate::{TreeIter, TreeModel, TreeSortable};
11use ffi::{GtkTreeIter, GtkTreeModel};
12use glib::ffi::gpointer;
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
15pub enum SortColumn {
16    #[doc(alias = "GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID")]
17    Default,
18    Index(u32),
19}
20
21#[doc(hidden)]
22impl IntoGlib for SortColumn {
23    type GlibType = i32;
24
25    #[inline]
26    fn into_glib(self) -> i32 {
27        match self {
28            Self::Default => ffi::GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
29            Self::Index(x) => {
30                assert!(x <= i32::MAX as u32, "column index is too big");
31                x as i32
32            }
33        }
34    }
35}
36
37#[doc(hidden)]
38impl FromGlib<i32> for SortColumn {
39    #[inline]
40    unsafe fn from_glib(val: i32) -> Self {
41        skip_assert_initialized!();
42        match val {
43            ffi::GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID => Self::Default,
44            x => {
45                assert!(x >= 0, "invalid column index");
46                Self::Index(x as u32)
47            }
48        }
49    }
50}
51
52impl fmt::Display for SortColumn {
53    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        write!(
55            f,
56            "SortColumn::{}",
57            match *self {
58                Self::Default => "Default",
59                Self::Index(_) => "Index",
60            }
61        )
62    }
63}
64
65mod sealed {
66    pub trait Sealed {}
67    impl<T: glib::object::IsA<crate::TreeSortable>> Sealed for T {}
68}
69
70pub trait TreeSortableExtManual: IsA<TreeSortable> + sealed::Sealed + 'static {
71    /// Sets the default comparison function used when sorting to be `sort_func`.
72    /// If the current sort column id of `self` is
73    /// `GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID`, then the model will sort using
74    /// this function.
75    ///
76    /// If `sort_func` is [`None`], then there will be no default comparison function.
77    /// This means that once the model has been sorted, it can’t go back to the
78    /// default state. In this case, when the current sort column id of `self`
79    /// is `GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID`, the model will be unsorted.
80    /// ## `sort_func`
81    /// The comparison function
82    #[doc(alias = "gtk_tree_sortable_set_default_sort_func")]
83    fn set_default_sort_func<F>(&self, sort_func: F)
84    where
85        F: Fn(&TreeModel, &TreeIter, &TreeIter) -> Ordering + 'static,
86    {
87        unsafe extern "C" fn trampoline<F: Fn(&TreeModel, &TreeIter, &TreeIter) -> Ordering>(
88            this: *mut GtkTreeModel,
89            iter: *mut GtkTreeIter,
90            iter2: *mut GtkTreeIter,
91            f: gpointer,
92        ) -> i32 {
93            unsafe {
94                let f: &F = &*(f as *const F);
95                f(
96                    &TreeModel::from_glib_borrow(this),
97                    &from_glib_borrow(iter),
98                    &from_glib_borrow(iter2),
99                )
100                .into_glib()
101            }
102        }
103        unsafe extern "C" fn destroy_closure<
104            F: Fn(&TreeModel, &TreeIter, &TreeIter) -> Ordering,
105        >(
106            ptr: gpointer,
107        ) {
108            unsafe {
109                let _ = Box::<F>::from_raw(ptr as *mut _);
110            }
111        }
112        unsafe {
113            ffi::gtk_tree_sortable_set_default_sort_func(
114                self.as_ref().to_glib_none().0,
115                Some(trampoline::<F>),
116                into_raw(sort_func),
117                Some(destroy_closure::<F>),
118            )
119        }
120    }
121    /// Sets the comparison function used when sorting to be `sort_func`. If the
122    /// current sort column id of `self` is the same as `sort_column_id`, then
123    /// the model will sort using this function.
124    /// ## `sort_column_id`
125    /// the sort column id to set the function for
126    /// ## `sort_func`
127    /// The comparison function
128    #[doc(alias = "gtk_tree_sortable_set_sort_func")]
129    fn set_sort_func<F>(&self, sort_column_id: SortColumn, sort_func: F)
130    where
131        F: Fn(&TreeModel, &TreeIter, &TreeIter) -> Ordering + 'static,
132    {
133        unsafe extern "C" fn trampoline<F: Fn(&TreeModel, &TreeIter, &TreeIter) -> Ordering>(
134            this: *mut GtkTreeModel,
135            iter: *mut GtkTreeIter,
136            iter2: *mut GtkTreeIter,
137            f: gpointer,
138        ) -> i32 {
139            unsafe {
140                let f: &F = &*(f as *const F);
141                f(
142                    &TreeModel::from_glib_borrow(this),
143                    &from_glib_borrow(iter),
144                    &from_glib_borrow(iter2),
145                )
146                .into_glib()
147            }
148        }
149        unsafe extern "C" fn destroy_closure<
150            F: Fn(&TreeModel, &TreeIter, &TreeIter) -> Ordering,
151        >(
152            ptr: gpointer,
153        ) {
154            unsafe {
155                let _ = Box::<F>::from_raw(ptr as *mut _);
156            }
157        }
158        unsafe {
159            ffi::gtk_tree_sortable_set_sort_func(
160                self.as_ref().to_glib_none().0,
161                sort_column_id.into_glib(),
162                Some(trampoline::<F>),
163                into_raw(sort_func),
164                Some(destroy_closure::<F>),
165            )
166        }
167    }
168    /// Fills in `sort_column_id` and `order` with the current sort column and the
169    /// order. It returns [`true`] unless the `sort_column_id` is
170    /// `GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID` or
171    /// `GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID`.
172    ///
173    /// # Returns
174    ///
175    /// [`true`] if the sort column is not one of the special sort
176    ///  column ids.
177    ///
178    /// ## `sort_column_id`
179    /// The sort column id to be filled in
180    ///
181    /// ## `order`
182    /// The [`SortType`][crate::SortType] to be filled in
183    #[doc(alias = "get_sort_column_id")]
184    #[doc(alias = "gtk_tree_sortable_get_sort_column_id")]
185    fn sort_column_id(&self) -> Option<(SortColumn, SortType)> {
186        unsafe {
187            let mut sort_column_id = mem::MaybeUninit::uninit();
188            let mut order = mem::MaybeUninit::uninit();
189            ffi::gtk_tree_sortable_get_sort_column_id(
190                self.as_ref().to_glib_none().0,
191                sort_column_id.as_mut_ptr(),
192                order.as_mut_ptr(),
193            );
194            let sort_column_id = sort_column_id.assume_init();
195            if sort_column_id != ffi::GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID {
196                Some((from_glib(sort_column_id), from_glib(order.assume_init())))
197            } else {
198                None
199            }
200        }
201    }
202    /// Sets the current sort column to be `sort_column_id`. The `self` will
203    /// resort itself to reflect this change, after emitting a
204    /// [`sort-column-changed`][struct@crate::TreeSortable#sort-column-changed] signal. `sort_column_id` may either be
205    /// a regular column id, or one of the following special values:
206    ///
207    /// - `GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID`: the default sort function
208    ///  will be used, if it is set
209    ///
210    /// - `GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID`: no sorting will occur
211    /// ## `sort_column_id`
212    /// the sort column id to set
213    /// ## `order`
214    /// The sort order of the column
215    #[doc(alias = "gtk_tree_sortable_set_sort_column_id")]
216    fn set_sort_column_id(&self, sort_column_id: SortColumn, order: SortType) {
217        unsafe {
218            ffi::gtk_tree_sortable_set_sort_column_id(
219                self.as_ref().to_glib_none().0,
220                sort_column_id.into_glib(),
221                order.into_glib(),
222            );
223        }
224    }
225    fn set_unsorted(&self) {
226        unsafe {
227            ffi::gtk_tree_sortable_set_sort_column_id(
228                self.as_ref().to_glib_none().0,
229                ffi::GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
230                SortType::Ascending.into_glib(),
231            );
232        }
233    }
234}
235
236fn into_raw<F, T>(func: F) -> gpointer
237where
238    F: Fn(&T, &TreeIter, &TreeIter) -> Ordering + 'static,
239{
240    skip_assert_initialized!();
241    let func: Box<F> = Box::new(func);
242    Box::into_raw(func) as gpointer
243}
244
245impl<O: IsA<TreeSortable>> TreeSortableExtManual for O {}